<commit_before>package cli

import (
	"bytes"
	"fmt"
	"os"
	"reflect"
	"strings"

	"github.com/labstack/gommon/color"
)

// Run runs a single command app
func Run(argv interface{}, fn CommandFunc, descs ...string) {
	RunWithArgs(argv, os.Args, fn, descs...)
}

// RunWithArgs is similar to Run, but with args instead of os.Args
func RunWithArgs(argv interface{}, args []string, fn CommandFunc, descs ...string) {
	desc := ""
	if len(descs) > 0 {
		desc = strings.Join(descs, "\n")
	}
	err := (&Command{
		Name:        args[0],
		Desc:        desc,
		Argv:        func() interface{} { return argv },
		CanSubRoute: true,
		Fn:          fn,
	}).Run(args[1:])
	if err != nil {
		fmt.Println(err)
	}
}

// Root registers forest for root and returns root
func Root(root *Command, forest ...*CommandTree) *Command {
	root.RegisterTree(forest...)
	return root
}

// Tree creates a CommandTree
func Tree(cmd *Command, forest ...*CommandTree) *CommandTree {
	return &CommandTree{
		command: cmd,
		forest:  forest,
	}
}

// Parse parses args to object argv
func Parse(args []string, argv interface{}) error {
	clr := color.Color{}
	fset := parseArgv(args, argv, clr)
	return fset.err
}

func parseArgv(args []string, argv interface{}, clr color.Color) *flagSet {
	return parseArgvList(args, []interface{}{argv}, clr)
}

func parseArgvList(args []string, argvList []interface{}, clr color.Color) *flagSet {
	flagSet := newFlagSet()
	for _, argv := range argvList {
		if argv == nil {
			continue
		}
		var (
			typ = reflect.TypeOf(argv)
			val = reflect.ValueOf(argv)
		)
		switch typ.Kind() {
		case reflect.Ptr:
			if reflect.Indirect(val).Type().Kind() != reflect.Struct {
				flagSet.err = errNotAPointerToStruct
				return flagSet
			}
			initFlagSet(typ, val, flagSet, clr, false)
			if flagSet.err != nil {
				return flagSet
			}
		default:
			flagSet.err = errNotAPointer
			return flagSet
		}
	}
	parseArgsToFlagSet(args, flagSet, clr)
	return flagSet
}

func usage(argvList []interface{}, clr color.Color, style UsageStyle) string {
	flagSet := newFlagSet()
	buf := bytes.NewBufferString("")
	for i := len(argvList) - 1; i >= 0; i-- {
		var (
			v   = argvList[i]
			typ = reflect.TypeOf(v)
			val = reflect.ValueOf(v)
		)
		if typ.Kind() == reflect.Ptr &&
			reflect.Indirect(val).Type().Kind() == reflect.Struct {
			// initialize flagSet
			initFlagSet(typ, val, flagSet, clr, true)
			if flagSet.err != nil {
				return ""
			}
		}
	}
	buf.WriteString(flagSlice(flagSet.flagSlice).StringWithStyle(clr, style))
	return buf.String()
}

func initFlagSet(typ reflect.Type, val reflect.Value, flagSet *flagSet, clr color.Color, dontSetValue bool) {
	var (
		typElem  = typ.Elem()
		valElem  = val.Elem()
		numField = valElem.NumField()
	)
	for i := 0; i < numField; i++ {
		var (
			typField          = typElem.Field(i)
			valField          = valElem.Field(i)
			tag, isEmpty, err = parseTag(typField.Name, typField.Tag)
		)
		if err != nil {
			flagSet.err = err
			return
		}
		if tag == nil {
			continue
		}

		// if `cli` tag is empty and the field is a struct
		if isEmpty && valField.Kind() == reflect.Struct {
			var (
				subObj   = valField.Addr().Interface()
				subType  = reflect.TypeOf(subObj)
				subValue = reflect.ValueOf(subObj)
			)
			initFlagSet(subType, subValue, flagSet, clr, dontSetValue)
			if flagSet.err != nil {
				return
			}
			continue
		}
		fl, err := newFlag(typField, valField, tag, clr, dontSetValue)
		if flagSet.err = err; err != nil {
			return
		}
		// ignored flag
		if fl == nil {
			continue
		}
		flagSet.flagSlice = append(flagSet.flagSlice, fl)

		// encode flag value
		value := ""
		if fl.isAssigned {
			if !valField.CanInterface() {
				flagSet.err = fmt.Errorf("field %s cannot interface", typField.Name)
				return
			}
			intf := valField.Interface()
			if encoder, ok := intf.(Encoder); ok {
				value = encoder.Encode()
			} else {
				value = fmt.Sprintf("%v", intf)
			}
		}

		names := append(fl.tag.shortNames, fl.tag.longNames...)
		for i, name := range names {
			if _, ok := flagSet.flagMap[name]; ok {
				flagSet.err = fmt.Errorf("flag %s repeated", clr.Bold(name))
				return
			}
			flagSet.flagMap[name] = fl
			if dontSetValue {
				continue
			}
			if fl.isAssigned && i == 0 {
				flagSet.values[name] = []string{value}
			}
		}
	}
}

func parseArgsToFlagSet(args []string, flagSet *flagSet, clr color.Color) {
	size := len(args)
	for i := 0; i < size; i++ {
		arg := args[i]
		if !strings.HasPrefix(arg, dashOne) {
			// append a free argument
			flagSet.args = append(flagSet.args, arg)
			continue
		}

		var (
			next   = ""
			offset = 0
		)
		if i+1 < size {
			if !strings.HasPrefix(args[i+1], dashOne) {
				next = args[i+1]
				offset = 1
			}
		}

		if arg == dashOne {
			flagSet.err = fmt.Errorf("unexpected single dash")
			return
		}

		// terminate the flag parse while occur `--`
		if arg == dashTwo {
			flagSet.args = append(flagSet.args, args[i+1:]...)
			break
		}

		// split arg by "="(key=value)
		strs := []string{arg}
		index := strings.Index(arg, "=")
		if index >= 0 {
			strs = []string{arg[:index], arg[index+1:]}
		}

		arg = strs[0]
		fl, ok := flagSet.flagMap[arg]

		// found in flagMap
		if ok {
			retOffset := parseToFoundFlag(flagSet, fl, strs, arg, next, offset, clr)
			if flagSet.err != nil {
				return
			}
			i += retOffset
			continue
		}

		// not found in flagMap
		// it's an invalid flag if arg has prefix `--`
		if strings.HasPrefix(arg, dashTwo) {
			flagSet.err = fmt.Errorf("undefined flag %s", clr.Bold(arg))
			return
		}

		// try parse `-F<value>`
		if parseSiameseFlag(flagSet, arg[0:2], args[i][2:], clr) {
			continue
		} else if flagSet.err != nil {
			return
		}

		// other cases, find flag char by char
		arg = strings.TrimPrefix(arg, dashOne)
		parseFlagCharByChar(flagSet, arg, clr)
		if flagSet.err != nil {
			return
		}
		continue
	}

	// read delay flags
	for _, fl := range flagSet.flagSlice {
		if fl.isNeedDelaySet && fl.isAssigned {
			err := setWithProperType(fl, fl.field.Type, fl.value, fl.lastValue, clr, false)
			if flagSet.err == nil && err != nil {
				flagSet.err = err
			}
		}
		if fl.tag.isForce && fl.getBool() {
			flagSet.hasForce = true
		}
	}

	// read prompt flags
	if !flagSet.hasForce {
		if flagSet.err != nil {
			return
		}
		flagSet.readPrompt(os.Stdout, clr)
		if flagSet.err != nil {
			return
		}
		flagSet.readEditor(clr)
		if flagSet.err != nil {
			return
		}
	} else {
		flagSet.err = nil
	}

	buff := bytes.NewBufferString("")
	for _, fl := range flagSet.flagSlice {
		if !fl.isAssigned && fl.tag.isRequired {
			if buff.Len() > 0 {
				buff.WriteByte('\n')
			}
			fmt.Fprintf(buff, "required argument %s missing", clr.Bold(fl.name()))
		}
	}
	if buff.Len() > 0 && !flagSet.hasForce {
		flagSet.err = fmt.Errorf(buff.String())
	}
}

func parseToFoundFlag(flagSet *flagSet, fl *flag, strs []string, arg, next string, offset int, clr color.Color) int {
	retOffset := 0
	l := len(strs)
	if l == 1 {
		if fl.isBoolean() {
			flagSet.err = fl.set(arg, "true", clr)
		} else {
			flagSet.err = fl.set(arg, next, clr)
			retOffset = offset
		}
	} else if l == 2 {
		flagSet.err = fl.set(arg, strs[1], clr)
	} else {
		flagSet.err = fmt.Errorf("too many(%d) value", l)
	}
	if flagSet.err != nil {
		name := clr.Bold(fl.name())
		flagSet.err = fmt.Errorf("argument %s invalid: %v", name, flagSet.err)
		return retOffset
	}
	flagSet.values[arg] = []string{fmt.Sprintf("%v", fl.value.Interface())}
	return retOffset
}

func parseFlagCharByChar(flagSet *flagSet, arg string, clr color.Color) {
	// NOTE: each fold flag should be boolean
	chars := []byte(arg)
	for _, c := range chars {
		tmp := dashOne + string([]byte{c})
		fl, ok := flagSet.flagMap[tmp]
		if !ok {
			flagSet.err = fmt.Errorf("undefined flag %s", clr.Bold(tmp))
			return
		}

		if !fl.isBoolean() {
			flagSet.err = fmt.Errorf("each fold flag should be boolean, but %s not", clr.Bold(tmp))
			return
		}

		fl.set(tmp, "true", clr)
		flagSet.values[tmp] = []string{"true"}
	}
}

func parseSiameseFlag(flagSet *flagSet, firstHalf, latterHalf string, clr color.Color) bool {
	// NOTE: fl must be not a boolean
	key, val := firstHalf, latterHalf
	if fl, ok := flagSet.flagMap[key]; ok && !fl.isBoolean() {
		if flagSet.err = fl.set(key, val, clr); flagSet.err != nil {
			return false
		}
		return true
	}
	return false
}
<commit_msg>fix crash bug for command's usage<commit_after>package cli

import (
	"bytes"
	"fmt"
	"os"
	"reflect"
	"strings"

	"github.com/labstack/gommon/color"
)

// Run runs a single command app
func Run(argv interface{}, fn CommandFunc, descs ...string) {
	RunWithArgs(argv, os.Args, fn, descs...)
}

// RunWithArgs is similar to Run, but with args instead of os.Args
func RunWithArgs(argv interface{}, args []string, fn CommandFunc, descs ...string) {
	desc := ""
	if len(descs) > 0 {
		desc = strings.Join(descs, "\n")
	}
	err := (&Command{
		Name:        args[0],
		Desc:        desc,
		Argv:        func() interface{} { return argv },
		CanSubRoute: true,
		Fn:          fn,
	}).Run(args[1:])
	if err != nil {
		fmt.Println(err)
	}
}

// Root registers forest for root and returns root
func Root(root *Command, forest ...*CommandTree) *Command {
	root.RegisterTree(forest...)
	return root
}

// Tree creates a CommandTree
func Tree(cmd *Command, forest ...*CommandTree) *CommandTree {
	return &CommandTree{
		command: cmd,
		forest:  forest,
	}
}

// Parse parses args to object argv
func Parse(args []string, argv interface{}) error {
	clr := color.Color{}
	fset := parseArgv(args, argv, clr)
	return fset.err
}

func parseArgv(args []string, argv interface{}, clr color.Color) *flagSet {
	return parseArgvList(args, []interface{}{argv}, clr)
}

func parseArgvList(args []string, argvList []interface{}, clr color.Color) *flagSet {
	flagSet := newFlagSet()
	for _, argv := range argvList {
		if argv == nil {
			continue
		}
		var (
			typ = reflect.TypeOf(argv)
			val = reflect.ValueOf(argv)
		)
		switch typ.Kind() {
		case reflect.Ptr:
			if reflect.Indirect(val).Type().Kind() != reflect.Struct {
				flagSet.err = errNotAPointerToStruct
				return flagSet
			}
			initFlagSet(typ, val, flagSet, clr, false)
			if flagSet.err != nil {
				return flagSet
			}
		default:
			flagSet.err = errNotAPointer
			return flagSet
		}
	}
	parseArgsToFlagSet(args, flagSet, clr)
	return flagSet
}

