<commit_before>// Copyright 2015 Matthew Collins
//
// 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 render

import (
	"encoding/json"
	"fmt"
	"image"
	"image/draw"
	"image/png"
	"sync"

	"github.com/thinkofdeath/steven/platform/gl"
	"github.com/thinkofdeath/steven/render/atlas"
	"github.com/thinkofdeath/steven/resource"
)

var (
	textures         []*atlas.Type
	textureViews     []*image.NRGBA
	textureMap       = map[string]TextureInfo{}
	textureLock      sync.RWMutex
	animatedTextures []*animatedTexture
)

const atlasSize = 1024

// TextureInfo returns information about a texture in an atlas
type TextureInfo struct {
	Atlas     int
	imageView *image.NRGBA
	*atlas.Rect
}

// GetTexture returns the related TextureInfo for the requested texture.
// If the texture isn't found a placeholder is returned instead.
func GetTexture(name string) *TextureInfo {
	textureLock.RLock()
	defer textureLock.RUnlock()
	t, ok := textureMap[name]
	if !ok {
		t = textureMap["missing_texture"]
	}
	return &t
}

// LoadTextures (re)loads all the block textures from the resource pack(s)
// TODO(Think) better error handling (if possible to recover?)
func LoadTextures() {
	textureLock.Lock()
	defer textureLock.Unlock()

	// Clear existing
	textures = nil
	textureMap = map[string]TextureInfo{}

	for _, file := range resource.Search("minecraft", "textures/blocks/", ".png") {
		r, err := resource.Open("minecraft", file)
		if err != nil {
			panic(err)
		}
		img, err := png.Decode(r)
		if err != nil {
			panic(err)
		}
		width, height := img.Bounds().Dx(), img.Bounds().Dy()
		var ani *animatedTexture
		if width != height {
			height = width
			old := img
			img := image.NewNRGBA(image.Rect(0, 0, width, width))
			draw.Draw(img, img.Bounds(), old, image.ZP, draw.Over)
			ani = loadAnimation(file, old.Bounds().Dy()/old.Bounds().Dx())
			ani.Image = old
			animatedTextures = append(animatedTextures, ani)
		}
		var pix []byte
		switch img := img.(type) {
		case *image.NRGBA:
			pix = img.Pix
		case *image.RGBA:
			pix = img.Pix
		default:
			panic(fmt.Sprintf("unsupported image type %T", img))
		}
		name := file[len("textures/blocks/") : len(file)-4]
		at, rect := addTexture(pix, width, height)
		info := TextureInfo{
			Rect:  rect,
			Atlas: at,
		}
		textureMap[name] = info
		if ani != nil {
			ani.Info = info
		}
	}

	at, rect := addTexture([]byte{
		0, 0, 0, 255,
		255, 0, 255, 255,
		255, 0, 255, 255,
		0, 0, 0, 255,
	}, 2, 2)
	textureMap["missing_texture"] = TextureInfo{
		Rect:  rect,
		Atlas: at,
	}
}

func addTexture(pix []byte, width, height int) (int, *atlas.Rect) {
	for i, a := range textures {
		rect, err := a.Add(pix, width, height)
		if err == nil {
			return i, rect
		}
	}
	a := atlas.New(atlasSize, atlasSize, 4)
	textures = append(textures, a)
	textureViews = append(textureViews, &image.NRGBA{
		Pix:    a.Buffer,
		Stride: 4 * atlasSize,
		Rect:   image.Rect(0, 0, atlasSize, atlasSize),
	})
	rect, err := a.Add(pix, width, height)
	if err != nil {
		panic(err)
	}
	return len(textures) - 1, rect
}

type animatedTexture struct {
	Info          TextureInfo
	Image         image.Image
	Interpolate   bool
	Frames        []textureFrame
	RemainingTime float64
	CurrentFrame  int
}

type textureFrame struct {
	Index int
	Time  int
}

func tickAnimatedTextures(delta float64) {
	delta /= 3 // default is 60 a second, minecraft is 20
	for _, ani := range animatedTextures {
		ani.RemainingTime -= delta
		if ani.RemainingTime < 0 {
			ani.CurrentFrame++
			ani.CurrentFrame %= len(ani.Frames)
			ani.RemainingTime += float64(ani.Frames[ani.CurrentFrame].Time)
			img := textureViews[ani.Info.Atlas]
			r := ani.Info.Rect
			y := r.Width * ani.Frames[ani.CurrentFrame].Index
			src := image.Rect(r.X, r.Y, r.X+r.Width, r.Y+r.Height)
			draw.Draw(img, src, ani.Image, image.Pt(0, y), draw.Src)
		}
	}
	for i, gt := range glTextures {
		gt.Bind(gl.Texture2D)
		gt.SubImage2D(0, 0, 0, atlasSize, atlasSize, gl.RGBA, gl.UnsignedByte, textures[i].Buffer)
		regenMipMaps(gt, textures[i].Buffer, atlasSize, atlasSize, 1)
	}
}

func loadAnimation(file string, max int) *animatedTexture {
	a := &animatedTexture{}
	defer func() {
		a.RemainingTime = float64(a.Frames[0].Time)
	}()

	type animation struct {
		FrameTime   int
		Interpolate bool
		Frames      []json.RawMessage
	}
	type base struct {
		Animation animation
	}

	meta, err := resource.Open("minecraft", file+".mcmeta")
	if err != nil {
		panic(err)
	}
	defer meta.Close()
	b := &base{}
	err = json.NewDecoder(meta).Decode(b)
	if err != nil {
		panic(err)
	}
	frameTime := b.Animation.FrameTime
	if frameTime == 0 {
		frameTime = 1
	}
	a.Interpolate = b.Animation.Interpolate

	if len(b.Animation.Frames) == 0 {
		a.Frames = make([]textureFrame, max)
		for i := range a.Frames {
			a.Frames[i] = textureFrame{
				Index: i,
				Time:  frameTime,
			}
		}
		return a
	}

	a.Frames = make([]textureFrame, len(b.Animation.Frames))
	for i := range a.Frames {
		a.Frames[i].Time = frameTime
		if b.Animation.Frames[i][0] == '{' {
			if err = json.Unmarshal(b.Animation.Frames[i], &a.Frames[i]); err != nil {
				panic(err)
			}
			a.Frames[i].Time *= frameTime
			continue
		}
		if err = json.Unmarshal(b.Animation.Frames[i], &a.Frames[i].Index); err != nil {
			panic(err)
		}
	}

	return a
}

func genMipMaps(g gl.Texture, buffer []byte, width, height, level int) {
	if level > 4 {
		g.Parameter(gl.TextureMaxLevel, gl.TextureValue(level-1))
		return
	}
	nw, nh, data := shrinkTexture(buffer, width, height)
	g.Image2D(level, nw, nh, gl.RGBA, gl.UnsignedByte, data)
	genMipMaps(g, data, nw, nh, level+1)
}

func regenMipMaps(g gl.Texture, buffer []byte, width, height, level int) {
	if level > 4 {
		g.Parameter(gl.TextureMaxLevel, gl.TextureValue(level-1))
		return
	}
	nw, nh, data := shrinkTexture(buffer, width, height)
	g.SubImage2D(level, 0, 0, nw, nh, gl.RGBA, gl.UnsignedByte, data)
	genMipMaps(g, data, nw, nh, level+1)
}

func shrinkTexture(buffer []byte, width, height int) (nw, nh int, data []byte) {
	nw = width >> 1
	nh = height >> 1
	data = make([]byte, nw*nh*4)
	for x := 0; x < nw; x++ {
		for y := 0; y < nh; y++ {
			i := (y*nw + x) * 4
			i2 := ((y<<1)*width + (x << 1)) * 4
			data[i] = buffer[i2]
			data[i+1] = buffer[i2+1]
			data[i+2] = buffer[i2+2]
			data[i+3] = buffer[i2+3]
		}
	}
	return
}

type glTexture struct {
	Data          []byte
	Width, Height int
	Format        gl.TextureFormat
	Type          gl.Type
	Filter        gl.TextureValue
	MinFilter     gl.TextureValue
	Wrap          gl.TextureValue
}

func createTexture(t glTexture) gl.Texture {
	if t.Format == 0 {
		t.Format = gl.RGB
	}
	if t.Type == 0 {
		t.Type = gl.UnsignedByte
	}
	if t.Filter == 0 {
		t.Filter = gl.Nearest
	}
	if t.MinFilter == 0 {
		t.MinFilter = t.Filter
	}
	if t.Wrap == 0 {
		t.Wrap = gl.ClampToEdge
	}

	texture := gl.CreateTexture()
	texture.Bind(gl.Texture2D)
	texture.Image2D(0, t.Width, t.Height, t.Format, t.Type, t.Data)
	texture.Parameter(gl.TextureMagFilter, t.Filter)
	texture.Parameter(gl.TextureMinFilter, t.MinFilter)
	texture.Parameter(gl.TextureWrapS, t.Wrap)
	texture.Parameter(gl.TextureWrapT, t.Wrap)
	return texture
}
<commit_msg>render: fix a mistake in regenMipMaps<commit_after>// Copyright 2015 Matthew Collins
//
// 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 render

import (
	"encoding/json"
	"fmt"
	"image"
	"image/draw"
	"image/png"
	"sync"

	"github.com/thinkofdeath/steven/platform/gl"
	"github.com/thinkofdeath/steven/render/atlas"
	"github.com/thinkofdeath/steven/resource"
)

var (
	textures         []*atlas.Type
	textureViews     []*image.NRGBA
	textureMap       = map[string]TextureInfo{}
	textureLock      sync.RWMutex
	animatedTextures []*animatedTexture
)

const atlasSize = 1024

// TextureInfo returns information about a texture in an atlas
type TextureInfo struct {
	Atlas     int
	imageView *image.NRGBA
	*atlas.Rect
}

// GetTexture returns the related TextureInfo for the requested texture.
// If the texture isn't found a placeholder is returned instead.
func GetTexture(name string) *TextureInfo {
	textureLock.RLock()
	defer textureLock.RUnlock()
	t, ok := textureMap[name]
	if !ok {
		t = textureMap["missing_texture"]
	}
	return &t
}

// LoadTextures (re)loads all the block textures from the resource pack(s)
// TODO(Think) better error handling (if possible to recover?)
func LoadTextures() {
	textureLock.Lock()
	defer textureLock.Unlock()

	// Clear existing
	textures = nil
	textureMap = map[string]TextureInfo{}

	for _, file := range resource.Search("minecraft", "textures/blocks/", ".png") {
		r, err := resource.Open("minecraft", file)
		if err != nil {
			panic(err)
		}
		img, err := png.Decode(r)
		if err != nil {
			panic(err)
		}
		width, height := img.Bounds().Dx(), img.Bounds().Dy()
		var ani *animatedTexture
		if width != height {
			height = width
			old := img
			img := image.NewNRGBA(image.Rect(0, 0, width, width))
			draw.Draw(img, img.Bounds(), old, image.ZP, draw.Over)
			ani = loadAnimation(file, old.Bounds().Dy()/old.Bounds().Dx())
			ani.Image = old
			animatedTextures = append(animatedTextures, ani)
		}
		var pix []byte
		switch img := img.(type) {
		case *image.NRGBA:
			pix = img.Pix
		case *image.RGBA:
			pix = img.Pix
		default:
			panic(fmt.Sprintf("unsupported image type %T", img))
		}
		name := file[len("textures/blocks/") : len(file)-4]
		at, rect := addTexture(pix, width, height)
		info := TextureInfo{
			Rect:  rect,
			Atlas: at,
		}
		textureMap[name] = info
		if ani != nil {
			ani.Info = info
		}
	}

	at, rect := addTexture([]byte{
		0, 0, 0, 255,
		255, 0, 255, 255,
		255, 0, 255, 255,
		0, 0, 0, 255,
	}, 2, 2)
	textureMap["missing_texture"] = TextureInfo{
		Rect:  rect,
		Atlas: at,
	}
}

func addTexture(pix []byte, width, height int) (int, *atlas.Rect) {
	for i, a := range textures {
		rect, err := a.Add(pix, width, height)
		if err == nil {
			return i, rect
		}
	}
	a := atlas.New(atlasSize, atlasSize, 4)
	textures = append(textures, a)
	textureViews = append(textureViews, &image.NRGBA{
		Pix:    a.Buffer,
		Stride: 4 * atlasSize,
		Rect:   image.Rect(0, 0, atlasSize, atlasSize),
	})
	rect, err := a.Add(pix, width, height)
	if err != nil {
		panic(err)
	}
	return len(textures) - 1, rect
}

type animatedTexture struct {
	Info          TextureInfo
	Image         image.Image
	Interpolate   bool
	Frames        []textureFrame
	RemainingTime float64
	CurrentFrame  int
}

type textureFrame struct {
	Index int
	Time  int
}

func tickAnimatedTextures(delta float64) {
	delta /= 3 // default is 60 a second, minecraft is 20
	for _, ani := range animatedTextures {
		ani.RemainingTime -= delta
		if ani.RemainingTime < 0 {
			ani.CurrentFrame++
			ani.CurrentFrame %= len(ani.Frames)
			ani.RemainingTime += float64(ani.Frames[ani.CurrentFrame].Time)
			img := textureViews[ani.Info.Atlas]
			r := ani.Info.Rect
			y := r.Width * ani.Frames[ani.CurrentFrame].Index
			src := image.Rect(r.X, r.Y, r.X+r.Width, r.Y+r.Height)
			draw.Draw(img, src, ani.Image, image.Pt(0, y), draw.Src)
		}
	}
	for i, gt := range glTextures {
		gt.Bind(gl.Texture2D)
		gt.SubImage2D(0, 0, 0, atlasSize, atlasSize, gl.RGBA, gl.UnsignedByte, textures[i].Buffer)
		regenMipMaps(gt, textures[i].Buffer, atlasSize, atlasSize, 1)
	}
}

