<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package forgetfs

import (
	"fmt"
	"os"
	"runtime"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/syncutil"
)

// Create a file system whose sole contents are a file named "foo" and a
// directory named "bar".
//
// The file "foo" may be opened for reading and/or writing, but reads and
// writes aren't supported. Additionally, any non-existent file or directory
// name may be created within any directory, but the resulting inode will
// appear to have been unlinked immediately.
//
// The file system maintains reference counts for the inodes involved. It will
// panic if a reference count becomes negative or if an inode ID is re-used
// after we expect it to be dead. Its Check method may be used to check that
// there are no inodes with unexpected reference counts remaining, after
// unmounting.
func NewFileSystem() (fs *ForgetFS) {
	// Set up the actual file system.
	impl := &fsImpl{
		inodes: map[fuseops.InodeID]*inode{
			cannedID_Root: &inode{
				attributes: fuseops.InodeAttributes{
					Nlink: 1,
					Mode:  0777 | os.ModeDir,
				},
			},
			cannedID_Foo: &inode{
				attributes: fuseops.InodeAttributes{
					Nlink: 1,
					Mode:  0777,
				},
			},
			cannedID_Bar: &inode{
				attributes: fuseops.InodeAttributes{
					Nlink: 1,
					Mode:  0777 | os.ModeDir,
				},
			},
		},
		nextInodeID: cannedID_Next,
	}

	// The root inode starts with a lookup count of one.
	impl.inodes[cannedID_Root].IncrementLookupCount()

	// Set up the mutex.
	impl.mu = syncutil.NewInvariantMutex(impl.checkInvariants)

	// Set up a wrapper that exposes only certain methods.
	fs = &ForgetFS{
		impl:   impl,
		server: fuseutil.NewFileSystemServer(impl),
	}

	return
}

////////////////////////////////////////////////////////////////////////
// ForgetFS
////////////////////////////////////////////////////////////////////////

type ForgetFS struct {
	impl   *fsImpl
	server fuse.Server
}

func (fs *ForgetFS) ServeOps(c *fuse.Connection) {
	fs.server.ServeOps(c)
}

// Panic if there are any inodes that have a non-zero reference count. For use
// after unmounting.
func (fs *ForgetFS) Check() {
	fs.impl.Check()
}

////////////////////////////////////////////////////////////////////////
// Actual implementation
////////////////////////////////////////////////////////////////////////

const (
	cannedID_Root = fuseops.RootInodeID + iota
	cannedID_Foo
	cannedID_Bar
	cannedID_Next
)

type fsImpl struct {
	fuseutil.NotImplementedFileSystem

	/////////////////////////
	// Mutable state
	/////////////////////////

	mu syncutil.InvariantMutex

	// An index of inode by ID, for all IDs we have issued.
	//
	// GUARDED_BY(mu)
	inodes map[fuseops.InodeID]*inode

	// The next ID to issue.
	//
	// INVARIANT: For each k in inodes, k < nextInodeID
	//
	// GUARDED_BY(mu)
	nextInodeID fuseops.InodeID
}

////////////////////////////////////////////////////////////////////////
// inode
////////////////////////////////////////////////////////////////////////

type inode struct {
	attributes fuseops.InodeAttributes

	// The current lookup count.
	lookupCount uint64

	// true if lookupCount has ever been positive.
	lookedUp bool
}

func (in *inode) Forgotten() bool {
	return in.lookedUp && in.lookupCount == 0
}

func (in *inode) IncrementLookupCount() {
	in.lookupCount++
	in.lookedUp = true
}

func (in *inode) DecrementLookupCount(n uint64) {
	if in.lookupCount < n {
		panic(fmt.Sprintf(
			"Overly large decrement: %v, %v",
			in.lookupCount,
			n))
	}

	in.lookupCount -= n
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// LOCKS_REQUIRED(fs.mu)
func (fs *fsImpl) checkInvariants() {
	// INVARIANT: For each k in inodes, k < nextInodeID
	for k, _ := range fs.inodes {
		if !(k < fs.nextInodeID) {
			panic("Unexpectedly large inode ID")
		}
	}
}

// LOCKS_EXCLUDED(fs.mu)
func (fs *fsImpl) Check() {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	// On Linux we often don't receive forget ops, and never receive destroy ops
	// (cf. http://goo.gl/EUbxEg, fuse-devel thread "Root inode lookup count").
	// So there's not really much we can check here.
	//
	// TODO(jacobsa): Figure out why we don't receive destroy. If we can reliably
	// receive it, we can treat it as "forget all".
	if runtime.GOOS == "linux" {
		return
	}

	for k, v := range fs.inodes {
		// Special case: we don't require the root inode to have reached zero.
		// OS X doesn't seem to send forgets for the root, and Linux only does
		// sometimes. But we want to make sure it's not greater than one, which
		// would be weird.
		if k == fuseops.RootInodeID {
			if v.lookupCount > 1 {
				panic(fmt.Sprintf("Root has lookup count %v", v.lookupCount))
			}

			continue
		}

		// Check other inodes.
		if v.lookupCount != 0 {
			panic(fmt.Sprintf("Inode %v has lookup count %v", k, v.lookupCount))
		}
	}
}

// Look up the inode and verify it hasn't been forgotten.
//
// LOCKS_REQUIRED(fs.mu)
func (fs *fsImpl) findInodeByID(id fuseops.InodeID) (in *inode) {
	in = fs.inodes[id]
	if in == nil {
		panic(fmt.Sprintf("Unknown inode: %v", id))
	}

	if in.Forgotten() {
		panic(fmt.Sprintf("Forgotten inode: %v", id))
	}

	return
}

////////////////////////////////////////////////////////////////////////
// FileSystem methods
////////////////////////////////////////////////////////////////////////

func (fs *fsImpl) Init(
	op *fuseops.InitOp) (err error) {
	return
}

func (fs *fsImpl) LookUpInode(
	op *fuseops.LookUpInodeOp) (err error) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Make sure the parent exists and has not been forgotten.
	_ = fs.findInodeByID(op.Parent)

	// Handle the names we support.
	var childID fuseops.InodeID
	switch {
	case op.Parent == cannedID_Root && op.Name == "foo":
		childID = cannedID_Foo

	case op.Parent == cannedID_Root && op.Name == "bar":
		childID = cannedID_Bar

	default:
		err = fuse.ENOENT
		return
	}

	// Look up the child.
	child := fs.findInodeByID(childID)
	child.IncrementLookupCount()

	// Return an appropriate entry.
	op.Entry = fuseops.ChildInodeEntry{
		Child:      childID,
		Attributes: child.attributes,
	}

	return
}

func (fs *fsImpl) GetInodeAttributes(
	op *fuseops.GetInodeAttributesOp) (err error) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Find the inode, verifying that it has not been forgotten.
	in := fs.findInodeByID(op.Inode)

	// Return appropriate attributes.
	op.Attributes = in.attributes

	return
}

func (fs *fsImpl) ForgetInode(
	op *fuseops.ForgetInodeOp) (err error) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Find the inode and decrement its count.
	in := fs.findInodeByID(op.Inode)
	in.DecrementLookupCount(op.N)

	return
}

func (fs *fsImpl) MkDir(
	op *fuseops.MkDirOp) (err error) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Make sure the parent exists and has not been forgotten.
	_ = fs.findInodeByID(op.Parent)

	// Mint a child inode.
	childID := fs.nextInodeID
	fs.nextInodeID++

	child := &inode{
		attributes: fuseops.InodeAttributes{
			Nlink: 0,
			Mode:  0777 | os.ModeDir,
		},
	}

	fs.inodes[childID] = child
	child.IncrementLookupCount()

	// Return an appropriate entry.
	op.Entry = fuseops.ChildInodeEntry{
		Child:      childID,
		Attributes: child.attributes,
	}

	return
}

func (fs *fsImpl) CreateFile(
	op *fuseops.CreateFileOp) (err error) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Make sure the parent exists and has not been forgotten.
	_ = fs.findInodeByID(op.Parent)

	// Mint a child inode.
	childID := fs.nextInodeID
	fs.nextInodeID++

	child := &inode{
		attributes: fuseops.InodeAttributes{
			Nlink: 0,
			Mode:  0777,
		},
	}

	fs.inodes[childID] = child
	child.IncrementLookupCount()

	// Return an appropriate entry.
	op.Entry = fuseops.ChildInodeEntry{
		Child:      childID,
		Attributes: child.attributes,
	}

	return
}

func (fs *fsImpl) OpenFile(
	op *fuseops.OpenFileOp) (err error) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Verify that the inode has not been forgotten.
	_ = fs.findInodeByID(op.Inode)

	return
}

func (fs *fsImpl) OpenDir(
	op *fuseops.OpenDirOp) (err error) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Verify that the inode has not been forgotten.
	_ = fs.findInodeByID(op.Inode)

	return
}
<commit_msg>Removed the Linux destroy workaround.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package forgetfs

import (
	"fmt"
	"os"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/syncutil"
)

// Create a file system whose sole contents are a file named "foo" and a
// directory named "bar".
//
// The file "foo" may be opened for reading and/or writing, but reads and
// writes aren't supported. Additionally, any non-existent file or directory
// name may be created within any directory, but the resulting inode will
// appear to have been unlinked immediately.
//
// The file system maintains reference counts for the inodes involved. It will
// panic if a reference count becomes negative or if an inode ID is re-used
// after we expect it to be dead. Its Check method may be used to check that
// there are no inodes with unexpected reference counts remaining, after
// unmounting.
func NewFileSystem() (fs *ForgetFS) {
	// Set up the actual file system.
	impl := &fsImpl{
		inodes: map[fuseops.InodeID]*inode{
			cannedID_Root: &inode{
				attributes: fuseops.InodeAttributes{
					Nlink: 1,
					Mode:  0777 | os.ModeDir,
				},
			},
			cannedID_Foo: &inode{
				attributes: fuseops.InodeAttributes{
					Nlink: 1,
					Mode:  0777,
				},
			},
			cannedID_Bar: &inode{
				attributes: fuseops.InodeAttributes{
					Nlink: 1,
					Mode:  0777 | os.ModeDir,
				},
			},
		},
		nextInodeID: cannedID_Next,
	}

	// The root inode starts with a lookup count of one.
	impl.inodes[cannedID_Root].IncrementLookupCount()

	// Set up the mutex.
	impl.mu = syncutil.NewInvariantMutex(impl.checkInvariants)

	// Set up a wrapper that exposes only certain methods.
	fs = &ForgetFS{
		impl:   impl,
		server: fuseutil.NewFileSystemServer(impl),
	}

	return
}

////////////////////////////////////////////////////////////////////////
// ForgetFS
////////////////////////////////////////////////////////////////////////

type ForgetFS struct {
	impl   *fsImpl
	server fuse.Server
}

func (fs *ForgetFS) ServeOps(c *fuse.Connection) {
	fs.server.ServeOps(c)
}

// Panic if there are any inodes that have a non-zero reference count. For use
// after unmounting.
func (fs *ForgetFS) Check() {
	fs.impl.Check()
}

////////////////////////////////////////////////////////////////////////
// Actual implementation
////////////////////////////////////////////////////////////////////////

const (
	cannedID_Root = fuseops.RootInodeID + iota
	cannedID_Foo
	cannedID_Bar
	cannedID_Next
)

type fsImpl struct {
	fuseutil.NotImplementedFileSystem

	/////////////////////////
	// Mutable state
	/////////////////////////

	mu syncutil.InvariantMutex

	// An index of inode by ID, for all IDs we have issued.
	//
	// GUARDED_BY(mu)
	inodes map[fuseops.InodeID]*inode

	// The next ID to issue.
	//
	// INVARIANT: For each k in inodes, k < nextInodeID
	//
	// GUARDED_BY(mu)
	nextInodeID fuseops.InodeID
}

////////////////////////////////////////////////////////////////////////
// inode
////////////////////////////////////////////////////////////////////////

type inode struct {
	attributes fuseops.InodeAttributes

	// The current lookup count.
	lookupCount uint64

	// true if lookupCount has ever been positive.
	lookedUp bool
}

func (in *inode) Forgotten() bool {
	return in.lookedUp && in.lookupCount == 0
}

func (in *inode) IncrementLookupCount() {
	in.lookupCount++
	in.lookedUp = true
}