func usage(argvList []interface{}, clr color.Color, style UsageStyle) string {
	flagSet := newFlagSet()
	buf := bytes.NewBufferString("")
	for i := len(argvList) - 1; i >= 0; i-- {
		v := argvList[i]
		if v == nil {
			continue
		}
		var (
			typ = reflect.TypeOf(v)
			val = reflect.ValueOf(v)
		)
		if typ.Kind() == reflect.Ptr &&
			reflect.Indirect(val).Type().Kind() == reflect.Struct {
			// initialize flagSet
			initFlagSet(typ, val, flagSet, clr, true)
			if flagSet.err != nil {
				return ""
			}
		}
	}
	buf.WriteString(flagSlice(flagSet.flagSlice).StringWithStyle(clr, style))
	return buf.String()
}

func initFlagSet(typ reflect.Type, val reflect.Value, flagSet *flagSet, clr color.Color, dontSetValue bool) {
	var (
		typElem  = typ.Elem()
		valElem  = val.Elem()
		numField = valElem.NumField()
	)
	for i := 0; i < numField; i++ {
		var (
			typField          = typElem.Field(i)
			valField          = valElem.Field(i)
			tag, isEmpty, err = parseTag(typField.Name, typField.Tag)
		)
		if err != nil {
			flagSet.err = err
			return
		}
		if tag == nil {
			continue
		}

		// if `cli` tag is empty and the field is a struct
		if isEmpty && valField.Kind() == reflect.Struct {
			var (
				subObj   = valField.Addr().Interface()
				subType  = reflect.TypeOf(subObj)
				subValue = reflect.ValueOf(subObj)
			)
			initFlagSet(subType, subValue, flagSet, clr, dontSetValue)
			if flagSet.err != nil {
				return
			}
			continue
		}
		fl, err := newFlag(typField, valField, tag, clr, dontSetValue)
		if flagSet.err = err; err != nil {
			return
		}
		// ignored flag
		if fl == nil {
			continue
		}
		flagSet.flagSlice = append(flagSet.flagSlice, fl)

		// encode flag value
		value := ""
		if fl.isAssigned {
			if !valField.CanInterface() {
				flagSet.err = fmt.Errorf("field %s cannot interface", typField.Name)
				return
			}
			intf := valField.Interface()
			if encoder, ok := intf.(Encoder); ok {
				value = encoder.Encode()
			} else {
				value = fmt.Sprintf("%v", intf)
			}
		}

		names := append(fl.tag.shortNames, fl.tag.longNames...)
		for i, name := range names {
			if _, ok := flagSet.flagMap[name]; ok {
				flagSet.err = fmt.Errorf("flag %s repeated", clr.Bold(name))
				return
			}
			flagSet.flagMap[name] = fl
			if dontSetValue {
				continue
			}
			if fl.isAssigned && i == 0 {
				flagSet.values[name] = []string{value}
			}
		}
	}
}

func parseArgsToFlagSet(args []string, flagSet *flagSet, clr color.Color) {
	size := len(args)
	for i := 0; i < size; i++ {
		arg := args[i]
		if !strings.HasPrefix(arg, dashOne) {
			// append a free argument
			flagSet.args = append(flagSet.args, arg)
			continue
		}

		var (
			next   = ""
			offset = 0
		)
		if i+1 < size {
			if !strings.HasPrefix(args[i+1], dashOne) {
				next = args[i+1]
				offset = 1
			}
		}

		if arg == dashOne {
			flagSet.err = fmt.Errorf("unexpected single dash")
			return
		}

		// terminate the flag parse while occur `--`
		if arg == dashTwo {
			flagSet.args = append(flagSet.args, args[i+1:]...)
			break
		}

		// split arg by "="(key=value)
		strs := []string{arg}
		index := strings.Index(arg, "=")
		if index >= 0 {
			strs = []string{arg[:index], arg[index+1:]}
		}

		arg = strs[0]
		fl, ok := flagSet.flagMap[arg]

		// found in flagMap
		if ok {
			retOffset := parseToFoundFlag(flagSet, fl, strs, arg, next, offset, clr)
			if flagSet.err != nil {
				return
			}
			i += retOffset
			continue
		}

		// not found in flagMap
		// it's an invalid flag if arg has prefix `--`
		if strings.HasPrefix(arg, dashTwo) {
			flagSet.err = fmt.Errorf("undefined flag %s", clr.Bold(arg))
			return
		}

		// try parse `-F<value>`
		if parseSiameseFlag(flagSet, arg[0:2], args[i][2:], clr) {
			continue
		} else if flagSet.err != nil {
			return
		}

		// other cases, find flag char by char
		arg = strings.TrimPrefix(arg, dashOne)
		parseFlagCharByChar(flagSet, arg, clr)
		if flagSet.err != nil {
			return
		}
		continue
	}

	// read delay flags
	for _, fl := range flagSet.flagSlice {
		if fl.isNeedDelaySet && fl.isAssigned {
			err := setWithProperType(fl, fl.field.Type, fl.value, fl.lastValue, clr, false)
			if flagSet.err == nil && err != nil {
				flagSet.err = err
			}
		}
		if fl.tag.isForce && fl.getBool() {
			flagSet.hasForce = true
		}
	}

	// read prompt flags
	if !flagSet.hasForce {
		if flagSet.err != nil {
			return
		}
		flagSet.readPrompt(os.Stdout, clr)
		if flagSet.err != nil {
			return
		}
		flagSet.readEditor(clr)
		if flagSet.err != nil {
			return
		}
	} else {
		flagSet.err = nil
	}

	buff := bytes.NewBufferString("")
	for _, fl := range flagSet.flagSlice {
		if !fl.isAssigned && fl.tag.isRequired {
			if buff.Len() > 0 {
				buff.WriteByte('\n')
			}
			fmt.Fprintf(buff, "required argument %s missing", clr.Bold(fl.name()))
		}
	}
	if buff.Len() > 0 && !flagSet.hasForce {
		flagSet.err = fmt.Errorf(buff.String())
	}
}

func parseToFoundFlag(flagSet *flagSet, fl *flag, strs []string, arg, next string, offset int, clr color.Color) int {
	retOffset := 0
	l := len(strs)
	if l == 1 {
		if fl.isBoolean() {
			flagSet.err = fl.set(arg, "true", clr)
		} else {
			flagSet.err = fl.set(arg, next, clr)
			retOffset = offset
		}
	} else if l == 2 {
		flagSet.err = fl.set(arg, strs[1], clr)
	} else {
		flagSet.err = fmt.Errorf("too many(%d) value", l)
	}
	if flagSet.err != nil {
		name := clr.Bold(fl.name())
		flagSet.err = fmt.Errorf("argument %s invalid: %v", name, flagSet.err)
		return retOffset
	}
	flagSet.values[arg] = []string{fmt.Sprintf("%v", fl.value.Interface())}
	return retOffset
}

func parseFlagCharByChar(flagSet *flagSet, arg string, clr color.Color) {
	// NOTE: each fold flag should be boolean
	chars := []byte(arg)
	for _, c := range chars {
		tmp := dashOne + string([]byte{c})
		fl, ok := flagSet.flagMap[tmp]
		if !ok {
			flagSet.err = fmt.Errorf("undefined flag %s", clr.Bold(tmp))
			return
		}

		if !fl.isBoolean() {
			flagSet.err = fmt.Errorf("each fold flag should be boolean, but %s not", clr.Bold(tmp))
			return
		}

		fl.set(tmp, "true", clr)
		flagSet.values[tmp] = []string{"true"}
	}
}

func parseSiameseFlag(flagSet *flagSet, firstHalf, latterHalf string, clr color.Color) bool {
	// NOTE: fl must be not a boolean
	key, val := firstHalf, latterHalf
	if fl, ok := flagSet.flagMap[key]; ok && !fl.isBoolean() {
		if flagSet.err = fl.set(key, val, clr); flagSet.err != nil {
			return false
		}
		return true
	}
	return false
}
<|endoftext|>