func loadAnimation(file string, max int) *animatedTexture {
	a := &animatedTexture{}
	defer func() {
		a.RemainingTime = float64(a.Frames[0].Time)
	}()

	type animation struct {
		FrameTime   int
		Interpolate bool
		Frames      []json.RawMessage
	}
	type base struct {
		Animation animation
	}

	meta, err := resource.Open("minecraft", file+".mcmeta")
	if err != nil {
		panic(err)
	}
	defer meta.Close()
	b := &base{}
	err = json.NewDecoder(meta).Decode(b)
	if err != nil {
		panic(err)
	}
	frameTime := b.Animation.FrameTime
	if frameTime == 0 {
		frameTime = 1
	}
	a.Interpolate = b.Animation.Interpolate

	if len(b.Animation.Frames) == 0 {
		a.Frames = make([]textureFrame, max)
		for i := range a.Frames {
			a.Frames[i] = textureFrame{
				Index: i,
				Time:  frameTime,
			}
		}
		return a
	}

	a.Frames = make([]textureFrame, len(b.Animation.Frames))
	for i := range a.Frames {
		a.Frames[i].Time = frameTime
		if b.Animation.Frames[i][0] == '{' {
			if err = json.Unmarshal(b.Animation.Frames[i], &a.Frames[i]); err != nil {
				panic(err)
			}
			a.Frames[i].Time *= frameTime
			continue
		}
		if err = json.Unmarshal(b.Animation.Frames[i], &a.Frames[i].Index); err != nil {
			panic(err)
		}
	}

	return a
}

func genMipMaps(g gl.Texture, buffer []byte, width, height, level int) {
	if level > 4 {
		g.Parameter(gl.TextureMaxLevel, gl.TextureValue(level-1))
		return
	}
	nw, nh, data := shrinkTexture(buffer, width, height)
	g.Image2D(level, nw, nh, gl.RGBA, gl.UnsignedByte, data)
	genMipMaps(g, data, nw, nh, level+1)
}

func regenMipMaps(g gl.Texture, buffer []byte, width, height, level int) {
	if level > 4 {
		g.Parameter(gl.TextureMaxLevel, gl.TextureValue(level-1))
		return
	}
	nw, nh, data := shrinkTexture(buffer, width, height)
	g.SubImage2D(level, 0, 0, nw, nh, gl.RGBA, gl.UnsignedByte, data)
	regenMipMaps(g, data, nw, nh, level+1)
}

func shrinkTexture(buffer []byte, width, height int) (nw, nh int, data []byte) {
	nw = width >> 1
	nh = height >> 1
	data = make([]byte, nw*nh*4)
	for x := 0; x < nw; x++ {
		for y := 0; y < nh; y++ {
			i := (y*nw + x) * 4
			i2 := ((y<<1)*width + (x << 1)) * 4
			data[i] = buffer[i2]
			data[i+1] = buffer[i2+1]
			data[i+2] = buffer[i2+2]
			data[i+3] = buffer[i2+3]
		}
	}
	return
}

type glTexture struct {
	Data          []byte
	Width, Height int
	Format        gl.TextureFormat
	Type          gl.Type
	Filter        gl.TextureValue
	MinFilter     gl.TextureValue
	Wrap          gl.TextureValue
}

func createTexture(t glTexture) gl.Texture {
	if t.Format == 0 {
		t.Format = gl.RGB
	}
	if t.Type == 0 {
		t.Type = gl.UnsignedByte
	}
	if t.Filter == 0 {
		t.Filter = gl.Nearest
	}
	if t.MinFilter == 0 {
		t.MinFilter = t.Filter
	}
	if t.Wrap == 0 {
		t.Wrap = gl.ClampToEdge
	}

	texture := gl.CreateTexture()
	texture.Bind(gl.Texture2D)
	texture.Image2D(0, t.Width, t.Height, t.Format, t.Type, t.Data)
	texture.Parameter(gl.TextureMagFilter, t.Filter)
	texture.Parameter(gl.TextureMinFilter, t.MinFilter)
	texture.Parameter(gl.TextureWrapS, t.Wrap)
	texture.Parameter(gl.TextureWrapT, t.Wrap)
	return texture
}
<|endoftext|>


<commit_before>package zygo
func init() { GITLASTTAG = "v1.3.9"; GITLASTCOMMIT = "a8c7b638660bcd09c979e3dd914fd42f9f033358" }
<commit_msg>tag v1.4.3<commit_after>package zygo
func init() { GITLASTTAG = "v1.4.3"; GITLASTCOMMIT = "6b83f6e66d245c88bc2a8af673813519f01964fd" }
<|endoftext|>


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

// This package implements a basic LISP interpretor for embedding in a go program for scripting.
// This file implements user defined functions.

package golisp

import (
	"errors"
	"fmt"
)

type Function struct {
	Name             string
	Params           *Data
	VarArgs          bool
	RequiredArgCount int
	Body             *Data
	Env              *SymbolTableFrame
}

func computeRequiredArgumentCount(args *Data) (requiredArgumentCount int, varArgs bool) {
	requiredArgumentCount = 0
	varArgs = false
	for a := args; NotNilP(a); a = Cdr(a) {
		if SymbolP(a) {
			varArgs = true
			return
		} else {
			requiredArgumentCount += 1
		}
	}
	return
}

func MakeFunction(name string, params *Data, body *Data, parentEnv *SymbolTableFrame) *Function {
	requiredArgs, varArgs := computeRequiredArgumentCount(params)
	return &Function{Name: name, Params: params, VarArgs: varArgs, RequiredArgCount: requiredArgs, Body: body, Env: parentEnv}
}

func (self *Function) String() string {
	return fmt.Sprintf("<func: %s>", self.Name)
}

func (self *Function) makeLocalBindings(args *Data, argEnv *SymbolTableFrame, localEnv *SymbolTableFrame, eval bool) (err error) {
	if self.VarArgs {
		if Length(args) < self.RequiredArgCount {
			return errors.New(fmt.Sprintf("%s expected at least %d parameters, received %d.", self.Name, self.RequiredArgCount, Length(args)))
		}
	} else {
		if Length(args) != self.RequiredArgCount {
			return errors.New(fmt.Sprintf("%s expected %d parameters, received %d.", self.Name, self.RequiredArgCount, Length(args)))
		}
	}

	var argValue *Data
	var accumulatingParam *Data = nil
	accumulatedArgs := make([]*Data, 0)
	for p, a := self.Params, args; NotNilP(a); a = Cdr(a) {
		if eval {
			argValue, err = Eval(Car(a), argEnv)
			if err != nil {
				return
			}
		} else {
			argValue = Car(a)
		}

		if accumulatingParam != nil {
			accumulatedArgs = append(accumulatedArgs, argValue)
		} else {
			localEnv.BindLocallyTo(Car(p), argValue)
		}
		if accumulatingParam == nil {
			p = Cdr(p)
		}
		if SymbolP(p) {
			accumulatingParam = p
		}
	}
	if accumulatingParam != nil {
		localEnv.BindLocallyTo(accumulatingParam, ArrayToList(accumulatedArgs))
	}
	return nil
}

func (self *Function) internalApply(args *Data, argEnv *SymbolTableFrame, eval bool) (result *Data, err error) {
	localEnv := NewSymbolTableFrameBelow(self.Env)
	err = self.makeLocalBindings(args, argEnv, localEnv, eval)
	if err != nil {
		return
	}
	for s := self.Body; NotNilP(s); s = Cdr(s) {
		result, err = Eval(Car(s), localEnv)
		if err != nil {
			return nil, errors.New(fmt.Sprintf("In '%s': %s", self.Name, err))
		}
	}
	return
}

func (self *Function) Apply(args *Data, argEnv *SymbolTableFrame) (result *Data, err error) {
	return self.internalApply(args, argEnv, true)
}

func (self *Function) ApplyWithoutEval(args *Data, argEnv *SymbolTableFrame) (result *Data, err error) {
	return self.internalApply(args, argEnv, false)
}
<commit_msg>A utility used to force the local environment to be an extension of the provided environment, rather that the one captured when the function was defined. The downside of using this application function is that you loose the closure.<commit_after>// Copyright 2014 SteelSeries ApS.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This package implements a basic LISP interpretor for embedding in a go program for scripting.
// This file implements user defined functions.

package golisp

import (
	"errors"
	"fmt"
)

type Function struct {
	Name             string
	Params           *Data
	VarArgs          bool
	RequiredArgCount int
	Body             *Data
	Env              *SymbolTableFrame
}

func computeRequiredArgumentCount(args *Data) (requiredArgumentCount int, varArgs bool) {
	requiredArgumentCount = 0
	varArgs = false
	for a := args; NotNilP(a); a = Cdr(a) {
		if SymbolP(a) {
			varArgs = true
			return
		} else {
			requiredArgumentCount += 1
		}
	}
	return
}

func MakeFunction(name string, params *Data, body *Data, parentEnv *SymbolTableFrame) *Function {
	requiredArgs, varArgs := computeRequiredArgumentCount(params)
	return &Function{Name: name, Params: params, VarArgs: varArgs, RequiredArgCount: requiredArgs, Body: body, Env: parentEnv}
}

func (self *Function) String() string {
	return fmt.Sprintf("<func: %s>", self.Name)
}

func (self *Function) makeLocalBindings(args *Data, argEnv *SymbolTableFrame, localEnv *SymbolTableFrame, eval bool) (err error) {
	if self.VarArgs {
		if Length(args) < self.RequiredArgCount {
			return errors.New(fmt.Sprintf("%s expected at least %d parameters, received %d.", self.Name, self.RequiredArgCount, Length(args)))
		}
	} else {
		if Length(args) != self.RequiredArgCount {
			return errors.New(fmt.Sprintf("%s expected %d parameters, received %d.", self.Name, self.RequiredArgCount, Length(args)))
		}
	}

	var argValue *Data
	var accumulatingParam *Data = nil
	accumulatedArgs := make([]*Data, 0)
	for p, a := self.Params, args; NotNilP(a); a = Cdr(a) {
		if eval {
			argValue, err = Eval(Car(a), argEnv)
			if err != nil {
				return
			}
		} else {
			argValue = Car(a)
		}

		if accumulatingParam != nil {
			accumulatedArgs = append(accumulatedArgs, argValue)
		} else {
			localEnv.BindLocallyTo(Car(p), argValue)
		}
		if accumulatingParam == nil {
			p = Cdr(p)
		}
		if SymbolP(p) {
			accumulatingParam = p
		}
	}
	if accumulatingParam != nil {
		localEnv.BindLocallyTo(accumulatingParam, ArrayToList(accumulatedArgs))
	}
	return nil
}

func (self *Function) internalApply(args *Data, argEnv *SymbolTableFrame, eval bool) (result *Data, err error) {
	localEnv := NewSymbolTableFrameBelow(self.Env)
	err = self.makeLocalBindings(args, argEnv, localEnv, eval)
	if err != nil {
		return
	}
	for s := self.Body; NotNilP(s); s = Cdr(s) {
		result, err = Eval(Car(s), localEnv)
		if err != nil {
			return nil, errors.New(fmt.Sprintf("In '%s': %s", self.Name, err))
		}
	}
	return
}

func (self *Function) Apply(args *Data, argEnv *SymbolTableFrame) (result *Data, err error) {
	return self.internalApply(args, argEnv, true)
}

func (self *Function) ApplyWithoutEval(args *Data, argEnv *SymbolTableFrame) (result *Data, err error) {
	return self.internalApply(args, argEnv, false)
}

func (self *Function) ApplyOveriddingEnvironment(args *Data, argEnv *SymbolTableFrame) (result *Data, err error) {
	localEnv := NewSymbolTableFrameBelow(argEnv)
	err = self.makeLocalBindings(args, argEnv, localEnv, true)
	if err != nil {
		return
	}
	for s := self.Body; NotNilP(s); s = Cdr(s) {
		result, err = Eval(Car(s), localEnv)
		if err != nil {
			return nil, errors.New(fmt.Sprintf("In '%s': %s", self.Name, err))
		}
	}
	return
}
<|endoftext|>


<commit_before>package main

/*
<form  action="/login">
   User<input type="text"     id="user" name="user">

   Pass<input type="password" id="pass" name="pass">

   Host<input type="text"     id="host" name="host" value="localhost">

   Port<input type="text"     id="port" name="port" value="3306">

   <button type="submit">Query</button>
</form>
*/