func (in *inode) DecrementLookupCount(n uint64) {
	if in.lookupCount < n {
		panic(fmt.Sprintf(
			"Overly large decrement: %v, %v",
			in.lookupCount,
			n))
	}

	in.lookupCount -= n
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// LOCKS_REQUIRED(fs.mu)
func (fs *fsImpl) checkInvariants() {
	// INVARIANT: For each k in inodes, k < nextInodeID
	for k, _ := range fs.inodes {
		if !(k < fs.nextInodeID) {
			panic("Unexpectedly large inode ID")
		}
	}
}

// LOCKS_EXCLUDED(fs.mu)
func (fs *fsImpl) Check() {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	for k, v := range fs.inodes {
		// Special case: we don't require the root inode to have reached zero.
		// OS X doesn't seem to send forgets for the root, and Linux only does
		// sometimes. But we want to make sure it's not greater than one, which
		// would be weird.
		if k == fuseops.RootInodeID {
			if v.lookupCount > 1 {
				panic(fmt.Sprintf("Root has lookup count %v", v.lookupCount))
			}

			continue
		}

		// Check other inodes.
		if v.lookupCount != 0 {
			panic(fmt.Sprintf("Inode %v has lookup count %v", k, v.lookupCount))
		}
	}
}

// Look up the inode and verify it hasn't been forgotten.
//
// LOCKS_REQUIRED(fs.mu)
func (fs *fsImpl) findInodeByID(id fuseops.InodeID) (in *inode) {
	in = fs.inodes[id]
	if in == nil {
		panic(fmt.Sprintf("Unknown inode: %v", id))
	}

	if in.Forgotten() {
		panic(fmt.Sprintf("Forgotten inode: %v", id))
	}

	return
}

////////////////////////////////////////////////////////////////////////
// FileSystem methods
////////////////////////////////////////////////////////////////////////

func (fs *fsImpl) Init(
	op *fuseops.InitOp) (err error) {
	return
}

func (fs *fsImpl) LookUpInode(
	op *fuseops.LookUpInodeOp) (err error) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Make sure the parent exists and has not been forgotten.
	_ = fs.findInodeByID(op.Parent)

	// Handle the names we support.
	var childID fuseops.InodeID
	switch {
	case op.Parent == cannedID_Root && op.Name == "foo":
		childID = cannedID_Foo

	case op.Parent == cannedID_Root && op.Name == "bar":
		childID = cannedID_Bar

	default:
		err = fuse.ENOENT
		return
	}

	// Look up the child.
	child := fs.findInodeByID(childID)
	child.IncrementLookupCount()

	// Return an appropriate entry.
	op.Entry = fuseops.ChildInodeEntry{
		Child:      childID,
		Attributes: child.attributes,
	}

	return
}

func (fs *fsImpl) GetInodeAttributes(
	op *fuseops.GetInodeAttributesOp) (err error) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Find the inode, verifying that it has not been forgotten.
	in := fs.findInodeByID(op.Inode)

	// Return appropriate attributes.
	op.Attributes = in.attributes

	return
}

func (fs *fsImpl) ForgetInode(
	op *fuseops.ForgetInodeOp) (err error) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Find the inode and decrement its count.
	in := fs.findInodeByID(op.Inode)
	in.DecrementLookupCount(op.N)

	return
}

func (fs *fsImpl) MkDir(
	op *fuseops.MkDirOp) (err error) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Make sure the parent exists and has not been forgotten.
	_ = fs.findInodeByID(op.Parent)

	// Mint a child inode.
	childID := fs.nextInodeID
	fs.nextInodeID++

	child := &inode{
		attributes: fuseops.InodeAttributes{
			Nlink: 0,
			Mode:  0777 | os.ModeDir,
		},
	}

	fs.inodes[childID] = child
	child.IncrementLookupCount()

	// Return an appropriate entry.
	op.Entry = fuseops.ChildInodeEntry{
		Child:      childID,
		Attributes: child.attributes,
	}

	return
}

func (fs *fsImpl) CreateFile(
	op *fuseops.CreateFileOp) (err error) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Make sure the parent exists and has not been forgotten.
	_ = fs.findInodeByID(op.Parent)

	// Mint a child inode.
	childID := fs.nextInodeID
	fs.nextInodeID++

	child := &inode{
		attributes: fuseops.InodeAttributes{
			Nlink: 0,
			Mode:  0777,
		},
	}

	fs.inodes[childID] = child
	child.IncrementLookupCount()

	// Return an appropriate entry.
	op.Entry = fuseops.ChildInodeEntry{
		Child:      childID,
		Attributes: child.attributes,
	}

	return
}

func (fs *fsImpl) OpenFile(
	op *fuseops.OpenFileOp) (err error) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Verify that the inode has not been forgotten.
	_ = fs.findInodeByID(op.Inode)

	return
}

func (fs *fsImpl) OpenDir(
	op *fuseops.OpenDirOp) (err error) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Verify that the inode has not been forgotten.
	_ = fs.findInodeByID(op.Inode)

	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"strings"
)

const (
	figurePrefix rune = '+'
	seqPrefix    rune = ':'
	patternPause rune = '.'
	patternBeat  rune = 'x'
)

type part struct {
	Name    string
	Set     noteMap
	Figures map[string]figure
	Step    int
	Bpm     string
	Fx      []map[string]string
	Lanes   []string
}

type seq []string
type seqMap map[string]seq

type drums struct {
	Sets []struct {
		Name string
		Kit  []struct {
			Key     string
			Channel int
			Note    int
		}
	}
	Figures []struct {
		Name     string
		Key      string
		Velocity int
		Pattern  string
	}
	Parts []struct {
		Name     string
		Set      string
		Step     int
		Bpm      string
		Fx       []map[string]string
		Lanes    []string
		Genlanes []map[string]map[string]string
	}
	Seqs []struct {
		Name  string
		Parts []string
	}
}

func (d *drums) dump() {
	fmt.Println(*d)
}

func translateKit(set noteMap, s string) (channel, note int) {
	channel = set[s].Channel
	note = set[s].Note
	return
}

// renders the pattern string of a figure into individual midi events,
// translating the kit keys on the way.
func translateFigure(set noteMap, figures map[string]figure, s string) midiFigure {
	var fig midiFigure
	f := figures[s]
	c, n := translateKit(set, f.key)
	for _, elem := range f.pattern {
		switch elem {
		case patternPause:
			fig = append(fig, midiEvent{})
		case patternBeat:
			fig = append(fig, midiEvent{c, n, f.velocity})
		}
	}
	return fig
}

// text2matrix takes the lanes as strings, looks up the channel
// and note number of the events and returns them as separate
// matrices.
// It also returns a map of micro pattern figures. The index of the
// map is the position in the lane, so the player can fire it
// at the right time later.
func text2matrix(set noteMap, figures map[string]figure, txt []string) (channels, notes matrix, figuremap map[int][]midiFigure) {
	figuremap = make(map[int][]midiFigure)
	for _, line := range txt {
		var chanV, noteV []int
		lane := strings.Split(strings.TrimSpace(line), " ")
		// use our own index because we need to be able to decrease it
		// if we encounter unwanted (uncounted) characters
		var i int = -1
		for _, elem := range lane {
			i += 1
			if elem == "" {
				i -= 1
				continue
			}
			first, rest := splitFirstRune(elem)
			if first == figurePrefix {
				// generate a micro pattern
				figuremap[i] = append(figuremap[i], translateFigure(set, figures, rest))
				// also generate an empty single note
				chanV = append(chanV, 0)
				noteV = append(noteV, 0)
			} else {
				// generate a normal note
				c, n := translateKit(set, elem)
				chanV = append(chanV, c)
				noteV = append(noteV, n)
			}
		}
		channels = append(channels, row(chanV))
		notes = append(notes, row(noteV))
	}
	debugf("text2matrix(): figuremap: %v", figuremap)
	return
}

func (d *drums) getSeqs() seqMap {
	seqs := make(seqMap)
	for _, s := range d.Seqs {
		var seqparts []string
		for _, partname := range s.Parts {
			seqparts = append(seqparts, partname)
		}
		seqs[s.Name] = seqparts
	}
	return seqs
}

// take on UTF-8 safe string splitting
func splitFirstRune(s string) (first rune, rest string) {
	r := []rune(s)
	first = r[0]
	rest = string(r[1:])
	return
}

// chains up all seqs by looking up references to other
// seqs or parts and returns a list of part names.

func (sm seqMap) flatten(startAt string) []string {
	var l []string
	for _, p := range sm[startAt] {
		ok := false
		first, rest := splitFirstRune(p)
		if first == seqPrefix {
			_, ok = sm[rest]
		}
		if ok {
			l = append(l, sm.flatten(rest)...)
		} else {
			l = append(l, p)
		}
	}
	return l
}

func (d *drums) getParts(sets map[string]noteMap, figures map[string]figure) map[string]part {
	parts := make(map[string]part)
	var partsetname string
	for _, inp := range d.Parts {
		if inp.Set == "" {
			partsetname = "default"
		} else {
			partsetname = inp.Set
		}
		partset, ok := sets[partsetname]
		if !ok {
			logger.Printf("unknown set \"%s\"", partsetname)
		}
		genlanes, err := renderGenlanes(inp.Genlanes)
		if err != nil {
			logger.Print(err)
		}
		lanes := inp.Lanes
		lanes = append(lanes, genlanes...)
		debugf("getParts(): %#v", lanes)
		parts[inp.Name] = part{
			Name:    inp.Name,
			Set:     partset,
			Figures: figures,
			Step:    inp.Step,
			Bpm:     inp.Bpm,
			Fx:      inp.Fx,
			Lanes:   lanes,
		}
	}
	return parts
}

func (d *drums) getSets() map[string]noteMap {
	sets := make(map[string]noteMap)
	for _, set := range d.Sets {
		debugf("getSets(): %+v", set)
		notes := make(noteMap)
		for _, note := range set.Kit {
			notes[note.Key] = midiNote{
				Channel: note.Channel,
				Note:    note.Note,
			}
			debugf("getSets(): %+v", note)
		}
		sets[set.Name] = notes
	}
	return sets
}

func (d *drums) getFigures() map[string]figure {
	figures := make(map[string]figure)
	for _, f := range d.Figures {
		debugf("getFigures(): %+v", f)
		figures[f.Name] = figure{
			key:      f.Key,
			velocity: f.Velocity,
			pattern:  f.Pattern,
		}
	}
	return figures
}

func (d *drums) loadFromFile(fn string) {
	data, err := ioutil.ReadFile(fn)
	if err != nil {
		logger.Fatalf("%v", err)
	}
	err = yaml.Unmarshal(data, d)
	if err != nil {
		logger.Fatalf("Syntax error when reading file \"%s\". Maybe it is not proper YAML format.\n%v", fn, err)
	}
}
<commit_msg>Assume a sane default value for step<commit_after>package main

import (
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"strings"
)

const (
	figurePrefix rune = '+'
	seqPrefix    rune = ':'
	patternPause rune = '.'
	patternBeat  rune = 'x'
	defaultStep  int  = 8
)

type part struct {
	Name    string
	Set     noteMap
	Figures map[string]figure
	Step    int
	Bpm     string
	Fx      []map[string]string
	Lanes   []string
}

type seq []string
type seqMap map[string]seq

type drums struct {
	Sets []struct {
		Name string
		Kit  []struct {
			Key     string
			Channel int
			Note    int
		}
	}
	Figures []struct {
		Name     string
		Key      string
		Velocity int
		Pattern  string
	}
	Parts []struct {
		Name     string
		Set      string
		Step     int
		Bpm      string
		Fx       []map[string]string
		Lanes    []string
		Genlanes []map[string]map[string]string
	}
	Seqs []struct {
		Name  string
		Parts []string
	}
}

func (d *drums) dump() {
	fmt.Println(*d)
}

func translateKit(set noteMap, s string) (channel, note int) {
	channel = set[s].Channel
	note = set[s].Note
	return
}

// renders the pattern string of a figure into individual midi events,
// translating the kit keys on the way.
func translateFigure(set noteMap, figures map[string]figure, s string) midiFigure {
	var fig midiFigure
	f := figures[s]
	c, n := translateKit(set, f.key)
	for _, elem := range f.pattern {
		switch elem {
		case patternPause:
			fig = append(fig, midiEvent{})
		case patternBeat:
			fig = append(fig, midiEvent{c, n, f.velocity})
		}
	}
	return fig
}