<commit_before>/*
Package telnet provides TELNET and TELNETS client and server implementations
in a style similar to the "net/http" library that is part of the Go standard library,
including support for "middleware"; TELNETS is secure TELNET, with the TELNET protocol
over a secured TLS (or SSL) connection.

Example TELNET Server

ListenAndServe starts a (un-secure) TELNET server with a given address and handler.

	handler := telnet.EchoHandler

	err := telnet.ListenAndServe(":23", handler)
	if nil != err {
		panic(err)
	}

Example TELNETS Server

ListenAndServeTLS starts a (secure) TELNETS server with a given address and handler,
using the specified "cert.pem" and "key.pem" files.

	handler := telnet.EchoHandler

	err := telnet.ListenAndServeTLS(":992", "cert.pem", "key.pem", handler)
	if nil != err {
		panic(err)
	}

TELNET vs TELNETS

If you are communicating over the open Internet, you should be using (the secure) TELNETS protocol and ListenAndServeTLS.

If you are communicating just on localhost, then using just (the un-secure) TELNET protocol and telnet.ListenAndServe may be OK.

If you are not sure which to use, use TELNETS and ListenAndServeTLS.

Example TELNET Shell Server

The previous 2 exaple servers were very very simple. Specifically, they just echoed back whatever
you submitted to it.

If you typed:

	Apple Banana Cherry\r\n

... it would send back:

	Apple Banana Cherry\r\n

(Exactly the same data you sent it.)

A more useful TELNET server can be made using the "github.com/reiver/go-telnet/telsh" sub-package.

The `telsh` sub-package provides "middleware" that enables you to create a "shell" interface (also
called a "command line interface" or "CLI") which most people would expect when using TELNET OR TELNETS.

For example:

	package main

	import (
		"github.com/reiver/go-oi"
		"github.com/reiver/go-telnet"
		"github.com/reiver/go-telnet/telsh"

		"time"
	)

	func main() {

		shellHandler := telsh.NewShellHandler()

		commandName := "date"
		shellHandler.Register(commandName, danceProducer)

		commandName = "animate"
		shellHandler.Register(commandName, animateProducer)

		addr := ":23"
		if err := telnet.ListenAndServe(addr, shellHandler); nil != err {
			panic(err)
		}
	}

Note that in the example, so far, we have registered 2 commands: `date` and `animate`.

For this to actually work, we need to have code for the `date` and `animate` commands.

The actual implemenation for the `date` command could be done like the following:

	func dateHandlerFunc(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {
		const layout = "Mon Jan 2 15:04:05 -0700 MST 2006"
		s := time.Now().Format(layout)

		if _, err := oi.LongWriteString(stdout, s); nil != err {
			return err
		}

		return nil
	}

	func dateProducerFunc(ctx telsh.Context, name string, args ...string) telsh.Handler{
		return telsh.PromoteHandlerFunc(dateHandler)
	}

	var dateProducer = ProducerFunc(dateProducerFunc)

Note that your "real" work is in the `dateHandlerFunc` func.

And the actual implementation for the `animate` command could be done as follows:

	func animateHandlerFunc(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {

		for i:=0; i<20; i++ {
			oi.LongWriteString(stdout, "\r⠋")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠙")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠹")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠸")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠼")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠴")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠦")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠧")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠇")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠏")
			time.Sleep(50*time.Millisecond)
		}
		oi.LongWriteString(stdout, "\r \r\n")

		return nil
	}

	func animateProducerFunc(ctx telsh.Context, name string, args ...string) telsh.Handler{
		return telsh.PromoteHandlerFunc(animateHandler)
	}

	var animateProducer = ProducerFunc(animateProducerFunc)

Again, note that your "real" work is in the `animateHandlerFunc` func.

Generating PEM Files

If you are using the telnet.ListenAndServeTLS func or the telnet.Server.ListenAndServeTLS method, you will need to
supply "cert.pem" and "key.pem" files.

If you do not already have these files, the Go soure code contains a tool for generating these files for you.

It can be found at:

	$GOROOT/src/crypto/tls/generate_cert.go

So, for example, if your `$GOROOT` is the "/usr/local/go" directory, then it would be at:

	/usr/local/go/src/crypto/tls/generate_cert.go

If you run the command:

	go run $GOROOT/src/crypto/tls/generate_cert.go --help

... then you get the help information for "generate_cert.go".

Of course, you would replace or set `$GOROOT` with whatever your path actually is. Again, for example,
if your `$GOROOT` is the "/usr/local/go" directory, then it would be:

	go run /usr/local/go/src/crypto/tls/generate_cert.go --help

To demonstrate the usage of "generate_cert.go", you might run the following to generate certificates
that were bound to the hosts `127.0.0.1` and `localhost`:

	go run /usr/local/go/src/crypto/tls/generate_cert.go --ca --host='127.0.0.1,localhost'

If you are not sure where "generate_cert.go" is on your computer, on Linux and Unix based systems, you might
be able to find the file with the command:

	locate /src/crypto/tls/generate_cert.go

(If it finds it, it should output the full path to this file.)

Example TELNET Client

You can make a simple (un-secure) TELNET client with code like the following:

	package main

	import (
		"github.com/reiver/go-telnet"
	)

	func main() {
		var caller Caller = telnet.StandardCaller

		telnet.DialToAndCall("example.net:5555", caller)
	}

Example TELNETS Client

You can make a simple (secure) TELNETS client with code like the following:

	package main

	import (
		"github.com/reiver/go-telnet"
	)

	func main() {
		var caller Caller = telnet.StandardCaller

		telnet.DialToAndCallTLS("example.net:5555", caller)
	}

Terminal Codes

Terminal codes (also sometimes called 'terminal control codes') are used to issue various kinds of commands
to the terminal.

(Note that 'terminal control codes' are a completely separate concept for 'TELNET commands',
and the two should NOT be conflated or confused.)

The most common types of 'terminal codes' are the 'ANSI escape codes'. (Although there are other types too.)

ANSI Escape Codes

ANSI escape codes (also sometimes called 'ANSI escape sequences') are a common type of 'terminal code' used
to do things such as:

• moving the cursor,

• erasing the display,

• erasing the line,

• setting the graphics mode,

• setting the foregroup color,

• setting the background color,

• setting the screen resolution, and

• setting keyboard strings.

Setting The Foreground Color With ANSI Escape Codes

One of the abilities of ANSI escape codes is to set the foreground color.

Here is a table showing codes for this:

	| ANSI Color   | Go string    | Go []byte                       |
	| ------------ | ------------ | ------------------------------- |
	| Black        | `"\x1b[30m"` | `[]byte{27, '[', '3','0', 'm'}` |
	| Red          | `"\x1b[31m"` | `[]byte{27, '[', '3','1', 'm'}` |
	| Green        | `"\x1b[32m"` | `[]byte{27, '[', '3','2', 'm'}` |
	| Brown/Yellow | `"\x1b[33m"` | `[]byte{27, '[', '3','3', 'm'}` |
	| Blue         | `"\x1b[34m"` | `[]byte{27, '[', '3','4', 'm'}` |
	| Magenta      | `"\x1b[35m"` | `[]byte{27, '[', '3','5', 'm'}` |
	| Cyan         | `"\x1b[36m"` | `[]byte{27, '[', '3','6', 'm'}` |
	| Gray/White   | `"\x1b[37m"` | `[]byte{27, '[', '3','7', 'm'}` |

(Note that in the `[]byte` that the first `byte` is the number `27` (which
is the "escape" character) where the third and fouth characters are the
**not** number literals, but instead character literals `'3'` and whatever.)

Setting The Background Color With ANSI Escape Codes

Another of the abilities of ANSI escape codes is to set the background color.

	| ANSI Color   | Go string    | Go []byte                       |
	| ------------ | ------------ | ------------------------------- |
	| Black        | `"\x1b[40m"` | `[]byte{27, '[', '4','0', 'm'}` |
	| Red          | `"\x1b[41m"` | `[]byte{27, '[', '4','1', 'm'}` |
	| Green        | `"\x1b[42m"` | `[]byte{27, '[', '4','2', 'm'}` |
	| Brown/Yellow | `"\x1b[43m"` | `[]byte{27, '[', '4','3', 'm'}` |
	| Blue         | `"\x1b[44m"` | `[]byte{27, '[', '4','4', 'm'}` |
	| Magenta      | `"\x1b[45m"` | `[]byte{27, '[', '4','5', 'm'}` |
	| Cyan         | `"\x1b[46m"` | `[]byte{27, '[', '4','6', 'm'}` |
	| Gray/White   | `"\x1b[47m"` | `[]byte{27, '[', '4','7', 'm'}` |

(Note that in the `[]byte` that the first `byte` is the number `27` (which
is the "escape" character) where the third and fouth characters are the
**not** number literals, but instead character literals `'4'` and whatever.)

Using ANSI Escape Codes

In Go code, if I wanted to use an ANSI escape code to use a blue background,
a white foreground, and bold, I could do that with the ANSI escape code:

	"\x1b[44;37;1m"

Note that that start with byte value 27, which we have encoded as hexadecimal
as \x1b. Followed by the '[' character.

Coming after that is the sub-string "44", which is the code that sets our background color to blue.

We follow that with the ';' character (which separates codes).

And the after that comes the sub-string "37", which is the code that set our foreground color to white.

After that, we follow with another ";" character (which, again, separates codes).

And then we follow it the sub-string "1", which is the code that makes things bold.

And finally, the ANSI escape sequence is finished off with the 'm' character.

To show this in a more complete example, our `dateHandlerFunc` from before could incorporate ANSI escape sequences as follows:

	func dateHandlerFunc(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {
		const layout = "Mon Jan 2 15:04:05 -0700 MST 2006"
		s := "\x1b[44;37;1m" + time.Now().Format(layout) + "\x1b[0m"

		if _, err := oi.LongWriteString(stdout, s); nil != err {
			return err
		}

		return nil
	}

Note that in that example, in addition to using the ANSI escape sequence "\x1b[44;37;1m"
to set the background color to blue, set the foreground color to white, and make it bold,
we also used the ANSI escape sequence "\x1b[0m" to reset the background and foreground colors
and boldness back to "normal".

*/
package telnet
<commit_msg>added more content to docs<commit_after>/*
Package telnet provides TELNET and TELNETS client and server implementations
in a style similar to the "net/http" library that is part of the Go standard library,
including support for "middleware"; TELNETS is secure TELNET, with the TELNET protocol
over a secured TLS (or SSL) connection.

Example TELNET Server

ListenAndServe starts a (un-secure) TELNET server with a given address and handler.

	handler := telnet.EchoHandler

	err := telnet.ListenAndServe(":23", handler)
	if nil != err {
		panic(err)
	}

Example TELNETS Server

ListenAndServeTLS starts a (secure) TELNETS server with a given address and handler,
using the specified "cert.pem" and "key.pem" files.

	handler := telnet.EchoHandler

	err := telnet.ListenAndServeTLS(":992", "cert.pem", "key.pem", handler)
	if nil != err {
		panic(err)
	}

TELNET vs TELNETS

If you are communicating over the open Internet, you should be using (the secure) TELNETS protocol and ListenAndServeTLS.

If you are communicating just on localhost, then using just (the un-secure) TELNET protocol and telnet.ListenAndServe may be OK.

If you are not sure which to use, use TELNETS and ListenAndServeTLS.

Example TELNET Shell Server

The previous 2 exaple servers were very very simple. Specifically, they just echoed back whatever
you submitted to it.

If you typed:

	Apple Banana Cherry\r\n

... it would send back:

	Apple Banana Cherry\r\n

(Exactly the same data you sent it.)

A more useful TELNET server can be made using the "github.com/reiver/go-telnet/telsh" sub-package.

The `telsh` sub-package provides "middleware" that enables you to create a "shell" interface (also
called a "command line interface" or "CLI") which most people would expect when using TELNET OR TELNETS.

For example:

	package main

	import (
		"github.com/reiver/go-oi"
		"github.com/reiver/go-telnet"
		"github.com/reiver/go-telnet/telsh"

		"time"
	)

	func main() {

		shellHandler := telsh.NewShellHandler()

		commandName := "date"
		shellHandler.Register(commandName, danceProducer)

		commandName = "animate"
		shellHandler.Register(commandName, animateProducer)

		addr := ":23"
		if err := telnet.ListenAndServe(addr, shellHandler); nil != err {
			panic(err)
		}
	}

Note that in the example, so far, we have registered 2 commands: `date` and `animate`.

For this to actually work, we need to have code for the `date` and `animate` commands.

The actual implemenation for the `date` command could be done like the following:

	func dateHandlerFunc(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {
		const layout = "Mon Jan 2 15:04:05 -0700 MST 2006"
		s := time.Now().Format(layout)

		if _, err := oi.LongWriteString(stdout, s); nil != err {
			return err
		}

		return nil
	}

	func dateProducerFunc(ctx telsh.Context, name string, args ...string) telsh.Handler{
		return telsh.PromoteHandlerFunc(dateHandler)
	}

	var dateProducer = ProducerFunc(dateProducerFunc)

Note that your "real" work is in the `dateHandlerFunc` func.

And the actual implementation for the `animate` command could be done as follows:

	func animateHandlerFunc(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {

		for i:=0; i<20; i++ {
			oi.LongWriteString(stdout, "\r⠋")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠙")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠹")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠸")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠼")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠴")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠦")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠧")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠇")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠏")
			time.Sleep(50*time.Millisecond)
		}
		oi.LongWriteString(stdout, "\r \r\n")

		return nil
	}

	func animateProducerFunc(ctx telsh.Context, name string, args ...string) telsh.Handler{
		return telsh.PromoteHandlerFunc(animateHandler)
	}

	var animateProducer = ProducerFunc(animateProducerFunc)

Again, note that your "real" work is in the `animateHandlerFunc` func.

Generating PEM Files

If you are using the telnet.ListenAndServeTLS func or the telnet.Server.ListenAndServeTLS method, you will need to
supply "cert.pem" and "key.pem" files.

If you do not already have these files, the Go soure code contains a tool for generating these files for you.

It can be found at:

	$GOROOT/src/crypto/tls/generate_cert.go

So, for example, if your `$GOROOT` is the "/usr/local/go" directory, then it would be at:

	/usr/local/go/src/crypto/tls/generate_cert.go

If you run the command:

	go run $GOROOT/src/crypto/tls/generate_cert.go --help

... then you get the help information for "generate_cert.go".

Of course, you would replace or set `$GOROOT` with whatever your path actually is. Again, for example,
if your `$GOROOT` is the "/usr/local/go" directory, then it would be:

	go run /usr/local/go/src/crypto/tls/generate_cert.go --help

To demonstrate the usage of "generate_cert.go", you might run the following to generate certificates
that were bound to the hosts `127.0.0.1` and `localhost`:

	go run /usr/local/go/src/crypto/tls/generate_cert.go --ca --host='127.0.0.1,localhost'

If you are not sure where "generate_cert.go" is on your computer, on Linux and Unix based systems, you might
be able to find the file with the command:

	locate /src/crypto/tls/generate_cert.go

(If it finds it, it should output the full path to this file.)

Example TELNET Client

You can make a simple (un-secure) TELNET client with code like the following:

	package main

	import (
		"github.com/reiver/go-telnet"
	)

	func main() {
		var caller Caller = telnet.StandardCaller

		//@TOOD: replace "example.net:5555" with address you want to connect to.
		telnet.DialToAndCall("example.net:5555", caller)
	}

Example TELNETS Client

You can make a simple (secure) TELNETS client with code like the following:

	package main

	import (
		"github.com/reiver/go-telnet"
	)

	func main() {
		var caller Caller = telnet.StandardCaller

		//@TOOD: replace "example.net:5555" with address you want to connect to.
		telnet.DialToAndCallTLS("example.net:5555", caller)
	}

Terminal Codes

Terminal codes (also sometimes called 'terminal control codes') are used to issue various kinds of commands
to the terminal.

(Note that 'terminal control codes' are a completely separate concept for 'TELNET commands',
and the two should NOT be conflated or confused.)

The most common types of 'terminal codes' are the 'ANSI escape codes'. (Although there are other types too.)

ANSI Escape Codes

ANSI escape codes (also sometimes called 'ANSI escape sequences') are a common type of 'terminal code' used
to do things such as:

• moving the cursor,

• erasing the display,

• erasing the line,

• setting the graphics mode,

• setting the foregroup color,

• setting the background color,

• setting the screen resolution, and

• setting keyboard strings.

Setting The Foreground Color With ANSI Escape Codes

One of the abilities of ANSI escape codes is to set the foreground color.

Here is a table showing codes for this:

	| ANSI Color   | Go string    | Go []byte                       |
	| ------------ | ------------ | ------------------------------- |
	| Black        | `"\x1b[30m"` | `[]byte{27, '[', '3','0', 'm'}` |
	| Red          | `"\x1b[31m"` | `[]byte{27, '[', '3','1', 'm'}` |
	| Green        | `"\x1b[32m"` | `[]byte{27, '[', '3','2', 'm'}` |
	| Brown/Yellow | `"\x1b[33m"` | `[]byte{27, '[', '3','3', 'm'}` |
	| Blue         | `"\x1b[34m"` | `[]byte{27, '[', '3','4', 'm'}` |
	| Magenta      | `"\x1b[35m"` | `[]byte{27, '[', '3','5', 'm'}` |
	| Cyan         | `"\x1b[36m"` | `[]byte{27, '[', '3','6', 'm'}` |
	| Gray/White   | `"\x1b[37m"` | `[]byte{27, '[', '3','7', 'm'}` |

(Note that in the `[]byte` that the first `byte` is the number `27` (which
is the "escape" character) where the third and fouth characters are the
**not** number literals, but instead character literals `'3'` and whatever.)

Setting The Background Color With ANSI Escape Codes

Another of the abilities of ANSI escape codes is to set the background color.

	| ANSI Color   | Go string    | Go []byte                       |
	| ------------ | ------------ | ------------------------------- |
	| Black        | `"\x1b[40m"` | `[]byte{27, '[', '4','0', 'm'}` |
	| Red          | `"\x1b[41m"` | `[]byte{27, '[', '4','1', 'm'}` |
	| Green        | `"\x1b[42m"` | `[]byte{27, '[', '4','2', 'm'}` |
	| Brown/Yellow | `"\x1b[43m"` | `[]byte{27, '[', '4','3', 'm'}` |
	| Blue         | `"\x1b[44m"` | `[]byte{27, '[', '4','4', 'm'}` |
	| Magenta      | `"\x1b[45m"` | `[]byte{27, '[', '4','5', 'm'}` |
	| Cyan         | `"\x1b[46m"` | `[]byte{27, '[', '4','6', 'm'}` |
	| Gray/White   | `"\x1b[47m"` | `[]byte{27, '[', '4','7', 'm'}` |

(Note that in the `[]byte` that the first `byte` is the number `27` (which
is the "escape" character) where the third and fouth characters are the
**not** number literals, but instead character literals `'4'` and whatever.)

Using ANSI Escape Codes

In Go code, if I wanted to use an ANSI escape code to use a blue background,
a white foreground, and bold, I could do that with the ANSI escape code:

	"\x1b[44;37;1m"

Note that that start with byte value 27, which we have encoded as hexadecimal
as \x1b. Followed by the '[' character.

Coming after that is the sub-string "44", which is the code that sets our background color to blue.

We follow that with the ';' character (which separates codes).

And the after that comes the sub-string "37", which is the code that set our foreground color to white.

After that, we follow with another ";" character (which, again, separates codes).

And then we follow it the sub-string "1", which is the code that makes things bold.

And finally, the ANSI escape sequence is finished off with the 'm' character.

To show this in a more complete example, our `dateHandlerFunc` from before could incorporate ANSI escape sequences as follows:

	func dateHandlerFunc(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {
		const layout = "Mon Jan 2 15:04:05 -0700 MST 2006"
		s := "\x1b[44;37;1m" + time.Now().Format(layout) + "\x1b[0m"

		if _, err := oi.LongWriteString(stdout, s); nil != err {
			return err
		}

		return nil
	}

Note that in that example, in addition to using the ANSI escape sequence "\x1b[44;37;1m"
to set the background color to blue, set the foreground color to white, and make it bold,
we also used the ANSI escape sequence "\x1b[0m" to reset the background and foreground colors
and boldness back to "normal".

*/
package telnet
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"

	"github.com/cep21/xdgbasedir"
	"gopkg.in/yaml.v1"
)

