<commit_before>package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"regexp"
	"strconv"
	"strings"
)

const NOT_SPACE = "[^\\s]"

var buffer []string

func readLine(scanner *bufio.Scanner, replace_lines IntSet, replace_strings StrSlice) {

	scanner.Split(bufio.ScanLines)

	line := 1

	ra, _ := regexp.Compile(NOT_SPACE)

	for scanner.Scan() {
		_, s := replace_lines[line]
		if s {
			buffer = append(buffer, ra.ReplaceAllString(scanner.Text(), "-"))
		} else {
			if len(replace_strings) > 0 {
				str := scanner.Text()
				for _, rs := range replace_strings {
					str = strings.Replace(str, rs, strings.Repeat("-", len(rs)), -1)
				}
				buffer = append(buffer, str)
			} else {
				buffer = append(buffer, scanner.Text())
			}
		}
		line += 1
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

}

func Exists(name string) bool {
	if _, err := os.Stat(name); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// set of ints
type IntSet map[int]struct{}

// set of strings
type StrSlice []string

func (i *IntSet) String() string {
	return fmt.Sprint(*i)
}

func (i *IntSet) Set(value string) error {
	if len(*i) > 0 {
		return errors.New("line flag already set")
	}
	for _, n := range strings.Split(value, ",") {
		num, err := strconv.Atoi(n)
		if err != nil {
			continue
		}
		if _, found := (*i)[num]; found {
			continue
		}
		(*i)[num] = struct{}{}
	}
	return nil
}

func (s *StrSlice) String() string {
	return fmt.Sprint(*s)
}

func (s *StrSlice) Set(value string) error {
	*s = append(*s, value)
	return nil
}

func main() {

	var replace_lines = IntSet{}
	var replace_strings StrSlice

	flag.Var(&replace_lines, "l", ">>>>>>>>>>>>>>>>> l")
	flag.Var(&replace_strings, "r", ">>>>>>>>>>>>>>> r")
	push := flag.Bool("p", false, "")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [-lrp] file\n\n", os.Args[0])
		fmt.Printf("  example: %s -l 3,7 -r secret -r 'my passphrase' file.conf\n\n", os.Args[0])
		fmt.Println("  -l: Number of the line(s) to be replaced, comma separated.")
		fmt.Println("  -r: Word to be replaced, can be used multiple times.")
		fmt.Println("  -p: Push the gist.")
	}

	flag.Parse()

	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) == 0 {
		readLine(bufio.NewScanner(os.Stdin), replace_lines, replace_strings)
	} else {
		if flag.NArg() != 1 {
			fmt.Fprintf(os.Stderr, "Usage: %s [-lrp] file, use -h for more info\n\n", os.Args[0])
			os.Exit(1)
		}
		f := flag.Arg(0)
		if Exists(f) {
			file, err := os.Open(f)
			defer file.Close()
			if err != nil {
				log.Fatal(err)
			}
			readLine(bufio.NewScanner(file), replace_lines, replace_strings)
		} else {
			fmt.Printf("Cannot read file: %s\n", f)
		}
	}

	// print preview
	pad := len(fmt.Sprint(len(buffer)))
	for k, v := range buffer {
		fmt.Printf("%*d  %s\n", pad, k+1, v)
	}

	if *push {
		fmt.Println("->>>>>>>>>>>>>>>> push")
	}

}
<commit_msg>	modified:   gist.go<commit_after>package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"regexp"
	"strconv"
	"strings"
)

const NOT_SPACE = "[^\\s]"

var buffer []string

func readLine(scanner *bufio.Scanner, replace_lines IntSet, replace_strings StrSlice) {

	scanner.Split(bufio.ScanLines)

	line := 1

	ra, _ := regexp.Compile(NOT_SPACE)

	for scanner.Scan() {
		_, s := replace_lines[line]
		if s {
			buffer = append(buffer, ra.ReplaceAllString(scanner.Text(), "-"))
		} else {
			if len(replace_strings) > 0 {
				str := scanner.Text()
				for _, rs := range replace_strings {
					str = strings.Replace(str, rs, strings.Repeat("-", len(rs)), -1)
				}
				buffer = append(buffer, str)
			} else {
				buffer = append(buffer, scanner.Text())
			}
		}
		line += 1
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

}

func Exists(name string) bool {
	if _, err := os.Stat(name); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// set of ints
type IntSet map[int]struct{}

// set of strings
type StrSlice []string

func (i *IntSet) String() string {
	return fmt.Sprint(*i)
}

func (i *IntSet) Set(value string) error {
	if len(*i) > 0 {
		return errors.New("line flag already set")
	}
	for _, n := range strings.Split(value, ",") {
		num, err := strconv.Atoi(n)
		if err != nil {
			continue
		}
		if _, found := (*i)[num]; found {
			continue
		}
		(*i)[num] = struct{}{}
	}
	return nil
}

func (s *StrSlice) String() string {
	return fmt.Sprint(*s)
}

func (s *StrSlice) Set(value string) error {
	*s = append(*s, value)
	return nil
}

func main() {

	var replace_lines = IntSet{}
	var replace_strings StrSlice

	flag.Var(&replace_lines, "l", ">>>>>>>>>>>>>>>>> l")
	flag.Var(&replace_strings, "r", ">>>>>>>>>>>>>>> r")
	push := flag.Bool("p", false, "")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [-lrp] file\n\n", os.Args[0])
		fmt.Printf("  example: %s -l 3,7 -r secret -r 'my passphrase' file.conf\n\n", os.Args[0])
		fmt.Println("  -l: Number of the line(s) to be replaced, comma separated.")
		fmt.Println("  -r: Word to be replaced, can be used multiple times.")
		fmt.Println("  -p: Push the gist.")
	}

	flag.Parse()

	// check if there is something to read from STDIN
	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) == 0 {
		readLine(bufio.NewScanner(os.Stdin), replace_lines, replace_strings)
	} else {
		if flag.NArg() != 1 {
			fmt.Fprintf(os.Stderr, "Usage: %s [-lrp] file, use -h for more info\n\n", os.Args[0])
			os.Exit(1)
		}
		f := flag.Arg(0)
		if Exists(f) {
			file, err := os.Open(f)
			defer file.Close()
			if err != nil {
				log.Fatal(err)
			}
			readLine(bufio.NewScanner(file), replace_lines, replace_strings)
		} else {
			fmt.Printf("Cannot read file: %s\n", f)
		}
	}

	// print preview
	pad := len(fmt.Sprint(len(buffer)))
	for k, v := range buffer {
		fmt.Printf("%*d  %s\n", pad, k+1, v)
	}

	if *push {
		fmt.Println("->>>>>>>>>>>>>>>> push")
	}

}
<|endoftext|>