// text2matrix takes the lanes as strings, looks up the channel
// and note number of the events and returns them as separate
// matrices.
// It also returns a map of micro pattern figures. The index of the
// map is the position in the lane, so the player can fire it
// at the right time later.
func text2matrix(set noteMap, figures map[string]figure, txt []string) (channels, notes matrix, figuremap map[int][]midiFigure) {
	figuremap = make(map[int][]midiFigure)
	for _, line := range txt {
		var chanV, noteV []int
		lane := strings.Split(strings.TrimSpace(line), " ")
		// use our own index because we need to be able to decrease it
		// if we encounter unwanted (uncounted) characters
		var i int = -1
		for _, elem := range lane {
			i += 1
			if elem == "" {
				i -= 1
				continue
			}
			first, rest := splitFirstRune(elem)
			if first == figurePrefix {
				// generate a micro pattern
				figuremap[i] = append(figuremap[i], translateFigure(set, figures, rest))
				// also generate an empty single note
				chanV = append(chanV, 0)
				noteV = append(noteV, 0)
			} else {
				// generate a normal note
				c, n := translateKit(set, elem)
				chanV = append(chanV, c)
				noteV = append(noteV, n)
			}
		}
		channels = append(channels, row(chanV))
		notes = append(notes, row(noteV))
	}
	debugf("text2matrix(): figuremap: %v", figuremap)
	return
}

func (d *drums) getSeqs() seqMap {
	seqs := make(seqMap)
	for _, s := range d.Seqs {
		var seqparts []string
		for _, partname := range s.Parts {
			seqparts = append(seqparts, partname)
		}
		seqs[s.Name] = seqparts
	}
	return seqs
}

// take on UTF-8 safe string splitting
func splitFirstRune(s string) (first rune, rest string) {
	r := []rune(s)
	first = r[0]
	rest = string(r[1:])
	return
}

// chains up all seqs by looking up references to other
// seqs or parts and returns a list of part names.

func (sm seqMap) flatten(startAt string) []string {
	var l []string
	for _, p := range sm[startAt] {
		ok := false
		first, rest := splitFirstRune(p)
		if first == seqPrefix {
			_, ok = sm[rest]
		}
		if ok {
			l = append(l, sm.flatten(rest)...)
		} else {
			l = append(l, p)
		}
	}
	return l
}

func (d *drums) getParts(sets map[string]noteMap, figures map[string]figure) map[string]part {
	parts := make(map[string]part)
	var partsetname string
	var partstep int
	for _, inp := range d.Parts {
		if inp.Set == "" {
			partsetname = "default"
		} else {
			partsetname = inp.Set
		}
		partset, ok := sets[partsetname]
		if inp.Step == 0 {
			partstep = defaultStep
		} else {
			partstep = inp.Step
		}
		if !ok {
			logger.Printf("unknown set \"%s\"", partsetname)
		}
		genlanes, err := renderGenlanes(inp.Genlanes)
		if err != nil {
			logger.Print(err)
		}
		lanes := inp.Lanes
		lanes = append(lanes, genlanes...)
		debugf("getParts(): %#v", lanes)
		parts[inp.Name] = part{
			Name:    inp.Name,
			Set:     partset,
			Figures: figures,
			Step:    partstep,
			Bpm:     inp.Bpm,
			Fx:      inp.Fx,
			Lanes:   lanes,
		}
	}
	return parts
}

func (d *drums) getSets() map[string]noteMap {
	sets := make(map[string]noteMap)
	for _, set := range d.Sets {
		debugf("getSets(): %+v", set)
		notes := make(noteMap)
		for _, note := range set.Kit {
			notes[note.Key] = midiNote{
				Channel: note.Channel,
				Note:    note.Note,
			}
			debugf("getSets(): %+v", note)
		}
		sets[set.Name] = notes
	}
	return sets
}

func (d *drums) getFigures() map[string]figure {
	figures := make(map[string]figure)
	for _, f := range d.Figures {
		debugf("getFigures(): %+v", f)
		figures[f.Name] = figure{
			key:      f.Key,
			velocity: f.Velocity,
			pattern:  f.Pattern,
		}
	}
	return figures
}

func (d *drums) loadFromFile(fn string) {
	data, err := ioutil.ReadFile(fn)
	if err != nil {
		logger.Fatalf("%v", err)
	}
	err = yaml.Unmarshal(data, d)
	if err != nil {
		logger.Fatalf("Syntax error when reading file \"%s\". Maybe it is not proper YAML format.\n%v", fn, err)
	}
}
<|endoftext|>


<commit_before>package main

import (
    "io/ioutil"
    "net/http"
    "strings"
    "testing"
    "time"
)

func curl(url string) string {
    if r, err := http.Get("http://localhost:8080/" + url); err == nil {
        b, err := ioutil.ReadAll(r.Body)
        r.Body.Close()
        if err == nil {
            return string(b)
        }
    }
    return ""
}

func mustContain(t *testing.T, page string, what string) {
    if !strings.Contains(page, what) {
        t.Errorf("Test page did not contain %q", what)
    }
}

var simpleTests = []struct {
    url string
    out string
}{
    {"", "container"},
    {"", "header"},
    {"", "subheader"},
    {"", "content"},
    {"", "sidebar"},
    {"", "footer"},
    {"", "blueprint"},
    {"", "utf-8"},
    {"", "gopher.png"},
    {"", "vim_created.png"},
}

func TestMainPage(t *testing.T) {
    go main()
    time.Sleep(50 * time.Millisecond)
    for _, test := range simpleTests {
        mustContain(t, curl(test.url), test.out)
    }
}
<commit_msg>Extract test server startup<commit_after>package main

import (
    "io/ioutil"
    "net/http"
    "strings"
    "testing"
    "time"
)

func curl(url string) string {
    if r, err := http.Get("http://localhost:8080/" + url); err == nil {
        b, err := ioutil.ReadAll(r.Body)
        r.Body.Close()
        if err == nil {
            return string(b)
        }
    }
    return ""
}

func mustContain(t *testing.T, page string, what string) {
    if !strings.Contains(page, what) {
        t.Errorf("Test page did not contain %q", what)
    }
}

var simpleTests = []struct {
    url string
    out string
}{
    {"", "container"},
    {"", "header"},
    {"", "subheader"},
    {"", "content"},
    {"", "sidebar"},
    {"", "footer"},
    {"", "blueprint"},
    {"", "utf-8"},
    {"", "gopher.png"},
    {"", "vim_created.png"},
}

func TestStartServer(t *testing.T) {
    go main()
    time.Sleep(50 * time.Millisecond)
}

func TestMainPage(t *testing.T) {
    for _, test := range simpleTests {
        mustContain(t, curl(test.url), test.out)
    }
}
<|endoftext|>


<commit_before>package framework

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

	"github.com/jarosser06/fastfood"
	"github.com/jarosser06/fastfood/common/fileutil"
	"github.com/jarosser06/fastfood/common/stringutil"
	"github.com/jarosser06/fastfood/framework/chef"
)

type Chef struct {
	cookbook chef.Cookbook
	options  fastfood.FrameworkOptions
}

// Initializes a framework with options
func (c *Chef) Init(frameworkOptions fastfood.FrameworkOptions) error {
	c.options = frameworkOptions
	if chef.PathIsCookbook(c.options.Destination) {
		var err error
		c.cookbook, err = chef.NewCookbookFromPath(c.options.Destination)
		if err != nil {
			return err
		}

		return nil
	}

	cpath := path.Join(c.options.Destination, c.options.Name)
	// Create cookbook
	if fileutil.FileExist(cpath) {
		var err error
		c.cookbook, err = chef.NewCookbookFromPath(cpath)
		if err != nil {
			return err
		}
	} else {
		c.cookbook = chef.NewCookbook(c.options.Destination, c.options.Name)
	}

	return nil
}

// Generates a basic cookbook structure
func (c *Chef) GenerateBase() ([]string, error) {
	// Generate the cookbook if it doesn't exist
	if !fileutil.FileExist(c.cookbook.Path) {
		err := os.Mkdir(c.cookbook.Path, 0755)
		if err != nil {
			return []string{}, fmt.Errorf(
				"error creating cookbook directory %s: %v",
				c.cookbook.Path,
				err,
			)
		}
	}

	err := c.genDirs(c.options.BaseDirs)
	if err != nil {
		return []string{}, err
	}

	var moddedFile []string
	for _, file := range c.options.BaseFiles {
		cfile := path.Join(c.cookbook.Path, file)
		// If the file already exists then dont overwrite it
		if fileutil.FileExist(cfile) {
			continue
		}

		tfile := path.Join(c.options.TemplateDir, "base", file)
		err := c.genBaseFile(cfile, tfile)
		if err != nil {
			return moddedFile, err
		}

		moddedFile = append(moddedFile, cfile)
	}

	return moddedFile, nil
}

// Generates a stencil
// Takes the stencil name to be generated
func (c *Chef) GenerateStencil(name string, stencilset fastfood.StencilSet, opts map[string]string) ([]string, error) {
	var moddedFiles []string
	opts = stencilset.MergeOpts(name, opts)
	chefOpts, err := c.chefOpts(name, stencilset)
	if err != nil {
		return moddedFiles, err
	}

	tOpts := struct {
		*fastfood.Helpers
		Cookbook chef.Cookbook
		Options  map[string]string
	}{
		Cookbook: c.cookbook,
		Options:  CapitalizeOptions(opts),
	}

	// Generate the directories
	c.genDirs(append(chefOpts.Directories))

	// Generate the files
	files := chefOpts.Files
	pfiles := chefOpts.Partials

	for cfile, tfile := range files {
		// Find any instance of <NAME> and replace with the name
		cfile = strings.Replace(cfile, "<NAME>", tOpts.Options["Name"], 1)
		tfile = path.Join(stencilset.BasePath, tfile)

		// Make sure the path exists before attempting to write the file
		c.genDirs([]string{path.Dir(cfile)})

		var pContent []string
		for _, partial := range pfiles {
			b, err := ioutil.ReadFile(path.Join(stencilset.BasePath, partial))
			if err != nil {
				return moddedFiles, fmt.Errorf("error %v occured while reading file %s", err, partial)
			}

			pContent = append(pContent, string(b))
		}

		err := c.genStencilFile(cfile, tfile, pContent, tOpts)
		if err != nil {
			// don't care if the file exists
			if err.Error() != "file exists" {
				return moddedFiles, err
			}
			// File wasn't created moving on
			continue
		}

		moddedFiles = append(moddedFiles, cfile)
	}
	c.cookbook.AppendDependencies(chefOpts.Dependencies)

	//TODO: Clean up this mess
	// Convert berks map to slice
	var bSlice []chef.BerksCookbook
	for _, b := range chefOpts.BerksDeps {
		bSlice = append(bSlice, b)
	}

	c.cookbook.Berks.Append(path.Join(c.cookbook.Path, "Berksfile"), bSlice)

	return moddedFiles, nil
}

func (c *Chef) chefOpts(name string, s fastfood.StencilSet) (chef.Options, error) {
	g, err := chef.NewOptions(s.Raw)
	if err != nil {
		return g, err
	}

	l, err := chef.NewOptions(s.Stencils[name].Raw)
	if err != nil {
		return l, err
	}

	return chef.Merge(g, l), nil
}

// Generates a stencil file, meant to be called by Chef interface
func (c *Chef) genStencilFile(cfile string, tfile string, pContent []string, tOpts interface{}) error {
	cfilePath := path.Join(c.cookbook.Path, cfile)

	if fileutil.FileExist(cfilePath) && !c.options.Force {
		return fmt.Errorf("file exists")
	}

	// Read content from
	content := pContent
	b, err := ioutil.ReadFile(tfile)
	if err != nil {
		return fmt.Errorf("error %v occured while reading file %s", err, tfile)
	}
	content = append(content, string(b))

	t, err := fastfood.NewTemplate(cfile, tOpts, content)
	if err != nil {
		return fmt.Errorf("error %v occured while creating template %s", err, tfile)
	}

	t.CleanNewlines()

	if err := t.Flush(cfilePath); err != nil {
		return fmt.Errorf("error %v occured while writing file %s", err, cfile)
	}

	return nil
}

func (c *Chef) genBaseFile(file string, tfile string) error {
	content, err := ioutil.ReadFile(tfile)
	if err != nil {
		return fmt.Errorf("error reading file %s, %v", tfile, err)
	}

	t, err := fastfood.NewTemplate(file, c.cookbook, []string{string(content)})
	if err != nil {
		return fmt.Errorf("error %v returned while creating new template %s", err, file)
	}

	t.CleanNewlines()
	if err := t.Flush(file); err != nil {
		return fmt.Errorf("error %v while writing file %s", err, file)
	}

	return nil
}

// Generates directories, meant for internal usage to Chef
func (c *Chef) genDirs(dirs []string) error {
	for _, dir := range dirs {
		fpath := path.Join(c.cookbook.Path, dir)

		if !fileutil.FileExist(fpath) {
			err := os.MkdirAll(fpath, 0755)
			if err != nil {
				return fmt.Errorf("error %v occured while creating directory %s", err, fpath)
			}
		}
	}

	return nil
}