func main() {
	lf := NewLocalFiles("")
	Main(os.Args, os.Stdout, os.Stderr, &lf)
}

// The programs usage help.
var Usage = `dwi.

Usage:
	dwi <file>
`

// FileIndex provides methods for manipulating a list of files.
type FileIndex interface {
	Exists(filename string) bool
	Open(filename string) (*os.File, error)
}

// LocalFiles is a FileIndex for files in the local filesystem.
type LocalFiles struct {
	Root string
}

// Exists returns true if a file given by path exists.
func (lf *LocalFiles) Exists(filename string) bool {
	if _, err := os.Stat(lf.getFullPath(filename)); err != nil {
		return false
	}
	return true
}

// Open opens a file or returns an error.
func (lf *LocalFiles) Open(filename string) (*os.File, error) {
	return os.Open(filename)
}

// GetFullPath returns the full path.
func (lf *LocalFiles) getFullPath(filename string) string {
	return path.Join(lf.Root, filename)
}

// NewLocalFiles creates and initializes a new LocalFiles with a given path root.
func NewLocalFiles(root string) LocalFiles {
	return LocalFiles{root}
}

// Main function.
func Main(args []string, out io.Writer, eout io.Writer, fi FileIndex) {
	if len(args) < 2 {
		fmt.Fprintln(out, Usage)
		return
	}

	files := ParseArgs(args)
	if AnyFilesDontExist(eout, fi, files) {
		return
	}

	configDir, err := xdgbasedir.ConfigHomeDirectory()
	if err != nil {
		return
	}

	rules := LoadRules(path.Join(configDir, "/dealwithit/rules.yaml"), fi, eout)

	log.Println(rules)
}

// AnyFilesDontExist will look for each of the given files in the given FileIndex,
// output an error to eout for any file that doesn't exist and return true if where any missing.
func AnyFilesDontExist(eout io.Writer, fi FileIndex, files []string) bool {
	err := false
	for _, f := range files {
		if !fi.Exists(f) {
			fmt.Fprintln(eout, "Error: File not found:", f)
			err = true
		}
	}
	return err
}

// ParseArgs takes a list of args and returns the list of files.
// The first arg must be the executable name.
func ParseArgs(args []string) []string {
	return args[1:]
}

// LoadRules loads the rules file given by filename from the given FileIndex.
func LoadRules(filename string, fi FileIndex, eout io.Writer) []map[string]string {
	var rules []map[string]string
	file, err := fi.Open(filename)
	if err != nil {
		fmt.Fprintln(eout, "Error: Could not open rules file", filename, err)
		return rules
	}

	data, err := ioutil.ReadAll(file)
	if err != nil {
		fmt.Fprintln(eout, "Error: Could not read rules file", filename, err)
		return rules
	}

	err = yaml.Unmarshal(data, &rules)
	if err != nil {
		fmt.Fprintln(eout, "Error: Could not parse rules file", filename, err)
		return rules
	}

	return rules
}
<commit_msg>Matches rules to given filenames.<commit_after>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"regexp"

	"github.com/cep21/xdgbasedir"
	"gopkg.in/yaml.v1"
)

func main() {
	lf := NewLocalFiles("")
	Main(os.Args, os.Stdout, os.Stderr, &lf)
}

// The programs usage help.
var Usage = `dwi.

Usage:
	dwi <file>
`

// FileIndex provides methods for manipulating a list of files.
type FileIndex interface {
	Exists(filename string) bool
	Open(filename string) (*os.File, error)
}

// LocalFiles is a FileIndex for files in the local filesystem.
type LocalFiles struct {
	Root string
}

// Exists returns true if a file given by path exists.
func (lf *LocalFiles) Exists(filename string) bool {
	if _, err := os.Stat(lf.getFullPath(filename)); err != nil {
		return false
	}
	return true
}

// Open opens a file or returns an error.
func (lf *LocalFiles) Open(filename string) (*os.File, error) {
	return os.Open(filename)
}

// GetFullPath returns the full path.
func (lf *LocalFiles) getFullPath(filename string) string {
	return path.Join(lf.Root, filename)
}

// NewLocalFiles creates and initializes a new LocalFiles with a given path root.
func NewLocalFiles(root string) LocalFiles {
	return LocalFiles{root}
}

// Main function.
func Main(args []string, out io.Writer, eout io.Writer, fi FileIndex) {
	if len(args) < 2 {
		fmt.Fprintln(out, Usage)
		return
	}

	files := ParseArgs(args)
	if AnyFilesDontExist(eout, fi, files) {
		return
	}

	configDir, err := xdgbasedir.ConfigHomeDirectory()
	if err != nil {
		return
	}

	rules := LoadRules(path.Join(configDir, "/dealwithit/rules.yaml"), fi, eout)

	ForEachMatchingFile(files, rules, eout, fi)
}

// AnyFilesDontExist will look for each of the given files in the given FileIndex,
// output an error to eout for any file that doesn't exist and return true if where any missing.
func AnyFilesDontExist(eout io.Writer, fi FileIndex, files []string) bool {
	err := false
	for _, f := range files {
		if !fi.Exists(f) {
			fmt.Fprintln(eout, "Error: File not found:", f)
			err = true
		}
	}
	return err
}

// ParseArgs takes a list of args and returns the list of files.
// The first arg must be the executable name.
func ParseArgs(args []string) []string {
	return args[1:]
}

type Rule map[string]string
type Rules []Rule

// LoadRules loads the rules file given by filename from the given FileIndex.
func LoadRules(filename string, fi FileIndex, eout io.Writer) Rules {
	var rules Rules

	file, err := fi.Open(filename)
	if err != nil {
		fmt.Fprintln(eout, "Error: Could not open rules file", filename, err)
		return rules
	}

	data, err := ioutil.ReadAll(file)
	if err != nil {
		fmt.Fprintln(eout, "Error: Could not read rules file", filename, err)
		return rules
	}

	err = yaml.Unmarshal(data, &rules)
	if err != nil {
		fmt.Fprintln(eout, "Error: Could not parse rules file", filename, err)
		return rules
	}

	return rules
}

// ForEachMatchingFile executes a function on each file if the function match returns true.
func ForEachMatchingFile(files []string, rules Rules, eout io.Writer, fi FileIndex) {
	for _, f := range files {
		matches, rule := FileMatchesRules(f, rules, eout, fi)

		if !matches {
			continue
		}

		ExecuteRule(f, rule, fi)

	}
}

// FileMatchesRules if filename matches a rule given in rules returns true and the rule.
func FileMatchesRules(filename string, rules Rules, eout io.Writer, fi FileIndex) (bool, Rule) {
	for _, rule := range rules {
		if FileMatchesRule(filename, rule, eout) {
			return true, rule
		}
	}
	return false, nil
}

// FileMatchesRule return true if filename matches the rule given.
func FileMatchesRule(filename string, rule Rule, eout io.Writer) bool {
	re, err := regexp.Compile(rule["file"])
	if err != nil {
		fmt.Fprintln(eout, "Error: Could not compile regexp", err)
		return false
	}
	return re.MatchString(filename)
}

// ExecuteRule executes the given rules on file.
func ExecuteRule(filename string, rule Rule, fi FileIndex) {
	fmt.Println("DEBUG: ", filename, "matches", rule)
}
<|endoftext|>


<commit_before>/*
 This package is used to implement a "line oriented command interpreter", inspired by the python package with
 the same name http://docs.python.org/2/library/cmd.html

 Usage:

	 commander := &Cmd{...}
	 commander.Init()

	 commander.Add(Command{...})
	 commander.Add(Command{...})

	 commander.CmdLoop()
*/
package cmd

import (
	"github.com/gobs/args"
	"github.com/gobs/pretty"
	"github.com/gobs/readline"

	"fmt"
	"os"
	"os/exec"
	"path"
	"sort"
	"strings"
        "strconv"
        "sync"
)

//
// This is used to describe a new command
//
type Command struct {
	// command name
	Name string
	// command description
	Help string
	// the function to call to execute the command
	Call func(string) bool
}

//
// The context for command completion
//
type Completer struct {
	// the list of words to match on
	Words []string
	// the list of current matches
	Matches []string
}

//
// Return a word matching the prefix
// If there are multiple matches, index selects which one to pick
//
func (c *Completer) Complete(prefix string, index int) string {
	if index == 0 {
		c.Matches = c.Matches[:0]

		for _, w := range c.Words {
			if strings.HasPrefix(w, prefix) {
				c.Matches = append(c.Matches, w)
			}
		}
	}

	if index < len(c.Matches) {
		return c.Matches[index]
	} else {
		return ""
	}
}

//
// Create a Completer and initialize with list of words
//
func NewCompleter(words []string) (c *Completer) {
	c = new(Completer)
	c.Words = words
	c.Matches = make([]string, 0, len(c.Words))
	return
}