import (
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

type CContext struct {
	Number    string
	Name      string
	Label     string
	IsNumeric string
	IsString  string
	Value     string
	Readonly  string
}

type FContext struct {
	CSS      string
	Action   string
	Selector string
	Button   string
	Database string
	Table    string
	Key      string
	Value    string
	Back     string
	Columns  []CContext
	Trail    []Entry
}

func shipErrorPage(w http.ResponseWriter, cred Access, db string, t string, cols []CContext) {

	c := FContext{
		CSS:      CSS_FILE,
		Action:   "BACK",
		Selector: "",
		Button:   "Back",
		Database: db,
		Table:    t,
		Back:     makeBack(cred.Host, db, t, "", "", ""),
		Columns:  cols,
		Trail:    makeTrail(cred.Host, db, t, "", "", "", ""),
	}

	if DEBUGFLAG {
		initTemplate()
	}
	err := templateError.Execute(w, c)
	checkY(err)
}

func shipError(w http.ResponseWriter, cred Access, db string, t string, query string, e error) {
	cols := []CContext{CContext{"1", "", "Query", "", "", query, ""}, CContext{"2", "", "Result", "", "", fmt.Sprint(e), ""}}
	shipErrorPage(w, cred, db, t, cols)
}

func shipMessage(w http.ResponseWriter, cred Access, db string, msg string) {
	cols := []CContext{CContext{"1", "", "Message", "", "", msg, ""}}
	shipErrorPage(w, cred, db, "", cols)
}

func shipForm(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, action string, button string, selector string) {

	cols := getColumnInfo(cred, db, t)
	primary := getPrimary(cred, db, t)
	q := r.URL.Query()
	q.Del("action")
	linkback := q.Encode()
	newcols := []CContext{}

	for _, col := range cols {
		label := col.Name
		if label == primary {
			label = label + " (ID)"
		}
		newcols = append(newcols, CContext{col.Number, col.Name, label, col.IsNumeric, col.IsString, "", ""})
	}

	c := FContext{
		CSS:      CSS_FILE,
		Action:   action,
		Selector: selector,
		Button:   button,
		Database: db,
		Table:    t,
		Key:      "",
		Value:    "",
		Back:     linkback,
		Columns:  newcols,
		Trail:    makeTrail(cred.Host, db, t, "", "", "", ""),
	}

	if DEBUGFLAG {
		initTemplate()
	}
	err := templateFormFields.Execute(w, c)
	checkY(err)
}

func shipFormWithValues(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, k string, v string, action string, button string, selector string, query string) {

	cols := getColumnInfo(cred, db, t)
	fieldmap := getFieldMap(w, db, t, cred, query)
	primary := getPrimary(cred, db, t)
	newcols := []CContext{}

	for _, col := range cols {
		label := col.Name
		readonly := ""
		if label == primary {
			label = label + " (ID)"
			readonly = "true"
		}
		newcols = append(newcols, CContext{col.Number, col.Name, label, col.IsNumeric, col.IsString, fieldmap[col.Name], readonly})
	}

	q := r.URL.Query()
	q.Del("action")
	linkback := q.Encode()

	c := FContext{
		CSS:      CSS_FILE,
		Action:   action,
		Selector: selector,
		Button:   button,
		Database: db,
		Table:    t,
		Key:      k,
		Value:    v,
		Back:     linkback,
		Columns:  newcols,
		Trail:    makeTrail(cred.Host, db, t, "", "", "", ""),
	}

	if DEBUGFLAG {
		initTemplate()
	}
	err := templateFormFields.Execute(w, c)
	checkY(err)
}

func actionSubset(w http.ResponseWriter, r *http.Request, cred Access, database string, table string) {
	shipForm(w, r, cred, database, table, "QUERY", "Query", "true")
}

func actionDelete(w http.ResponseWriter, r *http.Request, cred Access, database string, table string) {
	shipForm(w, r, cred, database, table, "DELETEEXEC", "Delete", "true")
}

func actionAdd(w http.ResponseWriter, r *http.Request, cred Access, database string, table string) {
	shipForm(w, r, cred, database, table, "INSERT", "Insert", "")
}

func actionEdit(w http.ResponseWriter, r *http.Request, cred Access, database string, t string, k string, v string) {
	query := "select * from `" + t + "` where `" + k + "` = " + v
	shipFormWithValues(w, r, cred, database, t, k, v, "EDITEXEC", "Submit", "", query)
}

func collectClauses(r *http.Request, cred Access, db string, t string, set string) []string {

	var clauses []string
	cols := getCols(cred, db, t)
	for _, col := range cols {
		val := sqlProtectString(r.FormValue(col + "C"))
		if val != "" {
			comparator := sqlProtectString(r.FormValue(col + "O"))
			if comparator == "" {
				if set == "" {
					clauses = append(clauses, "`" + col + "`" + sqlProtectNumericComparison(val))
				} else {
					clauses = append(clauses, "`" + col + "` " + set + " \"" + val + "\"")
				}
			} else {
				clauses = append(clauses, "`" + col + "`" + comparator + "\"" + val + "\"")
			}
		}
	}
	return clauses
}

func collectSet(r *http.Request, cred Access, db string, t string) string {
	return strings.Join(collectClauses (r, cred, db, t, "="), " , ")
}

func collectWhere(r *http.Request, cred Access, db string, t string) string {
	return strings.Join(collectClauses (r, cred, db, t, "")," && ")
}

func actionQuery(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	where := collectWhere(r, cred, db, t)
	if len(where) > 0 {
		query := "SELECT * FROM `" + t + "` WHERE " + where
		dumpRows(w, db, t, "", "", cred, query, where)
	}
}

func actionDeleteExec(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	where := collectWhere(r, cred, db, t)
	if len(where) > 0 {
		stmt := "DELETE FROM `" + t + "` WHERE " + where
		log.Println("[SQL]", stmt)
		conn := getConnection(cred, db)
		defer conn.Close()

		statement, err := conn.Prepare(stmt)
		checkY(err)
		_, err = statement.Exec()
		checkY(err)
		http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
	}
}

func actionDeleteWhere(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, where string) {

	stmt := "DELETE FROM `" + t + "` WHERE " + where
	log.Println("[SQL]", stmt)
	conn := getConnection(cred, db)
	defer conn.Close()

	statement, err := conn.Prepare(stmt)
	checkY(err)
	_, err = statement.Exec()
	checkY(err)
	http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
}

func actionInsert(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	clauses := collectSet(r, cred, db, t)
	if len(clauses) > 0 {
		stmt := "INSERT INTO `" + t + "` SET " + clauses
		log.Println("[SQL]", stmt)
		conn := getConnection(cred, db)
		defer conn.Close()

		statement, err := conn.Prepare(stmt)
		checkY(err)
		_, err = statement.Exec()
		checkY(err)
		http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
	}
}

func actionEditExec(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, k string, v string) {

	clauses := collectSet(r, cred, db, t)
	if len(clauses) > 0 {
		stmt := "UPDATE `" + t + "` SET " + clauses + " where ? = ?"
		log.Println("[SQL]", stmt, k, v)
		conn := getConnection(cred, db)
		defer conn.Close()

		statement, err := conn.Prepare(stmt)
		checkY(err)
		_, err = statement.Exec(k,v)
		checkY(err)
		http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
	}
}

func actionRemove(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, k string, v string) {

	stmt := "DELETE FROM `" + t + "` WHERE ? = ?"
	log.Println("[SQL]", stmt, k , v)
	conn := getConnection(cred, db)
	defer conn.Close()

	statement, err := conn.Prepare(stmt)
	checkY(err)
	_, err = statement.Exec(k,v)
	checkY(err)
	http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t+"&k"+k, 302)
}

/*
 show columns from posts;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| title | varchar(64) | YES  |     | NULL    |       |
| start | date        | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
*/

func actionInfo(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	rows, err := getRows(cred, db, "show columns from `" + t + "`")
	checkY(err)
	defer rows.Close()

	q := url.Values{}
	q.Add("db", db)
	q.Add("t", t)

	menu := []Entry{}
	q.Set("action", "ADD")
	linkinsert := "/?" + q.Encode()
	menu = append(menu, Entry{linkinsert, "+"})

	records := [][]string{}
	head := []string{"#", "Field", "Type", "Null", "Key", "Default", "Extra"}

	var i int = 1
	for rows.Next() {
		var f, t, n, k, e string
		var d []byte // or use http://golang.org/pkg/database/sql/#NullString
		err := rows.Scan(&f, &t, &n, &k, &d, &e)
		checkY(err)
		records = append(records, []string{strconv.Itoa(i), f, t, n, k, string(d), e})
		i = i + 1
	}
	tableOutSimple(w, cred, db, t, head, records, menu)
}
<commit_msg>Security: collecting clauses for where and set at one place<commit_after>package main

/*
<form  action="/login">
   User<input type="text"     id="user" name="user">

   Pass<input type="password" id="pass" name="pass">

   Host<input type="text"     id="host" name="host" value="localhost">

   Port<input type="text"     id="port" name="port" value="3306">

   <button type="submit">Query</button>
</form>
*/

import (
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

type CContext struct {
	Number    string
	Name      string
	Label     string
	IsNumeric string
	IsString  string
	Value     string
	Readonly  string
}

type FContext struct {
	CSS      string
	Action   string
	Selector string
	Button   string
	Database string
	Table    string
	Key      string
	Value    string
	Back     string
	Columns  []CContext
	Trail    []Entry
}

func shipErrorPage(w http.ResponseWriter, cred Access, db string, t string, cols []CContext) {

	c := FContext{
		CSS:      CSS_FILE,
		Action:   "BACK",
		Selector: "",
		Button:   "Back",
		Database: db,
		Table:    t,
		Back:     makeBack(cred.Host, db, t, "", "", ""),
		Columns:  cols,
		Trail:    makeTrail(cred.Host, db, t, "", "", "", ""),
	}

	if DEBUGFLAG {
		initTemplate()
	}
	err := templateError.Execute(w, c)
	checkY(err)
}

func shipError(w http.ResponseWriter, cred Access, db string, t string, query string, e error) {
	cols := []CContext{CContext{"1", "", "Query", "", "", query, ""}, CContext{"2", "", "Result", "", "", fmt.Sprint(e), ""}}
	shipErrorPage(w, cred, db, t, cols)
}

func shipMessage(w http.ResponseWriter, cred Access, db string, msg string) {
	cols := []CContext{CContext{"1", "", "Message", "", "", msg, ""}}
	shipErrorPage(w, cred, db, "", cols)
}

func shipForm(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, action string, button string, selector string) {

	cols := getColumnInfo(cred, db, t)
	primary := getPrimary(cred, db, t)
	q := r.URL.Query()
	q.Del("action")
	linkback := q.Encode()
	newcols := []CContext{}

	for _, col := range cols {
		label := col.Name
		if label == primary {
			label = label + " (ID)"
		}
		newcols = append(newcols, CContext{col.Number, col.Name, label, col.IsNumeric, col.IsString, "", ""})
	}

	c := FContext{
		CSS:      CSS_FILE,
		Action:   action,
		Selector: selector,
		Button:   button,
		Database: db,
		Table:    t,
		Key:      "",
		Value:    "",
		Back:     linkback,
		Columns:  newcols,
		Trail:    makeTrail(cred.Host, db, t, "", "", "", ""),
	}

	if DEBUGFLAG {
		initTemplate()
	}
	err := templateFormFields.Execute(w, c)
	checkY(err)
}

func shipFormWithValues(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, k string, v string, action string, button string, selector string, query string) {

	cols := getColumnInfo(cred, db, t)
	fieldmap := getFieldMap(w, db, t, cred, query)
	primary := getPrimary(cred, db, t)
	newcols := []CContext{}

	for _, col := range cols {
		label := col.Name
		readonly := ""
		if label == primary {
			label = label + " (ID)"
			readonly = "true"
		}
		newcols = append(newcols, CContext{col.Number, col.Name, label, col.IsNumeric, col.IsString, fieldmap[col.Name], readonly})
	}

	q := r.URL.Query()
	q.Del("action")
	linkback := q.Encode()

	c := FContext{
		CSS:      CSS_FILE,
		Action:   action,
		Selector: selector,
		Button:   button,
		Database: db,
		Table:    t,
		Key:      k,
		Value:    v,
		Back:     linkback,
		Columns:  newcols,
		Trail:    makeTrail(cred.Host, db, t, "", "", "", ""),
	}

	if DEBUGFLAG {
		initTemplate()
	}
	err := templateFormFields.Execute(w, c)
	checkY(err)
}

func actionSubset(w http.ResponseWriter, r *http.Request, cred Access, database string, table string) {
	shipForm(w, r, cred, database, table, "QUERY", "Query", "true")
}

func actionDelete(w http.ResponseWriter, r *http.Request, cred Access, database string, table string) {
	shipForm(w, r, cred, database, table, "DELETEEXEC", "Delete", "true")
}

func actionAdd(w http.ResponseWriter, r *http.Request, cred Access, database string, table string) {
	shipForm(w, r, cred, database, table, "INSERT", "Insert", "")
}

func actionEdit(w http.ResponseWriter, r *http.Request, cred Access, database string, t string, k string, v string) {
	query := "select * from `" + t + "` where `" + k + "` = " + v
	shipFormWithValues(w, r, cred, database, t, k, v, "EDITEXEC", "Submit", "", query)
}

func collectClauses(r *http.Request, cred Access, db string, t string, set string) []string {

	var clauses []string
	cols := getCols(cred, db, t)
	for _, col := range cols {
		val := sqlProtectString(r.FormValue(col + "C"))
		if val != "" {
			comparator := sqlProtectString(r.FormValue(col + "O"))
			if comparator == "" {
				if set == "" {
					clauses = append(clauses, "`" + col + "`" + sqlProtectNumericComparison(val))
				} else {
					clauses = append(clauses, "`" + col + "` " + set + " \"" + val + "\"")
				}
			} else {
				clauses = append(clauses, "`" + col + "`" + comparator + "\"" + val + "\"")
			}
		}
	}
	return clauses
}

func collectSet(r *http.Request, cred Access, db string, t string) string {
	return strings.Join(collectClauses (r, cred, db, t, "="), " , ")
}

func collectWhere(r *http.Request, cred Access, db string, t string) string {
	return strings.Join(collectClauses (r, cred, db, t, "")," && ")
}

func actionQuery(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	where := collectWhere(r, cred, db, t)
	if len(where) > 0 {
		query := "SELECT * FROM `" + t + "` WHERE " + where
		dumpRows(w, db, t, "", "", cred, query, where)
	}
}

func actionDeleteExec(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	where := collectWhere(r, cred, db, t)
	if len(where) > 0 {
		stmt := "DELETE FROM `" + t + "` WHERE " + where
		log.Println("[SQL]", stmt)
		conn := getConnection(cred, db)
		defer conn.Close()

		statement, err := conn.Prepare(stmt)
		checkY(err)
		_, err = statement.Exec()
		checkY(err)
		http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
	}
}

func actionDeleteWhere(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, where string) {

	stmt := "DELETE FROM `" + t + "` WHERE " + where
	log.Println("[SQL]", stmt)
	conn := getConnection(cred, db)
	defer conn.Close()

	statement, err := conn.Prepare(stmt)
	checkY(err)
	_, err = statement.Exec()
	checkY(err)
	http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
}

func actionInsert(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	clauses := collectSet(r, cred, db, t)
	if len(clauses) > 0 {
		stmt := "INSERT INTO `" + t + "` SET " + clauses
		log.Println("[SQL]", stmt)
		conn := getConnection(cred, db)
		defer conn.Close()

		statement, err := conn.Prepare(stmt)
		checkY(err)
		_, err = statement.Exec()
		checkY(err)
		http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
	}
}

func actionEditExec(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, k string, v string) {

	clauses := collectSet(r, cred, db, t)
	if len(clauses) > 0 {
		stmt := "UPDATE `" + t + "` SET " + clauses + "` WHERE `" + k + "` = ?"
		log.Println("[SQL]", stmt, v)
		conn := getConnection(cred, db)
		defer conn.Close()

		statement, err := conn.Prepare(stmt)
		checkY(err)
		_, err = statement.Exec(v)
		checkY(err)
		http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t, 302)
	}
}