func CapitalizeOptions(opts map[string]string) map[string]string {
	cmap := make(map[string]string)

	for k, v := range opts {
		cmap[stringutil.CapitalizeString(k)] = v
	}

	return cmap
}
<commit_msg>fixed chef new issue<commit_after>package framework

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

	"github.com/jarosser06/fastfood"
	"github.com/jarosser06/fastfood/common/fileutil"
	"github.com/jarosser06/fastfood/common/stringutil"
	"github.com/jarosser06/fastfood/framework/chef"
)

type Chef struct {
	cookbook chef.Cookbook
	options  fastfood.FrameworkOptions
}

// Initializes a framework with options
func (c *Chef) Init(frameworkOptions fastfood.FrameworkOptions) error {
	c.options = frameworkOptions
	cpath := path.Join(c.options.Destination, c.options.Name)
	if chef.PathIsCookbook(cpath) {
		var err error
		c.cookbook, err = chef.NewCookbookFromPath(cpath)
		if err != nil {
			return err
		}

		return nil
	}

	// Create cookbook
	if fileutil.FileExist(cpath) {
		var err error
		c.cookbook, err = chef.NewCookbookFromPath(cpath)
		if err != nil {
			return err
		}
	} else {
		c.cookbook = chef.NewCookbook(c.options.Destination, c.options.Name)
	}

	return nil
}

// Generates a basic cookbook structure
func (c *Chef) GenerateBase() ([]string, error) {
	// Generate the cookbook if it doesn't exist
	if !fileutil.FileExist(c.cookbook.Path) {
		err := os.Mkdir(c.cookbook.Path, 0755)
		if err != nil {
			return []string{}, fmt.Errorf(
				"error creating cookbook directory %s: %v",
				c.cookbook.Path,
				err,
			)
		}
	}

	err := c.genDirs(c.options.BaseDirs)
	if err != nil {
		return []string{}, err
	}

	var moddedFile []string
	for _, file := range c.options.BaseFiles {
		cfile := path.Join(c.cookbook.Path, file)
		// If the file already exists then dont overwrite it
		if fileutil.FileExist(cfile) {
			continue
		}

		tfile := path.Join(c.options.TemplateDir, "base", file)
		err := c.genBaseFile(cfile, tfile)
		if err != nil {
			return moddedFile, err
		}

		moddedFile = append(moddedFile, cfile)
	}

	return moddedFile, nil
}

// Generates a stencil
// Takes the stencil name to be generated
func (c *Chef) GenerateStencil(name string, stencilset fastfood.StencilSet, opts map[string]string) ([]string, error) {
	var moddedFiles []string
	opts = stencilset.MergeOpts(name, opts)
	chefOpts, err := c.chefOpts(name, stencilset)
	if err != nil {
		return moddedFiles, err
	}

	tOpts := struct {
		*fastfood.Helpers
		Cookbook chef.Cookbook
		Options  map[string]string
	}{
		Cookbook: c.cookbook,
		Options:  CapitalizeOptions(opts),
	}

	// Generate the directories
	c.genDirs(append(chefOpts.Directories))

	// Generate the files
	files := chefOpts.Files
	pfiles := chefOpts.Partials

	for cfile, tfile := range files {
		// Find any instance of <NAME> and replace with the name
		cfile = strings.Replace(cfile, "<NAME>", tOpts.Options["Name"], 1)
		tfile = path.Join(stencilset.BasePath, tfile)

		// Make sure the path exists before attempting to write the file
		c.genDirs([]string{path.Dir(cfile)})

		var pContent []string
		for _, partial := range pfiles {
			b, err := ioutil.ReadFile(path.Join(stencilset.BasePath, partial))
			if err != nil {
				return moddedFiles, fmt.Errorf("error %v occured while reading file %s", err, partial)
			}

			pContent = append(pContent, string(b))
		}

		err := c.genStencilFile(cfile, tfile, pContent, tOpts)
		if err != nil {
			// don't care if the file exists
			if err.Error() != "file exists" {
				return moddedFiles, err
			}
			// File wasn't created moving on
			continue
		}

		moddedFiles = append(moddedFiles, cfile)
	}
	c.cookbook.AppendDependencies(chefOpts.Dependencies)

	//TODO: Clean up this mess
	// Convert berks map to slice
	var bSlice []chef.BerksCookbook
	for _, b := range chefOpts.BerksDeps {
		bSlice = append(bSlice, b)
	}

	c.cookbook.Berks.Append(path.Join(c.cookbook.Path, "Berksfile"), bSlice)

	return moddedFiles, nil
}

func (c *Chef) chefOpts(name string, s fastfood.StencilSet) (chef.Options, error) {
	g, err := chef.NewOptions(s.Raw)
	if err != nil {
		return g, err
	}

	l, err := chef.NewOptions(s.Stencils[name].Raw)
	if err != nil {
		return l, err
	}

	return chef.Merge(g, l), nil
}

// Generates a stencil file, meant to be called by Chef interface
func (c *Chef) genStencilFile(cfile string, tfile string, pContent []string, tOpts interface{}) error {
	cfilePath := path.Join(c.cookbook.Path, cfile)

	if fileutil.FileExist(cfilePath) && !c.options.Force {
		return fmt.Errorf("file exists")
	}

	// Read content from
	content := pContent
	b, err := ioutil.ReadFile(tfile)
	if err != nil {
		return fmt.Errorf("error %v occured while reading file %s", err, tfile)
	}
	content = append(content, string(b))

	t, err := fastfood.NewTemplate(cfile, tOpts, content)
	if err != nil {
		return fmt.Errorf("error %v occured while creating template %s", err, tfile)
	}

	t.CleanNewlines()

	if err := t.Flush(cfilePath); err != nil {
		return fmt.Errorf("error %v occured while writing file %s", err, cfile)
	}

	return nil
}

func (c *Chef) genBaseFile(file string, tfile string) error {
	content, err := ioutil.ReadFile(tfile)
	if err != nil {
		return fmt.Errorf("error reading file %s, %v", tfile, err)
	}

	t, err := fastfood.NewTemplate(file, c.cookbook, []string{string(content)})
	if err != nil {
		return fmt.Errorf("error %v returned while creating new template %s", err, file)
	}

	t.CleanNewlines()
	if err := t.Flush(file); err != nil {
		return fmt.Errorf("error %v while writing file %s", err, file)
	}

	return nil
}

// Generates directories, meant for internal usage to Chef
func (c *Chef) genDirs(dirs []string) error {
	for _, dir := range dirs {
		fpath := path.Join(c.cookbook.Path, dir)

		if !fileutil.FileExist(fpath) {
			err := os.MkdirAll(fpath, 0755)
			if err != nil {
				return fmt.Errorf("error %v occured while creating directory %s", err, fpath)
			}
		}
	}

	return nil
}

func CapitalizeOptions(opts map[string]string) map[string]string {
	cmap := make(map[string]string)

	for k, v := range opts {
		cmap[stringutil.CapitalizeString(k)] = v
	}

	return cmap
}
<|endoftext|>


<commit_before>//: ----------------------------------------------------------------------------
//: Copyright (C) 2017 Verizon.  All Rights Reserved.
//: All Rights Reserved
//:
//: file:    flow_counter.go
//: details: TODO
//: author:  Mehrdad Arshad Rad
//: date:    08/08/2018
//:
//: Licensed under the Apache License, Version 2.0 (the "License");
//: you may not use this file except in compliance with the License.
//: You may obtain a copy of the License at
//:
//:     http://www.apache.org/licenses/LICENSE-2.0
//:
//: Unless required by applicable law or agreed to in writing, software
//: distributed under the License is distributed on an "AS IS" BASIS,
//: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//: See the License for the specific language governing permissions and
//: limitations under the License.
//: ----------------------------------------------------------------------------

package sflow

import (
	"encoding/binary"
	"io"
)

const (
	// GenericInterfaceCounters is Generic interface counters - see RFC 2233
	SFGenericInterfaceCounters = 1

	// EthernetInterfaceCounters is Ethernet interface counters - see RFC 2358
	SFEthernetInterfaceCounters = 2

	// SFTokenRingInterfaceCounters is Token ring counters - see RFC 1748
	SFTokenRingInterfaceCounters = 3

	// SF100BaseVGInterfaceCounters is 100 BaseVG interface counters - see RFC 2020
	SF100BaseVGInterfaceCounters = 4

	// SFVLANCounters is VLAN counters
	SFVLANCounters = 5

	// SFProcessorCounters is processor counters
	SFProcessorCounters = 1001
)

// GenericInterfaceCounters represents Generic Interface Counters RFC2233
type GenericInterfaceCounters struct {
	Index               uint32
	Type                uint32
	Speed               uint64
	Direction           uint32
	Status              uint32
	InOctets            uint64
	InUnicastPackets    uint32
	InMulticastPackets  uint32
	InBroadcastPackets  uint32
	InDiscards          uint32
	InErrors            uint32
	InUnknownProtocols  uint32
	OutOctets           uint64
	OutUnicastPackets   uint32
	OutMulticastPackets uint32
	OutBroadcastPackets uint32
	OutDiscards         uint32
	OutErrors           uint32
	PromiscuousMode     uint32
}

// EthernetInterfaceCounters represents Ethernet Interface Counters RFC2358
type EthernetInterfaceCounters struct {
	AlignmentErrors           uint32
	FCSErrors                 uint32
	SingleCollisionFrames     uint32
	MultipleCollisionFrames   uint32
	SQETestErrors             uint32
	DeferredTransmissions     uint32
	LateCollisions            uint32
	ExcessiveCollisions       uint32
	InternalMACTransmitErrors uint32
	CarrierSenseErrors        uint32
	FrameTooLongs             uint32
	InternalMACReceiveErrors  uint32
	SymbolErrors              uint32
}

// VlanCounters represents VLAN Counters
type VlanCounters struct {
	ID               uint32
	Octets           uint64
	UnicastPackets   uint32
	MulticastPackets uint32
	BroadcastPackets uint32
	Discards         uint32
}

// ProcessorCounters represents Processor Information
type ProcessorCounters struct {
	CPU5s       uint32
	CPU1m       uint32
	CPU5m       uint32
	TotalMemory uint64
	FreeMemory  uint64
}

type CounterSample struct {
	SequenceNo   uint32
	SourceIdType byte
	SourceIdIdx  uint32
	RecordsNo    uint32
	Records      map[string]Record
}

func decodeFlowCounter(r io.ReadSeeker) (*CounterSample, error) {
	var (
		cs          = new(CounterSample)
		rTypeFormat uint32
		rTypeLength uint32
		err         error
	)

	if err = cs.unmarshal(r); err != nil {
		return nil, err
	}

	cs.Records = make(map[string]Record)

	for i := uint32(0); i < cs.RecordsNo; i++ {
		if err = read(r, &rTypeFormat); err != nil {
			return nil, err
		}
		if err = read(r, &rTypeLength); err != nil {
			return nil, err
		}

		switch rTypeFormat {

		case SFGenericInterfaceCounters:
			d, err := decodeGenericIntCounters(r)
			if err != nil {
				return cs, err
			}
			cs.Records["GenIntCntrs"] = d
		case SFEthernetInterfaceCounters:
			d, err := decodeEthIntCounters(r)
			if err != nil {
				return cs, err
			}
			cs.Records["EthIntCntrs"] = d
		case SFVLANCounters:
			d, err := decodeVlanCounters(r)
			if err != nil {
				return cs, err
			}
			cs.Records["VlanCntrs"] = d
		case SFProcessorCounters:
			d, err := decodedProcessorCounters(r)
			if err != nil {
				return cs, err
			}
			cs.Records["ProcCntrs"] = d
		default:
			r.Seek(int64(rTypeLength), 1)
		}
	}

	return cs, nil
}

func decodeGenericIntCounters(r io.Reader) (*GenericInterfaceCounters, error) {
	var gic = new(GenericInterfaceCounters)

	if err := gic.unmarshal(r); err != nil {
		return nil, err
	}

	return gic, nil
}

func (gic *GenericInterfaceCounters) unmarshal(r io.Reader) error {
	var err error

	fields := []interface{}{
		&gic.Index,
		&gic.Type,
		&gic.Speed,
		&gic.Direction,
		&gic.Status,
		&gic.InOctets,
		&gic.InUnicastPackets,
		&gic.InMulticastPackets,
		&gic.InBroadcastPackets,
		&gic.InDiscards,
		&gic.InErrors,
		&gic.InUnknownProtocols,
		&gic.OutOctets,
		&gic.OutUnicastPackets,
		&gic.OutMulticastPackets,
		&gic.OutBroadcastPackets,
		&gic.OutDiscards,
		&gic.OutErrors,
		&gic.PromiscuousMode,
	}

	for _, field := range fields {
		if err = read(r, field); err != nil {
			return err
		}
	}

	return nil
}
func decodeEthIntCounters(r io.Reader) (*EthernetInterfaceCounters, error) {
	var eic = new(EthernetInterfaceCounters)

	if err := eic.unmarshal(r); err != nil {
		return nil, err
	}

	return eic, nil
}