<commit_before>/****************************************************************************
 * Copyright (c) 2013, Scott Ferguson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the software nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY SCOTT FERGUSON ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL SCOTT FERGUSON BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

package goat

import (
	"encoding/gob"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/scottferg/gospdy"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"net"
	"net/http"
	"net/url"
	"strconv"
)

const (
	GET    = 1 << 0
	POST   = 1 << 1
	PUT    = 1 << 2
	DELETE = 1 << 3

	methodGet    = "GET"
	methodPost   = "POST"
	methodPut    = "PUT"
	methodDelete = "DELETE"
)

type Config struct {
	Spdy bool
}

type Goat struct {
	Router       *mux.Router
	Config       Config
	routes       map[string]*route
	middleware   []Middleware
	dbsession    *mgo.Session
	dbname       string
	sessionstore sessions.Store
	listener     *net.TCPListener
	servemux     *http.ServeMux
}

type Handler func(http.ResponseWriter, *http.Request, *Context) error

type route struct {
	*Goat
	path        string
	name        string
	handler     Handler
	interceptor Interceptor
}

func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var c *Context
	var err error

	if c, err = NewContext(); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	defer c.Close()

	// Execute Middleware
	for _, m := range r.middleware {
		m(req, c)
	}

	// Execute the handler
	if r.handler != nil {
		err = r.handler(w, req, c)
	} else if r.interceptor != nil {
		rh := r.interceptor(w, req, c)
		err = rh(w, req, c)
	}

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func methodList(methods int) (r []string) {
	if methods&GET == GET {
		r = append(r, methodGet)
	}

	if methods&POST == POST {
		r = append(r, methodPost)
	}

	if methods&PUT == PUT {
		r = append(r, methodPut)
	}

	if methods&DELETE == DELETE {
		r = append(r, methodDelete)
	}

	return
}

func New(c *Config) *Goat {
	// Initialize session store
	gob.Register(bson.ObjectId(""))
	s := sessions.NewCookieStore([]byte("sevenbyelevensecretbomberboy"))
	r := mux.NewRouter()

	mx := http.NewServeMux()
	mx.Handle("/", r)

	return &Goat{
		Router:       r,
		Config:       *c,
		sessionstore: s,
		routes:       make(map[string]*route),
		servemux:     mx,
	}
}

func (g *Goat) RegisterRoute(path, name string, method int, handler interface{}) {
	// Initialize the HTTP router
	r := new(route)
	r.Goat = g
	r.path = path
	r.name = name

	if g.routes[r.name] != nil {
		return
	}

	g.routes[r.name] = r

	if h, ok := handler.(func(http.ResponseWriter, *http.Request, *Context) error); ok {
		r.handler = h
	} else if h, ok := handler.(Handler); ok {
		r.handler = h
	} else if i, ok := handler.(Interceptor); ok {
		r.interceptor = i
	} else if h, ok := handler.(http.Handler); ok {
		g.Router.Handle(path, h)
		return
	} else {
		panic("Unknown handler passed to RegisterRoute")
	}

	methods := methodList(method)
	g.Router.Handle(path, r).Methods(methods...).Name(r.name)
}

func (g *Goat) CopyDB() *mgo.Database {
	return g.dbsession.Copy().DB(g.dbname)
}

func (g *Goat) CloneDB() *mgo.Database {
	return g.dbsession.Clone().DB(g.dbname)
}

func (g *Goat) RegisterStaticFileHandler(remote, local string) {
	// Static file handler
	http.Handle(remote, http.FileServer(http.Dir(local)))
}

func (g *Goat) RegisterMiddleware(m Middleware) {
	g.middleware = append(g.middleware, m)
}

func (g *Goat) Reverse(root string, params ...string) (*url.URL, error) {
	return g.Router.Get(root).URL(params...)
}

func (g *Goat) ListenAndServe(port string) error {
	p := 8080

	if port != "" {
		p, _ = strconv.Atoi(port)
	}

	server := &http.Server{
		Handler: g.servemux,
	}

	g.listener, _ = net.ListenTCP("tcp", &net.TCPAddr{
		Port: p,
	})

	return server.Serve(g.listener)
}

func (g *Goat) ListenAndServeTLS(cert, key, addr string) error {
	if g.Config.Spdy {
		return spdy.ListenAndServeTLS(addr, cert, key, g.servemux)
	}

	server := &http.Server{
		Addr:    addr,
		Handler: g.servemux,
	}
	return server.ListenAndServeTLS(cert, key)
}

func (g *Goat) Close() {
	g.listener.Close()
}
<commit_msg>Allow nil goat.Config<commit_after>/****************************************************************************
 * Copyright (c) 2013, Scott Ferguson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the software nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY SCOTT FERGUSON ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL SCOTT FERGUSON BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

package goat

import (
	"encoding/gob"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/scottferg/gospdy"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"net"
	"net/http"
	"net/url"
	"strconv"
)

const (
	GET    = 1 << 0
	POST   = 1 << 1
	PUT    = 1 << 2
	DELETE = 1 << 3

	methodGet    = "GET"
	methodPost   = "POST"
	methodPut    = "PUT"
	methodDelete = "DELETE"
)

type Config struct {
	Spdy bool
}

type Goat struct {
	Router       *mux.Router
	Config       Config
	routes       map[string]*route
	middleware   []Middleware
	dbsession    *mgo.Session
	dbname       string
	sessionstore sessions.Store
	listener     *net.TCPListener
	servemux     *http.ServeMux
}

type Handler func(http.ResponseWriter, *http.Request, *Context) error

type route struct {
	*Goat
	path        string
	name        string
	handler     Handler
	interceptor Interceptor
}

func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var c *Context
	var err error

	if c, err = NewContext(); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	defer c.Close()

	// Execute Middleware
	for _, m := range r.middleware {
		m(req, c)
	}

	// Execute the handler
	if r.handler != nil {
		err = r.handler(w, req, c)
	} else if r.interceptor != nil {
		rh := r.interceptor(w, req, c)
		err = rh(w, req, c)
	}

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func methodList(methods int) (r []string) {
	if methods&GET == GET {
		r = append(r, methodGet)
	}

	if methods&POST == POST {
		r = append(r, methodPost)
	}

	if methods&PUT == PUT {
		r = append(r, methodPut)
	}

	if methods&DELETE == DELETE {
		r = append(r, methodDelete)
	}

	return
}

func New(c *Config) *Goat {
	// Initialize session store
	gob.Register(bson.ObjectId(""))
	s := sessions.NewCookieStore([]byte("sevenbyelevensecretbomberboy"))
	r := mux.NewRouter()

	mx := http.NewServeMux()
	mx.Handle("/", r)

	result := &Goat{
		Router:       r,
		sessionstore: s,
		routes:       make(map[string]*route),
		servemux:     mx,
	}

	if c != nil {
		result.Config = *c
	}

	return result
}

func (g *Goat) RegisterRoute(path, name string, method int, handler interface{}) {
	// Initialize the HTTP router
	r := new(route)
	r.Goat = g
	r.path = path
	r.name = name

	if g.routes[r.name] != nil {
		return
	}

	g.routes[r.name] = r

	if h, ok := handler.(func(http.ResponseWriter, *http.Request, *Context) error); ok {
		r.handler = h
	} else if h, ok := handler.(Handler); ok {
		r.handler = h
	} else if i, ok := handler.(Interceptor); ok {
		r.interceptor = i
	} else if h, ok := handler.(http.Handler); ok {
		g.Router.Handle(path, h)
		return
	} else {
		panic("Unknown handler passed to RegisterRoute")
	}

	methods := methodList(method)
	g.Router.Handle(path, r).Methods(methods...).Name(r.name)
}

func (g *Goat) CopyDB() *mgo.Database {
	return g.dbsession.Copy().DB(g.dbname)
}

func (g *Goat) CloneDB() *mgo.Database {
	return g.dbsession.Clone().DB(g.dbname)
}

func (g *Goat) RegisterStaticFileHandler(remote, local string) {
	// Static file handler
	g.servemux.Handle(remote, http.FileServer(http.Dir(local)))
}

func (g *Goat) RegisterMiddleware(m Middleware) {
	g.middleware = append(g.middleware, m)
}

func (g *Goat) Reverse(root string, params ...string) (*url.URL, error) {
	return g.Router.Get(root).URL(params...)
}

func (g *Goat) ListenAndServe(port string) error {
	p := 8080

	if port != "" {
		p, _ = strconv.Atoi(port)
	}

	server := &http.Server{
		Handler: g.servemux,
	}

	g.listener, _ = net.ListenTCP("tcp", &net.TCPAddr{
		Port: p,
	})

	return server.Serve(g.listener)
}

func (g *Goat) ListenAndServeTLS(cert, key, addr string) error {
	if g.Config.Spdy {
		return spdy.ListenAndServeTLS(addr, cert, key, g.servemux)
	}

	server := &http.Server{
		Addr:    addr,
		Handler: g.servemux,
	}
	return server.ListenAndServeTLS(cert, key)
}

func (g *Goat) Close() {
	g.listener.Close()
}
<|endoftext|>


<commit_before>/****************************************************************************
 * Copyright (c) 2013, Scott Ferguson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the software nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY SCOTT FERGUSON ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL SCOTT FERGUSON BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

package goat

import (
	"encoding/gob"
	"fmt"
	"github.com/gorilla/sessions"
	"github.com/gorilla/mux"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"net"
	"net/http"
	"net/url"
	"strconv"
)

const (
	GET    = 1 << 0
	POST   = 1 << 1
	PUT    = 1 << 2
	DELETE = 1 << 3

	methodGet    = "GET"
	methodPost   = "POST"
	methodPut    = "PUT"
	methodDelete = "DELETE"
)

type Goat struct {
	Router       *mux.Router
	routes       map[string]*route
	middleware   []Middleware
	dbsession    *mgo.Session
	dbname       string
	sessionstore sessions.Store
	listener     *net.TCPListener
}

type Handler func(http.ResponseWriter, *http.Request, *Context) error

type route struct {
	*Goat
	path        string
	name        string
	handler     Handler
	interceptor Interceptor
}

func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var c *Context
	var err error

	if c, err = NewContext(); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	defer c.Close()

	// Execute Middleware
	for _, m := range r.middleware {
		m(req, c)
	}

	// Execute the handler
	if r.handler != nil {
		err = r.handler(w, req, c)
	} else if r.interceptor != nil {
		rh := r.interceptor(w, req, c)
		err = rh(w, req, c)
	}

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func methodList(methods int) (r []string) {
	if methods&GET == GET {
		r = append(r, methodGet)
	}

	if methods&POST == POST {
		r = append(r, methodPost)
	}

	if methods&PUT == PUT {
		r = append(r, methodPut)
	}

	if methods&DELETE == DELETE {
		r = append(r, methodDelete)
	}

	return
}

func NewGoat() *Goat {
	// Initialize session store
	gob.Register(bson.ObjectId(""))
	s := sessions.NewCookieStore([]byte("sevenbyelevensecretbomberboy"))
	r := mux.NewRouter()

	http.Handle("/", r)

	return &Goat{
		Router:       r,
		sessionstore: s,
		routes:       make(map[string]*route),
	}
}

func (g *Goat) RegisterRoute(path, name string, method int, handler interface{}) {
	// Initialize the HTTP router
	r := new(route)
	r.Goat = g
	r.path = path
	r.name = name

	if g.routes[r.name] != nil {
		return
	}

	g.routes[r.name] = r

	if h, ok := handler.(func(http.ResponseWriter, *http.Request, *Context) error); ok {
		r.handler = h
	} else if h, ok := handler.(Handler); ok {
		r.handler = h
	} else if i, ok := handler.(Interceptor); ok {
		r.interceptor = i
	} else if h, ok := handler.(http.Handler); ok {
		g.Router.Handle(path, h)
		return
	} else {
		panic("Unknown handler passed to RegisterRoute")
	}

	methods := methodList(method)
	g.Router.Handle(path, r).Methods(methods...).Name(r.name)
}

func (g *Goat) CloneDB() *mgo.Database {
	return g.dbsession.Clone().DB(g.dbname)
}

func (g *Goat) RegisterStaticFileHandler(remote, local string) {
	// Static file handler
	http.Handle(remote, http.FileServer(http.Dir(local)))
}

func (g *Goat) RegisterMiddleware(m Middleware) {
	g.middleware = append(g.middleware, m)
}

func (g *Goat) Reverse(root string, params ...string) (*url.URL, error) {
	return g.Router.Get(root).URL(params...)
}

func (g *Goat) ListenAndServe(port string) {
	p := 8080

	if port != "" {
		p, _ = strconv.Atoi(port)
	}

	server := &http.Server{}

	g.listener, _ = net.ListenTCP("tcp", &net.TCPAddr{
		Port: p,
	})

	if err := server.Serve(g.listener); err != nil {
		fmt.Printf("Error when starting server: %s", err.Error())
	}
}

func (g *Goat) Close() {
	g.listener.Close()
}
<commit_msg>Run go fmt<commit_after>/****************************************************************************
 * Copyright (c) 2013, Scott Ferguson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the software nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY SCOTT FERGUSON ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL SCOTT FERGUSON BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

package goat

import (
	"encoding/gob"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"net"
	"net/http"
	"net/url"
	"strconv"
)

const (
	GET    = 1 << 0
	POST   = 1 << 1
	PUT    = 1 << 2
	DELETE = 1 << 3

	methodGet    = "GET"
	methodPost   = "POST"
	methodPut    = "PUT"
	methodDelete = "DELETE"
)

type Goat struct {
	Router       *mux.Router
	routes       map[string]*route
	middleware   []Middleware
	dbsession    *mgo.Session
	dbname       string
	sessionstore sessions.Store
	listener     *net.TCPListener
}

type Handler func(http.ResponseWriter, *http.Request, *Context) error

type route struct {
	*Goat
	path        string
	name        string
	handler     Handler
	interceptor Interceptor
}

func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var c *Context
	var err error

	if c, err = NewContext(); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	defer c.Close()

	// Execute Middleware
	for _, m := range r.middleware {
		m(req, c)
	}

	// Execute the handler
	if r.handler != nil {
		err = r.handler(w, req, c)
	} else if r.interceptor != nil {
		rh := r.interceptor(w, req, c)
		err = rh(w, req, c)
	}

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func methodList(methods int) (r []string) {
	if methods&GET == GET {
		r = append(r, methodGet)
	}

	if methods&POST == POST {
		r = append(r, methodPost)
	}

	if methods&PUT == PUT {
		r = append(r, methodPut)
	}

	if methods&DELETE == DELETE {
		r = append(r, methodDelete)
	}

	return
}

func NewGoat() *Goat {
	// Initialize session store
	gob.Register(bson.ObjectId(""))
	s := sessions.NewCookieStore([]byte("sevenbyelevensecretbomberboy"))
	r := mux.NewRouter()

	http.Handle("/", r)

	return &Goat{
		Router:       r,
		sessionstore: s,
		routes:       make(map[string]*route),
	}
}

func (g *Goat) RegisterRoute(path, name string, method int, handler interface{}) {
	// Initialize the HTTP router
	r := new(route)
	r.Goat = g
	r.path = path
	r.name = name

	if g.routes[r.name] != nil {
		return
	}

	g.routes[r.name] = r

	if h, ok := handler.(func(http.ResponseWriter, *http.Request, *Context) error); ok {
		r.handler = h
	} else if h, ok := handler.(Handler); ok {
		r.handler = h
	} else if i, ok := handler.(Interceptor); ok {
		r.interceptor = i
	} else if h, ok := handler.(http.Handler); ok {
		g.Router.Handle(path, h)
		return
	} else {
		panic("Unknown handler passed to RegisterRoute")
	}

	methods := methodList(method)
	g.Router.Handle(path, r).Methods(methods...).Name(r.name)
}

func (g *Goat) CloneDB() *mgo.Database {
	return g.dbsession.Clone().DB(g.dbname)
}

func (g *Goat) RegisterStaticFileHandler(remote, local string) {
	// Static file handler
	http.Handle(remote, http.FileServer(http.Dir(local)))
}

func (g *Goat) RegisterMiddleware(m Middleware) {
	g.middleware = append(g.middleware, m)
}

func (g *Goat) Reverse(root string, params ...string) (*url.URL, error) {
	return g.Router.Get(root).URL(params...)
}

func (g *Goat) ListenAndServe(port string) {
	p := 8080

	if port != "" {
		p, _ = strconv.Atoi(port)
	}

	server := &http.Server{}

	g.listener, _ = net.ListenTCP("tcp", &net.TCPAddr{
		Port: p,
	})

	if err := server.Serve(g.listener); err != nil {
		fmt.Printf("Error when starting server: %s", err.Error())
	}
}

func (g *Goat) Close() {
	g.listener.Close()
}
<|endoftext|>


<commit_before>package gock

import (
	"net/http"
	"net/url"
	"regexp"
	"sync"
)

// mutex is used interally for locking thread-sensitive functions.
var mutex = &sync.Mutex{}

// config global singleton store.
var config = struct {
	Networking        bool
	NetworkingFilters []FilterRequestFunc
}{}

// track unmatched requests so they can be tested for
var unmatchedRequests = []*http.Request{}

// New creates and registers a new HTTP mock with
// default settings and returns the Request DSL for HTTP mock
// definition and set up.
func New(uri string) *Request {
	Intercept()

	res := NewResponse()
	req := NewRequest()
	req.URLStruct, res.Error = url.Parse(normalizeURI(uri))

	// Create the new mock expectation
	exp := NewMock(req, res)
	Register(exp)

	return req
}

// Intercepting returns true if gock is currently able to intercept.
func Intercepting() bool {
	return http.DefaultTransport == DefaultTransport
}

// Intercept enables HTTP traffic interception via http.DefaultTransport.
// If you are using a custom HTTP transport, you have to use `gock.Transport()`
func Intercept() {
	if !Intercepting() {
		http.DefaultTransport = DefaultTransport
	}
}

// InterceptClient allows the developer to intercept HTTP traffic using
// a custom http.Client who uses a non default http.Transport/http.RoundTripper implementation.
func InterceptClient(cli *http.Client) {
	trans := NewTransport()
	trans.Transport = cli.Transport
	cli.Transport = trans
}

// RestoreClient allows the developer to disable and restore the
// original transport in the given http.Client.
func RestoreClient(cli *http.Client) {
	trans, ok := cli.Transport.(*Transport)
	if !ok {
		return
	}
	cli.Transport = trans.Transport
}

// Disable disables HTTP traffic interception by gock.
func Disable() {
	mutex.Lock()
	defer mutex.Unlock()
	http.DefaultTransport = NativeTransport
}

// Off disables the default HTTP interceptors and removes
// all the registered mocks, even if they has not been intercepted yet.
func Off() {
	Flush()
	Disable()
}

// EnableNetworking enables real HTTP networking
func EnableNetworking() {
	mutex.Lock()
	defer mutex.Unlock()
	config.Networking = true
}

// DisableNetworking disables real HTTP networking
func DisableNetworking() {
	mutex.Lock()
	defer mutex.Unlock()
	config.Networking = false
}

// NetworkingFilter determines if an http.Request should be triggered or not.
func NetworkingFilter(fn FilterRequestFunc) {
	mutex.Lock()
	defer mutex.Unlock()
	config.NetworkingFilters = append(config.NetworkingFilters, fn)
}

// DisableNetworkingFilters disables registered networking filters.
func DisableNetworkingFilters() {
	mutex.Lock()
	defer mutex.Unlock()
	config.NetworkingFilters = []FilterRequestFunc{}
}

// GetUnmatchedRequests returns all requests that have been received but haven't matched any mock
func GetUnmatchedRequests() []*http.Request {
	mutex.Lock()
	defer mutex.Unlock()
	return unmatchedRequests
}

// HasUnmatchedRequest returns true if gock has received any requests that didn't match a mock
func HasUnmatchedRequest() bool {
	return len(GetUnmatchedRequests()) > 0
}

func trackUnmatchedRequest(req *http.Request) {
	mutex.Lock()
	defer mutex.Unlock()
	unmatchedRequests = append(unmatchedRequests, req)
}

func normalizeURI(uri string) string {
	if ok, _ := regexp.MatchString("^http[s]?", uri); !ok {
		return "http://" + uri
	}
	return uri
}
<commit_msg>feat(#24): add CleanUnmatchedRequests() and OffAll() public functions<commit_after>package gock

import (
	"net/http"
	"net/url"
	"regexp"
	"sync"
)

// mutex is used interally for locking thread-sensitive functions.
var mutex = &sync.Mutex{}

// config global singleton store.
var config = struct {
	Networking        bool
	NetworkingFilters []FilterRequestFunc
}{}

// track unmatched requests so they can be tested for
var unmatchedRequests = []*http.Request{}

// New creates and registers a new HTTP mock with
// default settings and returns the Request DSL for HTTP mock
// definition and set up.
func New(uri string) *Request {
	Intercept()

	res := NewResponse()
	req := NewRequest()
	req.URLStruct, res.Error = url.Parse(normalizeURI(uri))

	// Create the new mock expectation
	exp := NewMock(req, res)
	Register(exp)

	return req
}

// Intercepting returns true if gock is currently able to intercept.
func Intercepting() bool {
	return http.DefaultTransport == DefaultTransport
}

// Intercept enables HTTP traffic interception via http.DefaultTransport.
// If you are using a custom HTTP transport, you have to use `gock.Transport()`
func Intercept() {
	if !Intercepting() {
		http.DefaultTransport = DefaultTransport
	}
}

// InterceptClient allows the developer to intercept HTTP traffic using
// a custom http.Client who uses a non default http.Transport/http.RoundTripper implementation.
func InterceptClient(cli *http.Client) {
	trans := NewTransport()
	trans.Transport = cli.Transport
	cli.Transport = trans
}

// RestoreClient allows the developer to disable and restore the
// original transport in the given http.Client.
func RestoreClient(cli *http.Client) {
	trans, ok := cli.Transport.(*Transport)
	if !ok {
		return
	}
	cli.Transport = trans.Transport
}

// Disable disables HTTP traffic interception by gock.
func Disable() {
	mutex.Lock()
	defer mutex.Unlock()
	http.DefaultTransport = NativeTransport
}

// Off disables the default HTTP interceptors and removes
// all the registered mocks, even if they has not been intercepted yet.
func Off() {
	Flush()
	Disable()
}

// OffAll is like `Off()`, but it also removes the unmatched requests registry.
func OffAll() {
	Flush()
	Disable()
	CleanUnmatchedRequest()
}

// EnableNetworking enables real HTTP networking
func EnableNetworking() {
	mutex.Lock()
	defer mutex.Unlock()
	config.Networking = true
}

// DisableNetworking disables real HTTP networking
func DisableNetworking() {
	mutex.Lock()
	defer mutex.Unlock()
	config.Networking = false
}

// NetworkingFilter determines if an http.Request should be triggered or not.
func NetworkingFilter(fn FilterRequestFunc) {
	mutex.Lock()
	defer mutex.Unlock()
	config.NetworkingFilters = append(config.NetworkingFilters, fn)
}

// DisableNetworkingFilters disables registered networking filters.
func DisableNetworkingFilters() {
	mutex.Lock()
	defer mutex.Unlock()
	config.NetworkingFilters = []FilterRequestFunc{}
}

// GetUnmatchedRequests returns all requests that have been received but haven't matched any mock
func GetUnmatchedRequests() []*http.Request {
	mutex.Lock()
	defer mutex.Unlock()
	return unmatchedRequests
}

// HasUnmatchedRequest returns true if gock has received any requests that didn't match a mock
func HasUnmatchedRequest() bool {
	return len(GetUnmatchedRequests()) > 0
}

// CleanUnmatchedRequest cleans the unmatched requests internal registry.
func CleanUnmatchedRequest() {
	mutex.Lock()
	defer mutex.Unlock()
	unmatchedRequests = []*http.Request{}
}

func trackUnmatchedRequest(req *http.Request) {
	mutex.Lock()
	defer mutex.Unlock()
	unmatchedRequests = append(unmatchedRequests, req)
}

func normalizeURI(uri string) string {
	if ok, _ := regexp.MatchString("^http[s]?", uri); !ok {
		return "http://" + uri
	}
	return uri
}
<|endoftext|>


<commit_before>package frontmatter

import (
	"bytes"
	"encoding/json"
	"errors"
	"log"
	"reflect"
	"sort"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/BurntSushi/toml"
	"github.com/hacdias/caddy-filemanager/utils/variables"
	"github.com/spf13/cast"
)

const (
	mainName   = "#MAIN#"
	objectType = "object"
	arrayType  = "array"
)

var mainTitle = ""

// Pretty creates a new FrontMatter object
func Pretty(content []byte) (*Content, string, error) {
	data, err := Unmarshal(content)

	if err != nil {
		return &Content{}, "", err
	}

	kind := reflect.ValueOf(data).Kind()

	object := new(Block)
	object.Type = objectType
	object.Name = mainName

	if kind == reflect.Map {
		object.Type = objectType
	} else if kind == reflect.Slice || kind == reflect.Array {
		object.Type = arrayType
	}

	return rawToPretty(data, object), mainTitle, nil
}

// Unmarshal returns the data of the frontmatter
func Unmarshal(content []byte) (interface{}, error) {
	mark := rune(content[0])
	var data interface{}

	switch mark {
	case '-':
		// If it's YAML
		if err := yaml.Unmarshal(content, &data); err != nil {
			return nil, err
		}
	case '+':
		// If it's TOML
		content = bytes.Replace(content, []byte("+"), []byte(""), -1)
		if _, err := toml.Decode(string(content), &data); err != nil {
			return nil, err
		}
	case '{', '[':
		// If it's JSON
		if err := json.Unmarshal(content, &data); err != nil {
			return nil, err
		}
	default:
		return nil, errors.New("Invalid frontmatter type.")
	}

	return data, nil
}

// Content is the block content
type Content struct {
	Other   interface{}
	Fields  []*Block
	Arrays  []*Block
	Objects []*Block
}

// Block is a block
type Block struct {
	Name     string
	Title    string
	Type     string
	HTMLType string
	Content  *Content
	Parent   *Block
}

func rawToPretty(config interface{}, parent *Block) *Content {
	objects := []*Block{}
	arrays := []*Block{}
	fields := []*Block{}

	cnf := map[string]interface{}{}
	kind := reflect.TypeOf(config)

	if kind == reflect.TypeOf(map[interface{}]interface{}{}) {
		for key, value := range config.(map[interface{}]interface{}) {
			cnf[key.(string)] = value
		}
	} else if kind == reflect.TypeOf([]interface{}{}) {
		for key, value := range config.([]interface{}) {
			cnf[string(key)] = value
		}
	} else {
		cnf = config.(map[string]interface{})
	}

	for name, element := range cnf {
		if variables.IsMap(element) {
			objects = append(objects, handleObjects(element, parent, name))
		} else if variables.IsSlice(element) {
			arrays = append(arrays, handleArrays(element, parent, name))
		} else {
			if name == "title" && parent.Name == mainName {
				mainTitle = element.(string)
			}

			fields = append(fields, handleFlatValues(element, parent, name))
		}
	}

	sort.Sort(sortByTitle(fields))
	sort.Sort(sortByTitle(arrays))
	sort.Sort(sortByTitle(objects))
	return &Content{
		Fields:  fields,
		Arrays:  arrays,
		Objects: objects,
	}
}

type sortByTitle []*Block

func (f sortByTitle) Len() int      { return len(f) }
func (f sortByTitle) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
func (f sortByTitle) Less(i, j int) bool {
	return strings.ToLower(f[i].Name) < strings.ToLower(f[j].Name)
}

func handleObjects(content interface{}, parent *Block, name string) *Block {
	c := new(Block)
	c.Parent = parent
	c.Type = objectType
	c.Title = name

	if parent.Name == mainName {
		c.Name = c.Title
	} else if parent.Type == arrayType {
		c.Name = parent.Name + "[]"
	} else {
		c.Name = parent.Name + "." + c.Title
	}

	c.Content = rawToPretty(content, c)
	return c
}

func handleArrays(content interface{}, parent *Block, name string) *Block {
	c := new(Block)
	c.Parent = parent
	c.Type = arrayType
	c.Title = name

	if parent.Name == mainName {
		c.Name = name
	} else {
		c.Name = parent.Name + "." + name
	}

	c.Content = rawToPretty(content, c)
	return c
}

func handleFlatValues(content interface{}, parent *Block, name string) *Block {
	c := new(Block)
	c.Parent = parent

	switch reflect.ValueOf(content).Kind() {
	case reflect.Bool:
		c.Type = "boolean"
	case reflect.Int, reflect.Float32, reflect.Float64:
		c.Type = "number"
	default:
		c.Type = "string"
	}

	c.Content = &Content{Other: content}

	switch strings.ToLower(name) {
	case "description":
		c.HTMLType = "textarea"
	case "date", "publishdate":
		c.HTMLType = "datetime"
		c.Content = &Content{Other: cast.ToTime(content)}
	default:
		c.HTMLType = "text"
	}

	if parent.Type == arrayType {
		c.Name = parent.Name + "[]"
		c.Title = content.(string)
	} else if parent.Type == objectType {
		c.Title = name
		c.Name = parent.Name + "." + name

		if parent.Name == mainName {
			c.Name = name
		}
	} else {
		log.Panic("Parent type not allowed in handleFlatValues.")
	}

	return c
}
<commit_msg>fix hacdias/caddy-hugo#74<commit_after>package frontmatter

import (
	"bytes"
	"encoding/json"
	"errors"
	"log"
	"reflect"
	"sort"
	"strconv"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/BurntSushi/toml"
	"github.com/hacdias/caddy-filemanager/utils/variables"
	"github.com/spf13/cast"
)

const (
	mainName   = "#MAIN#"
	objectType = "object"
	arrayType  = "array"
)

var mainTitle = ""

// Pretty creates a new FrontMatter object
func Pretty(content []byte) (*Content, string, error) {
	data, err := Unmarshal(content)

	if err != nil {
		return &Content{}, "", err
	}

	kind := reflect.ValueOf(data).Kind()

	object := new(Block)
	object.Type = objectType
	object.Name = mainName

	if kind == reflect.Map {
		object.Type = objectType
	} else if kind == reflect.Slice || kind == reflect.Array {
		object.Type = arrayType
	}

	return rawToPretty(data, object), mainTitle, nil
}

// Unmarshal returns the data of the frontmatter
func Unmarshal(content []byte) (interface{}, error) {
	mark := rune(content[0])
	var data interface{}

	switch mark {
	case '-':
		// If it's YAML
		if err := yaml.Unmarshal(content, &data); err != nil {
			return nil, err
		}
	case '+':
		// If it's TOML
		content = bytes.Replace(content, []byte("+"), []byte(""), -1)
		if _, err := toml.Decode(string(content), &data); err != nil {
			return nil, err
		}
	case '{', '[':
		// If it's JSON
		if err := json.Unmarshal(content, &data); err != nil {
			return nil, err
		}
	default:
		return nil, errors.New("Invalid frontmatter type.")
	}

	return data, nil
}

// Content is the block content
type Content struct {
	Other   interface{}
	Fields  []*Block
	Arrays  []*Block
	Objects []*Block
}

// Block is a block
type Block struct {
	Name     string
	Title    string
	Type     string
	HTMLType string
	Content  *Content
	Parent   *Block
}

func rawToPretty(config interface{}, parent *Block) *Content {
	objects := []*Block{}
	arrays := []*Block{}
	fields := []*Block{}

	cnf := map[string]interface{}{}
	kind := reflect.TypeOf(config)

	switch kind {
	case reflect.TypeOf(map[interface{}]interface{}{}):
		for key, value := range config.(map[interface{}]interface{}) {
			cnf[key.(string)] = value
		}
	case reflect.TypeOf([]map[string]interface{}{}):
		for index, value := range config.([]map[string]interface{}) {
			cnf[strconv.Itoa(index)] = value
		}
	case reflect.TypeOf([]map[interface{}]interface{}{}):
		for index, value := range config.([]map[interface{}]interface{}) {
			cnf[strconv.Itoa(index)] = value
		}
	case reflect.TypeOf([]interface{}{}):
		for index, value := range config.([]interface{}) {
			cnf[strconv.Itoa(index)] = value
		}
	default:
		cnf = config.(map[string]interface{})
	}

	for name, element := range cnf {
		if variables.IsMap(element) {
			objects = append(objects, handleObjects(element, parent, name))
		} else if variables.IsSlice(element) {
			arrays = append(arrays, handleArrays(element, parent, name))
		} else {
			if name == "title" && parent.Name == mainName {
				mainTitle = element.(string)
			}
			fields = append(fields, handleFlatValues(element, parent, name))
		}
	}

	sort.Sort(sortByTitle(fields))
	sort.Sort(sortByTitle(arrays))
	sort.Sort(sortByTitle(objects))
	return &Content{
		Fields:  fields,
		Arrays:  arrays,
		Objects: objects,
	}
}

type sortByTitle []*Block

func (f sortByTitle) Len() int      { return len(f) }
func (f sortByTitle) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
func (f sortByTitle) Less(i, j int) bool {
	return strings.ToLower(f[i].Name) < strings.ToLower(f[j].Name)
}

func handleObjects(content interface{}, parent *Block, name string) *Block {
	c := new(Block)
	c.Parent = parent
	c.Type = objectType
	c.Title = name

	if parent.Name == mainName {
		c.Name = c.Title
	} else if parent.Type == arrayType {
		c.Name = parent.Name + "[" + name + "]"
	} else {
		c.Name = parent.Name + "." + c.Title
	}

	c.Content = rawToPretty(content, c)
	return c
}

func handleArrays(content interface{}, parent *Block, name string) *Block {
	c := new(Block)
	c.Parent = parent
	c.Type = arrayType
	c.Title = name

	if parent.Name == mainName {
		c.Name = name
	} else {
		c.Name = parent.Name + "." + name
	}

	c.Content = rawToPretty(content, c)
	return c
}

func handleFlatValues(content interface{}, parent *Block, name string) *Block {
	c := new(Block)
	c.Parent = parent

	switch reflect.ValueOf(content).Kind() {
	case reflect.Bool:
		c.Type = "boolean"
	case reflect.Int, reflect.Float32, reflect.Float64:
		c.Type = "number"
	default:
		c.Type = "string"
	}

	c.Content = &Content{Other: content}

	switch strings.ToLower(name) {
	case "description":
		c.HTMLType = "textarea"
	case "date", "publishdate":
		c.HTMLType = "datetime"
		c.Content = &Content{Other: cast.ToTime(content)}
	default:
		c.HTMLType = "text"
	}

	if parent.Type == arrayType {
		c.Name = parent.Name + "[]"
		c.Title = content.(string)
	} else if parent.Type == objectType {
		c.Title = name
		c.Name = parent.Name + "." + name

		if parent.Name == mainName {
			c.Name = name
		}
	} else {
		log.Panic("Parent type not allowed in handleFlatValues.")
	}

	return c
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
	"html/template"
	"log"
	"net/http"
	"os"
	"sort"
	"time"
)

type Tracker map[Window]*Track

type Track struct {
	Start time.Time
	Spent time.Duration
}

type Window struct {
	Class string
	Name  string
}

type Xorg struct {
	X           *xgb.Conn
	root        xproto.Window
	activeAtom  *xproto.InternAtomReply
	netNameAtom *xproto.InternAtomReply
	nameAtom    *xproto.InternAtomReply
	classAtom   *xproto.InternAtomReply
}

func (t Track) String() string {
	return fmt.Sprint(t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (x Xorg) atom(aname string) *xproto.InternAtomReply {
	a, err := xproto.InternAtom(x.X, true, uint16(len(aname)), aname).Reply()
	if err != nil {
		log.Fatal("atom: ", err)
	}
	return a
}

func (x Xorg) property(w xproto.Window, a *xproto.InternAtomReply) (*xproto.GetPropertyReply, error) {
	return xproto.GetProperty(x.X, false, w, a.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
}

func (x Xorg) active() xproto.Window {
	p, err := x.property(x.root, x.activeAtom)
	if err != nil {
		log.Fatal("active: ", err)
	}
	return xproto.Window(xgb.Get32(p.Value))
}

func (x Xorg) name(w xproto.Window) string {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		log.Fatal("net name: ", err)
	}
	if string(name.Value) != "" {
		return string(name.Value)
	}
	name, err = x.property(w, x.nameAtom)
	if err != nil {
		log.Fatal("wm name: ", err)
	}
	return string(name.Value)
}

func (x Xorg) class(w xproto.Window) string {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		log.Fatal("class: ", err)
	}
	i := bytes.IndexByte(class.Value, 0)
	if i == -1 || string(class.Value[:i]) == "" {
		return "unknown"
	}
	return string(class.Value[:i])
}

func (x Xorg) winName() (Window, bool) {
	windowId := x.active()
	/* skip invalid window id */
	if windowId == 0 {
		return Window{}, false
	}
	x.spy(windowId)
	return Window{
		Class: x.class(windowId),
		Name:  x.name(windowId),
	}, true
}