func actionRemove(w http.ResponseWriter, r *http.Request, cred Access, db string, t string, k string, v string) {

	stmt := "DELETE FROM `" + t + "` WHERE `" + k + "` = ?"
	log.Println("[SQL]", stmt, v)
	conn := getConnection(cred, db)
	defer conn.Close()

	statement, err := conn.Prepare(stmt)
	checkY(err)
	_, err = statement.Exec(v)
	checkY(err)
	http.Redirect(w, r, r.URL.Host+"?db="+db+"&t="+t+"&k"+k, 302)
}

/*
 show columns from posts;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| title | varchar(64) | YES  |     | NULL    |       |
| start | date        | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
*/

func actionInfo(w http.ResponseWriter, r *http.Request, cred Access, db string, t string) {

	rows, err := getRows(cred, db, "show columns from `" + t + "`")
	checkY(err)
	defer rows.Close()

	q := url.Values{}
	q.Add("db", db)
	q.Add("t", t)

	menu := []Entry{}
	q.Set("action", "ADD")
	linkinsert := "/?" + q.Encode()
	menu = append(menu, Entry{linkinsert, "+"})

	records := [][]string{}
	head := []string{"#", "Field", "Type", "Null", "Key", "Default", "Extra"}

	var i int = 1
	for rows.Next() {
		var f, t, n, k, e string
		var d []byte // or use http://golang.org/pkg/database/sql/#NullString
		err := rows.Scan(&f, &t, &n, &k, &d, &e)
		checkY(err)
		records = append(records, []string{strconv.Itoa(i), f, t, n, k, string(d), e})
		i = i + 1
	}
	tableOutSimple(w, cred, db, t, head, records, menu)
}
<|endoftext|>


<commit_before>package peco

import (
	"unicode"

	"github.com/nsf/termbox-go"
	"github.com/peco/peco/keyseq"
)

// Action describes an action that can be executed upon receiving user
// input. It's an interface so you can create any kind of Action you need,
// but most everything is implemented in terms of ActionFunc, which is
// callback based Action
type Action interface {
	Register(string, ...termbox.Key)
	Execute(*Input, termbox.Event)
}

// ActionFunc is a type of Action that is basically just a callback.
type ActionFunc func(*Input, termbox.Event)

// This is the global map of canonical action name to actions
var nameToActions map[string]Action

// This is the default keybinding used by NewKeymap()
var defaultKeyBinding map[termbox.Key]Action

// Execute fulfills the Action interface for AfterFunc
func (a ActionFunc) Execute(i *Input, e termbox.Event) {
	a(i, e)
}

// Register fulfills the Actin interface for AfterFunc. Registers `a`
// into the global action registry by the name `name`, and maps to
// default keys via `defaultKeys`
func (a ActionFunc) Register(name string, defaultKeys ...termbox.Key) {
	nameToActions["peco."+name] = a
	for _, k := range defaultKeys {
		Keyseq.Add(keyseq.KeyList{keyseq.NewKeyFromKey(k)}, a)
	}
}

func init() {
	// Build the global maps
	nameToActions = map[string]Action{}
	defaultKeyBinding = map[termbox.Key]Action{}

	ActionFunc(doBeginningOfLine).Register("BeginningOfLine", termbox.KeyCtrlA)
	ActionFunc(doBackwardChar).Register("BackwardChar", termbox.KeyCtrlB)
	ActionFunc(doBackwardWord).Register("BackwardWord")
	ActionFunc(doCancel).Register("Cancel", termbox.KeyCtrlC, termbox.KeyEsc)
	ActionFunc(doDeleteAll).Register("DeleteAll")
	ActionFunc(doDeleteBackwardChar).Register(
		"DeleteBackwardChar",
		termbox.KeyBackspace,
		termbox.KeyBackspace2,
	)
	ActionFunc(doDeleteBackwardWord).Register(
		"DeleteBackwardWord",
		termbox.KeyCtrlW,
	)
	ActionFunc(doDeleteForwardChar).Register("DeleteForwardChar", termbox.KeyCtrlD)
	ActionFunc(doDeleteForwardWord).Register("DeleteForwardWord")
	ActionFunc(doEndOfFile).Register("EndOfFile")
	ActionFunc(doEndOfLine).Register("EndOfLine", termbox.KeyCtrlE)
	ActionFunc(doFinish).Register("Finish", termbox.KeyEnter)
	ActionFunc(doForwardChar).Register("ForwardChar", termbox.KeyCtrlF)
	ActionFunc(doForwardWord).Register("ForwardWord")
	ActionFunc(doKillEndOfLine).Register("KillEndOfLine", termbox.KeyCtrlK)
	ActionFunc(doKillBeginningOfLine).Register("KillBeginningOfLine", termbox.KeyCtrlU)
	ActionFunc(doRotateMatcher).Register("RotateMatcher", termbox.KeyCtrlR)
	ActionFunc(doSelectNext).Register(
		"SelectNext",
		termbox.KeyArrowDown,
		termbox.KeyCtrlN,
	)
	ActionFunc(doSelectNextPage).Register(
		"SelectNextPage",
		termbox.KeyArrowRight,
	)
	ActionFunc(doSelectPrevious).Register(
		"SelectPrevious",
		termbox.KeyArrowUp,
		termbox.KeyCtrlP,
	)
	ActionFunc(doSelectPreviousPage).Register(
		"SelectPreviousPage",
		termbox.KeyArrowLeft,
	)

	ActionFunc(doToggleSelection).Register("ToggleSelection")
	ActionFunc(doToggleSelectionAndSelectNext).Register(
		"ToggleSelectionAndSelectNext",
		termbox.KeyCtrlSpace,
	)
	ActionFunc(doSelectNone).Register(
		"SelectNone",
		termbox.KeyCtrlG,
	)
	ActionFunc(doSelectAll).Register("SelectAll")
	ActionFunc(doSelectVisible).Register("SelectVisible")
	ActionFunc(doToggleSelectMode).Register("ToggleSelectMode")
	ActionFunc(doCancelSelectMode).Register("CancelSelectMode")

	Keyseq.Compile()
}

func doRotateMatcher(i *Input, ev termbox.Event) {
	i.Ctx.CurrentMatcher++
	if i.Ctx.CurrentMatcher >= len(i.Ctx.Matchers) {
		i.Ctx.CurrentMatcher = 0
	}
	if i.ExecQuery() {
		return
	}
	i.DrawMatches(nil)
}

func doToggleSelection(i *Input, _ termbox.Event) {
	if i.selection.Has(i.currentLine) {
		i.selection.Remove(i.currentLine)
		return
	}
	i.selection.Add(i.currentLine)
}

func doToggleSelectMode(i *Input, _ termbox.Event) {
	if i.IsSelectMode() {
		for _, line := range i.RangeSelection() {
			i.selection.Add(line)
		}
		i.selection.Add(i.currentLine)

		i.selectionRangeStart = NoSelectionRange
	} else {
		i.selectionRangeStart = i.currentLine
	}
	i.DrawMatches(nil)
}

func doCancelSelectMode(i *Input, _ termbox.Event) {
	i.selectionRangeStart = NoSelectionRange
	i.DrawMatches(nil)
}

func doSelectNone(i *Input, _ termbox.Event) {
	i.selection.Clear()
	i.DrawMatches(nil)
}

func doSelectAll(i *Input, _ termbox.Event) {
	for lineno := 1; lineno <= len(i.current); lineno++ {
		i.selection.Add(lineno)
	}
	i.DrawMatches(nil)
}

func doSelectVisible(i *Input, _ termbox.Event) {
	pageStart := i.currentPage.offset
	pageEnd := pageStart + i.currentPage.perPage
	for lineno := pageStart; lineno <= pageEnd; lineno++ {
		i.selection.Add(lineno)
	}
	i.DrawMatches(nil)
}

func doFinish(i *Input, _ termbox.Event) {
	// Must end with all the selected lines.
	i.selection.Add(i.currentLine)

	i.result = []Match{}
	for _, lineno := range append(i.selection, i.RangeSelection()...) {
		if lineno <= len(i.current) {
			i.result = append(i.result, i.current[lineno-1])
		}
	}
	i.ExitWith(0)
}

func doCancel(i *Input, ev termbox.Event) {
	if i.IsSelectMode() {
		doCancelSelectMode(i, ev)
		return
	}

	// peco.Cancel -> end program, exit with failure
	i.ExitWith(1)
}

func doSelectPrevious(i *Input, ev termbox.Event) {
	i.PagingCh() <- ToPrevLine
	i.DrawMatches(nil)
}

func doSelectNext(i *Input, ev termbox.Event) {
	i.PagingCh() <- ToNextLine
	i.DrawMatches(nil)
}

func doSelectPreviousPage(i *Input, ev termbox.Event) {
	i.PagingCh() <- ToPrevPage
	i.DrawMatches(nil)
}

func doSelectNextPage(i *Input, ev termbox.Event) {
	i.PagingCh() <- ToNextPage
	i.DrawMatches(nil)
}

func doToggleSelectionAndSelectNext(i *Input, ev termbox.Event) {
	doToggleSelection(i, ev)
	doSelectNext(i, ev)
}

func doDeleteBackwardWord(i *Input, _ termbox.Event) {
	if i.caretPos == 0 {
		return
	}

	for pos := i.caretPos - 1; pos >= 0; pos-- {
		if pos == 0 {
			i.query = i.query[i.caretPos:]
			break
		}

		if unicode.IsSpace(i.query[pos]) {
			buf := make([]rune, len(i.query)-(i.caretPos-pos))
			copy(buf, i.query[:pos])
			copy(buf[pos:], i.query[i.caretPos:])
			i.query = buf
			i.caretPos = pos
			break
		}
	}

	if i.ExecQuery() {
		return
	}

	i.current = nil
	i.DrawMatches(nil)
}

func doForwardWord(i *Input, _ termbox.Event) {
	if i.caretPos >= len(i.query) {
		return
	}

	foundSpace := false
	for pos := i.caretPos; pos < len(i.query); pos++ {
		r := i.query[pos]
		if foundSpace {
			if !unicode.IsSpace(r) {
				i.caretPos = pos
				i.DrawMatches(nil)
				return
			}
		} else {
			if unicode.IsSpace(r) {
				foundSpace = true
			}
		}
	}

	// not found. just move to the end of the buffer
	i.caretPos = len(i.query)
	i.DrawMatches(nil)

}

func doBackwardWord(i *Input, _ termbox.Event) {
	if i.caretPos == 0 {
		return
	}

	if i.caretPos >= len(i.query) {
		i.caretPos--
	}

	// if we start from a whitespace-ish position, we should
	// rewind to the end of the previous word, and then do the
	// search all over again
SEARCH_PREV_WORD:
	if unicode.IsSpace(i.query[i.caretPos]) {
		for pos := i.caretPos; pos > 0; pos-- {
			if !unicode.IsSpace(i.query[pos]) {
				i.caretPos = pos
				break
			}
		}
	}

	// if we start from the first character of a word, we
	// should attempt to move back and search for the previous word
	if i.caretPos > 0 && unicode.IsSpace(i.query[i.caretPos-1]) {
		i.caretPos--
		goto SEARCH_PREV_WORD
	}

	// Now look for a space
	for pos := i.caretPos; pos > 0; pos-- {
		if unicode.IsSpace(i.query[pos]) {
			i.caretPos = pos + 1
			i.DrawMatches(nil)
			return
		}
	}

	// not found. just move to the beginning of the buffer
	i.caretPos = 0
	i.DrawMatches(nil)
}

func doForwardChar(i *Input, _ termbox.Event) {
	if i.caretPos >= len(i.query) {
		return
	}
	i.caretPos++
	i.DrawMatches(nil)
}

func doBackwardChar(i *Input, _ termbox.Event) {
	if i.caretPos <= 0 {
		return
	}
	i.caretPos--
	i.DrawMatches(nil)
}

func doDeleteForwardWord(i *Input, _ termbox.Event) {
	if len(i.query) <= i.caretPos {
		return
	}

	for pos := i.caretPos; pos < len(i.query); pos++ {
		if pos == len(i.query)-1 {
			i.query = i.query[:i.caretPos]
			break
		}

		if unicode.IsSpace(i.query[pos]) {
			buf := make([]rune, len(i.query)-(pos-i.caretPos))
			copy(buf, i.query[:i.caretPos])
			copy(buf[i.caretPos:], i.query[pos:])
			i.query = buf
			break
		}
	}

	if i.ExecQuery() {
		return
	}

	i.current = nil
	i.DrawMatches(nil)
}

func doBeginningOfLine(i *Input, _ termbox.Event) {
	i.caretPos = 0
	i.DrawMatches(nil)
}

func doEndOfLine(i *Input, _ termbox.Event) {
	i.caretPos = len(i.query)
	i.DrawMatches(nil)
}

func doEndOfFile(i *Input, ev termbox.Event) {
	if len(i.query) > 0 {
		doDeleteForwardChar(i, ev)
	} else {
		doCancel(i, ev)
	}
}