func (eic *EthernetInterfaceCounters) unmarshal(r io.Reader) error {
	var err error

	fields := []interface{}{
		&eic.AlignmentErrors,
		&eic.FCSErrors,
		&eic.SingleCollisionFrames,
		&eic.MultipleCollisionFrames,
		&eic.SQETestErrors,
		&eic.DeferredTransmissions,
		&eic.LateCollisions,
		&eic.ExcessiveCollisions,
		&eic.InternalMACTransmitErrors,
		&eic.CarrierSenseErrors,
		&eic.FrameTooLongs,
		&eic.InternalMACReceiveErrors,
		&eic.SymbolErrors,
	}

	for _, field := range fields {
		if err = read(r, field); err != nil {
			return err
		}
	}

	return nil
}

func decodeVlanCounters(r io.Reader) (*VlanCounters, error) {
	var vc = new(VlanCounters)

	if err := vc.unmarshal(r); err != nil {
		return nil, err
	}

	return vc, nil
}

func (vc *VlanCounters) unmarshal(r io.Reader) error {
	var err error
	fields := []interface{}{
		&vc.ID,
		&vc.Octets,
		&vc.UnicastPackets,
		&vc.MulticastPackets,
		&vc.BroadcastPackets,
		&vc.Discards,
	}

	for _, field := range fields {
		if err = read(r, field); err != nil {
			return err
		}
	}

	return nil
}

func decodedProcessorCounters(r io.Reader) (*ProcessorCounters, error) {
	var pc = new(ProcessorCounters)

	if err := pc.unmarshal(r); err != nil {
		return nil, err
	}

	return pc, nil
}

func (pc *ProcessorCounters) unmarshal(r io.Reader) error {
	var err error
	fields := []interface{}{
		&pc.CPU5s,
		&pc.CPU1m,
		&pc.CPU5m,
		&pc.TotalMemory,
		&pc.FreeMemory,
	}

	for _, field := range fields {
		if err = read(r, field); err != nil {
			return err
		}
	}

	return nil
}

func (cs *CounterSample) unmarshal(r io.Reader) error {

	var err error

	if err = read(r, &cs.SequenceNo); err != nil {
		return err
	}

	if err = read(r, &cs.SourceIdType); err != nil {
		return err
	}

	buf := make([]byte, 3)
	if err = read(r, &buf); err != nil {
		return err
	}
	cs.SourceIdIdx = binary.BigEndian.Uint32(buf)

	if err = read(r, &cs.RecordsNo); err != nil {
		return err
	}

	return nil
}
<commit_msg>fix sourceIdIdx decoding<commit_after>//: ----------------------------------------------------------------------------
//: Copyright (C) 2017 Verizon.  All Rights Reserved.
//: All Rights Reserved
//:
//: file:    flow_counter.go
//: details: TODO
//: author:  Mehrdad Arshad Rad
//: date:    08/08/2018
//:
//: Licensed under the Apache License, Version 2.0 (the "License");
//: you may not use this file except in compliance with the License.
//: You may obtain a copy of the License at
//:
//:     http://www.apache.org/licenses/LICENSE-2.0
//:
//: Unless required by applicable law or agreed to in writing, software
//: distributed under the License is distributed on an "AS IS" BASIS,
//: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//: See the License for the specific language governing permissions and
//: limitations under the License.
//: ----------------------------------------------------------------------------

package sflow

import "io"

const (
	// GenericInterfaceCounters is Generic interface counters - see RFC 2233
	SFGenericInterfaceCounters = 1

	// EthernetInterfaceCounters is Ethernet interface counters - see RFC 2358
	SFEthernetInterfaceCounters = 2

	// SFTokenRingInterfaceCounters is Token ring counters - see RFC 1748
	SFTokenRingInterfaceCounters = 3

	// SF100BaseVGInterfaceCounters is 100 BaseVG interface counters - see RFC 2020
	SF100BaseVGInterfaceCounters = 4

	// SFVLANCounters is VLAN counters
	SFVLANCounters = 5

	// SFProcessorCounters is processor counters
	SFProcessorCounters = 1001
)

// GenericInterfaceCounters represents Generic Interface Counters RFC2233
type GenericInterfaceCounters struct {
	Index               uint32
	Type                uint32
	Speed               uint64
	Direction           uint32
	Status              uint32
	InOctets            uint64
	InUnicastPackets    uint32
	InMulticastPackets  uint32
	InBroadcastPackets  uint32
	InDiscards          uint32
	InErrors            uint32
	InUnknownProtocols  uint32
	OutOctets           uint64
	OutUnicastPackets   uint32
	OutMulticastPackets uint32
	OutBroadcastPackets uint32
	OutDiscards         uint32
	OutErrors           uint32
	PromiscuousMode     uint32
}

// EthernetInterfaceCounters represents Ethernet Interface Counters RFC2358
type EthernetInterfaceCounters struct {
	AlignmentErrors           uint32
	FCSErrors                 uint32
	SingleCollisionFrames     uint32
	MultipleCollisionFrames   uint32
	SQETestErrors             uint32
	DeferredTransmissions     uint32
	LateCollisions            uint32
	ExcessiveCollisions       uint32
	InternalMACTransmitErrors uint32
	CarrierSenseErrors        uint32
	FrameTooLongs             uint32
	InternalMACReceiveErrors  uint32
	SymbolErrors              uint32
}

// VlanCounters represents VLAN Counters
type VlanCounters struct {
	ID               uint32
	Octets           uint64
	UnicastPackets   uint32
	MulticastPackets uint32
	BroadcastPackets uint32
	Discards         uint32
}

// ProcessorCounters represents Processor Information
type ProcessorCounters struct {
	CPU5s       uint32
	CPU1m       uint32
	CPU5m       uint32
	TotalMemory uint64
	FreeMemory  uint64
}

type CounterSample struct {
	SequenceNo   uint32
	SourceIdType byte
	SourceIdIdx  uint32
	RecordsNo    uint32
	Records      map[string]Record
}

func decodeFlowCounter(r io.ReadSeeker) (*CounterSample, error) {
	var (
		cs          = new(CounterSample)
		rTypeFormat uint32
		rTypeLength uint32
		err         error
	)

	if err = cs.unmarshal(r); err != nil {
		return nil, err
	}

	cs.Records = make(map[string]Record)

	for i := uint32(0); i < cs.RecordsNo; i++ {
		if err = read(r, &rTypeFormat); err != nil {
			return nil, err
		}
		if err = read(r, &rTypeLength); err != nil {
			return nil, err
		}

		switch rTypeFormat {

		case SFGenericInterfaceCounters:
			d, err := decodeGenericIntCounters(r)
			if err != nil {
				return cs, err
			}
			cs.Records["GenIntCntrs"] = d
		case SFEthernetInterfaceCounters:
			d, err := decodeEthIntCounters(r)
			if err != nil {
				return cs, err
			}
			cs.Records["EthIntCntrs"] = d
		case SFVLANCounters:
			d, err := decodeVlanCounters(r)
			if err != nil {
				return cs, err
			}
			cs.Records["VlanCntrs"] = d
		case SFProcessorCounters:
			d, err := decodedProcessorCounters(r)
			if err != nil {
				return cs, err
			}
			cs.Records["ProcCntrs"] = d
		default:
			r.Seek(int64(rTypeLength), 1)
		}
	}

	return cs, nil
}

func decodeGenericIntCounters(r io.Reader) (*GenericInterfaceCounters, error) {
	var gic = new(GenericInterfaceCounters)

	if err := gic.unmarshal(r); err != nil {
		return nil, err
	}

	return gic, nil
}

func (gic *GenericInterfaceCounters) unmarshal(r io.Reader) error {
	var err error

	fields := []interface{}{
		&gic.Index,
		&gic.Type,
		&gic.Speed,
		&gic.Direction,
		&gic.Status,
		&gic.InOctets,
		&gic.InUnicastPackets,
		&gic.InMulticastPackets,
		&gic.InBroadcastPackets,
		&gic.InDiscards,
		&gic.InErrors,
		&gic.InUnknownProtocols,
		&gic.OutOctets,
		&gic.OutUnicastPackets,
		&gic.OutMulticastPackets,
		&gic.OutBroadcastPackets,
		&gic.OutDiscards,
		&gic.OutErrors,
		&gic.PromiscuousMode,
	}

	for _, field := range fields {
		if err = read(r, field); err != nil {
			return err
		}
	}

	return nil
}
func decodeEthIntCounters(r io.Reader) (*EthernetInterfaceCounters, error) {
	var eic = new(EthernetInterfaceCounters)

	if err := eic.unmarshal(r); err != nil {
		return nil, err
	}

	return eic, nil
}

func (eic *EthernetInterfaceCounters) unmarshal(r io.Reader) error {
	var err error

	fields := []interface{}{
		&eic.AlignmentErrors,
		&eic.FCSErrors,
		&eic.SingleCollisionFrames,
		&eic.MultipleCollisionFrames,
		&eic.SQETestErrors,
		&eic.DeferredTransmissions,
		&eic.LateCollisions,
		&eic.ExcessiveCollisions,
		&eic.InternalMACTransmitErrors,
		&eic.CarrierSenseErrors,
		&eic.FrameTooLongs,
		&eic.InternalMACReceiveErrors,
		&eic.SymbolErrors,
	}

	for _, field := range fields {
		if err = read(r, field); err != nil {
			return err
		}
	}

	return nil
}

func decodeVlanCounters(r io.Reader) (*VlanCounters, error) {
	var vc = new(VlanCounters)

	if err := vc.unmarshal(r); err != nil {
		return nil, err
	}

	return vc, nil
}

func (vc *VlanCounters) unmarshal(r io.Reader) error {
	var err error
	fields := []interface{}{
		&vc.ID,
		&vc.Octets,
		&vc.UnicastPackets,
		&vc.MulticastPackets,
		&vc.BroadcastPackets,
		&vc.Discards,
	}

	for _, field := range fields {
		if err = read(r, field); err != nil {
			return err
		}
	}

	return nil
}

func decodedProcessorCounters(r io.Reader) (*ProcessorCounters, error) {
	var pc = new(ProcessorCounters)

	if err := pc.unmarshal(r); err != nil {
		return nil, err
	}

	return pc, nil
}

func (pc *ProcessorCounters) unmarshal(r io.Reader) error {
	var err error
	fields := []interface{}{
		&pc.CPU5s,
		&pc.CPU1m,
		&pc.CPU5m,
		&pc.TotalMemory,
		&pc.FreeMemory,
	}

	for _, field := range fields {
		if err = read(r, field); err != nil {
			return err
		}
	}

	return nil
}