func (x Xorg) spy(w xproto.Window) {
	xproto.ChangeWindowAttributes(x.X, w, xproto.CwEventMask,
		[]uint32{xproto.EventMaskPropertyChange})
}

func (x Xorg) update(t Tracker) (prev *Track) {
	if win, ok := x.winName(); ok {
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Start = time.Now()
		prev = t[win]
	}
	return
}

func connect() Xorg {
	var x Xorg
	var err error

	x.X, err = xgb.NewConn()
	if err != nil {
		log.Fatal("xgb: ", err)
	}

	err = screensaver.Init(x.X)
	if err != nil {
		log.Fatal("screensaver: ", err)
	}

	setup := xproto.Setup(x.X)
	x.root = setup.DefaultScreen(x.X).Root

	drw := xproto.Drawable(x.root)
	screensaver.SelectInput(x.X, drw, screensaver.EventNotifyMask)

	x.activeAtom = x.atom("_NET_ACTIVE_WINDOW")
	x.netNameAtom = x.atom("_NET_WM_NAME")
	x.nameAtom = x.atom("WM_NAME")
	x.classAtom = x.atom("WM_CLASS")

	x.spy(x.root)

	return x
}

func (t Tracker) collect() {
	x := connect()
	defer x.X.Close()

	prev := x.update(t)
	for {
		ev, everr := x.X.WaitForEvent()
		if everr != nil {
			log.Fatal("wait for event: ", everr)
		}
		switch event := ev.(type) {
		case xproto.PropertyNotifyEvent:
			if prev != nil {
				prev.Spent += time.Since(prev.Start)
			}
			prev = x.update(t)
		case screensaver.NotifyEvent:
			switch event.State {
			case screensaver.StateOn:
				fmt.Println("away from keyboard")
				prev = nil
				zzz = true
			default:
				fmt.Println("back to keyboard")
				zzz = false
			}
		}
	}
}