func doKillBeginningOfLine(i *Input, _ termbox.Event) {
	i.query = i.query[i.caretPos:]
	i.caretPos = 0
	if i.ExecQuery() {
		return
	}
	i.current = nil
	i.DrawMatches(nil)
}

func doKillEndOfLine(i *Input, _ termbox.Event) {
	if len(i.query) <= i.caretPos {
		return
	}

	i.query = i.query[0:i.caretPos]
	if i.ExecQuery() {
		return
	}
	i.current = nil
	i.DrawMatches(nil)
}

func doDeleteAll(i *Input, _ termbox.Event) {
	i.query = make([]rune, 0)
	i.current = nil
	i.DrawMatches(nil)
}

func doDeleteForwardChar(i *Input, _ termbox.Event) {
	if len(i.query) <= i.caretPos {
		return
	}

	buf := make([]rune, len(i.query)-1)
	copy(buf, i.query[:i.caretPos])
	copy(buf[i.caretPos:], i.query[i.caretPos+1:])
	i.query = buf

	if i.ExecQuery() {
		return
	}

	i.current = nil
	i.DrawMatches(nil)
}

func doDeleteBackwardChar(i *Input, ev termbox.Event) {
	if len(i.query) <= 0 {
		return
	}

	switch i.caretPos {
	case 0:
		// No op
		return
	case len(i.query):
		i.query = i.query[:len(i.query)-1]
	default:
		buf := make([]rune, len(i.query)-1)
		copy(buf, i.query[:i.caretPos])
		copy(buf[i.caretPos-1:], i.query[i.caretPos:])
		i.query = buf
	}
	i.caretPos--

	if i.ExecQuery() {
		return
	}

	i.current = nil
	i.DrawMatches(nil)
}
<commit_msg>Still various stuff broken, but konami command kinda works<commit_after>package peco

import (
	"unicode"

	"github.com/nsf/termbox-go"
	"github.com/peco/peco/keyseq"
)

// Action describes an action that can be executed upon receiving user
// input. It's an interface so you can create any kind of Action you need,
// but most everything is implemented in terms of ActionFunc, which is
// callback based Action
type Action interface {
	Register(string, ...termbox.Key)
	RegisterKeySequence(keyseq.KeyList)
	Execute(*Input, termbox.Event)
}

// ActionFunc is a type of Action that is basically just a callback.
type ActionFunc func(*Input, termbox.Event)

// This is the global map of canonical action name to actions
var nameToActions map[string]Action

// This is the default keybinding used by NewKeymap()
var defaultKeyBinding map[termbox.Key]Action

// Execute fulfills the Action interface for AfterFunc
func (a ActionFunc) Execute(i *Input, e termbox.Event) {
	a(i, e)
}

// Register fulfills the Actin interface for AfterFunc. Registers `a`
// into the global action registry by the name `name`, and maps to
// default keys via `defaultKeys`
func (a ActionFunc) Register(name string, defaultKeys ...termbox.Key) {
	nameToActions["peco."+name] = a
	for _, k := range defaultKeys {
		Keyseq.Add(keyseq.KeyList{keyseq.NewKeyFromKey(k)}, a)
	}
}

func (a ActionFunc) RegisterKeySequence(k keyseq.KeyList) {
	Keyseq.Add(k, a)
}

func init() {
	// Build the global maps
	nameToActions = map[string]Action{}
	defaultKeyBinding = map[termbox.Key]Action{}

	ActionFunc(doBeginningOfLine).Register("BeginningOfLine", termbox.KeyCtrlA)
	ActionFunc(doBackwardChar).Register("BackwardChar", termbox.KeyCtrlB)
	ActionFunc(doBackwardWord).Register("BackwardWord")
	ActionFunc(doCancel).Register("Cancel", termbox.KeyCtrlC, termbox.KeyEsc)
	ActionFunc(doDeleteAll).Register("DeleteAll")
	ActionFunc(doDeleteBackwardChar).Register(
		"DeleteBackwardChar",
		termbox.KeyBackspace,
		termbox.KeyBackspace2,
	)
	ActionFunc(doDeleteBackwardWord).Register(
		"DeleteBackwardWord",
		termbox.KeyCtrlW,
	)
	ActionFunc(doDeleteForwardChar).Register("DeleteForwardChar", termbox.KeyCtrlD)
	ActionFunc(doDeleteForwardWord).Register("DeleteForwardWord")
	ActionFunc(doEndOfFile).Register("EndOfFile")
	ActionFunc(doEndOfLine).Register("EndOfLine", termbox.KeyCtrlE)
	ActionFunc(doFinish).Register("Finish", termbox.KeyEnter)
	ActionFunc(doForwardChar).Register("ForwardChar", termbox.KeyCtrlF)
	ActionFunc(doForwardWord).Register("ForwardWord")
	ActionFunc(doKillEndOfLine).Register("KillEndOfLine", termbox.KeyCtrlK)
	ActionFunc(doKillBeginningOfLine).Register("KillBeginningOfLine", termbox.KeyCtrlU)
	ActionFunc(doRotateMatcher).Register("RotateMatcher", termbox.KeyCtrlR)
	ActionFunc(doSelectNext).Register(
		"SelectNext",
		termbox.KeyArrowDown,
		termbox.KeyCtrlN,
	)
	ActionFunc(doSelectNextPage).Register(
		"SelectNextPage",
		termbox.KeyArrowRight,
	)
	ActionFunc(doSelectPrevious).Register(
		"SelectPrevious",
		termbox.KeyArrowUp,
		termbox.KeyCtrlP,
	)
	ActionFunc(doSelectPreviousPage).Register(
		"SelectPreviousPage",
		termbox.KeyArrowLeft,
	)

	ActionFunc(doToggleSelection).Register("ToggleSelection")
	ActionFunc(doToggleSelectionAndSelectNext).Register(
		"ToggleSelectionAndSelectNext",
		termbox.KeyCtrlSpace,
	)
	ActionFunc(doSelectNone).Register(
		"SelectNone",
		termbox.KeyCtrlG,
	)
	ActionFunc(doSelectAll).Register("SelectAll")
	ActionFunc(doSelectVisible).Register("SelectVisible")
	ActionFunc(doToggleSelectMode).Register("ToggleSelectMode")
	ActionFunc(doCancelSelectMode).Register("CancelSelectMode")

	ActionFunc(doKonamiCommand).RegisterKeySequence(
		keyseq.KeyList{
			keyseq.Key{0,termbox.KeyArrowUp,0},
			keyseq.Key{0,termbox.KeyArrowUp,0},
			keyseq.Key{0,termbox.KeyArrowDown,0},
			keyseq.Key{0,termbox.KeyArrowDown,0},
			keyseq.Key{0,termbox.KeyArrowLeft,0},
			keyseq.Key{0,termbox.KeyArrowRight,0},
			keyseq.Key{0,termbox.KeyArrowLeft,0},
			keyseq.Key{0,termbox.KeyArrowRight,0},
			keyseq.Key{0,0,'b'},
			keyseq.Key{0,0,'a'},
		},
	)

	Keyseq.Compile()
}

func doRotateMatcher(i *Input, ev termbox.Event) {
	i.Ctx.CurrentMatcher++
	if i.Ctx.CurrentMatcher >= len(i.Ctx.Matchers) {
		i.Ctx.CurrentMatcher = 0
	}
	if i.ExecQuery() {
		return
	}
	i.DrawMatches(nil)
}

func doToggleSelection(i *Input, _ termbox.Event) {
	if i.selection.Has(i.currentLine) {
		i.selection.Remove(i.currentLine)
		return
	}
	i.selection.Add(i.currentLine)
}

func doToggleSelectMode(i *Input, _ termbox.Event) {
	if i.IsSelectMode() {
		for _, line := range i.RangeSelection() {
			i.selection.Add(line)
		}
		i.selection.Add(i.currentLine)

		i.selectionRangeStart = NoSelectionRange
	} else {
		i.selectionRangeStart = i.currentLine
	}
	i.DrawMatches(nil)
}

func doCancelSelectMode(i *Input, _ termbox.Event) {
	i.selectionRangeStart = NoSelectionRange
	i.DrawMatches(nil)
}

func doSelectNone(i *Input, _ termbox.Event) {
	i.selection.Clear()
	i.DrawMatches(nil)
}

func doSelectAll(i *Input, _ termbox.Event) {
	for lineno := 1; lineno <= len(i.current); lineno++ {
		i.selection.Add(lineno)
	}
	i.DrawMatches(nil)
}

func doSelectVisible(i *Input, _ termbox.Event) {
	pageStart := i.currentPage.offset
	pageEnd := pageStart + i.currentPage.perPage
	for lineno := pageStart; lineno <= pageEnd; lineno++ {
		i.selection.Add(lineno)
	}
	i.DrawMatches(nil)
}

func doFinish(i *Input, _ termbox.Event) {
	// Must end with all the selected lines.
	i.selection.Add(i.currentLine)

	i.result = []Match{}
	for _, lineno := range append(i.selection, i.RangeSelection()...) {
		if lineno <= len(i.current) {
			i.result = append(i.result, i.current[lineno-1])
		}
	}
	i.ExitWith(0)
}

func doCancel(i *Input, ev termbox.Event) {
	if i.IsSelectMode() {
		doCancelSelectMode(i, ev)
		return
	}

	// peco.Cancel -> end program, exit with failure
	i.ExitWith(1)
}

func doSelectPrevious(i *Input, ev termbox.Event) {
	i.PagingCh() <- ToPrevLine
	i.DrawMatches(nil)
}

func doSelectNext(i *Input, ev termbox.Event) {
	i.PagingCh() <- ToNextLine
	i.DrawMatches(nil)
}

func doSelectPreviousPage(i *Input, ev termbox.Event) {
	i.PagingCh() <- ToPrevPage
	i.DrawMatches(nil)
}

func doSelectNextPage(i *Input, ev termbox.Event) {
	i.PagingCh() <- ToNextPage
	i.DrawMatches(nil)
}

func doToggleSelectionAndSelectNext(i *Input, ev termbox.Event) {
	doToggleSelection(i, ev)
	doSelectNext(i, ev)
}

func doDeleteBackwardWord(i *Input, _ termbox.Event) {
	if i.caretPos == 0 {
		return
	}

	for pos := i.caretPos - 1; pos >= 0; pos-- {
		if pos == 0 {
			i.query = i.query[i.caretPos:]
			break
		}

		if unicode.IsSpace(i.query[pos]) {
			buf := make([]rune, len(i.query)-(i.caretPos-pos))
			copy(buf, i.query[:pos])
			copy(buf[pos:], i.query[i.caretPos:])
			i.query = buf
			i.caretPos = pos
			break
		}
	}

	if i.ExecQuery() {
		return
	}

	i.current = nil
	i.DrawMatches(nil)
}

func doForwardWord(i *Input, _ termbox.Event) {
	if i.caretPos >= len(i.query) {
		return
	}

	foundSpace := false
	for pos := i.caretPos; pos < len(i.query); pos++ {
		r := i.query[pos]
		if foundSpace {
			if !unicode.IsSpace(r) {
				i.caretPos = pos
				i.DrawMatches(nil)
				return
			}
		} else {
			if unicode.IsSpace(r) {
				foundSpace = true
			}
		}
	}

	// not found. just move to the end of the buffer
	i.caretPos = len(i.query)
	i.DrawMatches(nil)

}

func doBackwardWord(i *Input, _ termbox.Event) {
	if i.caretPos == 0 {
		return
	}

	if i.caretPos >= len(i.query) {
		i.caretPos--
	}

	// if we start from a whitespace-ish position, we should
	// rewind to the end of the previous word, and then do the
	// search all over again
SEARCH_PREV_WORD:
	if unicode.IsSpace(i.query[i.caretPos]) {
		for pos := i.caretPos; pos > 0; pos-- {
			if !unicode.IsSpace(i.query[pos]) {
				i.caretPos = pos
				break
			}
		}
	}

	// if we start from the first character of a word, we
	// should attempt to move back and search for the previous word
	if i.caretPos > 0 && unicode.IsSpace(i.query[i.caretPos-1]) {
		i.caretPos--
		goto SEARCH_PREV_WORD
	}

	// Now look for a space
	for pos := i.caretPos; pos > 0; pos-- {
		if unicode.IsSpace(i.query[pos]) {
			i.caretPos = pos + 1
			i.DrawMatches(nil)
			return
		}
	}

	// not found. just move to the beginning of the buffer
	i.caretPos = 0
	i.DrawMatches(nil)
}

func doForwardChar(i *Input, _ termbox.Event) {
	if i.caretPos >= len(i.query) {
		return
	}
	i.caretPos++
	i.DrawMatches(nil)
}

func doBackwardChar(i *Input, _ termbox.Event) {
	if i.caretPos <= 0 {
		return
	}
	i.caretPos--
	i.DrawMatches(nil)
}

func doDeleteForwardWord(i *Input, _ termbox.Event) {
	if len(i.query) <= i.caretPos {
		return
	}

	for pos := i.caretPos; pos < len(i.query); pos++ {
		if pos == len(i.query)-1 {
			i.query = i.query[:i.caretPos]
			break
		}

		if unicode.IsSpace(i.query[pos]) {
			buf := make([]rune, len(i.query)-(pos-i.caretPos))
			copy(buf, i.query[:i.caretPos])
			copy(buf[i.caretPos:], i.query[pos:])
			i.query = buf
			break
		}
	}

	if i.ExecQuery() {
		return
	}

	i.current = nil
	i.DrawMatches(nil)
}

func doBeginningOfLine(i *Input, _ termbox.Event) {
	i.caretPos = 0
	i.DrawMatches(nil)
}

func doEndOfLine(i *Input, _ termbox.Event) {
	i.caretPos = len(i.query)
	i.DrawMatches(nil)
}