func (cs *CounterSample) unmarshal(r io.Reader) error {

	var err error

	if err = read(r, &cs.SequenceNo); err != nil {
		return err
	}

	if err = read(r, &cs.SourceIdType); err != nil {
		return err
	}

	buf := make([]byte, 3)
	if err = read(r, &buf); err != nil {
		return err
	}
	cs.SourceIdIdx = uint32(buf[2]) | uint32(buf[1])<<8 | uint32(buf[0])<<16

	if err = read(r, &cs.RecordsNo); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package sftp

import (
	"github.com/pkg/sftp"
	"path/filepath"
	"github.com/pufferpanel/pufferd/utils"
	"strings"
	"errors"
	"io"
	"os"
	"fmt"
)

type requestPrefix struct {
	prefix string
}

func CreateRequestPrefix(prefix string) sftp.Handlers {
	h := requestPrefix{prefix: prefix}

	return sftp.Handlers{h, h, h, h}
}

func (rp requestPrefix) Fileread(request sftp.Request) (io.ReaderAt, error) {
	file, err := rp.getFile(request.Filepath, os.O_RDONLY, 0644)
	return file, err
}

func (rp requestPrefix) Filewrite(request sftp.Request) (io.WriterAt, error) {
	file, err := rp.getFile(request.Filepath, os.O_WRONLY | os.O_APPEND | os.O_CREATE, 0644)
	return file, err
}

func (rp requestPrefix) Filecmd(request sftp.Request) error {
	sourceName, err := rp.validate(request.Filepath)
	if err != nil {
		return rp.maskError(err)
	}
	var targetName string
	if request.Target != ""	{
		targetName, err = rp.validate(request.Target)
		if err != nil {
			return rp.maskError(err)
		}
	}
	switch (request.Method) {
	case "SetStat", "Setstat": {
		return nil
	}
	case "Rename": {
		return os.Rename(sourceName, targetName)
	}
	case "Rmdir": {
		return os.RemoveAll(sourceName)
	}
	case "Mkdir": {
		return os.Mkdir(sourceName, 0755)
	}
	case "Symlink": {
		return nil
	}
	case "Remove": {
		return os.Remove(sourceName)
	}
	default:
		return errors.New(fmt.Sprint("Unknown request method: %s", request.Method))
	}
}

func (rp requestPrefix) Fileinfo(request sftp.Request) ([]os.FileInfo, error) {
	sourceName, err := rp.validate(request.Filepath)
	if err != nil {
		return nil, rp.maskError(err)
	}
	switch (request.Method) {
	case "List": {
		file, err := os.Open(sourceName)
		if err != nil {
			return nil, rp.maskError(err)
		}
		return file.Readdir(0)
	}
	case "Stat": {
		file, err := os.Open(sourceName)
		if err != nil {
			return nil, rp.maskError(err)
		}
		fi, err := file.Stat()
		if err != nil {
			return nil, rp.maskError(err)
		}
		return []os.FileInfo{fi}, nil
	}
	case "Readlink": {
		target, err := os.Readlink(sourceName)
		if err != nil {
			return nil, rp.maskError(err)
		}
		file, err := os.Open(target)
		if err != nil {
			return nil, rp.maskError(err)
		}
		fi, err := file.Stat()
		if err != nil {
			return nil, rp.maskError(err)
		}
		return []os.FileInfo{fi}, nil
	}
	default:
		return nil, errors.New(fmt.Sprint("Unknown request method: %s", request.Method));
	}
}

func (rp requestPrefix) getFile(path string, flags int, mode os.FileMode) (*os.File, error) {
	filePath, err := rp.validate(path)
	if err != nil {
		return nil, rp.maskError(err)
	}
	file, err := os.OpenFile(filePath, flags, mode)
	if err != nil {
		return nil, rp.maskError(err)
	}
	return file, err
}

func (rp requestPrefix) validate(path string) (string, error) {
	ok, path := rp.tryPrefix(path)
	if !ok {
		return "", errors.New("Access denied")
	}
	return path, nil
}

func (rp requestPrefix) tryPrefix(path string) (bool, string) {
	newPath := filepath.Clean(filepath.Join(rp.prefix, path))
	if utils.EnsureAccess(newPath, rp.prefix) {
		return true, newPath
	} else {
		return false, ""
	}
}

func (rp requestPrefix) stripPrefix(path string) string {
	newStr := strings.Replace(path, rp.prefix, "", -1)
	if len(newStr) == 0 {
		newStr = "/"
	}
	return newStr
}

func (rp requestPrefix) maskError(err error) error {
	return errors.New(rp.stripPrefix(err.Error()))
}
<commit_msg>Ensure file is empty before writing to it via SFTP<commit_after>package sftp

import (
	"github.com/pkg/sftp"
	"path/filepath"
	"github.com/pufferpanel/pufferd/utils"
	"strings"
	"errors"
	"io"
	"os"
	"fmt"
)

type requestPrefix struct {
	prefix string
}

func CreateRequestPrefix(prefix string) sftp.Handlers {
	h := requestPrefix{prefix: prefix}

	return sftp.Handlers{h, h, h, h}
}

func (rp requestPrefix) Fileread(request sftp.Request) (io.ReaderAt, error) {
	file, err := rp.getFile(request.Filepath, os.O_RDONLY, 0644)
	return file, err
}

func (rp requestPrefix) Filewrite(request sftp.Request) (io.WriterAt, error) {
	file, err := rp.getFile(request.Filepath, os.O_WRONLY | os.O_TRUNC | os.O_CREATE, 0644)
	return file, err
}

func (rp requestPrefix) Filecmd(request sftp.Request) error {
	sourceName, err := rp.validate(request.Filepath)
	if err != nil {
		return rp.maskError(err)
	}
	var targetName string
	if request.Target != ""	{
		targetName, err = rp.validate(request.Target)
		if err != nil {
			return rp.maskError(err)
		}
	}
	switch (request.Method) {
	case "SetStat", "Setstat": {
		return nil
	}
	case "Rename": {
		return os.Rename(sourceName, targetName)
	}
	case "Rmdir": {
		return os.RemoveAll(sourceName)
	}
	case "Mkdir": {
		return os.Mkdir(sourceName, 0755)
	}
	case "Symlink": {
		return nil
	}
	case "Remove": {
		return os.Remove(sourceName)
	}
	default:
		return errors.New(fmt.Sprint("Unknown request method: %s", request.Method))
	}
}

func (rp requestPrefix) Fileinfo(request sftp.Request) ([]os.FileInfo, error) {
	sourceName, err := rp.validate(request.Filepath)
	if err != nil {
		return nil, rp.maskError(err)
	}
	switch (request.Method) {
	case "List": {
		file, err := os.Open(sourceName)
		if err != nil {
			return nil, rp.maskError(err)
		}
		return file.Readdir(0)
	}
	case "Stat": {
		file, err := os.Open(sourceName)
		if err != nil {
			return nil, rp.maskError(err)
		}
		fi, err := file.Stat()
		if err != nil {
			return nil, rp.maskError(err)
		}
		return []os.FileInfo{fi}, nil
	}
	case "Readlink": {
		target, err := os.Readlink(sourceName)
		if err != nil {
			return nil, rp.maskError(err)
		}
		file, err := os.Open(target)
		if err != nil {
			return nil, rp.maskError(err)
		}
		fi, err := file.Stat()
		if err != nil {
			return nil, rp.maskError(err)
		}
		return []os.FileInfo{fi}, nil
	}
	default:
		return nil, errors.New(fmt.Sprint("Unknown request method: %s", request.Method));
	}
}

func (rp requestPrefix) getFile(path string, flags int, mode os.FileMode) (*os.File, error) {
	filePath, err := rp.validate(path)
	if err != nil {
		return nil, rp.maskError(err)
	}
	file, err := os.OpenFile(filePath, flags, mode)
	if err != nil {
		return nil, rp.maskError(err)
	}
	return file, err
}

func (rp requestPrefix) validate(path string) (string, error) {
	ok, path := rp.tryPrefix(path)
	if !ok {
		return "", errors.New("Access denied")
	}
	return path, nil
}

func (rp requestPrefix) tryPrefix(path string) (bool, string) {
	newPath := filepath.Clean(filepath.Join(rp.prefix, path))
	if utils.EnsureAccess(newPath, rp.prefix) {
		return true, newPath
	} else {
		return false, ""
	}
}

func (rp requestPrefix) stripPrefix(path string) string {
	newStr := strings.Replace(path, rp.prefix, "", -1)
	if len(newStr) == 0 {
		newStr = "/"
	}
	return newStr
}

func (rp requestPrefix) maskError(err error) error {
	return errors.New(rp.stripPrefix(err.Error()))
}
<|endoftext|>


<commit_before>package shodan

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
)

const (
	testClientToken = "TEST_TOKEN"
	stubsDir        = "stubs"

	unauthorizedPath = "/http-error/401"
	notFoundPath     = "/http-error/404"
)

var (
	mux    *http.ServeMux
	server *httptest.Server
	client *Client
)

func setUpTestServe() {
	mux = http.NewServeMux()
	server = httptest.NewServer(mux)
	client = NewClient(testClientToken)
	client.BaseURL = server.URL
	client.ExploitBaseURL = server.URL
	client.StreamBaseURL = server.URL
}

func getStub(t *testing.T, stubName string) []byte {
	stubPath := fmt.Sprintf("%s/%s.json", stubsDir, stubName)
	content, err := ioutil.ReadFile(stubPath)
	if err != nil {
		t.Errorf("getStub error %v", err)
	}

	return content
}

func tearDownTestServe() {
	server.Close()
}

func TestNewClient(t *testing.T) {
	client := NewClient(testClientToken)

	assert.Equal(t, testClientToken, client.Token)
}

func TestClient_buildURL(t *testing.T) {
	client := NewClient(testClientToken)
	testOptions := struct {
		Page    int  `url:"page"`
		ShowAll bool `url:"show_all"`
	}{
		100,
		true,
	}
	testCases := []struct {
		path     string
		params   interface{}
		expected string
	}{
		{
			"/testing/test/1",
			nil,
			baseURL + "/testing/test/1?key=" + testClientToken,
		},
		{
			"/testing/test/2",
			testOptions,
			baseURL + "/testing/test/2?key=" + testClientToken + "&page=100&show_all=true",
		},
	}

	for _, caseParams := range testCases {
		url, err := client.buildURL(baseURL, caseParams.path, caseParams.params)

		assert.Nil(t, err)
		assert.Equal(t, caseParams.expected, url)
	}
}

func TestClient_executeRequest_textUnauthorized(t *testing.T) {
	setUpTestServe()
	defer tearDownTestServe()

	errorText := "401 Unauthorized\n\n"
	errorText += "This server could not verify that you are authorized to access the document you requested.  " +
		"Either you supplied the wrong credentials (e.g., bad password), or your browser does not understand how to " +
		"supply the credentials required."

	mux.HandleFunc(unauthorizedPath, func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, errorText, http.StatusUnauthorized)
	})

	url, err := client.buildBaseURL(unauthorizedPath, nil)
	assert.Nil(t, err)

	err = client.executeRequest("GET", url, nil, nil)
	assert.NotNil(t, err)
}

func TestClient_executeRequest_jsonNotFound(t *testing.T) {
	setUpTestServe()
	defer tearDownTestServe()

	mux.HandleFunc(notFoundPath, func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, `{"error": "No information available for that IP."}`, http.StatusNotFound)
	})

	url, err := client.buildBaseURL(notFoundPath, nil)
	assert.Nil(t, err)

	err = client.executeRequest("GET", url, nil, nil)
	assert.NotNil(t, err)
}
<commit_msg>add build url wrappers<commit_after>package shodan

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
)

const (
	testClientToken = "TEST_TOKEN"
	stubsDir        = "stubs"

	unauthorizedPath = "/http-error/401"
	notFoundPath     = "/http-error/404"
)

var (
	mux    *http.ServeMux
	server *httptest.Server
	client *Client
)

func setUpTestServe() {
	mux = http.NewServeMux()
	server = httptest.NewServer(mux)
	client = NewClient(testClientToken)
	client.BaseURL = server.URL
	client.ExploitBaseURL = server.URL
	client.StreamBaseURL = server.URL
}

func getStub(t *testing.T, stubName string) []byte {
	stubPath := fmt.Sprintf("%s/%s.json", stubsDir, stubName)
	content, err := ioutil.ReadFile(stubPath)
	if err != nil {
		t.Errorf("getStub error %v", err)
	}

	return content
}

func tearDownTestServe() {
	server.Close()
}

func TestNewClient(t *testing.T) {
	client := NewClient(testClientToken)

	assert.Equal(t, testClientToken, client.Token)
}

func TestClient_buildURL(t *testing.T) {
	client := NewClient(testClientToken)
	testOptions := struct {
		Page    int  `url:"page"`
		ShowAll bool `url:"show_all"`
	}{
		100,
		true,
	}
	testCases := []struct {
		path     string
		params   interface{}
		expected string
	}{
		{
			"/testing/test/1",
			nil,
			baseURL + "/testing/test/1?key=" + testClientToken,
		},
		{
			"/testing/test/2",
			testOptions,
			baseURL + "/testing/test/2?key=" + testClientToken + "&page=100&show_all=true",
		},
	}

	for _, caseParams := range testCases {
		url, err := client.buildURL(baseURL, caseParams.path, caseParams.params)

		assert.Nil(t, err)
		assert.Equal(t, caseParams.expected, url)
	}
}

func TestClient_buildBaseURL(t *testing.T) {
	expected := client.BaseURL + "/test-base-url-building/?key=" + testClientToken
	actual, err := client.buildBaseURL("/test-base-url-building/", nil)

	assert.Nil(t, err)
	assert.Equal(t, expected, actual)
}

func TestClient_buildExploitBaseURL(t *testing.T) {
	expected := client.ExploitBaseURL + "/test-exploit-url-building/?key=" + testClientToken
	actual, err := client.buildExploitBaseURL("/test-exploit-url-building/", nil)

	assert.Nil(t, err)
	assert.Equal(t, expected, actual)
}

func TestClient_buildStreamBaseURL(t *testing.T) {
	expected := client.BaseURL + "/test-stream-url-building/?key=" + testClientToken
	actual, err := client.buildStreamBaseURL("/test-stream-url-building/", nil)

	assert.Nil(t, err)
	assert.Equal(t, expected, actual)
}