//
// This the the "context" for the command interpreter
//
type Cmd struct {
	// the prompt string
	Prompt string

	// the history file
	HistoryFile string

	// this function is called before starting the command loop
	PreLoop func()

	// this function is called before terminating the command loop
	PostLoop func()

	// this function is called before executing the selected command
	PreCmd func(string)

	// this function is called after a command has been executed
	// return true to terminate the interpreter, false to continue
	PostCmd func(string, bool) bool

	// this function is called if the last typed command was an empty line
	EmptyLine func()

	// this function is called if the command line doesn't match any existing command
	// by default it displays an error message
	Default func(string)

	// this function is called to implement command completion.
	// it should return a list of words that match the input text
	Complete func(string, string, int, int) []string

	// if true, enable shell commands
	EnableShell bool

	// this is the list of available commands indexed by command name
	Commands map[string]Command

	///////// private stuff /////////////
	completer    *Completer
	commandNames []string

        waitGroup *sync.WaitGroup
        waitMax, waitCount int
}

func (cmd *Cmd) readHistoryFile() {
	if len(cmd.HistoryFile) == 0 {
		// no history file
		return
	}

	filepath := cmd.HistoryFile // start with current directory
	if _, err := os.Stat(filepath); err == nil {
		if err := readline.ReadHistoryFile(filepath); err != nil {
			fmt.Println(err)
		}

		return
	}

	filepath = path.Join(os.Getenv("HOME"), filepath) // then check home directory
	if _, err := os.Stat(filepath); err == nil {
		if err := readline.ReadHistoryFile(filepath); err != nil {
			fmt.Println(err)
		}
	}

	// update HistoryFile with home path
	cmd.HistoryFile = filepath
}

func (cmd *Cmd) writeHistoryFile() {
	if len(cmd.HistoryFile) == 0 {
		// no history file
		return
	}

	if err := readline.WriteHistoryFile(cmd.HistoryFile); err != nil {
		fmt.Println(err)
	}
}

//
// Initialize the command interpreter context
//
func (cmd *Cmd) Init() {
	if cmd.PreLoop == nil {
		cmd.PreLoop = func() {}
	}
	if cmd.PostLoop == nil {
		cmd.PostLoop = func() {}
	}
	if cmd.PreCmd == nil {
		cmd.PreCmd = func(string) {}
	}
	if cmd.PostCmd == nil {
		cmd.PostCmd = func(line string, stop bool) bool { return stop }
	}
	if cmd.EmptyLine == nil {
		cmd.EmptyLine = func() {}
	}
	if cmd.Default == nil {
		cmd.Default = func(line string) { fmt.Printf("invalid command: %v\n", line) }
	}

	cmd.Commands = make(map[string]Command)
	cmd.Add(Command{"help", `list available commands`, cmd.Help})
	cmd.Add(Command{"go", `go cmd: asynchronous execution of cmd`, cmd.Go})
}

//
// Add a completer that matches on command names
//
func (cmd *Cmd) addCommandCompleter() {
	cmd.commandNames = make([]string, 0, len(cmd.Commands))

	for n, _ := range cmd.Commands {
		cmd.commandNames = append(cmd.commandNames, n)
	}

	// sorting for Help()
	sort.Strings(cmd.commandNames)

	cmd.completer = NewCompleter(cmd.commandNames)
	//readline.SetCompletionEntryFunction(completer.Complete)

	readline.SetAttemptedCompletionFunction(cmd.attemptedCompletion)
}

func (cmd *Cmd) attemptedCompletion(text string, start, end int) []string {
	if start == 0 { // this is the command to match
		return readline.CompletionMatches(text, cmd.completer.Complete)
	} else if cmd.Complete != nil {
		return cmd.Complete(text, readline.GetLineBuffer(), start, end)
	} else {
		return nil
	}
}

//
// execute shell command
//
func shellExec(command string) {
	args := args.GetArgs(command)
	if len(args) < 1 {
		fmt.Println("No command to exec")
	} else {
		cmd := exec.Command(args[0])
		cmd.Args = args
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		if err := cmd.Run(); err != nil {
			fmt.Println(err)
		}
	}
}

// Add a command to the command interpreter.
// Overrides a command with the same name, if there was one
//
func (cmd *Cmd) Add(command Command) {
	cmd.Commands[command.Name] = command
}

//
// Default help command.
// It lists all available commands or it displays the help for the specified command
//
func (cmd *Cmd) Help(line string) (stop bool) {
	fmt.Println("")

	if len(line) == 0 {
		fmt.Println("Available commands (use 'help <topic>'):")
		fmt.Println("================================================================")

		tp := pretty.NewTabPrinter(8)

		for _, c := range cmd.commandNames {
			tp.Print(c)
		}

		tp.Println()
	} else {
		c, ok := cmd.Commands[line]
		if ok {
			if len(c.Help) > 0 {
				fmt.Println(c.Help)
			} else {
				fmt.Println("No help for ", line)
			}
		} else {
			fmt.Println("unknown command")
		}
	}

	fmt.Println("")
	return
}

func (cmd *Cmd) Go(line string) (stop bool) {
        if strings.HasPrefix(line, "-") {
            // should be --start or --wait

	    args := args.ParseArgs(line)

            if _, ok := args.Options["start"]; ok {
                cmd.waitGroup = new(sync.WaitGroup)
                cmd.waitCount = 0
                cmd.waitMax = 0

                if len(args.Arguments) > 0 {
                    cmd.waitMax, _ = strconv.Atoi(args.Arguments[0])
                }

                return
            }

            if _, ok := args.Options["wait"]; ok {
                if cmd.waitGroup == nil {
                    fmt.Println("nothing to wait on")
                } else {
                    cmd.waitGroup.Wait()
                    cmd.waitGroup = nil
                }

                return
            }
        }

	if strings.HasPrefix(line, "go ") {
		fmt.Println("Don't go go me!")
	} else {
                if cmd.waitGroup == nil {
		    go cmd.OneCmd(line)
                } else {
                    if cmd.waitMax > 0 {
                        if cmd.waitCount >= cmd.waitMax {
                            cmd.waitGroup.Wait()
                            cmd.waitCount = 0
                        }
                    }

                    cmd.waitCount++
                    cmd.waitGroup.Add(1)
                    go func() {
                        defer cmd.waitGroup.Done()
                        cmd.OneCmd(line)
                    }()
                }
	}

	return
}

//
// This method executes one command
//
func (cmd *Cmd) OneCmd(line string) (stop bool) {

	if cmd.EnableShell && strings.HasPrefix(line, "!") {
		shellExec(line[1:])
		return
	}

	parts := strings.SplitN(line, " ", 2)
	cname := parts[0]

	command, ok := cmd.Commands[cname]

	if ok {
		var params string

		if len(parts) > 1 {
			params = strings.TrimSpace(parts[1])
		}

		stop = command.Call(params)
	} else {
		cmd.Default(line)
	}

	return
}

//
// This is the command interpreter entry point.
// It displays a prompt, waits for a command and executes it until the selected command returns true
//
func (cmd *Cmd) CmdLoop() {
	if len(cmd.Prompt) == 0 {
		cmd.Prompt = "> "
	}

	cmd.addCommandCompleter()

	cmd.PreLoop()

	cmd.readHistoryFile()

	// loop until ReadLine returns nil (signalling EOF)
	for {
		result := readline.ReadLine(&cmd.Prompt)
		if result == nil {
			break
		}

		line := strings.TrimSpace(*result)
		if line == "" {
			cmd.EmptyLine()
			continue
		}

		readline.AddHistory(*result) // allow user to recall this line

		cmd.PreCmd(line)

		stop := cmd.OneCmd(line)
		stop = cmd.PostCmd(line, stop)

		if stop {
			break
		}
	}

	cmd.writeHistoryFile()

	cmd.PostLoop()
}
<commit_msg>Added (minimal) explanation of go --start|--wait in help.<commit_after>/*
 This package is used to implement a "line oriented command interpreter", inspired by the python package with
 the same name http://docs.python.org/2/library/cmd.html

 Usage:

	 commander := &Cmd{...}
	 commander.Init()

	 commander.Add(Command{...})
	 commander.Add(Command{...})

	 commander.CmdLoop()
*/
package cmd

import (
	"github.com/gobs/args"
	"github.com/gobs/pretty"
	"github.com/gobs/readline"

	"fmt"
	"os"
	"os/exec"
	"path"
	"sort"
	"strings"
        "strconv"
        "sync"
)

//
// This is used to describe a new command
//
type Command struct {
	// command name
	Name string
	// command description
	Help string
	// the function to call to execute the command
	Call func(string) bool
}

//
// The context for command completion
//
type Completer struct {
	// the list of words to match on
	Words []string
	// the list of current matches
	Matches []string
}

//
// Return a word matching the prefix
// If there are multiple matches, index selects which one to pick
//
func (c *Completer) Complete(prefix string, index int) string {
	if index == 0 {
		c.Matches = c.Matches[:0]

		for _, w := range c.Words {
			if strings.HasPrefix(w, prefix) {
				c.Matches = append(c.Matches, w)
			}
		}
	}

	if index < len(c.Matches) {
		return c.Matches[index]
	} else {
		return ""
	}
}

//
// Create a Completer and initialize with list of words
//
func NewCompleter(words []string) (c *Completer) {
	c = new(Completer)
	c.Words = words
	c.Matches = make([]string, 0, len(c.Words))
	return
}

//
// This the the "context" for the command interpreter
//
type Cmd struct {
	// the prompt string
	Prompt string

	// the history file
	HistoryFile string

	// this function is called before starting the command loop
	PreLoop func()

	// this function is called before terminating the command loop
	PostLoop func()

	// this function is called before executing the selected command
	PreCmd func(string)

	// this function is called after a command has been executed
	// return true to terminate the interpreter, false to continue
	PostCmd func(string, bool) bool

	// this function is called if the last typed command was an empty line
	EmptyLine func()

	// this function is called if the command line doesn't match any existing command
	// by default it displays an error message
	Default func(string)

	// this function is called to implement command completion.
	// it should return a list of words that match the input text
	Complete func(string, string, int, int) []string

	// if true, enable shell commands
	EnableShell bool

	// this is the list of available commands indexed by command name
	Commands map[string]Command

	///////// private stuff /////////////
	completer    *Completer
	commandNames []string

        waitGroup *sync.WaitGroup
        waitMax, waitCount int
}

func (cmd *Cmd) readHistoryFile() {
	if len(cmd.HistoryFile) == 0 {
		// no history file
		return
	}

	filepath := cmd.HistoryFile // start with current directory
	if _, err := os.Stat(filepath); err == nil {
		if err := readline.ReadHistoryFile(filepath); err != nil {
			fmt.Println(err)
		}

		return
	}

	filepath = path.Join(os.Getenv("HOME"), filepath) // then check home directory
	if _, err := os.Stat(filepath); err == nil {
		if err := readline.ReadHistoryFile(filepath); err != nil {
			fmt.Println(err)
		}
	}

	// update HistoryFile with home path
	cmd.HistoryFile = filepath
}

func (cmd *Cmd) writeHistoryFile() {
	if len(cmd.HistoryFile) == 0 {
		// no history file
		return
	}

	if err := readline.WriteHistoryFile(cmd.HistoryFile); err != nil {
		fmt.Println(err)
	}
}

//
// Initialize the command interpreter context
//
func (cmd *Cmd) Init() {
	if cmd.PreLoop == nil {
		cmd.PreLoop = func() {}
	}
	if cmd.PostLoop == nil {
		cmd.PostLoop = func() {}
	}
	if cmd.PreCmd == nil {
		cmd.PreCmd = func(string) {}
	}
	if cmd.PostCmd == nil {
		cmd.PostCmd = func(line string, stop bool) bool { return stop }
	}
	if cmd.EmptyLine == nil {
		cmd.EmptyLine = func() {}
	}
	if cmd.Default == nil {
		cmd.Default = func(line string) { fmt.Printf("invalid command: %v\n", line) }
	}

	cmd.Commands = make(map[string]Command)
	cmd.Add(Command{"help", `list available commands`, cmd.Help})
	cmd.Add(Command{"go", `go cmd: asynchronous execution of cmd, or 'go [--start|--wait]'`, cmd.Go})
}

//
// Add a completer that matches on command names
//
func (cmd *Cmd) addCommandCompleter() {
	cmd.commandNames = make([]string, 0, len(cmd.Commands))

	for n, _ := range cmd.Commands {
		cmd.commandNames = append(cmd.commandNames, n)
	}

	// sorting for Help()
	sort.Strings(cmd.commandNames)

	cmd.completer = NewCompleter(cmd.commandNames)
	//readline.SetCompletionEntryFunction(completer.Complete)

	readline.SetAttemptedCompletionFunction(cmd.attemptedCompletion)
}