func (t Tracker) cleanup(d time.Duration) {
	for k, v := range t {
		if time.Since(v.Start) > d {
			log.Println("removing", k)
			delete(t, k)
		}
	}
}

func (t Tracker) load(fname string) {
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	err = dec.Decode(&t)
	if err != nil {
		log.Println(err)
	}
}

func (t Tracker) store(fname string) {
	dump, err := os.Create(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	err = enc.Encode(t)
	if err != nil {
		log.Println(err)
	}
}

type Index struct {
	Title  string
	Tracks Tracks
	Class  Tracks
	Total  time.Duration
	Zzz    bool
}

type Tracks []track

type track struct {
	Class string
	Name  string
	Time  time.Duration
	Odd   bool
}

func (t Tracks) Len() int           { return len(t) }
func (t Tracks) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t Tracks) Less(i, j int) bool { return t[i].Time < t[j].Time }

func indexHandler(w http.ResponseWriter, r *http.Request) {
	var i Index
	i.Title = "Gone Time Tracker"
	i.Zzz = zzz
	class := r.URL.Path[1:]

	classtotal := make(map[string]time.Duration)

	for k, v := range tracks {
		classtotal[k.Class] += v.Spent
		i.Total += v.Spent
		if class != "" && class != k.Class {
			continue
		}
		i.Tracks = append(i.Tracks, track{
			Class: k.Class,
			Name:  k.Name,
			Time:  v.Spent})
	}
	for k, v := range classtotal {
		i.Class = append(i.Class, track{Class: k, Time: v})
	}
	sort.Sort(sort.Reverse(i.Class))
	sort.Sort(sort.Reverse(i.Tracks))
	for j, _ := range i.Tracks {
		i.Tracks[j].Odd = j%2 == 0
	}
	err := tmpl.Execute(w, i)
	if err != nil {
		log.Println(err)
	}
}

const (
	port = ":8001"
	file = "dump.gob"
)

var (
	tracks = make(Tracker)
	tmpl   = template.Must(template.ParseFiles("index.html"))
	zzz    bool
)