func TestClient_executeRequest_textUnauthorized(t *testing.T) {
	setUpTestServe()
	defer tearDownTestServe()

	errorText := "401 Unauthorized\n\n"
	errorText += "This server could not verify that you are authorized to access the document you requested.  " +
		"Either you supplied the wrong credentials (e.g., bad password), or your browser does not understand how to " +
		"supply the credentials required."

	mux.HandleFunc(unauthorizedPath, func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, errorText, http.StatusUnauthorized)
	})

	url, err := client.buildBaseURL(unauthorizedPath, nil)
	assert.Nil(t, err)

	err = client.executeRequest("GET", url, nil, nil)
	assert.NotNil(t, err)
}

func TestClient_executeRequest_jsonNotFound(t *testing.T) {
	setUpTestServe()
	defer tearDownTestServe()

	mux.HandleFunc(notFoundPath, func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, `{"error": "No information available for that IP."}`, http.StatusNotFound)
	})

	url, err := client.buildBaseURL(notFoundPath, nil)
	assert.Nil(t, err)

	err = client.executeRequest("GET", url, nil, nil)
	assert.NotNil(t, err)
}
<|endoftext|>


<commit_before>package signer

import (
	"bytes"
	"crypto/x509"
	"encoding/asn1"
	"encoding/hex"
	"fmt"
	"reflect"
	"testing"

	"github.com/cloudflare/cfssl/config"
)

func TestSplitHosts(t *testing.T) {
	list := SplitHosts("")
	if list != nil {
		t.Fatal("SplitHost should return nil with empty input")
	}

	list = SplitHosts("single.domain")
	if len(list) != 1 {
		t.Fatal("SplitHost fails to split single domain")
	}

	list = SplitHosts("comma,separated,values")
	if len(list) != 3 {
		t.Fatal("SplitHost fails to split multiple domains")
	}
	if list[0] != "comma" || list[1] != "separated" || list[2] != "values" {
		t.Fatal("SplitHost fails to split multiple domains")
	}
}

func TestAddPolicies(t *testing.T) {
	var cert x509.Certificate
	addPolicies(&cert, []config.CertificatePolicy{
		config.CertificatePolicy{
			ID: config.OID{1, 2, 3, 4},
		},
	})

	if len(cert.ExtraExtensions) != 1 {
		t.Fatal("No extension added")
	}
	ext := cert.ExtraExtensions[0]
	if !reflect.DeepEqual(ext.Id, asn1.ObjectIdentifier{2, 5, 29, 32}) {
		t.Fatal(fmt.Sprintf("Wrong OID for policy qualifier %v", ext.Id))
	}
	if ext.Critical {
		t.Fatal("Policy qualifier marked critical")
	}
	expectedBytes, _ := hex.DecodeString("3009300706032a03043000")
	if !bytes.Equal(ext.Value, expectedBytes) {
		t.Fatal(fmt.Sprintf("Value didn't match expected bytes: %s vs %s",
			hex.EncodeToString(ext.Value), hex.EncodeToString(expectedBytes)))
	}
}

func TestAddPoliciesWithQualifiers(t *testing.T) {
	var cert x509.Certificate
	addPolicies(&cert, []config.CertificatePolicy{
		config.CertificatePolicy{
			ID: config.OID{1, 2, 3, 4},
			Qualifiers: []config.CertificatePolicyQualifier{
				config.CertificatePolicyQualifier{
					Type:  "id-qt-cps",
					Value: "http://example.com/cps",
				},
				config.CertificatePolicyQualifier{
					Type:  "id-qt-unotice",
					Value: "Do What Thou Wilt",
				},
			},
		},
	})

	if len(cert.ExtraExtensions) != 1 {
		t.Fatal("No extension added")
	}
	ext := cert.ExtraExtensions[0]
	if !reflect.DeepEqual(ext.Id, asn1.ObjectIdentifier{2, 5, 29, 32}) {
		t.Fatal(fmt.Sprintf("Wrong OID for policy qualifier %v", ext.Id))
	}
	if ext.Critical {
		t.Fatal("Policy qualifier marked critical")
	}
	expectedBytes, _ := hex.DecodeString("304e304c06032a03043045302206082b060105050702011616687474703a2f2f6578616d706c652e636f6d2f637073301f06082b0601050507020230130c11446f20576861742054686f752057696c74")
	if !bytes.Equal(ext.Value, expectedBytes) {
		t.Fatal(fmt.Sprintf("Value didn't match expected bytes: %s vs %s",
			hex.EncodeToString(ext.Value), hex.EncodeToString(expectedBytes)))
	}
}
<commit_msg>Add appendIf test<commit_after>package signer

import (
	"bytes"
	"crypto/x509"
	"encoding/asn1"
	"encoding/hex"
	"fmt"
	"reflect"
	"testing"

	"github.com/cloudflare/cfssl/config"
)

func TestAppendIf(t *testing.T) {
	s := ""
	a := make([]string, 0, 5)
	appendIf(s, &a)
	if len(a) != 0 {
		t.Fatal("appendIf should not append to a with an empty s")
	}
	s = "test"
	appendIf(s, &a)
	if len(a[0]) != 4 {
		t.Fatal("appendIf should append s to a")
	}
}

func TestSplitHosts(t *testing.T) {
	list := SplitHosts("")
	if list != nil {
		t.Fatal("SplitHost should return nil with empty input")
	}

	list = SplitHosts("single.domain")
	if len(list) != 1 {
		t.Fatal("SplitHost fails to split single domain")
	}

	list = SplitHosts("comma,separated,values")
	if len(list) != 3 {
		t.Fatal("SplitHost fails to split multiple domains")
	}
	if list[0] != "comma" || list[1] != "separated" || list[2] != "values" {
		t.Fatal("SplitHost fails to split multiple domains")
	}
}

func TestAddPolicies(t *testing.T) {
	var cert x509.Certificate
	addPolicies(&cert, []config.CertificatePolicy{
		config.CertificatePolicy{
			ID: config.OID{1, 2, 3, 4},
		},
	})

	if len(cert.ExtraExtensions) != 1 {
		t.Fatal("No extension added")
	}
	ext := cert.ExtraExtensions[0]
	if !reflect.DeepEqual(ext.Id, asn1.ObjectIdentifier{2, 5, 29, 32}) {
		t.Fatal(fmt.Sprintf("Wrong OID for policy qualifier %v", ext.Id))
	}
	if ext.Critical {
		t.Fatal("Policy qualifier marked critical")
	}
	expectedBytes, _ := hex.DecodeString("3009300706032a03043000")
	if !bytes.Equal(ext.Value, expectedBytes) {
		t.Fatal(fmt.Sprintf("Value didn't match expected bytes: %s vs %s",
			hex.EncodeToString(ext.Value), hex.EncodeToString(expectedBytes)))
	}
}

func TestAddPoliciesWithQualifiers(t *testing.T) {
	var cert x509.Certificate
	addPolicies(&cert, []config.CertificatePolicy{
		config.CertificatePolicy{
			ID: config.OID{1, 2, 3, 4},
			Qualifiers: []config.CertificatePolicyQualifier{
				config.CertificatePolicyQualifier{
					Type:  "id-qt-cps",
					Value: "http://example.com/cps",
				},
				config.CertificatePolicyQualifier{
					Type:  "id-qt-unotice",
					Value: "Do What Thou Wilt",
				},
			},
		},
	})

	if len(cert.ExtraExtensions) != 1 {
		t.Fatal("No extension added")
	}
	ext := cert.ExtraExtensions[0]
	if !reflect.DeepEqual(ext.Id, asn1.ObjectIdentifier{2, 5, 29, 32}) {
		t.Fatal(fmt.Sprintf("Wrong OID for policy qualifier %v", ext.Id))
	}
	if ext.Critical {
		t.Fatal("Policy qualifier marked critical")
	}
	expectedBytes, _ := hex.DecodeString("304e304c06032a03043045302206082b060105050702011616687474703a2f2f6578616d706c652e636f6d2f637073301f06082b0601050507020230130c11446f20576861742054686f752057696c74")
	if !bytes.Equal(ext.Value, expectedBytes) {
		t.Fatal(fmt.Sprintf("Value didn't match expected bytes: %s vs %s",
			hex.EncodeToString(ext.Value), hex.EncodeToString(expectedBytes)))
	}
}
<|endoftext|>


<commit_before>// package edit implements a full-feature line editor.
package edit

import (
	"os"
	"fmt"
	"unicode"
	"unicode/utf8"
	"./tty"
	"../async"
)

// Editor keeps the status of the line editor.
type Editor struct {
	savedTermios *tty.Termios
	file *os.File
	writer *writer
	reader *reader
}

// LineRead is the result of ReadLine. Exactly one member is non-zero, making
// it effectively a tagged union.
type LineRead struct {
	Line string
	Eof bool
	Err error
}

// Init initializes an Editor on the terminal referenced by fd.
func Init(file *os.File, tr *async.TimedReader) (*Editor, error) {
	fd := int(file.Fd())
	term, err := tty.NewTermiosFromFd(fd)
	if err != nil {
		return nil, fmt.Errorf("Can't get terminal attribute: %s", err)
	}

	editor := &Editor{
		savedTermios: term.Copy(),
		file: file,
		writer: newWriter(file),
		reader: newReader(tr),
	}

	term.SetIcanon(false)
	term.SetEcho(false)
	term.SetMin(1)
	term.SetTime(0)

	err = term.ApplyToFd(fd)
	if err != nil {
		return nil, fmt.Errorf("Can't set up terminal attribute: %s", err)
	}

	fmt.Fprint(editor.file, "\033[?7l")
	return editor, nil
}

// Cleanup restores the terminal referenced by fd so that other commands
// that use the terminal can be executed.
func (ed *Editor) Cleanup() error {
	fmt.Fprint(ed.file, "\033[?7h")

	fd := int(ed.file.Fd())
	err := ed.savedTermios.ApplyToFd(fd)
	if err != nil {
		return fmt.Errorf("Can't restore terminal attribute of stdin: %s", err)
	}
	ed.savedTermios = nil
	return nil
}

func (ed *Editor) beep() {
}

func (ed *Editor) refresh(prompt, text, tip string) error {
	return ed.writer.refresh(prompt, text, tip)
}

// ReadLine reads a line interactively.
func (ed *Editor) ReadLine(prompt string) (lr LineRead) {
	line := ""
	tip := ""

	for {
		err := ed.refresh(prompt, line, tip)
		if err != nil {
			return LineRead{Err: err}
		}

		k, err := ed.reader.readKey()
		if err != nil {
			return LineRead{Err: err}
		}

		switch k {
		case CtrlKey('J'):
			tip = ""
			err := ed.refresh(prompt, line, tip)
			if err != nil {
				return LineRead{Err: err}
			}
			fmt.Fprintln(ed.file)
			return LineRead{Line: line}
		case PlainKey(Backspace): // Backspace
			if l := len(line); l > 0 {
				_, w := utf8.DecodeLastRuneInString(line)
				line = line[:l-w]
			} else {
				ed.beep()
			}
		case CtrlKey('U'):
			line = ""
		/*
		case CtrlKey('B'):
			fmt.Fprintf(ed.file, "\033[D")
		case CtrlKey('F'):
			fmt.Fprintf(ed.file, "\033[C")
		*/
		case CtrlKey('D'):
			if len(line) == 0 {
				return LineRead{Eof: true}
			}
			fallthrough
		default:
			if !(k.Ctrl || k.Alt) && unicode.IsGraphic(k.rune) {
				line += string(k.rune)
			} else {
				tip = fmt.Sprintf("Unknown: %s", k)
			}
		}
	}
}
<commit_msg>edit: Use the tip to inform reader errors<commit_after>// package edit implements a full-feature line editor.
package edit

import (
	"os"
	"fmt"
	"unicode"
	"unicode/utf8"
	"./tty"
	"../async"
)

// Editor keeps the status of the line editor.
type Editor struct {
	savedTermios *tty.Termios
	file *os.File
	writer *writer
	reader *reader
}

// LineRead is the result of ReadLine. Exactly one member is non-zero, making
// it effectively a tagged union.
type LineRead struct {
	Line string
	Eof bool
	Err error
}

// Init initializes an Editor on the terminal referenced by fd.
func Init(file *os.File, tr *async.TimedReader) (*Editor, error) {
	fd := int(file.Fd())
	term, err := tty.NewTermiosFromFd(fd)
	if err != nil {
		return nil, fmt.Errorf("Can't get terminal attribute: %s", err)
	}

	editor := &Editor{
		savedTermios: term.Copy(),
		file: file,
		writer: newWriter(file),
		reader: newReader(tr),
	}

	term.SetIcanon(false)
	term.SetEcho(false)
	term.SetMin(1)
	term.SetTime(0)

	err = term.ApplyToFd(fd)
	if err != nil {
		return nil, fmt.Errorf("Can't set up terminal attribute: %s", err)
	}

	fmt.Fprint(editor.file, "\033[?7l")
	return editor, nil
}