func (cmd *Cmd) attemptedCompletion(text string, start, end int) []string {
	if start == 0 { // this is the command to match
		return readline.CompletionMatches(text, cmd.completer.Complete)
	} else if cmd.Complete != nil {
		return cmd.Complete(text, readline.GetLineBuffer(), start, end)
	} else {
		return nil
	}
}

//
// execute shell command
//
func shellExec(command string) {
	args := args.GetArgs(command)
	if len(args) < 1 {
		fmt.Println("No command to exec")
	} else {
		cmd := exec.Command(args[0])
		cmd.Args = args
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		if err := cmd.Run(); err != nil {
			fmt.Println(err)
		}
	}
}

// Add a command to the command interpreter.
// Overrides a command with the same name, if there was one
//
func (cmd *Cmd) Add(command Command) {
	cmd.Commands[command.Name] = command
}

//
// Default help command.
// It lists all available commands or it displays the help for the specified command
//
func (cmd *Cmd) Help(line string) (stop bool) {
	fmt.Println("")

	if len(line) == 0 {
		fmt.Println("Available commands (use 'help <topic>'):")
		fmt.Println("================================================================")

		tp := pretty.NewTabPrinter(8)

		for _, c := range cmd.commandNames {
			tp.Print(c)
		}

		tp.Println()
	} else {
		c, ok := cmd.Commands[line]
		if ok {
			if len(c.Help) > 0 {
				fmt.Println(c.Help)
			} else {
				fmt.Println("No help for ", line)
			}
		} else {
			fmt.Println("unknown command")
		}
	}

	fmt.Println("")
	return
}

func (cmd *Cmd) Go(line string) (stop bool) {
        if strings.HasPrefix(line, "-") {
            // should be --start or --wait

	    args := args.ParseArgs(line)

            if _, ok := args.Options["start"]; ok {
                cmd.waitGroup = new(sync.WaitGroup)
                cmd.waitCount = 0
                cmd.waitMax = 0

                if len(args.Arguments) > 0 {
                    cmd.waitMax, _ = strconv.Atoi(args.Arguments[0])
                }

                return
            }

            if _, ok := args.Options["wait"]; ok {
                if cmd.waitGroup == nil {
                    fmt.Println("nothing to wait on")
                } else {
                    cmd.waitGroup.Wait()
                    cmd.waitGroup = nil
                }

                return
            }
        }

	if strings.HasPrefix(line, "go ") {
		fmt.Println("Don't go go me!")
	} else {
                if cmd.waitGroup == nil {
		    go cmd.OneCmd(line)
                } else {
                    if cmd.waitMax > 0 {
                        if cmd.waitCount >= cmd.waitMax {
                            cmd.waitGroup.Wait()
                            cmd.waitCount = 0
                        }
                    }

                    cmd.waitCount++
                    cmd.waitGroup.Add(1)
                    go func() {
                        defer cmd.waitGroup.Done()
                        cmd.OneCmd(line)
                    }()
                }
	}

	return
}

//
// This method executes one command
//
func (cmd *Cmd) OneCmd(line string) (stop bool) {

	if cmd.EnableShell && strings.HasPrefix(line, "!") {
		shellExec(line[1:])
		return
	}

	parts := strings.SplitN(line, " ", 2)
	cname := parts[0]

	command, ok := cmd.Commands[cname]

	if ok {
		var params string

		if len(parts) > 1 {
			params = strings.TrimSpace(parts[1])
		}

		stop = command.Call(params)
	} else {
		cmd.Default(line)
	}

	return
}

//
// This is the command interpreter entry point.
// It displays a prompt, waits for a command and executes it until the selected command returns true
//
func (cmd *Cmd) CmdLoop() {
	if len(cmd.Prompt) == 0 {
		cmd.Prompt = "> "
	}

	cmd.addCommandCompleter()

	cmd.PreLoop()

	cmd.readHistoryFile()

	// loop until ReadLine returns nil (signalling EOF)
	for {
		result := readline.ReadLine(&cmd.Prompt)
		if result == nil {
			break
		}

		line := strings.TrimSpace(*result)
		if line == "" {
			cmd.EmptyLine()
			continue
		}

		readline.AddHistory(*result) // allow user to recall this line

		cmd.PreCmd(line)

		stop := cmd.OneCmd(line)
		stop = cmd.PostCmd(line, stop)

		if stop {
			break
		}
	}

	cmd.writeHistoryFile()

	cmd.PostLoop()
}
<|endoftext|>


<commit_before>package env

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"strconv"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/goanywhere/fs"
)

const tag string = "env"

var pattern = regexp.MustCompile(`(export\s*)?(?P<key>\w+)\s*(=)\s*(?P<value>("|')?[\w\s-$,:/\.\+]+)("|')?`)

// findKeyValue finds '=' separated key/value pair from the given string.
func findKeyValue(line string) (key, value string) {
	// Intentionally insert a linebreak here to avoid non-stop characters on [[:graph:]].
	line = fmt.Sprintf("%s\n", line)
	match := pattern.FindStringSubmatch(line)
	if len(match) != 0 {
		result := make(map[string]string)
		for index, name := range pattern.SubexpNames() {
			result[name] = match[index]
		}
		// preserve those quoted spaces for value.
		key, value = result["key"], strings.TrimSpace(result["value"])
		value = strings.Trim(value, "'")
		value = strings.Trim(value, "\"")
	}
	return
}

// Load parses & set the values in the given files into os environment.
func Load(dotenv string) {
	if fs.Exists(dotenv) {
		dotenv, _ = filepath.Abs(dotenv)

		if file, err := os.Open(dotenv); err == nil {
			defer file.Close()
			scanner := bufio.NewScanner(file)
			for scanner.Scan() {
				k, v := findKeyValue(scanner.Text())
				if k != "" && v != "" {
					Set(k, v)
				} else {
					continue
				}
			}
		}
	}
}

// Get retrieves the string value of the environment variable named by the key.
// It returns the value, which will be empty if the variable is not present.
func Get(key string) (value string, exists bool) {
	if v := os.Getenv(key); v != "" {
		value, exists = v, true
	}
	return
}

// Set stores the value of the environment variable named by the key. It returns an error, if any.
func Set(key string, value interface{}) error {
	var sv string

	switch T := value.(type) {
	case bool:
		sv = strconv.FormatBool(T)

	case float32, float64:
		sv = strconv.FormatFloat(reflect.ValueOf(value).Float(), 'g', -1, 64)

	case int, int8, int16, int32, int64:
		sv = strconv.FormatInt(reflect.ValueOf(value).Int(), 10)

	case uint, uint8, uint16, uint32, uint64:
		sv = strconv.FormatUint(reflect.ValueOf(value).Uint(), 10)

	case string:
		sv = value.(string)

	case []string:
		sv = strings.Join(value.([]string), ",")

	default:
		return fmt.Errorf("Unsupported type: %v", T)
	}
	return os.Setenv(key, sv)
}