func doEndOfFile(i *Input, ev termbox.Event) {
	if len(i.query) > 0 {
		doDeleteForwardChar(i, ev)
	} else {
		doCancel(i, ev)
	}
}

func doKillBeginningOfLine(i *Input, _ termbox.Event) {
	i.query = i.query[i.caretPos:]
	i.caretPos = 0
	if i.ExecQuery() {
		return
	}
	i.current = nil
	i.DrawMatches(nil)
}

func doKillEndOfLine(i *Input, _ termbox.Event) {
	if len(i.query) <= i.caretPos {
		return
	}

	i.query = i.query[0:i.caretPos]
	if i.ExecQuery() {
		return
	}
	i.current = nil
	i.DrawMatches(nil)
}

func doDeleteAll(i *Input, _ termbox.Event) {
	i.query = make([]rune, 0)
	i.current = nil
	i.DrawMatches(nil)
}

func doDeleteForwardChar(i *Input, _ termbox.Event) {
	if len(i.query) <= i.caretPos {
		return
	}

	buf := make([]rune, len(i.query)-1)
	copy(buf, i.query[:i.caretPos])
	copy(buf[i.caretPos:], i.query[i.caretPos+1:])
	i.query = buf

	if i.ExecQuery() {
		return
	}

	i.current = nil
	i.DrawMatches(nil)
}

func doDeleteBackwardChar(i *Input, ev termbox.Event) {
	if len(i.query) <= 0 {
		return
	}

	switch i.caretPos {
	case 0:
		// No op
		return
	case len(i.query):
		i.query = i.query[:len(i.query)-1]
	default:
		buf := make([]rune, len(i.query)-1)
		copy(buf, i.query[:i.caretPos])
		copy(buf[i.caretPos-1:], i.query[i.caretPos:])
		i.query = buf
	}
	i.caretPos--

	if i.ExecQuery() {
		return
	}

	i.current = nil
	i.DrawMatches(nil)
}

func doKonamiCommand(i *Input, ev termbox.Event) {
	i.StatusMsgCh() <- "All your filters are blongs to us"
}
<|endoftext|>


<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 gcs

import (
	"net/http"
	"time"

	"github.com/jacobsa/reqtrace"

	storagev1 "google.golang.org/api/storage/v1"
)

// OAuth scopes for GCS. For use with e.g. google.DefaultTokenSource.
const (
	Scope_FullControl = storagev1.DevstorageFullControlScope
	Scope_ReadOnly    = storagev1.DevstorageReadOnlyScope
	Scope_ReadWrite   = storagev1.DevstorageReadWriteScope
)

// Conn represents a connection to GCS, pre-bound with a project ID and
// information required for authorization.
type Conn interface {
	// Return a Bucket object representing the GCS bucket with the given name. No
	// immediate validation is performed.
	GetBucket(name string) Bucket
}

// Configuration accepted by NewConn.
type ConnConfig struct {
	// An HTTP client, assumed to handle authorization and authentication. See
	// github.com/jacobsa/gcloud/oauthutil for a convenient way to create one of
	// these.
	HTTPClient *http.Client

	// The value to set in User-Agent headers for outgoing HTTP requests. If
	// empty, a default will be used.
	UserAgent string

	// The maximum amount of time to spend sleeping in a retry loop with
	// exponential backoff for failed requests. The default of zero disables
	// automatic retries.
	//
	// If you enable automatic retries, beware of the following:
	//
	//  *  Bucket.CreateObject will buffer the entire object contents in memory,
	//     so your object contents must not be too large to fit.
	//
	//  *  Bucket.NewReader needs to perform an additional round trip to GCS in
	//     order to find the latest object generation if you don't specify a
	//     particular generation.
	//
	//  *  Make sure your operations are idempotent, or that your application can
	//     tolerate it if not.
	//
	MaxBackoffSleep time.Duration
}

// Open a connection to GCS.
func NewConn(cfg *ConnConfig) (c Conn, err error) {
	// Fix the user agent if there is none.
	userAgent := cfg.UserAgent
	if userAgent == "" {
		const defaultUserAgent = "github.com-jacobsa-gloud-gcs"
		userAgent = defaultUserAgent
	}

	c = &conn{
		client:          cfg.HTTPClient,
		userAgent:       userAgent,
		maxBackoffSleep: cfg.MaxBackoffSleep,
	}

	return
}

type conn struct {
	client          *http.Client
	userAgent       string
	maxBackoffSleep time.Duration
}

func (c *conn) GetBucket(name string) (b Bucket) {
	b = newBucket(c.client, c.userAgent, name)

	// Enable retry loops if requested.
	if c.maxBackoffSleep > 0 {
		// TODO(jacobsa): Show the retries as distinct spans in the trace.
		b = newRetryBucket(c.maxBackoffSleep, b)
	}

	// Enable tracing if appropriate.
	if reqtrace.Enabled() {
		b = &reqtraceBucket{
			Wrapped: b,
		}
	}

	// Print debug output when enabled.
	b = newDebugBucket(b)

	return
}
<commit_msg>Accept a token source instead of an HTTP client.<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 gcs

import (
	"net/http"
	"time"

	"golang.org/x/oauth2"

	"github.com/jacobsa/reqtrace"

	storagev1 "google.golang.org/api/storage/v1"
)

// OAuth scopes for GCS. For use with e.g. google.DefaultTokenSource.
const (
	Scope_FullControl = storagev1.DevstorageFullControlScope
	Scope_ReadOnly    = storagev1.DevstorageReadOnlyScope
	Scope_ReadWrite   = storagev1.DevstorageReadWriteScope
)

// Conn represents a connection to GCS, pre-bound with a project ID and
// information required for authorization.
type Conn interface {
	// Return a Bucket object representing the GCS bucket with the given name. No
	// immediate validation is performed.
	GetBucket(name string) Bucket
}

// Configuration accepted by NewConn.
type ConnConfig struct {
	// An oauth2 token source to use for authenticating to GCS.
	//
	// You probably want this one:
	//     http://godoc.org/golang.org/x/oauth2/google#DefaultTokenSource
	TokenSource oauth2.TokenSource

	// The value to set in User-Agent headers for outgoing HTTP requests. If
	// empty, a default will be used.
	UserAgent string

	// The maximum amount of time to spend sleeping in a retry loop with
	// exponential backoff for failed requests. The default of zero disables
	// automatic retries.
	//
	// If you enable automatic retries, beware of the following:
	//
	//  *  Bucket.CreateObject will buffer the entire object contents in memory,
	//     so your object contents must not be too large to fit.
	//
	//  *  Bucket.NewReader needs to perform an additional round trip to GCS in
	//     order to find the latest object generation if you don't specify a
	//     particular generation.
	//
	//  *  Make sure your operations are idempotent, or that your application can
	//     tolerate it if not.
	//
	MaxBackoffSleep time.Duration
}

// Open a connection to GCS.
func NewConn(cfg *ConnConfig) (c Conn, err error) {
	// Fix the user agent if there is none.
	userAgent := cfg.UserAgent
	if userAgent == "" {
		const defaultUserAgent = "github.com-jacobsa-gloud-gcs"
		userAgent = defaultUserAgent
	}

	c = &conn{
		client:          cfg.HTTPClient,
		userAgent:       userAgent,
		maxBackoffSleep: cfg.MaxBackoffSleep,
	}

	return
}

type conn struct {
	client          *http.Client
	userAgent       string
	maxBackoffSleep time.Duration
}

func (c *conn) GetBucket(name string) (b Bucket) {
	b = newBucket(c.client, c.userAgent, name)

	// Enable retry loops if requested.
	if c.maxBackoffSleep > 0 {
		// TODO(jacobsa): Show the retries as distinct spans in the trace.
		b = newRetryBucket(c.maxBackoffSleep, b)
	}

	// Enable tracing if appropriate.
	if reqtrace.Enabled() {
		b = &reqtraceBucket{
			Wrapped: b,
		}
	}

	// Print debug output when enabled.
	b = newDebugBucket(b)

	return
}
<|endoftext|>


<commit_before>package dokugen

import (
	"math/rand"
)

//Fill will find a random filling of the puzzle that is valid. If it cannot find one,
// it will return False and leave the grid as it found it. Generally you would only want to call this on
//grids that have more than one solution (e.g. a fully blank grid)
func (self *Grid) Fill() bool {

	solutions := self.nOrFewerSolutions(1)

	if len(solutions) != 0 {
		self.Load(solutions[0].DataString())
		return true
	}

	return false
}

func GenerateGrid() *Grid {
	grid := NewGrid()
	//Do a random fill of the grid
	grid.Fill()

	keepGoing := 50

	//TODO: have a more robust exit criteria.
	for keepGoing > 0 {
		cell := grid.Cell(rand.Intn(DIM), rand.Intn(DIM))
		num := cell.Number()
		if num == 0 {
			continue
		}
		//Unfill it.
		cell.SetNumber(0)
		if grid.HasMultipleSolutions() {
			//Put it back in.
			cell.SetNumber(num)
			keepGoing--
		}
	}

	return grid
}
<commit_msg>Better exit criteria for GenerateGrid so we minimize unnecessary work (sort of). Grids have ~25 filled cells.<commit_after>package dokugen

import (
	"math/rand"
)

//Fill will find a random filling of the puzzle that is valid. If it cannot find one,
// it will return False and leave the grid as it found it. Generally you would only want to call this on
//grids that have more than one solution (e.g. a fully blank grid)
func (self *Grid) Fill() bool {

	solutions := self.nOrFewerSolutions(1)

	if len(solutions) != 0 {
		self.Load(solutions[0].DataString())
		return true
	}

	return false
}

func GenerateGrid() *Grid {
	grid := NewGrid()
	//Do a random fill of the grid
	grid.Fill()

	keepGoing := true

	for keepGoing {
		//Unless we make a successful change this loop, don't bother continuing.
		keepGoing = false

		cells := make([]*Cell, len(grid.cells[:]))

		for i, j := range rand.Perm(len(grid.cells[:])) {
			cells[i] = &grid.cells[j]
		}

		for _, cell := range cells {
			num := cell.Number()
			if num == 0 {
				continue
			}
			//Unfill it.
			cell.SetNumber(0)
			if grid.HasMultipleSolutions() {
				//Put it back in.
				cell.SetNumber(num)
			} else {
				//we had a success! keep going around again.
				keepGoing = true
			}
		}
	}

	return grid
}
<|endoftext|>


<commit_before>package components

import (
	"strings"

	"github.com/scipipe/scipipe"
)

// IPToParamConverter takes a file target on its FilePath in-port, reads its
// content (assuming a single value), removing any newlines, spaces or tabs,
// and sends the value on the OutParam parameter port.
type IPToParamConverter struct {
	scipipe.EmptyWorkflowProcess
	name     string
	InFile   *scipipe.InPort
	OutParam *scipipe.ParamOutPort
}

// NewIPToParamConverter instantiates a new IPToParamConverter
func NewIPToParamConverter(wf *scipipe.Workflow, name string) *IPToParamConverter {
	p := &IPToParamConverter{
		name:     name,
		InFile:   scipipe.NewInPort("in_file"),
		OutParam: scipipe.NewParamOutPort("out_param"),
	}
	wf.AddProc(p)
	return p
}

// Name returns the name of the IPToParamConverter process
func (p *IPToParamConverter) Name() string {
	return p.name
}

// OutParamPorts returns the out-param-ports of the IPToParamConverter process
func (p *IPToParamConverter) OutParamPorts() map[string]*scipipe.ParamOutPort {
	return map[string]*scipipe.ParamOutPort{"out_param": p.OutParam}
}

// IsConnected tells whether all the ports of the IPToParamConverter process are
// connected
func (p *IPToParamConverter) IsConnected() bool {
	return p.InFile.IsConnected() && p.OutParam.IsConnected()
}

// Run the IPToParamConverter
func (p *IPToParamConverter) Run() {
	defer p.OutParam.Close()

	for ip := range p.InFile.Chan {
		s := string(ip.Read())
		s = strings.Trim(s, " \r\n\t")
		p.OutParam.Send(s)
	}
}
<commit_msg>Remove unused IPToParamConverter<commit_after><|endoftext|>


<commit_before>// Package datastorex extends the cloud.google.com/go/datastore package.
package datastorex

import (
	"reflect"
	"testing"

	"cloud.google.com/go/datastore"
)

func TestMap_Load(t *testing.T) {
	ps := []datastore.Property{
		{
			Name:  "a",
			Value: "b",
		},
		{
			Name:  "c",
			Value: "d",
		},
		{
			// Should be ignored.
			Name:  "i",
			Value: 100,
		},
	}

	m := Map{}
	if err := m.Load(ps); err != nil {
		t.Errorf("Map.Load() error = %v, want nil", err)
	}

	expected := Map{"a": "b", "c": "d"}
	if !reflect.DeepEqual(m, expected) {
		t.Errorf("Map.Load() got = %#v, want %#v", m, expected)
	}
}

func TestMap_Save(t *testing.T) {
	m := Map{"a": "b", "c": "d"}

	got, err := m.Save()
	if err != nil {
		t.Errorf("Map.Save() error = %v, want nil", err)
		return
	}

	expected := []datastore.Property{
		{
			Name:  "a",
			Value: "b",
		},
		{
			Name:  "c",
			Value: "d",
		},
	}
	if !reflect.DeepEqual(got, expected) {
		t.Errorf("Map.Save() = %#v, want %#v", got, expected)
	}
}
<commit_msg>Sort property order before checking<commit_after>// Package datastorex extends the cloud.google.com/go/datastore package.
package datastorex

import (
	"reflect"
	"sort"
	"testing"

	"cloud.google.com/go/datastore"
)