func main() {
	tracks.load(file)
	go tracks.collect()
	go func() {
		for {
			tracks.cleanup(8 * time.Hour)
			tracks.store(file)
			time.Sleep(time.Minute)
		}
	}()
	log.Println("listen on", port)
	http.HandleFunc("/", indexHandler)
	http.ListenAndServe(port, nil)
}
<commit_msg>prevent race conditon on load/store, gofmt<commit_after>package main

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"sort"
	"sync"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
)

type Tracker map[Window]*Track

type Track struct {
	Start time.Time
	Spent time.Duration
}

type Window struct {
	Class string
	Name  string
}

type Xorg struct {
	X           *xgb.Conn
	root        xproto.Window
	activeAtom  *xproto.InternAtomReply
	netNameAtom *xproto.InternAtomReply
	nameAtom    *xproto.InternAtomReply
	classAtom   *xproto.InternAtomReply
}

func (t Track) String() string {
	return fmt.Sprint(t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (x Xorg) atom(aname string) *xproto.InternAtomReply {
	a, err := xproto.InternAtom(x.X, true, uint16(len(aname)), aname).Reply()
	if err != nil {
		log.Fatal("atom: ", err)
	}
	return a
}

func (x Xorg) property(w xproto.Window, a *xproto.InternAtomReply) (*xproto.GetPropertyReply, error) {
	return xproto.GetProperty(x.X, false, w, a.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
}

func (x Xorg) active() xproto.Window {
	p, err := x.property(x.root, x.activeAtom)
	if err != nil {
		log.Fatal("active: ", err)
	}
	return xproto.Window(xgb.Get32(p.Value))
}

func (x Xorg) name(w xproto.Window) string {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		log.Fatal("net name: ", err)
	}
	if string(name.Value) != "" {
		return string(name.Value)
	}
	name, err = x.property(w, x.nameAtom)
	if err != nil {
		log.Fatal("wm name: ", err)
	}
	return string(name.Value)
}

func (x Xorg) class(w xproto.Window) string {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		log.Fatal("class: ", err)
	}
	i := bytes.IndexByte(class.Value, 0)
	if i == -1 || string(class.Value[:i]) == "" {
		return "unknown"
	}
	return string(class.Value[:i])
}

func (x Xorg) winName() (Window, bool) {
	windowId := x.active()
	/* skip invalid window id */
	if windowId == 0 {
		return Window{}, false
	}
	x.spy(windowId)
	return Window{
		Class: x.class(windowId),
		Name:  x.name(windowId),
	}, true
}

func (x Xorg) spy(w xproto.Window) {
	xproto.ChangeWindowAttributes(x.X, w, xproto.CwEventMask,
		[]uint32{xproto.EventMaskPropertyChange})
}

func (x Xorg) update(t Tracker) (prev *Track) {
	if win, ok := x.winName(); ok {
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Start = time.Now()
		prev = t[win]
	}
	return
}

func connect() Xorg {
	var x Xorg
	var err error

	x.X, err = xgb.NewConn()
	if err != nil {
		log.Fatal("xgb: ", err)
	}

	err = screensaver.Init(x.X)
	if err != nil {
		log.Fatal("screensaver: ", err)
	}

	setup := xproto.Setup(x.X)
	x.root = setup.DefaultScreen(x.X).Root

	drw := xproto.Drawable(x.root)
	screensaver.SelectInput(x.X, drw, screensaver.EventNotifyMask)

	x.activeAtom = x.atom("_NET_ACTIVE_WINDOW")
	x.netNameAtom = x.atom("_NET_WM_NAME")
	x.nameAtom = x.atom("WM_NAME")
	x.classAtom = x.atom("WM_CLASS")

	x.spy(x.root)

	return x
}

func (t Tracker) collect() {
	x := connect()
	defer x.X.Close()

	prev := x.update(t)
	for {
		ev, everr := x.X.WaitForEvent()
		if everr != nil {
			log.Fatal("wait for event: ", everr)
		}
		switch event := ev.(type) {
		case xproto.PropertyNotifyEvent:
			if prev != nil {
				prev.Spent += time.Since(prev.Start)
			}
			prev = x.update(t)
		case screensaver.NotifyEvent:
			switch event.State {
			case screensaver.StateOn:
				fmt.Println("away from keyboard")
				prev = nil
				zzz = true
			default:
				fmt.Println("back to keyboard")
				zzz = false
			}
		}
	}
}

func (t Tracker) cleanup(d time.Duration) {
	for k, v := range t {
		if time.Since(v.Start) > d {
			log.Println("removing", k)
			delete(t, k)
		}
	}
}

func (t Tracker) load(fname string) {
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	err = dec.Decode(&t)
	if err != nil {
		log.Println(err)
	}
}

func (t Tracker) store(fname string) {
	dump, err := os.Create(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	err = enc.Encode(t)
	if err != nil {
		log.Println(err)
	}
}

type Index struct {
	Title  string
	Tracks Tracks
	Class  Tracks
	Total  time.Duration
	Zzz    bool
}

type Tracks []track

type track struct {
	Class string
	Name  string
	Time  time.Duration
	Odd   bool
}

func (t Tracks) Len() int           { return len(t) }
func (t Tracks) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t Tracks) Less(i, j int) bool { return t[i].Time < t[j].Time }

func indexHandler(w http.ResponseWriter, r *http.Request) {
	var i Index
	i.Title = "Gone Time Tracker"
	i.Zzz = zzz
	class := r.URL.Path[1:]

	classtotal := make(map[string]time.Duration)

	for k, v := range tracks {
		classtotal[k.Class] += v.Spent
		i.Total += v.Spent
		if class != "" && class != k.Class {
			continue
		}
		i.Tracks = append(i.Tracks, track{
			Class: k.Class,
			Name:  k.Name,
			Time:  v.Spent})
	}
	for k, v := range classtotal {
		i.Class = append(i.Class, track{Class: k, Time: v})
	}
	sort.Sort(sort.Reverse(i.Class))
	sort.Sort(sort.Reverse(i.Tracks))
	for j, _ := range i.Tracks {
		i.Tracks[j].Odd = j%2 == 0
	}
	err := tmpl.Execute(w, i)
	if err != nil {
		log.Println(err)
	}
}

const (
	port = ":8001"
	file = "dump.gob"
)

var (
	tracks = make(Tracker)
	tmpl   = template.Must(template.ParseFiles("index.html"))
	zzz    bool
)

func main() {
	mutex := &sync.Mutex{}

	mutex.Lock()
	tracks.load(file)
	mutex.Unlock()

	go tracks.collect()
	go func() {
		for {
			tracks.cleanup(8 * time.Hour)
			mutex.Lock()
			tracks.store(file)
			mutex.Unlock()
			time.Sleep(time.Minute)
		}
	}()
	log.Println("listen on", port)
	http.HandleFunc("/", indexHandler)
	http.ListenAndServe(port, nil)
}
<|endoftext|>


<commit_before>// Gone Time Tracker -or- Where has my time gone?
package main

import (
	"encoding/gob"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/mewkiz/pkg/goutil"
)

var (
	goneDir       string
	dumpFileName  string
	logFileName   string
	indexFileName string
	tracks        Tracks
	zzz           bool
	logger        *log.Logger
	current       Window
	display       string
	listen        string
	timeout       int
	expire        int
)

func init() {
	var err error
	goneDir, err = goutil.SrcDir("github.com/dim13/gone")
	if err != nil {
		log.Fatal("init: ", err)
	}
	dumpFileName = filepath.Join(goneDir, "gone.gob")
	logFileName = filepath.Join(goneDir, "gone.log")
	indexFileName = filepath.Join(goneDir, "index.html")

	flag.StringVar(&display, "display", ":0", "X11 display")
	flag.StringVar(&listen, "listen", "127.0.0.1:8001", "web reporter")
	flag.IntVar(&timeout, "timeout", 20, "idle time in seconds")
	flag.IntVar(&expire, "expire", 8, "expire time in hours")
	flag.Parse()
}

type Tracks map[Window]Track

type Track struct {
	Seen  time.Time
	Spent time.Duration
	Idle  time.Duration
}

func (t Track) String() string {
	return fmt.Sprintf("%s %s",
		t.Seen.Format("2006/01/02 15:04:05"), t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (t Tracks) Snooze(idle time.Duration) {
	if !zzz {
		logger.Println("away from keyboard, idle for", idle)
		if c, ok := t[current]; ok {
			c.Idle += idle
			t[current] = c
		}
		zzz = true
	}
}

func (t Tracks) Wakeup() {
	if zzz {
		logger.Println("back to keyboard")
		if c, ok := t[current]; ok {
			c.Seen = time.Now()
			t[current] = c
		}
		zzz = false
	}
}

func (t Tracks) Update(w Window) {
	if !zzz {
		if c, ok := t[current]; ok {
			c.Spent += time.Since(c.Seen)
			t[current] = c
		}
	}

	if _, ok := t[w]; !ok {
		t[w] = Track{}
	}

	s := t[w]
	s.Seen = time.Now()
	t[w] = s

	current = w
}

func (t Tracks) Remove(d time.Duration) {
	for k, v := range t {
		if time.Since(v.Seen) > d {
			logger.Println(v, k)
			delete(t, k)
		}
	}
}

func Load(fname string) Tracks {
	t := make(Tracks)
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return t
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	err = dec.Decode(&t)
	if err != nil {
		log.Println(err)
	}
	return t
}

func (t Tracks) Store(fname string) {
	tmp := fname + ".tmp"
	dump, err := os.Create(tmp)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	err = enc.Encode(t)
	if err != nil {
		log.Println(err)
		os.Remove(tmp)
		return
	}
	os.Rename(tmp, fname)
}

func (t Tracks) Cleanup() {
	for {
		tracks.Remove(time.Duration(expire) * time.Hour)
		tracks.Store(dumpFileName)
		time.Sleep(time.Minute)
	}
}

func main() {
	X := Connect()
	defer X.Close()

	logfile, err := os.OpenFile(logFileName,
		os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer logfile.Close()
	logger = log.New(logfile, "", log.LstdFlags)

	tracks = Load(dumpFileName)

	go X.Collect(tracks, time.Duration(timeout)*time.Second)
	go tracks.Cleanup()

	webReporter(listen)
}
<commit_msg>fix overcounting: simple set idle time<commit_after>// Gone Time Tracker -or- Where has my time gone?
package main

import (
	"encoding/gob"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/mewkiz/pkg/goutil"
)

var (
	goneDir       string
	dumpFileName  string
	logFileName   string
	indexFileName string
	tracks        Tracks
	zzz           bool
	logger        *log.Logger
	current       Window
	display       string
	listen        string
	timeout       int
	expire        int
)

func init() {
	var err error
	goneDir, err = goutil.SrcDir("github.com/dim13/gone")
	if err != nil {
		log.Fatal("init: ", err)
	}
	dumpFileName = filepath.Join(goneDir, "gone.gob")
	logFileName = filepath.Join(goneDir, "gone.log")
	indexFileName = filepath.Join(goneDir, "index.html")

	flag.StringVar(&display, "display", ":0", "X11 display")
	flag.StringVar(&listen, "listen", "127.0.0.1:8001", "web reporter")
	flag.IntVar(&timeout, "timeout", 20, "idle time in seconds")
	flag.IntVar(&expire, "expire", 8, "expire time in hours")
	flag.Parse()
}

type Tracks map[Window]Track

type Track struct {
	Seen  time.Time
	Spent time.Duration
	Idle  time.Duration
}

func (t Track) String() string {
	return fmt.Sprintf("%s %s",
		t.Seen.Format("2006/01/02 15:04:05"), t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (t Tracks) Snooze(idle time.Duration) {
	if !zzz {
		logger.Println("away from keyboard, idle for", idle)
		if c, ok := t[current]; ok {
			c.Idle = idle
			t[current] = c
		}
		zzz = true
	}
}

func (t Tracks) Wakeup() {
	if zzz {
		logger.Println("back to keyboard")
		if c, ok := t[current]; ok {
			c.Seen = time.Now()
			t[current] = c
		}
		zzz = false
	}
}

func (t Tracks) Update(w Window) {
	if !zzz {
		if c, ok := t[current]; ok {
			c.Spent += time.Since(c.Seen)
			t[current] = c
		}
	}

	if _, ok := t[w]; !ok {
		t[w] = Track{}
	}

	s := t[w]
	s.Seen = time.Now()
	t[w] = s

	current = w
}

func (t Tracks) Remove(d time.Duration) {
	for k, v := range t {
		if time.Since(v.Seen) > d {
			logger.Println(v, k)
			delete(t, k)
		}
	}
}

func Load(fname string) Tracks {
	t := make(Tracks)
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return t
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	err = dec.Decode(&t)
	if err != nil {
		log.Println(err)
	}
	return t
}

func (t Tracks) Store(fname string) {
	tmp := fname + ".tmp"
	dump, err := os.Create(tmp)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	err = enc.Encode(t)
	if err != nil {
		log.Println(err)
		os.Remove(tmp)
		return
	}
	os.Rename(tmp, fname)
}

func (t Tracks) Cleanup() {
	for {
		tracks.Remove(time.Duration(expire) * time.Hour)
		tracks.Store(dumpFileName)
		time.Sleep(time.Minute)
	}
}

func main() {
	X := Connect()
	defer X.Close()

	logfile, err := os.OpenFile(logFileName,
		os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer logfile.Close()
	logger = log.New(logfile, "", log.LstdFlags)

	tracks = Load(dumpFileName)

	go X.Collect(tracks, time.Duration(timeout)*time.Second)
	go tracks.Cleanup()

	webReporter(listen)
}
<|endoftext|>


<commit_before>// The algorithm implemented here is based on "An O(NP) Sequence Comparison Algorithm"
// by described by Sun Wu, Udi Manber and Gene Myers

package gonp

import (
	"container/list"
	"fmt"
	"math"
	"unicode/utf8"
)

type SesType int

const (
	Delete SesType = iota
	Common
	Add
)

type Point struct {
	x, y, k int
}

type SesElem struct {
	c rune
	t SesType
}

type Diff struct {
	A    []rune
	B    []rune
	m, n int
	ed   int
	ctl  *Ctl
	lcs  *list.List
	ses  *list.List
}

type Ctl struct {
	reverse  bool
	path     []int
	onlyEd   bool
	pathposi map[int]Point
}

func max(x, y int) int {
	return int(math.Max(float64(x), float64(y)))
}

func New(a string, b string) *Diff {
	m, n := utf8.RuneCountInString(a), utf8.RuneCountInString(b)
	diff := new(Diff)
	ctl := new(Ctl)
	if m >= n {
		diff.A, diff.B = []rune(b), []rune(a)
		diff.m, diff.n = n, m
		ctl.reverse = true
	} else {
		diff.A, diff.B = []rune(a), []rune(b)
		diff.m, diff.n = m, n
		ctl.reverse = false
	}
	ctl.onlyEd = false
	diff.ctl = ctl
	return diff
}

func (diff *Diff) OnlyEd() {
	diff.ctl.onlyEd = true
}

func (diff *Diff) Editdistance() int {
	return diff.ed
}

func (diff *Diff) Lcs() string {
	var b = make([]rune, diff.lcs.Len())
	for i, e := 0, diff.lcs.Front(); e != nil; i, e = i+1, e.Next() {
		b[i] = e.Value.(rune)
	}
	return string(b)
}

func (diff *Diff) Ses() []SesElem {
	seq := make([]SesElem, diff.ses.Len())
	for i, e := 0, diff.ses.Front(); e != nil; i, e = i+1, e.Next() {
		seq[i].c = e.Value.(SesElem).c
		seq[i].t = e.Value.(SesElem).t
	}
	return seq
}

func (diff *Diff) PrintSes() {
	for _, e := 0, diff.ses.Front(); e != nil; e = e.Next() {
		ee := e.Value.(SesElem)
		switch ee.t {
		case Delete:
			fmt.Println("- " + string(ee.c))
		case Add:
			fmt.Println("+ " + string(ee.c))
		case Common:
			fmt.Println("  " + string(ee.c))
		}
	}
}

func (diff *Diff) Compose() {
	offset := diff.m + 1
	delta := diff.n - diff.m
	size := diff.m + diff.n + 3
	fp := make([]int, size)
	diff.ctl.path = make([]int, size)
	diff.ctl.pathposi = make(map[int]Point)
	diff.lcs = list.New()
	diff.ses = list.New()
	ctl := diff.ctl

	for i := range fp {
		fp[i] = -1
		ctl.path[i] = -1
	}

	for p := 0; ; p++ {

		for k := -p; k <= delta-1; k++ {
			fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset], offset, diff.ctl)
		}

		for k := delta + p; k >= delta+1; k-- {
			fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset], offset, diff.ctl)
		}

		fp[delta+offset] = diff.snake(delta, fp[delta-1+offset]+1, fp[delta+1+offset], offset, diff.ctl)

		if fp[delta+offset] >= diff.n {
			diff.ed = delta + 2*p
			break
		}
	}

	if ctl.onlyEd {
		return
	}

	r := ctl.path[delta+offset]
	epc := make(map[int]Point)
	for r != -1 {
		epc[len(epc)] = Point{x: ctl.pathposi[r].x, y: ctl.pathposi[r].y, k: -1}
		r = ctl.pathposi[r].k
	}
	diff.recordSeq(epc)
}

func (diff *Diff) recordSeq(epc map[int]Point) {
	x_idx, y_idx := 1, 1
	px_idx, py_idx := 0, 0
	ctl := diff.ctl
	for i := len(epc) - 1; i >= 0; i-- {
		for (px_idx < epc[i].x) || (py_idx < epc[i].y) {
			var t SesType
			if (epc[i].y - epc[i].x) > (py_idx - px_idx) {
				elem := diff.B[py_idx]
				if ctl.reverse {
					t = Delete
				} else {
					t = Add
				}
				diff.ses.PushBack(SesElem{c: elem, t: t})
				y_idx++
				py_idx++
			} else if epc[i].y-epc[i].x < py_idx-px_idx {
				elem := diff.A[px_idx]
				if ctl.reverse {
					t = Add
				} else {
					t = Delete
				}
				diff.ses.PushBack(SesElem{c: elem, t: t})
				x_idx++
				px_idx++
			} else {
				elem := diff.A[px_idx]
				t = Common
				diff.lcs.PushBack(elem)
				diff.ses.PushBack(SesElem{c: elem, t: t})
				x_idx++
				y_idx++
				px_idx++
				py_idx++
			}
		}
	}
}

func (diff *Diff) snake(k, p, pp, offset int, ctl *Ctl) int {
	r := 0
	if p > pp {
		r = ctl.path[k-1+offset]
	} else {
		r = ctl.path[k+1+offset]
	}

	y := max(p, pp)
	x := y - k

	for x < diff.m && y < diff.n && diff.A[x] == diff.B[y] {
		x++
		y++
	}

	if !ctl.onlyEd {
		ctl.path[k+offset] = len(ctl.pathposi)
		ctl.pathposi[len(ctl.pathposi)] = Point{x: x, y: y, k: r}
	}

	return y
}
<commit_msg>minor changed.<commit_after>// The algorithm implemented here is based on "An O(NP) Sequence Comparison Algorithm"
// by described by Sun Wu, Udi Manber and Gene Myers

package gonp

import (
	"container/list"
	"fmt"
	"math"
	"unicode/utf8"
)

type SesType int

const (
	Delete SesType = iota
	Common
	Add
)

type Point struct {
	x, y, k int
}

type SesElem struct {
	c rune
	t SesType
}

type Diff struct {
	A    []rune
	B    []rune
	m, n int
	ed   int
	ctl  *Ctl
	lcs  *list.List
	ses  *list.List
}

type Ctl struct {
	reverse  bool
	path     []int
	onlyEd   bool
	pathposi map[int]Point
}

func max(x, y int) int {
	return int(math.Max(float64(x), float64(y)))
}

func New(a string, b string) *Diff {
	m, n := utf8.RuneCountInString(a), utf8.RuneCountInString(b)
	diff := new(Diff)
	ctl := new(Ctl)
	if m >= n {
		diff.A, diff.B = []rune(b), []rune(a)
		diff.m, diff.n = n, m
		ctl.reverse = true
	} else {
		diff.A, diff.B = []rune(a), []rune(b)
		diff.m, diff.n = m, n
		ctl.reverse = false
	}
	ctl.onlyEd = false
	diff.ctl = ctl
	return diff
}

func (diff *Diff) OnlyEd() {
	diff.ctl.onlyEd = true
}

func (diff *Diff) Editdistance() int {
	return diff.ed
}

func (diff *Diff) Lcs() string {
	var b = make([]rune, diff.lcs.Len())
	for i, e := 0, diff.lcs.Front(); e != nil; i, e = i+1, e.Next() {
		b[i] = e.Value.(rune)
	}
	return string(b)
}

func (diff *Diff) Ses() []SesElem {
	seq := make([]SesElem, diff.ses.Len())
	for i, e := 0, diff.ses.Front(); e != nil; i, e = i+1, e.Next() {
		seq[i].c = e.Value.(SesElem).c
		seq[i].t = e.Value.(SesElem).t
	}
	return seq
}

func (diff *Diff) PrintSes() {
	for _, e := 0, diff.ses.Front(); e != nil; e = e.Next() {
		ee := e.Value.(SesElem)
		switch ee.t {
		case Delete:
			fmt.Println("- " + string(ee.c))
		case Add:
			fmt.Println("+ " + string(ee.c))
		case Common:
			fmt.Println("  " + string(ee.c))
		}
	}
}

func (diff *Diff) Compose() {
	offset := diff.m + 1
	delta := diff.n - diff.m
	size := diff.m + diff.n + 3
	fp := make([]int, size)
	diff.ctl.path = make([]int, size)
	diff.ctl.pathposi = make(map[int]Point)
	diff.lcs = list.New()
	diff.ses = list.New()

	for i := range fp {
		fp[i] = -1
		diff.ctl.path[i] = -1
	}

	for p := 0; ; p++ {

		for k := -p; k <= delta-1; k++ {
			fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset], offset)
		}

		for k := delta + p; k >= delta+1; k-- {
			fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset], offset)
		}

		fp[delta+offset] = diff.snake(delta, fp[delta-1+offset]+1, fp[delta+1+offset], offset)

		if fp[delta+offset] >= diff.n {
			diff.ed = delta + 2*p
			break
		}
	}

	if diff.ctl.onlyEd {
		return
	}

	r := diff.ctl.path[delta+offset]
	epc := make(map[int]Point)
	for r != -1 {
		epc[len(epc)] = Point{x: diff.ctl.pathposi[r].x, y: diff.ctl.pathposi[r].y, k: -1}
		r = diff.ctl.pathposi[r].k
	}
	diff.recordSeq(epc)
}