// String retrieves the string value from environment named by the key.
func String(key string, fallback ...string) (value string) {
	if str, exists := Get(key); exists {
		value = str
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Strings retrieves the string values separated by comma from the environment.
func Strings(key string, fallback ...[]string) (value []string) {
	if v, exists := Get(key); exists {
		for _, item := range strings.Split(strings.TrimSpace(v), ",") {
			value = append(value, strings.TrimSpace(item))
		}
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Int retrieves the integer values separated by comma from the environment.
func Int(key string, fallback ...int) (value int) {
	if str, exists := Get(key); exists {
		if v, e := strconv.ParseInt(str, 10, 0); e == nil {
			value = int(v)
		}
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Int retrieves the 64-bit integer values separated by comma from the environment.
func Int64(key string, fallback ...int64) (value int64) {
	if str, exists := Get(key); exists {
		if v, e := strconv.ParseInt(str, 10, 64); e == nil {
			value = v
		}
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Uint retrieves the unsigned integer values separated by comma from the environment.
func Uint(key string, fallback ...uint) (value uint) {
	if str, exists := Get(key); exists {
		if v, e := strconv.ParseUint(str, 10, 0); e == nil {
			value = uint(v)
		}
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Uint64 retrieves the 64-bit unsigned integer values separated by comma from the environment.
func Uint64(key string, fallback ...uint64) (value uint64) {
	if str, exists := Get(key); exists {
		if v, e := strconv.ParseUint(str, 10, 64); e == nil {
			value = v
		}
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Bool retrieves boolean value from the environment.
func Bool(key string, fallback ...bool) (value bool) {
	if str, exists := Get(key); exists {
		if v, e := strconv.ParseBool(str); e == nil {
			value = v
		}
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Float retrieves float (64) value from the environment.
func Float(key string, fallback ...float64) (value float64) {
	if str, exists := Get(key); exists {
		if v, e := strconv.ParseFloat(str, 64); e == nil {
			value = v
		}
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Map fetches the key/value pair from os.Environ into the given spec.
// Tags are supported via `env:ACTUAL_OS_KEY`.
func Map(spec interface{}) error {
	value := reflect.ValueOf(spec)
	s := value.Elem()

	var stype reflect.Type = s.Type()
	var field reflect.Value

	for index := 0; index < s.NumField(); index++ {
		field = s.Field(index)
		if field.CanSet() {

			key := stype.Field(index).Tag.Get(tag)
			if key == "" {
				key = stype.Field(index).Name
			}

			value, exists := Get(key)
			if !exists {
				continue
			}
			// converts the environmental value from string to its real type.
			// Supports: String | Strings | Bool | Float | Integer | Unsiged Integer
			switch field.Kind() {
			case reflect.String:
				field.SetString(value)

			case reflect.Bool:
				field.SetBool(Bool(key))

			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				field.SetInt(Int64(key))

			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				field.SetUint(Uint64(key))

			case reflect.Float32, reflect.Float64:
				field.SetFloat(Float(key))

			case reflect.Slice:
				switch field.Interface().(type) {
				case []string:
					field.Set(reflect.ValueOf(Strings(key)))

				default:
					log.Fatalf("Only string slice is supported")
				}
			}
		}
	}
	return nil
}
<commit_msg>Update env.go<commit_after>package env

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"strconv"
	"strings"

	log "github.com/sirupsen/logrus"
	"github.com/goanywhere/fs"
)

const tag string = "env"

var pattern = regexp.MustCompile(`(export\s*)?(?P<key>\w+)\s*(=)\s*(?P<value>("|')?[\w\s-$,:/\.\+]+)("|')?`)

// findKeyValue finds '=' separated key/value pair from the given string.
func findKeyValue(line string) (key, value string) {
	// Intentionally insert a linebreak here to avoid non-stop characters on [[:graph:]].
	line = fmt.Sprintf("%s\n", line)
	match := pattern.FindStringSubmatch(line)
	if len(match) != 0 {
		result := make(map[string]string)
		for index, name := range pattern.SubexpNames() {
			result[name] = match[index]
		}
		// preserve those quoted spaces for value.
		key, value = result["key"], strings.TrimSpace(result["value"])
		value = strings.Trim(value, "'")
		value = strings.Trim(value, "\"")
	}
	return
}

// Load parses & set the values in the given files into os environment.
func Load(dotenv string) {
	if fs.Exists(dotenv) {
		dotenv, _ = filepath.Abs(dotenv)

		if file, err := os.Open(dotenv); err == nil {
			defer file.Close()
			scanner := bufio.NewScanner(file)
			for scanner.Scan() {
				k, v := findKeyValue(scanner.Text())
				if k != "" && v != "" {
					Set(k, v)
				} else {
					continue
				}
			}
		}
	}
}

// Get retrieves the string value of the environment variable named by the key.
// It returns the value, which will be empty if the variable is not present.
func Get(key string) (value string, exists bool) {
	if v := os.Getenv(key); v != "" {
		value, exists = v, true
	}
	return
}

// Set stores the value of the environment variable named by the key. It returns an error, if any.
func Set(key string, value interface{}) error {
	var sv string

	switch T := value.(type) {
	case bool:
		sv = strconv.FormatBool(T)

	case float32, float64:
		sv = strconv.FormatFloat(reflect.ValueOf(value).Float(), 'g', -1, 64)

	case int, int8, int16, int32, int64:
		sv = strconv.FormatInt(reflect.ValueOf(value).Int(), 10)

	case uint, uint8, uint16, uint32, uint64:
		sv = strconv.FormatUint(reflect.ValueOf(value).Uint(), 10)

	case string:
		sv = value.(string)

	case []string:
		sv = strings.Join(value.([]string), ",")

	default:
		return fmt.Errorf("Unsupported type: %v", T)
	}
	return os.Setenv(key, sv)
}

// String retrieves the string value from environment named by the key.
func String(key string, fallback ...string) (value string) {
	if str, exists := Get(key); exists {
		value = str
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Strings retrieves the string values separated by comma from the environment.
func Strings(key string, fallback ...[]string) (value []string) {
	if v, exists := Get(key); exists {
		for _, item := range strings.Split(strings.TrimSpace(v), ",") {
			value = append(value, strings.TrimSpace(item))
		}
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Int retrieves the integer values separated by comma from the environment.
func Int(key string, fallback ...int) (value int) {
	if str, exists := Get(key); exists {
		if v, e := strconv.ParseInt(str, 10, 0); e == nil {
			value = int(v)
		}
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Int retrieves the 64-bit integer values separated by comma from the environment.
func Int64(key string, fallback ...int64) (value int64) {
	if str, exists := Get(key); exists {
		if v, e := strconv.ParseInt(str, 10, 64); e == nil {
			value = v
		}
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Uint retrieves the unsigned integer values separated by comma from the environment.
func Uint(key string, fallback ...uint) (value uint) {
	if str, exists := Get(key); exists {
		if v, e := strconv.ParseUint(str, 10, 0); e == nil {
			value = uint(v)
		}
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Uint64 retrieves the 64-bit unsigned integer values separated by comma from the environment.
func Uint64(key string, fallback ...uint64) (value uint64) {
	if str, exists := Get(key); exists {
		if v, e := strconv.ParseUint(str, 10, 64); e == nil {
			value = v
		}
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Bool retrieves boolean value from the environment.
func Bool(key string, fallback ...bool) (value bool) {
	if str, exists := Get(key); exists {
		if v, e := strconv.ParseBool(str); e == nil {
			value = v
		}
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Float retrieves float (64) value from the environment.
func Float(key string, fallback ...float64) (value float64) {
	if str, exists := Get(key); exists {
		if v, e := strconv.ParseFloat(str, 64); e == nil {
			value = v
		}
	} else if len(fallback) > 0 {
		value = fallback[0]
	}
	return
}

// Map fetches the key/value pair from os.Environ into the given spec.
// Tags are supported via `env:ACTUAL_OS_KEY`.
func Map(spec interface{}) error {
	value := reflect.ValueOf(spec)
	s := value.Elem()

	var stype reflect.Type = s.Type()
	var field reflect.Value

	for index := 0; index < s.NumField(); index++ {
		field = s.Field(index)
		if field.CanSet() {

			key := stype.Field(index).Tag.Get(tag)
			if key == "" {
				key = stype.Field(index).Name
			}

			value, exists := Get(key)
			if !exists {
				continue
			}
			// converts the environmental value from string to its real type.
			// Supports: String | Strings | Bool | Float | Integer | Unsiged Integer
			switch field.Kind() {
			case reflect.String:
				field.SetString(value)

			case reflect.Bool:
				field.SetBool(Bool(key))

			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				field.SetInt(Int64(key))

			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				field.SetUint(Uint64(key))

			case reflect.Float32, reflect.Float64:
				field.SetFloat(Float(key))

			case reflect.Slice:
				switch field.Interface().(type) {
				case []string:
					field.Set(reflect.ValueOf(Strings(key)))

				default:
					log.Fatalf("Only string slice is supported")
				}
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package multiconfig

import (
	"fmt"
	"os"
	"strings"

	"github.com/fatih/structs"
)

// EnvironmentLoader satisifies the loader interface. It loads the
// configuration from the environment variables in the form of
// STRUCTNAME_FIELDNAME.
type EnvironmentLoader struct{}

func (e *EnvironmentLoader) Load(s interface{}) error {
	strct := structs.New(s)
	strctName := strct.Name()

	for _, field := range strct.Fields() {
		envName := strings.ToUpper(strctName) + "_" + strings.ToUpper(field.Name())

		v := os.Getenv(envName)
		if v == "" {
			continue
		}

		if err := fieldSet(field, v); err != nil {
			return err
		}
	}

	return nil
}

// PrintEnvs prints the generated environment variables to the std out.
func (e *EnvironmentLoader) PrintEnvs(s interface{}) {
	strct := structs.New(s)
	strctName := strct.Name()

	for _, field := range strct.Fields() {
		envName := strings.ToUpper(strctName) + "_" + strings.ToUpper(field.Name())
		fmt.Println("  ", envName)
	}
}
<commit_msg>Multiconfig: add embedded struct support<commit_after>package multiconfig

import (
	"fmt"
	"os"
	"reflect"
	"strings"

	"github.com/fatih/structs"
)

// EnvironmentLoader satisifies the loader interface. It loads the
// configuration from the environment variables in the form of
// STRUCTNAME_FIELDNAME.
type EnvironmentLoader struct{}

func (e *EnvironmentLoader) Load(s interface{}) error {
	strct := structs.New(s)
	strctName := strct.Name()

	for _, field := range strct.Fields() {
		if err := processField(strctName, field); err != nil {
			return err
		}
	}

	return nil
}

// processField gets leading name for the env variable and combines the current
// field's name and generates environemnt variable names recursively
func processField(prefix string, field *structs.Field) error {
	fieldName := strings.ToUpper(prefix) + "_" + strings.ToUpper(field.Name())

	switch field.Kind() {
	case reflect.Struct:
		for _, f := range field.Fields() {
			if err := processField(fieldName, f); err != nil {
				return err
			}
		}
	default:
		v := os.Getenv(fieldName)
		if v == "" {
			return nil
		}

		if err := fieldSet(field, v); err != nil {
			return err
		}
	}

	return nil
}

// PrintEnvs prints the generated environment variables to the std out.
func (e *EnvironmentLoader) PrintEnvs(s interface{}) {
	strct := structs.New(s)
	strctName := strct.Name()

	for _, field := range strct.Fields() {
		envName := strings.ToUpper(strctName) + "_" + strings.ToUpper(field.Name())
		fmt.Println("  ", envName)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Boise State University All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"sort"
	"strings"
)

var (
	// Enable Content Security Policy.  We'll use the full blanket of 'self' for
	// everything as long as we can.  We only had one inline javascript section,
	// and we moved that into assets/static.  The /csp_report URI receives the
	// reports of any violations.  Note that an empty string slice actually means
	// no value is set, but the key is present.  This has the affect of setting
	// that key to 'none', which disallows that resource.  For example:
	//
	// "script-src": []string{}
	//
	// generates: "script-src;"
	//
	// which is evaluated in firefox as "script-src 'none';"
	pairs = map[string][]string{
		"default-src":     []string{"'self'"},
		"script-src":      nil,
		"style-src":       nil,
		"img-src":         nil,
		"connect-src":     nil,
		"font-src":        nil,
		"object-src":      nil,
		"media-src":       nil,
		"sandbox":         nil,
		"report-uri":      []string{"/csp_report"},
		"child-src":       nil,
		"form-action":     nil,
		"frame-ancestors": nil,
		"plugin-types":    nil,
	}
	sortedCSPKeys []string
)

const (
	// cspKey is the header key for the assigned values
	cspKey = "Content-Security-Policy"

	// expCSP is what we should get based on the key value pairs above
	expCSP = "default-src 'self'; report-uri /csp_report; "
)

func init() {
	sortedCSPKeys = make([]string, len(pairs))
	i := 0
	for k := range pairs {
		sortedCSPKeys[i] = k
		i++
	}
	sort.Strings(sortedCSPKeys)
}

func csp() string {
	s := ""
	for _, k := range sortedCSPKeys {
		v, ok := pairs[k]
		if ok && v != nil {
			s += k + " " + strings.Join(v, " ")
			s += "; "
		}
	}
	return s
}
<commit_msg>add frame-ancestor csp<commit_after>// Copyright (c) 2017, Boise State University All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"sort"
	"strings"
)

var (
	// Enable Content Security Policy.  We'll use the full blanket of 'self' for
	// everything as long as we can.  We only had one inline javascript section,
	// and we moved that into assets/static.  The /csp_report URI receives the
	// reports of any violations.  Note that an empty string slice actually means
	// no value is set, but the key is present.  This has the affect of setting
	// that key to 'none', which disallows that resource.  For example:
	//
	// "script-src": []string{}
	//
	// generates: "script-src;"
	//
	// which is evaluated in firefox as "script-src 'none';"
	pairs = map[string][]string{
		"default-src":     []string{"'self'"},
		"script-src":      nil,
		"style-src":       nil,
		"img-src":         nil,
		"connect-src":     nil,
		"font-src":        nil,
		"object-src":      nil,
		"media-src":       nil,
		"sandbox":         nil,
		"report-uri":      []string{"/csp_report"},
		"child-src":       nil,
		"form-action":     nil,
		"frame-ancestors": []string{"'none'"},
		"plugin-types":    nil,
	}
	sortedCSPKeys []string
)

const (
	// cspKey is the header key for the assigned values
	cspKey = "Content-Security-Policy"

	// expCSP is what we should get based on the key value pairs above
	expCSP = "default-src 'self'; report-uri /csp_report; "
)

func init() {
	sortedCSPKeys = make([]string, len(pairs))
	i := 0
	for k := range pairs {
		sortedCSPKeys[i] = k
		i++
	}
	sort.Strings(sortedCSPKeys)
}

func csp() string {
	s := ""
	for _, k := range sortedCSPKeys {
		v, ok := pairs[k]
		if ok && v != nil {
			s += k + " " + strings.Join(v, " ")
			s += "; "
		}
	}
	return s
}
<|endoftext|>


<commit_before>package kcp

import (
	"encoding/binary"
	"sync/atomic"

	"github.com/klauspost/reedsolomon"
)

const (
	fecHeaderSize      = 6
	fecHeaderSizePlus2 = fecHeaderSize + 2 // plus 2B data size
	typeData           = 0xf1
	typeFEC            = 0xf2
)

type (
	// FEC defines forward error correction for packets
	FEC struct {
		rxlimit      int // queue size limit
		dataShards   int
		parityShards int
		shardSize    int
		next         uint32      // next seqid
		paws         uint32      // Protect Against Wrapped Sequence numbers
		rx           []fecPacket // ordered receive queue

		// caches
		decodeCache [][]byte
		encodeCache [][]byte
		shardsflag  []bool

		// RS encoder
		enc reedsolomon.Encoder
	}

	// fecPacket is a decoded FEC packet
	fecPacket struct {
		seqid uint32
		flag  uint16
		data  []byte
	}
)

func newFEC(rxlimit, dataShards, parityShards int) *FEC {
	if dataShards <= 0 || parityShards <= 0 {
		return nil
	}
	if rxlimit < dataShards+parityShards {
		return nil
	}

	fec := new(FEC)
	fec.rxlimit = rxlimit
	fec.dataShards = dataShards
	fec.parityShards = parityShards
	fec.shardSize = dataShards + parityShards
	fec.paws = (0xffffffff/uint32(fec.shardSize) - 1) * uint32(fec.shardSize)
	enc, err := reedsolomon.New(dataShards, parityShards, reedsolomon.WithMaxGoroutines(1))
	if err != nil {
		return nil
	}
	fec.enc = enc
	fec.decodeCache = make([][]byte, fec.shardSize)
	fec.encodeCache = make([][]byte, fec.shardSize)
	fec.shardsflag = make([]bool, fec.shardSize)
	return fec
}

// decodeBytes a fec packet
func (fec *FEC) decodeBytes(data []byte) fecPacket {
	var pkt fecPacket
	pkt.seqid = binary.LittleEndian.Uint32(data)
	pkt.flag = binary.LittleEndian.Uint16(data[4:])
	// allocate memory & copy
	buf := xmitBuf.Get().([]byte)[:len(data)-6]
	copy(buf, data[6:])
	pkt.data = buf
	return pkt
}

func (fec *FEC) markData(data []byte) {
	binary.LittleEndian.PutUint32(data, fec.next)
	binary.LittleEndian.PutUint16(data[4:], typeData)
	fec.next++
}

func (fec *FEC) markFEC(data []byte) {
	binary.LittleEndian.PutUint32(data, fec.next)
	binary.LittleEndian.PutUint16(data[4:], typeFEC)
	fec.next++
	fec.next %= fec.paws
}

// Decode a fec packet
func (fec *FEC) Decode(pkt fecPacket) (recovered [][]byte) {
	// insertion
	n := len(fec.rx) - 1
	insertIdx := 0
	for i := n; i >= 0; i-- {
		if pkt.seqid == fec.rx[i].seqid { // de-duplicate
			xmitBuf.Put(pkt.data)
			return nil
		} else if _itimediff(pkt.seqid, fec.rx[i].seqid) > 0 { // insertion
			insertIdx = i + 1
			break
		}
	}

	// insert into ordered rx queue
	if insertIdx == n+1 {
		fec.rx = append(fec.rx, pkt)
	} else {
		fec.rx = append(fec.rx, fecPacket{})
		copy(fec.rx[insertIdx+1:], fec.rx[insertIdx:])
		fec.rx[insertIdx] = pkt
	}

	// shard range for current packet
	shardBegin := pkt.seqid - pkt.seqid%uint32(fec.shardSize)
	shardEnd := shardBegin + uint32(fec.shardSize) - 1

	// max search range in ordered queue for current shard
	searchBegin := insertIdx - int(pkt.seqid%uint32(fec.shardSize))
	if searchBegin < 0 {
		searchBegin = 0
	}
	searchEnd := searchBegin + fec.shardSize - 1
	if searchEnd >= len(fec.rx) {
		searchEnd = len(fec.rx) - 1
	}

	// re-construct datashards
	if searchEnd > searchBegin && searchEnd-searchBegin+1 >= fec.dataShards {
		numshard := 0
		numDataShard := 0
		first := -1
		maxlen := 0
		shards := fec.decodeCache
		shardsflag := fec.shardsflag
		for k := range fec.decodeCache {
			shards[k] = nil
			shardsflag[k] = false
		}

		for i := searchBegin; i <= searchEnd; i++ {
			seqid := fec.rx[i].seqid
			if _itimediff(seqid, shardEnd) > 0 {
				break
			} else if _itimediff(seqid, shardBegin) >= 0 {
				shards[seqid%uint32(fec.shardSize)] = fec.rx[i].data
				shardsflag[seqid%uint32(fec.shardSize)] = true
				numshard++
				if fec.rx[i].flag == typeData {
					numDataShard++
				}
				if numshard == 1 {
					first = i
				}
				if len(fec.rx[i].data) > maxlen {
					maxlen = len(fec.rx[i].data)
				}
			}
		}

		if numDataShard == fec.dataShards { // no lost
			for i := first; i < first+numshard; i++ { // free
				xmitBuf.Put(fec.rx[i].data)
			}
			copy(fec.rx[first:], fec.rx[first+numshard:])
			for i := 0; i < numshard; i++ { // dereference
				fec.rx[len(fec.rx)-1-i] = fecPacket{}
			}
			fec.rx = fec.rx[:len(fec.rx)-numshard]
		} else if numshard >= fec.dataShards { // recoverable
			for k := range shards {
				if shards[k] != nil {
					dlen := len(shards[k])
					shards[k] = shards[k][:maxlen]
					xorBytes(shards[k][dlen:], shards[k][dlen:], shards[k][dlen:])
				}
			}
			if err := fec.enc.Reconstruct(shards); err == nil {
				for k := range shards[:fec.dataShards] {
					if !shardsflag[k] {
						recovered = append(recovered, shards[k])
					}
				}
			}

			for i := first; i < first+numshard; i++ { // free
				xmitBuf.Put(fec.rx[i].data)
			}
			copy(fec.rx[first:], fec.rx[first+numshard:])
			for i := 0; i < numshard; i++ { // dereference
				fec.rx[len(fec.rx)-1-i] = fecPacket{}
			}
			fec.rx = fec.rx[:len(fec.rx)-numshard]
		}
	}

	// keep rxlimit
	if len(fec.rx) > fec.rxlimit {
		if fec.rx[0].flag == typeData { // record unrecoverable data
			atomic.AddUint64(&DefaultSnmp.FECShortShards, 1)
		}
		xmitBuf.Put(fec.rx[0].data) // free
		fec.rx[0].data = nil
		fec.rx = fec.rx[1:]
	}
	return
}

// Encode a group of datashards
func (fec *FEC) Encode(data [][]byte, offset, maxlen int) (ecc [][]byte) {
	if len(data) != fec.shardSize {
		return nil
	}
	shards := fec.encodeCache
	for k := range shards {
		shards[k] = data[k][offset:maxlen]
	}

	if err := fec.enc.Encode(shards); err != nil {
		return nil
	}
	return data[fec.dataShards:]
}
<commit_msg>use shifting fec queue<commit_after>package kcp

import (
	"encoding/binary"
	"sync/atomic"

	"github.com/klauspost/reedsolomon"
)

const (
	fecHeaderSize      = 6
	fecHeaderSizePlus2 = fecHeaderSize + 2 // plus 2B data size
	typeData           = 0xf1
	typeFEC            = 0xf2
)

type (
	// FEC defines forward error correction for packets
	FEC struct {
		rxlimit      int // queue size limit
		dataShards   int
		parityShards int
		shardSize    int
		next         uint32      // next seqid
		paws         uint32      // Protect Against Wrapped Sequence numbers
		rx           []fecPacket // ordered receive queue

		// caches
		decodeCache [][]byte
		encodeCache [][]byte
		shardsflag  []bool

		// RS encoder
		enc reedsolomon.Encoder
	}

	// fecPacket is a decoded FEC packet
	fecPacket struct {
		seqid uint32
		flag  uint16
		data  []byte
	}
)

func newFEC(rxlimit, dataShards, parityShards int) *FEC {
	if dataShards <= 0 || parityShards <= 0 {
		return nil
	}
	if rxlimit < dataShards+parityShards {
		return nil
	}

	fec := new(FEC)
	fec.rxlimit = rxlimit
	fec.dataShards = dataShards
	fec.parityShards = parityShards
	fec.shardSize = dataShards + parityShards
	fec.paws = (0xffffffff/uint32(fec.shardSize) - 1) * uint32(fec.shardSize)
	enc, err := reedsolomon.New(dataShards, parityShards, reedsolomon.WithMaxGoroutines(1))
	if err != nil {
		return nil
	}
	fec.enc = enc
	fec.decodeCache = make([][]byte, fec.shardSize)
	fec.encodeCache = make([][]byte, fec.shardSize)
	fec.shardsflag = make([]bool, fec.shardSize)
	return fec
}

// decodeBytes a fec packet
func (fec *FEC) decodeBytes(data []byte) fecPacket {
	var pkt fecPacket
	pkt.seqid = binary.LittleEndian.Uint32(data)
	pkt.flag = binary.LittleEndian.Uint16(data[4:])
	// allocate memory & copy
	buf := xmitBuf.Get().([]byte)[:len(data)-6]
	copy(buf, data[6:])
	pkt.data = buf
	return pkt
}

func (fec *FEC) markData(data []byte) {
	binary.LittleEndian.PutUint32(data, fec.next)
	binary.LittleEndian.PutUint16(data[4:], typeData)
	fec.next++
}

func (fec *FEC) markFEC(data []byte) {
	binary.LittleEndian.PutUint32(data, fec.next)
	binary.LittleEndian.PutUint16(data[4:], typeFEC)
	fec.next++
	fec.next %= fec.paws
}

// Decode a fec packet
func (fec *FEC) Decode(pkt fecPacket) (recovered [][]byte) {
	// insertion
	n := len(fec.rx) - 1
	insertIdx := 0
	for i := n; i >= 0; i-- {
		if pkt.seqid == fec.rx[i].seqid { // de-duplicate
			xmitBuf.Put(pkt.data)
			return nil
		} else if _itimediff(pkt.seqid, fec.rx[i].seqid) > 0 { // insertion
			insertIdx = i + 1
			break
		}
	}

	// insert into ordered rx queue
	if insertIdx == n+1 {
		fec.rx = append(fec.rx, pkt)
	} else {
		fec.rx = append(fec.rx, fecPacket{})
		copy(fec.rx[insertIdx+1:], fec.rx[insertIdx:])
		fec.rx[insertIdx] = pkt
	}

	// shard range for current packet
	shardBegin := pkt.seqid - pkt.seqid%uint32(fec.shardSize)
	shardEnd := shardBegin + uint32(fec.shardSize) - 1

	// max search range in ordered queue for current shard
	searchBegin := insertIdx - int(pkt.seqid%uint32(fec.shardSize))
	if searchBegin < 0 {
		searchBegin = 0
	}
	searchEnd := searchBegin + fec.shardSize - 1
	if searchEnd >= len(fec.rx) {
		searchEnd = len(fec.rx) - 1
	}

	// re-construct datashards
	if searchEnd > searchBegin && searchEnd-searchBegin+1 >= fec.dataShards {
		numshard := 0
		numDataShard := 0
		first := -1
		maxlen := 0
		shards := fec.decodeCache
		shardsflag := fec.shardsflag
		for k := range fec.decodeCache {
			shards[k] = nil
			shardsflag[k] = false
		}

		for i := searchBegin; i <= searchEnd; i++ {
			seqid := fec.rx[i].seqid
			if _itimediff(seqid, shardEnd) > 0 {
				break
			} else if _itimediff(seqid, shardBegin) >= 0 {
				shards[seqid%uint32(fec.shardSize)] = fec.rx[i].data
				shardsflag[seqid%uint32(fec.shardSize)] = true
				numshard++
				if fec.rx[i].flag == typeData {
					numDataShard++
				}
				if numshard == 1 {
					first = i
				}
				if len(fec.rx[i].data) > maxlen {
					maxlen = len(fec.rx[i].data)
				}
			}
		}

		if numDataShard == fec.dataShards { // no lost
			for i := first; i < first+numshard; i++ { // free
				xmitBuf.Put(fec.rx[i].data)
			}
			copy(fec.rx[first:], fec.rx[first+numshard:])
			for i := 0; i < numshard; i++ { // dereference
				fec.rx[len(fec.rx)-1-i] = fecPacket{}
			}
			fec.rx = fec.rx[:len(fec.rx)-numshard]
		} else if numshard >= fec.dataShards { // recoverable
			for k := range shards {
				if shards[k] != nil {
					dlen := len(shards[k])
					shards[k] = shards[k][:maxlen]
					xorBytes(shards[k][dlen:], shards[k][dlen:], shards[k][dlen:])
				}
			}
			if err := fec.enc.Reconstruct(shards); err == nil {
				for k := range shards[:fec.dataShards] {
					if !shardsflag[k] {
						recovered = append(recovered, shards[k])
					}
				}
			}

			for i := first; i < first+numshard; i++ { // free
				xmitBuf.Put(fec.rx[i].data)
			}
			copy(fec.rx[first:], fec.rx[first+numshard:])
			for i := 0; i < numshard; i++ { // dereference
				fec.rx[len(fec.rx)-1-i] = fecPacket{}
			}
			fec.rx = fec.rx[:len(fec.rx)-numshard]
		}
	}

	// keep rxlimit
	if len(fec.rx) > fec.rxlimit {
		if fec.rx[0].flag == typeData { // record unrecoverable data
			atomic.AddUint64(&DefaultSnmp.FECShortShards, 1)
		}
		xmitBuf.Put(fec.rx[0].data) // free
		copy(fec.rx, fec.rx[1:])    // shift left
		fec.rx[len(fec.rx)-1] = fecPacket{}
		fec.rx = fec.rx[:len(fec.rx)-1]
	}
	return
}

// Encode a group of datashards
func (fec *FEC) Encode(data [][]byte, offset, maxlen int) (ecc [][]byte) {
	if len(data) != fec.shardSize {
		return nil
	}
	shards := fec.encodeCache
	for k := range shards {
		shards[k] = data[k][offset:maxlen]
	}

	if err := fec.enc.Encode(shards); err != nil {
		return nil
	}
	return data[fec.dataShards:]
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"monkey/formatter"
	"os"
)

func main() {
	args := os.Args[1:]

	var f []byte
	var err error
	if len(args) == 0 {
		f, err = ioutil.ReadAll(os.Stdin)
	} else {
		f, err = ioutil.ReadFile(args[0])
		if err != nil {
			fmt.Println("Highlighter: cannot read file", err.Error())
			os.Exit(1)
		}
	}

	formatter := formatter.New(string(f))
	formatter.Format()
}
<commit_msg>Fix typo error of fmt.go<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"monkey/formatter"
	"os"
)

func main() {
	args := os.Args[1:]

	var f []byte
	var err error
	if len(args) == 0 {
		f, err = ioutil.ReadAll(os.Stdin)
	} else {
		f, err = ioutil.ReadFile(args[0])
		if err != nil {
			fmt.Println("Formatter: cannot read file", err.Error())
			os.Exit(1)
		}
	}

	formatter := formatter.New(string(f))
	formatter.Format()
}
<|endoftext|>