func TestMap_Load(t *testing.T) {
	ps := []datastore.Property{
		{
			Name:  "a",
			Value: "b",
		},
		{
			Name:  "c",
			Value: "d",
		},
		{
			// Should be ignored.
			Name:  "i",
			Value: 100,
		},
	}

	m := Map{}
	if err := m.Load(ps); err != nil {
		t.Errorf("Map.Load() error = %v, want nil", err)
	}

	expected := Map{"a": "b", "c": "d"}
	if !reflect.DeepEqual(m, expected) {
		t.Errorf("Map.Load() got = %#v, want %#v", m, expected)
	}
}

func TestMap_Save(t *testing.T) {
	m := Map{"a": "b", "c": "d"}

	got, err := m.Save()
	if err != nil {
		t.Errorf("Map.Save() error = %v, want nil", err)
		return
	}

	sort.Slice(got, func(i, j int) bool {
		return got[i].Name < got[j].Name
	})

	expected := []datastore.Property{
		{
			Name:  "a",
			Value: "b",
		},
		{
			Name:  "c",
			Value: "d",
		},
	}
	if !reflect.DeepEqual(got, expected) {
		t.Errorf("Map.Save() = %#v, want %#v", got, expected)
	}
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"path/filepath"
	"regexp"
	"strings"

	"html/template"

	"github.com/gorilla/mux"
	geoip2 "github.com/oschwald/geoip2-golang"
)

const (
	IP_HEADER      = "x-ifconfig-ip"
	COUNTRY_HEADER = "x-ifconfig-country"
)

var cliUserAgentExp = regexp.MustCompile("^(?i)(curl|wget|fetch\\slibfetch)\\/.*$")

type API struct {
	db       *geoip2.Reader
	CORS     bool
	Template string
	Logger   *log.Logger
}

func New() *API { return &API{} }

func NewWithGeoIP(filepath string) (*API, error) {
	db, err := geoip2.Open(filepath)
	if err != nil {
		return nil, err
	}
	return &API{db: db}, nil
}

type Cmd struct {
	Name string
	Args string
}

func (c *Cmd) String() string {
	return c.Name + " " + c.Args
}

func cmdFromQueryParams(values url.Values) Cmd {
	cmd, exists := values["cmd"]
	if !exists || len(cmd) == 0 {
		return Cmd{Name: "curl"}
	}
	switch cmd[0] {
	case "fetch":
		return Cmd{Name: "fetch", Args: "-qo -"}
	case "wget":
		return Cmd{Name: "wget", Args: "-qO -"}
	}
	return Cmd{Name: "curl"}
}

func ipFromRequest(r *http.Request) (net.IP, error) {
	var host string
	realIP := r.Header.Get("X-Real-IP")
	var err error
	if realIP != "" {
		host = realIP
	} else {
		host, _, err = net.SplitHostPort(r.RemoteAddr)
		if err != nil {
			return nil, err
		}
	}
	ip := net.ParseIP(host)
	if ip == nil {
		return nil, fmt.Errorf("could not parse IP: %s", host)
	}
	return ip, nil
}

func headerKeyFromRequest(r *http.Request) string {
	vars := mux.Vars(r)
	key, ok := vars["key"]
	if !ok {
		return ""
	}
	return key
}

func (a *API) lookupCountry(ip net.IP) (string, error) {
	if a.db == nil {
		return "", nil
	}
	record, err := a.db.Country(ip)
	if err != nil {
		return "", err
	}
	if country, exists := record.Country.Names["en"]; exists {
		return country, nil
	}
	if country, exists := record.RegisteredCountry.Names["en"]; exists {
		return country, nil
	}
	return "", fmt.Errorf("could not determine country for IP: %s", ip)
}

func (a *API) handleError(w http.ResponseWriter, err error) {
	a.Logger.Print(err)
	w.WriteHeader(http.StatusInternalServerError)
	io.WriteString(w, "Internal server error")
}

func (a *API) DefaultHandler(w http.ResponseWriter, r *http.Request) {
	cmd := cmdFromQueryParams(r.URL.Query())
	funcMap := template.FuncMap{"ToLower": strings.ToLower}
	t, err := template.New(filepath.Base(a.Template)).Funcs(funcMap).ParseFiles(a.Template)
	if err != nil {
		a.handleError(w, err)
		return
	}
	b, err := json.MarshalIndent(r.Header, "", "  ")
	if err != nil {
		a.handleError(w, err)
		return
	}

	var data = struct {
		IP     string
		JSON   string
		Header http.Header
		Cmd
	}{r.Header.Get(IP_HEADER), string(b), r.Header, cmd}

	if err := t.Execute(w, &data); err != nil {
		a.handleError(w, err)
	}
}

func (a *API) JSONHandler(w http.ResponseWriter, r *http.Request) {
	key := headerKeyFromRequest(r)
	if key == "" {
		key = IP_HEADER
	}
	value := map[string]string{key: r.Header.Get(key)}
	b, err := json.MarshalIndent(value, "", "  ")
	if err != nil {
		a.handleError(w, err)
		return
	}
	w.Write(b)
}

func (a *API) CLIHandler(w http.ResponseWriter, r *http.Request) {
	key := headerKeyFromRequest(r)
	if key == "" {
		key = IP_HEADER
	}
	value := r.Header.Get(key)
	if !strings.HasSuffix(value, "\n") {
		value += "\n"
	}
	io.WriteString(w, value)
}

func cliMatcher(r *http.Request, rm *mux.RouteMatch) bool {
	return cliUserAgentExp.MatchString(r.UserAgent())
}

func (a *API) requestFilter(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ip, err := ipFromRequest(r)
		if err != nil {
			r.Header.Set(IP_HEADER, err.Error())
		} else {
			r.Header.Set(IP_HEADER, ip.String())
			country, err := a.lookupCountry(ip)
			if err != nil {
				r.Header.Set(COUNTRY_HEADER, err.Error())
			} else {
				r.Header.Set(COUNTRY_HEADER, country)
			}
		}
		if a.CORS {
			w.Header().Set("Access-Control-Allow-Methods", "GET")
			w.Header().Set("Access-Control-Allow-Origin", "*")
		}
		next.ServeHTTP(w, r)
	})
}

func (a *API) Handlers() http.Handler {
	r := mux.NewRouter()

	// JSON
	r.HandleFunc("/", a.JSONHandler).Methods("GET").Headers("Accept", "application/json")
	r.HandleFunc("/{key}", a.JSONHandler).Methods("GET").Headers("Accept", "application/json")
	r.HandleFunc("/{key}.json", a.JSONHandler).Methods("GET")

	// CLI
	r.HandleFunc("/", a.CLIHandler).Methods("GET").MatcherFunc(cliMatcher)
	r.HandleFunc("/{key}", a.CLIHandler).Methods("GET").MatcherFunc(cliMatcher)

	// Default
	r.HandleFunc("/", a.DefaultHandler).Methods("GET")

	// Pass all requests through the request filter
	return a.requestFilter(r)
}

func (a *API) ListenAndServe(addr string) error {
	http.Handle("/", a.Handlers())
	return http.ListenAndServe(addr, nil)
}
<commit_msg>Return error for unknown header<commit_after>package api

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"path/filepath"
	"regexp"
	"strings"

	"html/template"

	"github.com/gorilla/mux"
	geoip2 "github.com/oschwald/geoip2-golang"
)

const (
	IP_HEADER      = "x-ifconfig-ip"
	COUNTRY_HEADER = "x-ifconfig-country"
)

var cliUserAgentExp = regexp.MustCompile("^(?i)(curl|wget|fetch\\slibfetch)\\/.*$")

type API struct {
	db       *geoip2.Reader
	CORS     bool
	Template string
	Logger   *log.Logger
}

func New() *API { return &API{} }

func NewWithGeoIP(filepath string) (*API, error) {
	db, err := geoip2.Open(filepath)
	if err != nil {
		return nil, err
	}
	return &API{db: db}, nil
}

type Cmd struct {
	Name string
	Args string
}

func (c *Cmd) String() string {
	return c.Name + " " + c.Args
}

func cmdFromQueryParams(values url.Values) Cmd {
	cmd, exists := values["cmd"]
	if !exists || len(cmd) == 0 {
		return Cmd{Name: "curl"}
	}
	switch cmd[0] {
	case "fetch":
		return Cmd{Name: "fetch", Args: "-qo -"}
	case "wget":
		return Cmd{Name: "wget", Args: "-qO -"}
	}
	return Cmd{Name: "curl"}
}

func ipFromRequest(r *http.Request) (net.IP, error) {
	var host string
	realIP := r.Header.Get("X-Real-IP")
	var err error
	if realIP != "" {
		host = realIP
	} else {
		host, _, err = net.SplitHostPort(r.RemoteAddr)
		if err != nil {
			return nil, err
		}
	}
	ip := net.ParseIP(host)
	if ip == nil {
		return nil, fmt.Errorf("could not parse IP: %s", host)
	}
	return ip, nil
}

func headerKeyFromRequest(r *http.Request) string {
	vars := mux.Vars(r)
	key, ok := vars["key"]
	if !ok {
		return ""
	}
	return key
}

func (a *API) lookupCountry(ip net.IP) (string, error) {
	if a.db == nil {
		return "", nil
	}
	record, err := a.db.Country(ip)
	if err != nil {
		return "", err
	}
	if country, exists := record.Country.Names["en"]; exists {
		return country, nil
	}
	if country, exists := record.RegisteredCountry.Names["en"]; exists {
		return country, nil
	}
	return "", fmt.Errorf("could not determine country for IP: %s", ip)
}

func (a *API) handleError(w http.ResponseWriter, err error) {
	a.Logger.Print(err)
	w.WriteHeader(http.StatusInternalServerError)
	io.WriteString(w, "Internal server error")
}

func handleUnknownHeader(w http.ResponseWriter, key string) {
	w.WriteHeader(http.StatusBadRequest)
	io.WriteString(w, "Bad request: Unknown header: "+key)
}

func (a *API) DefaultHandler(w http.ResponseWriter, r *http.Request) {
	cmd := cmdFromQueryParams(r.URL.Query())
	funcMap := template.FuncMap{"ToLower": strings.ToLower}
	t, err := template.New(filepath.Base(a.Template)).Funcs(funcMap).ParseFiles(a.Template)
	if err != nil {
		a.handleError(w, err)
		return
	}
	b, err := json.MarshalIndent(r.Header, "", "  ")
	if err != nil {
		a.handleError(w, err)
		return
	}

	var data = struct {
		IP     string
		JSON   string
		Header http.Header
		Cmd
	}{r.Header.Get(IP_HEADER), string(b), r.Header, cmd}

	if err := t.Execute(w, &data); err != nil {
		a.handleError(w, err)
	}
}

func (a *API) JSONHandler(w http.ResponseWriter, r *http.Request) {
	key := headerKeyFromRequest(r)
	if key == "" {
		key = IP_HEADER
	}
	value := map[string]string{key: r.Header.Get(key)}
	if value[key] == "" {
		handleUnknownHeader(w, key)
		return
	}
	b, err := json.MarshalIndent(value, "", "  ")
	if err != nil {
		a.handleError(w, err)
		return
	}
	w.Write(b)
}

func (a *API) CLIHandler(w http.ResponseWriter, r *http.Request) {
	key := headerKeyFromRequest(r)
	if key == "" {
		key = IP_HEADER
	}
	value := r.Header.Get(key)
	if value == "" {
		handleUnknownHeader(w, key)
		return
	}
	if !strings.HasSuffix(value, "\n") {
		value += "\n"
	}
	io.WriteString(w, value)
}

func cliMatcher(r *http.Request, rm *mux.RouteMatch) bool {
	return cliUserAgentExp.MatchString(r.UserAgent())
}

func (a *API) requestFilter(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ip, err := ipFromRequest(r)
		if err != nil {
			r.Header.Set(IP_HEADER, err.Error())
		} else {
			r.Header.Set(IP_HEADER, ip.String())
			country, err := a.lookupCountry(ip)
			if err != nil {
				r.Header.Set(COUNTRY_HEADER, err.Error())
			} else {
				r.Header.Set(COUNTRY_HEADER, country)
			}
		}
		if a.CORS {
			w.Header().Set("Access-Control-Allow-Methods", "GET")
			w.Header().Set("Access-Control-Allow-Origin", "*")
		}
		next.ServeHTTP(w, r)
	})
}

func (a *API) Handlers() http.Handler {
	r := mux.NewRouter()

	// JSON
	r.HandleFunc("/", a.JSONHandler).Methods("GET").Headers("Accept", "application/json")
	r.HandleFunc("/{key}", a.JSONHandler).Methods("GET").Headers("Accept", "application/json")
	r.HandleFunc("/{key}.json", a.JSONHandler).Methods("GET")

	// CLI
	r.HandleFunc("/", a.CLIHandler).Methods("GET").MatcherFunc(cliMatcher)
	r.HandleFunc("/{key}", a.CLIHandler).Methods("GET").MatcherFunc(cliMatcher)

	// Default
	r.HandleFunc("/", a.DefaultHandler).Methods("GET")

	// Pass all requests through the request filter
	return a.requestFilter(r)
}

func (a *API) ListenAndServe(addr string) error {
	http.Handle("/", a.Handlers())
	return http.ListenAndServe(addr, nil)
}
<|endoftext|>


<commit_before>package api

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

const (
	apiURL    = "https://neocities.org/api/"
	userAgent = "neocities (Go 1.1 package http)"
)

// Credentials contains the username and password
type Credentials struct {
	User string
	Pass string
}

// UploadFiles takes a set of credentials and
// a list of filename paths to upload to Neocities.
func UploadFiles(cred *Credentials, paths []string) (Response, error) {
	req, err := newUploadRequest(cred, paths)
	check(err)

	return performHTTPRequest(req)
}

// DeleteFiles deletes the given filenames
func DeleteFiles(cred *Credentials, filenames []string) (Response, error) {
	data := url.Values{}

	for _, file := range filenames {
		data.Add("filenames[]", file)
	}

	req, err := newDeleteRequest(cred, data)
	check(err)

	return performHTTPRequest(req)
}