func (diff *Diff) recordSeq(epc map[int]Point) {
	x_idx, y_idx := 1, 1
	px_idx, py_idx := 0, 0
	ctl := diff.ctl
	for i := len(epc) - 1; i >= 0; i-- {
		for (px_idx < epc[i].x) || (py_idx < epc[i].y) {
			var t SesType
			if (epc[i].y - epc[i].x) > (py_idx - px_idx) {
				elem := diff.B[py_idx]
				if ctl.reverse {
					t = Delete
				} else {
					t = Add
				}
				diff.ses.PushBack(SesElem{c: elem, t: t})
				y_idx++
				py_idx++
			} else if epc[i].y-epc[i].x < py_idx-px_idx {
				elem := diff.A[px_idx]
				if ctl.reverse {
					t = Add
				} else {
					t = Delete
				}
				diff.ses.PushBack(SesElem{c: elem, t: t})
				x_idx++
				px_idx++
			} else {
				elem := diff.A[px_idx]
				t = Common
				diff.lcs.PushBack(elem)
				diff.ses.PushBack(SesElem{c: elem, t: t})
				x_idx++
				y_idx++
				px_idx++
				py_idx++
			}
		}
	}
}

func (diff *Diff) snake(k, p, pp, offset int) int {
	r := 0
	if p > pp {
		r = diff.ctl.path[k-1+offset]
	} else {
		r = diff.ctl.path[k+1+offset]
	}

	y := max(p, pp)
	x := y - k

	for x < diff.m && y < diff.n && diff.A[x] == diff.B[y] {
		x++
		y++
	}

	if !diff.ctl.onlyEd {
		diff.ctl.path[k+offset] = len(diff.ctl.pathposi)
		diff.ctl.pathposi[len(diff.ctl.pathposi)] = Point{x: x, y: y, k: r}
	}

	return y
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 Matt Jibson <matt.jibson@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package goon

import (
	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"bytes"
	"encoding/gob"
	"errors"
	"net/http"
	"reflect"
)

// Goon holds the app engine context and request memory cache.
type Goon struct {
	context appengine.Context
	cache   map[string]*Entity
}

func memkey(k *datastore.Key) string {
	return k.String()
}

func NewGoon(r *http.Request) *Goon {
	return &Goon{
		context: appengine.NewContext(r),
		cache:   make(map[string]*Entity),
	}
}

// Put stores Entity e.
// If e has an incomplete key, it is updated.
func (g *Goon) Put(e *Entity) error {
	return g.PutMulti([]*Entity{e})
}

// PutMulti stores a sequence of Entities.
// Any entity with an incomplete key will be updated.
func (g *Goon) PutMulti(es []*Entity) error {
	var err error

	var memkeys []string
	keys := make([]*datastore.Key, len(es))
	src := make([]interface{}, len(es))

	for i, e := range es {
		if !e.Key.Incomplete() {
			memkeys = append(memkeys, e.memkey())
		}

		keys[i] = e.Key
		src[i] = e.Src
	}

	err = memcache.DeleteMulti(g.context, memkeys)

	if err != nil {
		return err
	}

	keys, err = datastore.PutMulti(g.context, keys, src)

	if err != nil {
		return err
	}

	for i := range es {
		es[i].setKey(keys[i])
	}

	g.putMemoryMulti(es)

	return nil
}

func (g *Goon) putMemoryMulti(es []*Entity) {
	for _, e := range es {
		g.putMemory(e)
	}
}

func (g *Goon) putMemory(e *Entity) {
	g.cache[e.memkey()] = e
}

func (g *Goon) putMemcache(es []*Entity) error {
	items := make([]*memcache.Item, len(es))

	for i, e := range es {
		gob, err := e.gob()
		if err != nil {
			return err
		}

		items[i] = &memcache.Item{
			Key:   e.memkey(),
			Value: gob,
		}
	}

	err := memcache.SetMulti(g.context, items)

	if err != nil {
		return err
	}

	g.putMemoryMulti(es)
	return nil
}

// structKind returns the reflect.Kind name of src if it is a struct, else nil.
func structKind(src interface{}) (string, error) {
	v := reflect.ValueOf(src)
	v = reflect.Indirect(v)
	t := v.Type()
	k := t.Kind()

	if k == reflect.Struct {
		return t.Name(), nil
	}
	return "", errors.New("goon: src has invalid type")
}

// Get fetches an entity of kind src by.
// Refer to appengine/datastore.NewKey regarding key specification.
func (g *Goon) Get(src interface{}, stringID string, intID int64, parent *datastore.Key) (*Entity, error) {
	k, err := structKind(src)
	if err != nil {
		return nil, err
	}
	key := datastore.NewKey(g.context, k, stringID, intID, parent)
	return g.KeyGet(src, key)
}

// KeyGet fetches an entity of kind src by key.
func (g *Goon) KeyGet(src interface{}, key *datastore.Key) (*Entity, error) {
	e := NewEntity(key, src)
	es := []*Entity{e}
	err := g.GetMulti(es)
	if err != nil {
		return nil, err
	}
	return es[0], nil
}

// Get fetches a sequency of Entities, whose keys must already be valid.
// Entities with no correspending key have their NotFound field set to true.
func (g *Goon) GetMulti(es []*Entity) error {
	var memkeys []string
	var mixs []int

	for i, e := range es {
		m := e.memkey()
		if s, present := g.cache[m]; present {
			es[i] = s
		} else {
			memkeys = append(memkeys, m)
			mixs = append(mixs, i)
		}
	}

	memvalues, err := memcache.GetMulti(g.context, memkeys)
	if err != nil {
		return err
	}

	var dskeys []*datastore.Key
	var dst []interface{}
	var dixs []int

	for i, m := range memkeys {
		e := es[mixs[i]]
		if s, present := memvalues[m]; present {
			err := fromGob(e, s.Value)
			if err != nil {
				return err
			}

			g.putMemory(e)
		} else {
			dskeys = append(dskeys, e.Key)
			dst = append(dst, e.Src)
			dixs = append(dixs, mixs[i])
		}
	}

	var merr appengine.MultiError
	err = datastore.GetMulti(g.context, dskeys, dst)
	if err != nil {
		merr = err.(appengine.MultiError)
	}
	var mes []*Entity

	for i, idx := range dixs {
		e := es[idx]
		if merr != nil && merr[i] != nil {
			e.NotFound = true
		}
		mes = append(mes, e)
	}

	err = g.putMemcache(mes)
	if err != nil {
		return err
	}

	multiErr, any := make(appengine.MultiError, len(es)), false
	for i, e := range es {
		if e.NotFound {
			multiErr[i] = datastore.ErrNoSuchEntity
			any = true
		}
	}

	if any {
		return multiErr
	}

	return nil
}

func fromGob(e *Entity, b []byte) error {
	var buf bytes.Buffer
	_, _ = buf.Write(b)
	gob.Register(e.Src)
	dec := gob.NewDecoder(&buf)
	return dec.Decode(e)
}
<commit_msg>Ignore memcache delete cache misses<commit_after>/*
 * Copyright (c) 2012 Matt Jibson <matt.jibson@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package goon

import (
	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"bytes"
	"encoding/gob"
	"errors"
	"net/http"
	"reflect"
)

// Goon holds the app engine context and request memory cache.
type Goon struct {
	context appengine.Context
	cache   map[string]*Entity
}

func memkey(k *datastore.Key) string {
	return k.String()
}

func NewGoon(r *http.Request) *Goon {
	return &Goon{
		context: appengine.NewContext(r),
		cache:   make(map[string]*Entity),
	}
}

// Put stores Entity e.
// If e has an incomplete key, it is updated.
func (g *Goon) Put(e *Entity) error {
	return g.PutMulti([]*Entity{e})
}

// PutMulti stores a sequence of Entities.
// Any entity with an incomplete key will be updated.
func (g *Goon) PutMulti(es []*Entity) error {
	var err error

	var memkeys []string
	keys := make([]*datastore.Key, len(es))
	src := make([]interface{}, len(es))

	for i, e := range es {
		if !e.Key.Incomplete() {
			memkeys = append(memkeys, e.memkey())
		}

		keys[i] = e.Key
		src[i] = e.Src
	}

	memcache.DeleteMulti(g.context, memkeys)

	keys, err = datastore.PutMulti(g.context, keys, src)

	if err != nil {
		return err
	}

	for i := range es {
		es[i].setKey(keys[i])
	}

	g.putMemoryMulti(es)

	return nil
}

func (g *Goon) putMemoryMulti(es []*Entity) {
	for _, e := range es {
		g.putMemory(e)
	}
}

func (g *Goon) putMemory(e *Entity) {
	g.cache[e.memkey()] = e
}

func (g *Goon) putMemcache(es []*Entity) error {
	items := make([]*memcache.Item, len(es))

	for i, e := range es {
		gob, err := e.gob()
		if err != nil {
			return err
		}

		items[i] = &memcache.Item{
			Key:   e.memkey(),
			Value: gob,
		}
	}

	err := memcache.SetMulti(g.context, items)

	if err != nil {
		return err
	}

	g.putMemoryMulti(es)
	return nil
}

// structKind returns the reflect.Kind name of src if it is a struct, else nil.
func structKind(src interface{}) (string, error) {
	v := reflect.ValueOf(src)
	v = reflect.Indirect(v)
	t := v.Type()
	k := t.Kind()

	if k == reflect.Struct {
		return t.Name(), nil
	}
	return "", errors.New("goon: src has invalid type")
}

// Get fetches an entity of kind src by.
// Refer to appengine/datastore.NewKey regarding key specification.
func (g *Goon) Get(src interface{}, stringID string, intID int64, parent *datastore.Key) (*Entity, error) {
	k, err := structKind(src)
	if err != nil {
		return nil, err
	}
	key := datastore.NewKey(g.context, k, stringID, intID, parent)
	return g.KeyGet(src, key)
}

// KeyGet fetches an entity of kind src by key.
func (g *Goon) KeyGet(src interface{}, key *datastore.Key) (*Entity, error) {
	e := NewEntity(key, src)
	es := []*Entity{e}
	err := g.GetMulti(es)
	if err != nil {
		return nil, err
	}
	return es[0], nil
}

// Get fetches a sequency of Entities, whose keys must already be valid.
// Entities with no correspending key have their NotFound field set to true.
func (g *Goon) GetMulti(es []*Entity) error {
	var memkeys []string
	var mixs []int

	for i, e := range es {
		m := e.memkey()
		if s, present := g.cache[m]; present {
			es[i] = s
		} else {
			memkeys = append(memkeys, m)
			mixs = append(mixs, i)
		}
	}

	memvalues, err := memcache.GetMulti(g.context, memkeys)
	if err != nil {
		return err
	}

	var dskeys []*datastore.Key
	var dst []interface{}
	var dixs []int

	for i, m := range memkeys {
		e := es[mixs[i]]
		if s, present := memvalues[m]; present {
			err := fromGob(e, s.Value)
			if err != nil {
				return err
			}

			g.putMemory(e)
		} else {
			dskeys = append(dskeys, e.Key)
			dst = append(dst, e.Src)
			dixs = append(dixs, mixs[i])
		}
	}

	var merr appengine.MultiError
	err = datastore.GetMulti(g.context, dskeys, dst)
	if err != nil {
		merr = err.(appengine.MultiError)
	}
	var mes []*Entity

	for i, idx := range dixs {
		e := es[idx]
		if merr != nil && merr[i] != nil {
			e.NotFound = true
		}
		mes = append(mes, e)
	}

	err = g.putMemcache(mes)
	if err != nil {
		return err
	}

	multiErr, any := make(appengine.MultiError, len(es)), false
	for i, e := range es {
		if e.NotFound {
			multiErr[i] = datastore.ErrNoSuchEntity
			any = true
		}
	}

	if any {
		return multiErr
	}

	return nil
}

func fromGob(e *Entity, b []byte) error {
	var buf bytes.Buffer
	_, _ = buf.Write(b)
	gob.Register(e.Src)
	dec := gob.NewDecoder(&buf)
	return dec.Decode(e)
}
<|endoftext|>


<commit_before>package gopisysfs

import (
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"time"
)

type GPIOFlag struct {
	flag bool
	err  error
}

type GPIOMode int

const (
	GPIOInput GPIOMode = iota
	GPIOOutput
	GPIOOutputLow
	GPIOOutputHigh

	// from https://www.kernel.org/doc/Documentation/gpio/sysfs.txt
	direction_in     = "in"
	direction_out    = "out"
	direction_outlow = "low"
	direction_outhi  = "high"

	// the longest time to wait for an operation to complete
	timelimit = time.Second

	low  = "0"
	high = "1"
)

type GPIOPort interface {
	State() string
	IsEnabled() bool
	Enable() error
	Reset() error
	SetMode(GPIOMode) error
	IsOutput() (bool, error)
	SetValue(bool) error
	Value() (bool, error)
	Values() (<-chan bool, error)
}

type gport struct {
	mu        sync.Mutex
	host      *pi
	port      int
	sport     string
	folder    string
	value     string
	direction string
	edge      string
	export    string
	unexport  string
}

func newGPIO(host *pi, port int) *gport {

	sport := fmt.Sprintf("%d", port)
	gpio := host.gpiodir
	folder := filepath.Join(gpio, fmt.Sprintf("gpio%s", sport))
	export := filepath.Join(gpio, "export")
	unexport := filepath.Join(gpio, "unexport")

	return &gport{
		mu:        sync.Mutex{},
		host:      host,
		port:      port,
		sport:     sport,
		folder:    folder,
		value:     filepath.Join(folder, "value"),
		direction: filepath.Join(folder, "direction"),
		edge:      filepath.Join(folder, "edge"),
		export:    export,
		unexport:  unexport,
	}
}

func (p *gport) String() string {
	return p.folder
}

func (p *gport) IsEnabled() bool {

	defer p.unlock(p.lock())

	return checkFile(p.folder)
}

func (p *gport) Enable() error {

	defer p.unlock(p.lock())

	if checkFile(p.folder) {
		return nil
	}

	info("GPIO Enabling %v\n", p)

	if err := writeFile(p.export, p.sport); err != nil {
		return err
	}

	start := time.Now()
	// wait for folder to arrive....
	ch, err := awaitFileCreate(p.folder, timelimit)
	if err != nil {
		return err
	}
	if err := <-ch; err != nil {
		return err
	}
	// delay a bit.
	<-time.After(pollInterval * 2)
	// and for all control files to exist and be readable
	// there's an issue with timeouts perhaps.... but that's OK.
	for _, fname := range []string{p.folder, p.direction, p.value, p.edge} {
		for {
			remaining := timelimit - time.Since(start)
			info("GPIO Enabling %v checking file %v state (timeout limit %v)\n", p, fname, remaining)
			if checkFile(fname) {
				// check writable.... invalid data will be ignored, but permissions won't
				if err := writeFile(fname, " "); err == nil || !os.IsPermission(err) {
					info("GPIO Enabling %v checking file %v state\n", p, fname)
					break
				}
			}
			select {
			case <-time.After(remaining):
				return fmt.Errorf("Timed out enabling GPIO %v - %v not yet writable", p.sport, fname)
			case <-time.After(pollInterval):
				// next cycle
			}
		}

	}

	info("GPIO Enabled %v\n", p)

	return nil
}

func (p *gport) Reset() error {

	defer p.unlock(p.lock())

	if !checkFile(p.folder) {
		// already reset
		return nil
	}
	info("GPIO Resetting  %v\n", p)
	if err := writeFile(p.unexport, p.sport); err != nil {
		return err
	}
	ch, err := awaitFileRemove(p.folder, timelimit)
	if err != nil {
		return err
	}

	if err := <-ch; err != nil {
		return err
	}

	// wait for the file to be removed, and then return
	info("GPIO Reset  %v\n", p)
	return nil

}

// GPIOResetAsync will reset the specified port and only return when it is complete
// Configure will
func (p *gport) SetMode(mode GPIOMode) error {

	defer p.unlock(p.lock())

	err := p.checkEnabled()
	if err != nil {
		return err
	}

	switch mode {
	case GPIOInput:
		return p.writeDirection(direction_in)
	case GPIOOutput:
		return p.writeDirection(direction_out)
	case GPIOOutputHigh:
		return p.writeDirection(direction_outhi)
	case GPIOOutputLow:
		return p.writeDirection(direction_outlow)
	}
	return fmt.Errorf("GPIOMode %v does not exist")
}

func (p *gport) IsOutput() (bool, error) {

	defer p.unlock(p.lock())

	err := p.checkEnabled()
	if err != nil {
		return false, err
	}
	d, err := p.readDirection()
	if err != nil {
		return false, err
	}
	return d != "in", nil
}

func (p *gport) State() string {

	defer p.unlock(p.lock())

	base := fmt.Sprintf("GPIO %v: ", p.sport)
	if !checkFile(p.folder) {
		return base + "Reset"
	}

	dir, err := p.readDirection()
	if err != nil {
		return fmt.Sprintf("%v%v", base, err)
	}
	val, err := p.readValue()
	if err != nil {
		return fmt.Sprintf("%v%v", base, err)
	}

	return fmt.Sprintf("%v %v with value %v", base, dir, val)
}

func (p *gport) Value() (bool, error) {

	defer p.unlock(p.lock())

	err := p.checkEnabled()
	if err != nil {
		return false, err
	}

	d, err := p.readValue()
	if err != nil {
		return false, err
	}

	return d == "1", nil
}

func (p *gport) SetValue(value bool) error {

	defer p.unlock(p.lock())

	err := p.checkEnabled()
	if err != nil {
		return err
	}

	info("GPIO Set Value on %v to %v\n", p, value)

	val := low
	if value {
		val = high
	}

	return p.writeValue(val)

}

func (p *gport) Values() (<-chan bool, error) {
	defer p.unlock(p.lock())
	return nil, nil
}

func (p *gport) writeDirection(direction string) error {
	info("GPIO Setting mode on  %v to %v\n", p, direction)

	return writeFile(p.direction, direction)
}

func (p *gport) readDirection() (string, error) {
	return readFile(p.direction)
}

func (p *gport) writeValue(value string) error {
	return writeFile(p.value, value)
}

func (p *gport) readValue() (string, error) {
	return readFile(p.value)
}

func (p *gport) checkEnabled() error {
	if checkFile(p.folder) {
		return nil
	}
	return fmt.Errorf("GPIO %v is not enabled", p.port)
}

func (p *gport) lock() bool {
	p.mu.Lock()
	return true
}

func (p *gport) unlock(bool) {
	p.mu.Unlock()
}
<commit_msg>Mess with timing to get initialization working<commit_after>package gopisysfs

import (
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"time"
)

type GPIOFlag struct {
	flag bool
	err  error
}

type GPIOMode int

const (
	GPIOInput GPIOMode = iota
	GPIOOutput
	GPIOOutputLow
	GPIOOutputHigh

	// from https://www.kernel.org/doc/Documentation/gpio/sysfs.txt
	direction_in     = "in"
	direction_out    = "out"
	direction_outlow = "low"
	direction_outhi  = "high"

	// the longest time to wait for an operation to complete
	timelimit = time.Second * 2

	low  = "0"
	high = "1"
)

type GPIOPort interface {
	State() string
	IsEnabled() bool
	Enable() error
	Reset() error
	SetMode(GPIOMode) error
	IsOutput() (bool, error)
	SetValue(bool) error
	Value() (bool, error)
	Values() (<-chan bool, error)
}

type gport struct {
	mu        sync.Mutex
	host      *pi
	port      int
	sport     string
	folder    string
	value     string
	direction string
	edge      string
	export    string
	unexport  string
}

func newGPIO(host *pi, port int) *gport {

	sport := fmt.Sprintf("%d", port)
	gpio := host.gpiodir
	folder := filepath.Join(gpio, fmt.Sprintf("gpio%s", sport))
	export := filepath.Join(gpio, "export")
	unexport := filepath.Join(gpio, "unexport")

	return &gport{
		mu:        sync.Mutex{},
		host:      host,
		port:      port,
		sport:     sport,
		folder:    folder,
		value:     filepath.Join(folder, "value"),
		direction: filepath.Join(folder, "direction"),
		edge:      filepath.Join(folder, "edge"),
		export:    export,
		unexport:  unexport,
	}
}

func pause() {
	<-time.After(pollInterval)
}

func (p *gport) String() string {
	return p.folder
}

func (p *gport) IsEnabled() bool {

	defer p.unlock(p.lock())

	return checkFile(p.folder)
}

func (p *gport) Enable() error {

	defer p.unlock(p.lock())

	if checkFile(p.folder) {
		return nil
	}

	info("GPIO Enabling %v\n", p)

	if err := writeFile(p.export, p.sport); err != nil {
		return err
	}

	start := time.Now()

	pause()

	// wait for folder to arrive....
	ch, err := awaitFileCreate(p.folder, timelimit)
	if err != nil {
		return err
	}
	if err := <-ch; err != nil {
		return err
	}
	// delay a bit.
	pause()
	// and for all control files to exist and be readable
	// there's an issue with timeouts perhaps.... but that's OK.
	for _, fname := range []string{p.folder, p.direction, p.value, p.edge} {
		for {
			remaining := timelimit - time.Since(start)
			info("GPIO Enabling %v checking file %v state (timeout limit %v)\n", p, fname, remaining)
			if checkFile(fname) {
				// check writable.... invalid data will be ignored, but permissions won't
				if err := writeFile(fname, " "); err == nil || !os.IsPermission(err) {
					info("GPIO Enabling %v file %v state OK\n", p, fname)
					break
				} else {
					info("GPIO Enabling %v file %v state %v\n", p, fname, err)
				}
			}
			select {
			case <-time.After(remaining):
				return fmt.Errorf("Timed out enabling GPIO %v - %v not yet writable", p.sport, fname)
			case <-time.After(pollInterval):
				// next cycle
			}
		}

	}

	info("GPIO Enabled %v\n", p)

	return nil
}

func (p *gport) Reset() error {

	defer p.unlock(p.lock())

	if !checkFile(p.folder) {
		// already reset
		return nil
	}
	info("GPIO Resetting  %v\n", p)
	if err := writeFile(p.unexport, p.sport); err != nil {
		return err
	}
	pause()
	ch, err := awaitFileRemove(p.folder, timelimit)
	if err != nil {
		return err
	}

	if err := <-ch; err != nil {
		return err
	}

	// wait for the file to be removed, and then return
	info("GPIO Reset  %v\n", p)
	return nil

}

// GPIOResetAsync will reset the specified port and only return when it is complete
// Configure will
func (p *gport) SetMode(mode GPIOMode) error {

	defer p.unlock(p.lock())

	err := p.checkEnabled()
	if err != nil {
		return err
	}

	switch mode {
	case GPIOInput:
		return p.writeDirection(direction_in)
	case GPIOOutput:
		return p.writeDirection(direction_out)
	case GPIOOutputHigh:
		return p.writeDirection(direction_outhi)
	case GPIOOutputLow:
		return p.writeDirection(direction_outlow)
	}
	return fmt.Errorf("GPIOMode %v does not exist")
}

func (p *gport) IsOutput() (bool, error) {

	defer p.unlock(p.lock())

	err := p.checkEnabled()
	if err != nil {
		return false, err
	}
	d, err := p.readDirection()
	if err != nil {
		return false, err
	}
	return d != "in", nil
}

func (p *gport) State() string {

	defer p.unlock(p.lock())

	base := fmt.Sprintf("GPIO %v: ", p.sport)
	if !checkFile(p.folder) {
		return base + "Reset"
	}

	dir, err := p.readDirection()
	if err != nil {
		return fmt.Sprintf("%v%v", base, err)
	}
	val, err := p.readValue()
	if err != nil {
		return fmt.Sprintf("%v%v", base, err)
	}

	return fmt.Sprintf("%v %v with value %v", base, dir, val)
}

func (p *gport) Value() (bool, error) {

	defer p.unlock(p.lock())

	err := p.checkEnabled()
	if err != nil {
		return false, err
	}

	d, err := p.readValue()
	if err != nil {
		return false, err
	}

	return d == "1", nil
}

func (p *gport) SetValue(value bool) error {

	defer p.unlock(p.lock())

	err := p.checkEnabled()
	if err != nil {
		return err
	}

	info("GPIO Set Value on %v to %v\n", p, value)

	val := low
	if value {
		val = high
	}

	return p.writeValue(val)

}

func (p *gport) Values() (<-chan bool, error) {
	defer p.unlock(p.lock())
	return nil, nil
}

func (p *gport) writeDirection(direction string) error {
	info("GPIO Setting mode on  %v to %v\n", p, direction)

	return writeFile(p.direction, direction)
}

func (p *gport) readDirection() (string, error) {
	return readFile(p.direction)
}

func (p *gport) writeValue(value string) error {
	return writeFile(p.value, value)
}

func (p *gport) readValue() (string, error) {
	return readFile(p.value)
}

func (p *gport) checkEnabled() error {
	if checkFile(p.folder) {
		return nil
	}
	return fmt.Errorf("GPIO %v is not enabled", p.port)
}

func (p *gport) lock() bool {
	p.mu.Lock()
	return true
}

func (p *gport) unlock(bool) {
	p.mu.Unlock()
}
<|endoftext|>