// Cleanup restores the terminal referenced by fd so that other commands
// that use the terminal can be executed.
func (ed *Editor) Cleanup() error {
	fmt.Fprint(ed.file, "\033[?7h")

	fd := int(ed.file.Fd())
	err := ed.savedTermios.ApplyToFd(fd)
	if err != nil {
		return fmt.Errorf("Can't restore terminal attribute of stdin: %s", err)
	}
	ed.savedTermios = nil
	return nil
}

func (ed *Editor) beep() {
}

func (ed *Editor) refresh(prompt, text, tip string) error {
	return ed.writer.refresh(prompt, text, tip)
}

func pushTip(tip, more string) string {
	if len(tip) == 0 {
		return more
	}
	return tip + "; " + more
}

// ReadLine reads a line interactively.
func (ed *Editor) ReadLine(prompt string) (lr LineRead) {
	line := ""
	tip := ""

	for {
		err := ed.refresh(prompt, line, tip)
		if err != nil {
			return LineRead{Err: err}
		}

		tip = ""

		k, err := ed.reader.readKey()
		if err != nil {
			tip = pushTip(tip, err.Error())
		}

		switch k {
		case CtrlKey('J'):
			tip = ""
			err := ed.refresh(prompt, line, tip)
			if err != nil {
				return LineRead{Err: err}
			}
			fmt.Fprintln(ed.file)
			return LineRead{Line: line}
		case PlainKey(Backspace): // Backspace
			if l := len(line); l > 0 {
				_, w := utf8.DecodeLastRuneInString(line)
				line = line[:l-w]
			} else {
				ed.beep()
			}
		case CtrlKey('U'):
			line = ""
		/*
		case CtrlKey('B'):
			fmt.Fprintf(ed.file, "\033[D")
		case CtrlKey('F'):
			fmt.Fprintf(ed.file, "\033[C")
		*/
		case CtrlKey('D'):
			if len(line) == 0 {
				return LineRead{Eof: true}
			}
			fallthrough
		default:
			if !(k.Ctrl || k.Alt) && unicode.IsGraphic(k.rune) {
				line += string(k.rune)
			} else {
				tip = pushTip(tip, fmt.Sprintf("Unknown: %s", k))
			}
		}
	}
}
<|endoftext|>


<commit_before>// package edit implements a full-feature line editor.
package edit

import (
	"os"
	"fmt"
	"unicode"
	"unicode/utf8"
	"./tty"
	"../eval"
	"../util"
)

var Lackeol = "\033[7m\u23ce\033[m\n"

// Editor keeps the status of the line editor.
type Editor struct {
	savedTermios *tty.Termios
	file *os.File
	writer *writer
	reader *reader
	// Fields below are used when during ReadLine.
	prompt, line, tip string
	completion *completion
	dot int
}

// LineRead is the result of ReadLine. Exactly one member is non-zero, making
// it effectively a tagged union.
type LineRead struct {
	Line string
	Eof bool
	Err error
}

// Init initializes an Editor on the terminal referenced by fd.
func Init(file *os.File, tr *util.TimedReader, ev *eval.Evaluator) (*Editor, error) {
	fd := int(file.Fd())
	term, err := tty.NewTermiosFromFd(fd)
	if err != nil {
		return nil, fmt.Errorf("Can't get terminal attribute: %s", err)
	}

	editor := &Editor{
		savedTermios: term.Copy(),
		file: file,
		writer: newWriter(file, ev),
		reader: newReader(tr),
	}

	term.SetIcanon(false)
	term.SetEcho(false)
	term.SetMin(1)
	term.SetTime(0)

	err = term.ApplyToFd(fd)
	if err != nil {
		return nil, fmt.Errorf("Can't set up terminal attribute: %s", err)
	}

	fmt.Fprint(editor.file, "\033[?7l")

	err = tty.FlushInput(fd)
	if err != nil {
		return nil, err
	}

	file.WriteString("\033[6n")
	// XXX Possible race condition: user input sneaked in between WriteString
	// and readCPR
	x, _, err := editor.reader.readCPR()
	if err != nil {
		return nil, err
	}

	if x != 1 {
		file.WriteString(Lackeol)
	}

	return editor, nil
}

// Cleanup restores the terminal referenced by fd so that other commands
// that use the terminal can be executed.
func (ed *Editor) Cleanup() error {
	fmt.Fprint(ed.file, "\033[?7h")

	fd := int(ed.file.Fd())
	err := ed.savedTermios.ApplyToFd(fd)
	if err != nil {
		return fmt.Errorf("Can't restore terminal attribute of stdin: %s", err)
	}
	ed.savedTermios = nil
	return nil
}

func (ed *Editor) beep() {
}

func (ed *Editor) pushTip(more string) {
	if len(ed.tip) == 0 {
		ed.tip = more
	} else {
		ed.tip = ed.tip + "; " + more
	}
}

func (ed *Editor) refresh() error {
	return ed.writer.refresh(ed.prompt, ed.line, ed.tip, ed.completion, ed.dot)
}

// TODO Allow modifiable keybindings.
var keyBindings = map[Key]string {
	Key{'U', Ctrl}: "kill-line-b",
	Key{'K', Ctrl}: "kill-line-f",
	Key{Backspace, 0}: "kill-rune-b",
	Key{Left, 0}: "move-dot-b",
	Key{Right, 0}: "move-dot-f",
	Key{Tab, 0}: "complete",
}

var completionKeyBindings = map[Key]string {
	Key{Tab, 0}: "next-candidate",
	Key{Down, 0}: "next-candidate",
	Key{Up, 0}: "prev-candidate",
	Key{'[', Ctrl}: "exit-completion",
}

// ReadLine reads a line interactively.
func (ed *Editor) ReadLine(prompt string) (lr LineRead) {
	ed.prompt = prompt
	ed.line = ""
	ed.tip = ""
	ed.completion = nil
	ed.dot = 0

	for {
		err := ed.refresh()
		if err != nil {
			return LineRead{Err: err}
		}

		ed.tip = ""

		k, err := ed.reader.readKey()
		if err != nil {
			ed.pushTip(err.Error())
			continue
		}

		if ed.completion != nil {
			if name, bound := completionKeyBindings[k]; bound {
				leBuiltins[name](ed)
				continue
			} else {
				// Implicitly exit completion and fall back to normal keybinding
				ed.completion = nil
			}
		}

		if name, bound := keyBindings[k]; bound {
			leBuiltins[name](ed)
			continue
		}

		switch k {
		// XXX Keybindings that affect the flow of ReadLine can't yet be
		// implemented as functions.
		case Key{Enter, 0}:
			ed.tip = ""
			ed.completion = nil
			err := ed.refresh()
			if err != nil {
				return LineRead{Err: err}
			}
			fmt.Fprintln(ed.file)
			return LineRead{Line: ed.line}
		case Key{'D', Ctrl}:
			if len(ed.line) == 0 {
				return LineRead{Eof: true}
			}
			fallthrough
		default:
			if k.Mod == 0 && k.rune > 0 && unicode.IsGraphic(k.rune) {
				ed.line = ed.line[:ed.dot] + string(k.rune) + ed.line[ed.dot:]
				ed.dot += utf8.RuneLen(k.rune)
			} else {
				ed.pushTip(fmt.Sprintf("Unbound: %s", k))
			}
		}
	}
}
<commit_msg>Update comments<commit_after>// package edit implements a full-feature line editor.
package edit

import (
	"os"
	"fmt"
	"unicode"
	"unicode/utf8"
	"./tty"
	"../eval"
	"../util"
)

var Lackeol = "\033[7m\u23ce\033[m\n"

// Editor keeps the status of the line editor.
type Editor struct {
	savedTermios *tty.Termios
	file *os.File
	writer *writer
	reader *reader
	// Fields below are used during ReadLine.
	prompt, line, tip string
	completion *completion
	dot int
}

// LineRead is the result of ReadLine. Exactly one member is non-zero, making
// it effectively a tagged union.
type LineRead struct {
	Line string
	Eof bool
	Err error
}

// Init initializes an Editor on the terminal referenced by fd.
// The Editor is reinitialized every time the control of the terminal is
// transferred back to the line editor.
func Init(file *os.File, tr *util.TimedReader, ev *eval.Evaluator) (*Editor, error) {
	fd := int(file.Fd())
	term, err := tty.NewTermiosFromFd(fd)
	if err != nil {
		return nil, fmt.Errorf("Can't get terminal attribute: %s", err)
	}

	editor := &Editor{
		savedTermios: term.Copy(),
		file: file,
		writer: newWriter(file, ev),
		reader: newReader(tr),
	}

	term.SetIcanon(false)
	term.SetEcho(false)
	term.SetMin(1)
	term.SetTime(0)

	err = term.ApplyToFd(fd)
	if err != nil {
		return nil, fmt.Errorf("Can't set up terminal attribute: %s", err)
	}

	fmt.Fprint(editor.file, "\033[?7l")

	err = tty.FlushInput(fd)
	if err != nil {
		return nil, err
	}

	file.WriteString("\033[6n")
	// XXX Possible race condition: user input sneaked in between WriteString
	// and readCPR
	x, _, err := editor.reader.readCPR()
	if err != nil {
		return nil, err
	}

	if x != 1 {
		file.WriteString(Lackeol)
	}

	return editor, nil
}

// Cleanup restores the terminal referenced by fd so that other commands
// that use the terminal can be executed.
func (ed *Editor) Cleanup() error {
	fmt.Fprint(ed.file, "\033[?7h")

	fd := int(ed.file.Fd())
	err := ed.savedTermios.ApplyToFd(fd)
	if err != nil {
		return fmt.Errorf("Can't restore terminal attribute of stdin: %s", err)
	}
	ed.savedTermios = nil
	return nil
}

func (ed *Editor) beep() {
}

func (ed *Editor) pushTip(more string) {
	if len(ed.tip) == 0 {
		ed.tip = more
	} else {
		ed.tip = ed.tip + "; " + more
	}
}

func (ed *Editor) refresh() error {
	return ed.writer.refresh(ed.prompt, ed.line, ed.tip, ed.completion, ed.dot)
}

// TODO Allow modifiable keybindings.
var keyBindings = map[Key]string {
	Key{'U', Ctrl}: "kill-line-b",
	Key{'K', Ctrl}: "kill-line-f",
	Key{Backspace, 0}: "kill-rune-b",
	Key{Left, 0}: "move-dot-b",
	Key{Right, 0}: "move-dot-f",
	Key{Tab, 0}: "complete",
}

var completionKeyBindings = map[Key]string {
	Key{Tab, 0}: "next-candidate",
	Key{Down, 0}: "next-candidate",
	Key{Up, 0}: "prev-candidate",
	Key{'[', Ctrl}: "exit-completion",
}

// ReadLine reads a line interactively.
func (ed *Editor) ReadLine(prompt string) (lr LineRead) {
	ed.prompt = prompt
	ed.line = ""
	ed.tip = ""
	ed.completion = nil
	ed.dot = 0

	for {
		err := ed.refresh()
		if err != nil {
			return LineRead{Err: err}
		}

		ed.tip = ""

		k, err := ed.reader.readKey()
		if err != nil {
			ed.pushTip(err.Error())
			continue
		}

		if ed.completion != nil {
			if name, bound := completionKeyBindings[k]; bound {
				leBuiltins[name](ed)
				continue
			} else {
				// Implicitly exit completion and fall back to normal keybinding
				ed.completion = nil
			}
		}

		if name, bound := keyBindings[k]; bound {
			leBuiltins[name](ed)
			continue
		}

		switch k {
		// XXX Keybindings that affect the flow of ReadLine can't yet be
		// implemented as functions.
		case Key{Enter, 0}:
			ed.tip = ""
			ed.completion = nil
			err := ed.refresh()
			if err != nil {
				return LineRead{Err: err}
			}
			fmt.Fprintln(ed.file)
			return LineRead{Line: ed.line}
		case Key{'D', Ctrl}:
			if len(ed.line) == 0 {
				return LineRead{Eof: true}
			}
			fallthrough
		default:
			if k.Mod == 0 && k.rune > 0 && unicode.IsGraphic(k.rune) {
				ed.line = ed.line[:ed.dot] + string(k.rune) + ed.line[ed.dot:]
				ed.dot += utf8.RuneLen(k.rune)
			} else {
				ed.pushTip(fmt.Sprintf("Unbound: %s", k))
			}
		}
	}
}
<|endoftext|>