// SiteInfo returns a site info response
func SiteInfo(cred *Credentials, site string) (Response, error) {
	req, err := newInfoRequest(cred, site)
	check(err)

	return performHTTPRequest(req)
}

// Create a new info request
func newInfoRequest(cred *Credentials, site string) (*http.Request, error) {
	endpoint := "info"

	if site != "" {
		endpoint = endpoint + "?sitename=" + site
	}

	req, err := http.NewRequest("GET", apiURL+endpoint, nil)
	if err != nil {
		return req, err
	}

	if cred != nil {
		// Authenticate using the user and pass
		req.SetBasicAuth(cred.User, cred.Pass)
	}

	return req, nil
}

// Create a new delete request
func newDeleteRequest(cred *Credentials, data url.Values) (*http.Request, error) {
	req, err := http.NewRequest("POST", apiURL+"delete", strings.NewReader(data.Encode()))
	if err != nil {
		return req, err
	}

	// Authenticate using the user and pass
	req.SetBasicAuth(cred.User, cred.Pass)

	// Set the content type of the form data
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	return req, nil
}

// Create a new upload request
func newUploadRequest(cred *Credentials, paths []string) (*http.Request, error) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	// Add the contents of each file to the multipart body
	for _, path := range paths {
		file, err := os.Open(path)

		defer file.Close()

		if err != nil {
			return nil, err
		}

		base := filepath.Base(path)
		part, err := writer.CreateFormFile(base, base)

		if err != nil {
			return nil, err
		}

		_, err = io.Copy(part, file)
		check(err)
	}

	err := writer.Close()
	check(err)

	req, err := http.NewRequest("POST", apiURL+"upload", body)
	if err != nil {
		return req, err
	}

	// Authenticate using the user and pass
	req.SetBasicAuth(cred.User, cred.Pass)

	// Set the content type of the form data
	req.Header.Add("Content-Type", writer.FormDataContentType())

	return req, nil
}

func performHTTPRequest(req *http.Request) (Response, error) {
	res, err := sendHTTPRequest(req)
	check(err)

	defer res.Body.Close()

	var r Response

	r.PopulateFromHTTPResponse(res)

	if res.StatusCode == 200 {
		return r, nil
	}

	return r, errors.New("unsuccessful")
}

// Send HTTP request
func sendHTTPRequest(req *http.Request) (*http.Response, error) {
	// Create a HTTP client
	client := &http.Client{}

	// Make sure that the correct User-Agent is set
	req.Header.Add("User-Agent", userAgent)

	// Send the request
	res, err := client.Do(req)
	check(err)

	// Return the response
	return res, nil
}

func check(err error) {
	if err != nil {
		fmt.Println("Error:", err)

		os.Exit(1)
	}
}
<commit_msg>Folder support by removing use of filepath.Base<commit_after>package api

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strings"
)

const (
	apiURL    = "https://neocities.org/api/"
	userAgent = "neocities (Go 1.1 package http)"
)

// Credentials contains the username and password
type Credentials struct {
	User string
	Pass string
}

// UploadFiles takes a set of credentials and
// a list of filename paths to upload to Neocities.
func UploadFiles(cred *Credentials, paths []string) (Response, error) {
	req, err := newUploadRequest(cred, paths)
	check(err)

	return performHTTPRequest(req)
}

// DeleteFiles deletes the given filenames
func DeleteFiles(cred *Credentials, filenames []string) (Response, error) {
	data := url.Values{}

	for _, file := range filenames {
		data.Add("filenames[]", file)
	}

	req, err := newDeleteRequest(cred, data)
	check(err)

	return performHTTPRequest(req)
}

// SiteInfo returns a site info response
func SiteInfo(cred *Credentials, site string) (Response, error) {
	req, err := newInfoRequest(cred, site)
	check(err)

	return performHTTPRequest(req)
}

// Create a new info request
func newInfoRequest(cred *Credentials, site string) (*http.Request, error) {
	endpoint := "info"

	if site != "" {
		endpoint = endpoint + "?sitename=" + site
	}

	req, err := http.NewRequest("GET", apiURL+endpoint, nil)
	if err != nil {
		return req, err
	}

	if cred != nil {
		// Authenticate using the user and pass
		req.SetBasicAuth(cred.User, cred.Pass)
	}

	return req, nil
}

// Create a new delete request
func newDeleteRequest(cred *Credentials, data url.Values) (*http.Request, error) {
	req, err := http.NewRequest("POST", apiURL+"delete", strings.NewReader(data.Encode()))
	if err != nil {
		return req, err
	}

	// Authenticate using the user and pass
	req.SetBasicAuth(cred.User, cred.Pass)

	// Set the content type of the form data
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	return req, nil
}

// Create a new upload request
func newUploadRequest(cred *Credentials, paths []string) (*http.Request, error) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	// Add the contents of each file to the multipart body
	for _, path := range paths {
		file, err := os.Open(path)

		defer file.Close()

		if err != nil {
			return nil, err
		}

		part, err := writer.CreateFormFile(path, path)

		if err != nil {
			return nil, err
		}

		_, err = io.Copy(part, file)
		check(err)
	}

	err := writer.Close()
	check(err)

	req, err := http.NewRequest("POST", apiURL+"upload", body)
	if err != nil {
		return req, err
	}

	// Authenticate using the user and pass
	req.SetBasicAuth(cred.User, cred.Pass)

	// Set the content type of the form data
	req.Header.Add("Content-Type", writer.FormDataContentType())

	return req, nil
}

func performHTTPRequest(req *http.Request) (Response, error) {
	res, err := sendHTTPRequest(req)
	check(err)

	defer res.Body.Close()

	var r Response

	r.PopulateFromHTTPResponse(res)

	if res.StatusCode == 200 {
		return r, nil
	}

	return r, errors.New("unsuccessful")
}

// Send HTTP request
func sendHTTPRequest(req *http.Request) (*http.Response, error) {
	// Create a HTTP client
	client := &http.Client{}

	// Make sure that the correct User-Agent is set
	req.Header.Add("User-Agent", userAgent)

	// Send the request
	res, err := client.Do(req)
	check(err)

	// Return the response
	return res, nil
}

func check(err error) {
	if err != nil {
		fmt.Println("Error:", err)

		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package api

import (
	"errors"
  "fmt"
	"github.com/godbus/dbus"

	"github.com/muka/go-bluetooth/bluez"
	"github.com/muka/go-bluetooth/bluez/profile"
	"github.com/muka/go-bluetooth/emitter"
)

//Exit performs a clean exit
func Exit() {
	manager, err := GetManager()
	if err == nil {
		manager.Close()
	}
}

//GetDeviceByAddress return a Device object based on its address
func GetDeviceByAddress(address string) (*Device, error) {
	list, err := GetDeviceList()
	if err != nil {
		return nil, err
	}
	for _, path := range list {
		dev := NewDevice(string(path))
		if dev.Properties.Address == address {
			return dev, nil
		}
	}
	return nil, nil
}

//GetDevices returns a list of bluetooth discovered Devices
func GetDevices() ([]Device, error) {

	list, err := GetDeviceList()
	if err != nil {
	  fmt.Println("GetDeviceList", err)
		return nil, err
	}

	manager, err := GetManager()
	if err != nil {
		return nil, err
	}

	objects := manager.GetObjects()

	var devices = make([]Device, 0)
	for _, path := range list {
		props := (*objects)[path][bluez.Device1Interface]
		dev, err := ParseDevice(path, props)
		if err != nil {
			return nil, err
		}
		devices = append(devices, *dev)
	}

	return devices, nil
}

//GetDeviceList returns a list of discovered Devices paths
func GetDeviceList() ([]dbus.ObjectPath, error) {

	manager, err := GetManager()
	if err != nil {
		return nil, err
	}

	objects := manager.GetObjects()
	var devices []dbus.ObjectPath
	for path, ifaces := range *objects {
		for iface := range ifaces {
			switch iface {
			case bluez.Device1Interface:
				{
					devices = append(devices, path)
				}
			}
		}
	}

	return devices, nil
}

//AdapterExists checks if an adapter is available
func AdapterExists(adapterID string) (bool, error) {

	manager, err := GetManager()
	if err != nil {
		return false, err
	}

	objects := manager.GetObjects()

	path := dbus.ObjectPath("/org/bluez/" + adapterID)
	_, exists := (*objects)[path]

	return exists, nil
}

//GetAdapter return an adapter object instance
func GetAdapter(adapterID string) (*profile.Adapter1, error) {

	if exists, err := AdapterExists(adapterID); !exists {
		if err != nil {
			return nil, err
		}
		return nil, errors.New("Adapter " + adapterID + " not found")
	}

	return profile.NewAdapter1(adapterID), nil
}

//GetGattManager return an adapter object instance
func GetGattManager(adapterID string) (*profile.GattManager1, error) {

	if exists, err := AdapterExists(adapterID); !exists {
		if err != nil {
			return nil, err
		}
		return nil, errors.New("Adapter " + adapterID + " not found")
	}

	return profile.NewGattManager1(adapterID), nil
}

//StartDiscovery on adapter hci0
func StartDiscovery() error {
	return StartDiscoveryOn("hci0")
}

//StopDiscovery on adapter hci0
func StopDiscovery() error {
	return StopDiscoveryOn("hci0")
}

// StartDiscoveryOn start discovery on specified adapter
func StartDiscoveryOn(adapterID string) error {

	adapter, err := GetAdapter(adapterID)

	if err != nil {
		return err
	}

	err = adapter.StartDiscovery()

	if err != nil {
		return err
	}

	return nil
}

// StopDiscoveryOn start discovery on specified adapter
func StopDiscoveryOn(adapterID string) error {
	adapter, err := GetAdapter(adapterID)
	if err != nil {
		return err
	}
	return adapter.StopDiscovery()
}

//On add an event handler
func On(name string, fn *emitter.Callback) error {
	return emitter.On(name, fn)
}

//Off remove an event handler
func Off(name string, fn *emitter.Callback) error {
	return emitter.Off(name, fn)
}
<commit_msg>remove test fmt<commit_after>package api

import (
	"errors"
	"github.com/godbus/dbus"

	"github.com/muka/go-bluetooth/bluez"
	"github.com/muka/go-bluetooth/bluez/profile"
	"github.com/muka/go-bluetooth/emitter"
)

//Exit performs a clean exit
func Exit() {
	manager, err := GetManager()
	if err == nil {
		manager.Close()
	}
}

//GetDeviceByAddress return a Device object based on its address
func GetDeviceByAddress(address string) (*Device, error) {
	list, err := GetDeviceList()
	if err != nil {
		return nil, err
	}
	for _, path := range list {
		dev := NewDevice(string(path))
		if dev.Properties.Address == address {
			return dev, nil
		}
	}
	return nil, nil
}

//GetDevices returns a list of bluetooth discovered Devices
func GetDevices() ([]Device, error) {

	list, err := GetDeviceList()
	if err != nil {
		return nil, err
	}

	manager, err := GetManager()
	if err != nil {
		return nil, err
	}

	objects := manager.GetObjects()

	var devices = make([]Device, 0)
	for _, path := range list {
		props := (*objects)[path][bluez.Device1Interface]
		dev, err := ParseDevice(path, props)
		if err != nil {
			return nil, err
		}
		devices = append(devices, *dev)
	}

	return devices, nil
}

//GetDeviceList returns a list of discovered Devices paths
func GetDeviceList() ([]dbus.ObjectPath, error) {

	manager, err := GetManager()
	if err != nil {
		return nil, err
	}

	objects := manager.GetObjects()
	var devices []dbus.ObjectPath
	for path, ifaces := range *objects {
		for iface := range ifaces {
			switch iface {
			case bluez.Device1Interface:
				{
					devices = append(devices, path)
				}
			}
		}
	}

	return devices, nil
}

//AdapterExists checks if an adapter is available
func AdapterExists(adapterID string) (bool, error) {

	manager, err := GetManager()
	if err != nil {
		return false, err
	}

	objects := manager.GetObjects()

	path := dbus.ObjectPath("/org/bluez/" + adapterID)
	_, exists := (*objects)[path]

	return exists, nil
}

//GetAdapter return an adapter object instance
func GetAdapter(adapterID string) (*profile.Adapter1, error) {

	if exists, err := AdapterExists(adapterID); !exists {
		if err != nil {
			return nil, err
		}
		return nil, errors.New("Adapter " + adapterID + " not found")
	}

	return profile.NewAdapter1(adapterID), nil
}

//GetGattManager return an adapter object instance
func GetGattManager(adapterID string) (*profile.GattManager1, error) {

	if exists, err := AdapterExists(adapterID); !exists {
		if err != nil {
			return nil, err
		}
		return nil, errors.New("Adapter " + adapterID + " not found")
	}

	return profile.NewGattManager1(adapterID), nil
}

//StartDiscovery on adapter hci0
func StartDiscovery() error {
	return StartDiscoveryOn("hci0")
}

//StopDiscovery on adapter hci0
func StopDiscovery() error {
	return StopDiscoveryOn("hci0")
}

// StartDiscoveryOn start discovery on specified adapter
func StartDiscoveryOn(adapterID string) error {

	adapter, err := GetAdapter(adapterID)

	if err != nil {
		return err
	}

	err = adapter.StartDiscovery()

	if err != nil {
		return err
	}

	return nil
}

// StopDiscoveryOn start discovery on specified adapter
func StopDiscoveryOn(adapterID string) error {
	adapter, err := GetAdapter(adapterID)
	if err != nil {
		return err
	}
	return adapter.StopDiscovery()
}

//On add an event handler
func On(name string, fn *emitter.Callback) error {
	return emitter.On(name, fn)
}

//Off remove an event handler
func Off(name string, fn *emitter.Callback) error {
	return emitter.Off(name, fn)
}
<|endoftext|>