<commit_before>// Package kite is a library for creating small micro-services.
// Two main types implemented by this package are
// Kite for creating a micro-service server called "Kite" and
// RemoteKite for communicating with another kites.
package kite

import (
	"crypto/tls"
	"fmt"
	"kite/dnode/rpc"
	"kite/kitekey"
	"kite/protocol"
	"kite/systeminfo"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"syscall"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/nu7hatch/gouuid"
	"github.com/op/go-logging"
)

func init() {
	// Debugging helper: Prints stacktrace on SIGUSR1.
	c := make(chan os.Signal)
	signal.Notify(c, syscall.SIGUSR1)
	go func() {
		for {
			s := <-c
			fmt.Println("Got signal:", s)
			buf := make([]byte, 1<<16)
			runtime.Stack(buf, true)
			fmt.Println(string(buf))
			fmt.Print("Number of goroutines:", runtime.NumGoroutine())
			m := new(runtime.MemStats)
			runtime.GC()
			runtime.ReadMemStats(m)
			fmt.Printf(", Memory allocated: %+v\n", m.Alloc)
		}
	}()
}

// Kite defines a single process that enables distributed service messaging
// amongst the peers it is connected. A Kite process acts as a Client and as a
// Server. That means it can receive request, process them, but it also can
// make request to other kites.
//
// Do not use this struct directly. Use kite.New function, add your handlers
// with HandleFunc mehtod, then call Start or Run method.
type Kite struct {
	protocol.Kite

	// Is this Kite Public or Private? Default is Private.
	Visibility protocol.Visibility

	// Points to the Kontrol instance if enabled
	Kontrol *Kontrol

	// Parsed JWT token from ~/.kite/kite.key
	kiteKey *jwt.Token

	// Wheter we want to connect to Kontrol on startup, true by default.
	KontrolEnabled bool

	// Wheter we want to register our Kite to Kontrol, true by default.
	RegisterToKontrol bool

	// Use Koding.com's reverse-proxy server for incoming connections.
	// Instead of the Kite's address, address of the Proxy Kite will be
	// registered to Kontrol.
	proxyEnabled bool

	// will be used in query to kontrol for finding proxy kite
	proxyUsername string

	// method map for exported methods
	handlers map[string]HandlerFunc

	// Should handlers run concurrently? Default is true.
	concurrent bool

	// Dnode rpc server
	server *rpc.Server

	listener net.Listener

	// Handlers to call when a Kite opens a connection to this Kite.
	onConnectHandlers []func(*RemoteKite)

	// Handlers to call when a client has disconnected.
	onDisconnectHandlers []func(*RemoteKite)

	// Contains different functions for authenticating user from request.
	// Keys are the authentication types (options.authentication.type).
	Authenticators map[string]func(*Request) error

	// Should kite disable authenticators for incoming requests? Disabled by default
	disableAuthenticate bool

	// Kontrol keys to trust. Kontrol will issue access tokens for kites
	// that are signed with the private counterpart of these keys.
	// Key data must be PEM encoded.
	trustedKontrolKeys map[string]string

	// Trusted root certificates for TLS connections (wss://).
	// Certificate data must be PEM encoded.
	tlsCertificates []string

	// Used to signal if the kite is ready to start and make calls to
	// other kites.
	ready chan bool

	// Prints logging messages to stderr and syslog.
	Log *logging.Logger
}

// New creates, initialize and then returns a new Kite instance. It accepts
// a single options argument that is a config struct that needs to be filled
// with several informations like Name, Port, IP and so on.
func New(options *Options) *Kite {
	var err error

	if options == nil {
		options, err = ReadKiteOptions("manifest.json")
		if err != nil {
			log.Fatal("error: could not read config file", err)
		}
	}

	options.validate() // exits if validating fails

	hostname, err := os.Hostname()
	if err != nil {
		panic(err)
	}

	kiteID, err := uuid.NewV4()
	if err != nil {
		panic(err)
	}

	k := &Kite{
		Kite: protocol.Kite{
			Name:        options.Kitename,
			ID:          kiteID.String(),
			Version:     options.Version,
			Hostname:    hostname,
			Environment: options.Environment,
			Region:      options.Region,
			Visibility:  options.Visibility,
			URL: protocol.KiteURL{
				&url.URL{
					Scheme: "ws",
					Host:   net.JoinHostPort(options.PublicIP, options.Port),
					Path:   options.Path,
				},
			},
		},
		server:              rpc.NewServer(),
		concurrent:          true,
		KontrolEnabled:      true,
		RegisterToKontrol:   true,
		trustedKontrolKeys:  make(map[string]string),
		Authenticators:      make(map[string]func(*Request) error),
		disableAuthenticate: options.DisableAuthentication,
		handlers:            make(map[string]HandlerFunc),
		ready:               make(chan bool),
		end:                 make(chan bool, 1),
	}

	k.Log = newLogger(k.Name)

	k.kiteKey, err = kitekey.Parse()
	if err != nil {
		k.Log.Warning("Cannot read kite key. You must register by running \"kite register\" command.")
	} else if !k.kiteKey.Valid {
		panic(err)
	} else {
		k.Username = k.kiteKey.Claims["sub"].(string)

		if k.KontrolEnabled {
			parsedURL, err := url.Parse(k.kiteKey.Claims["kontrolURL"].(string))
			if err != nil {
				panic(err)
			}

			k.Kontrol = k.NewKontrol(parsedURL)

			k.TrustKontrolKey(k.kiteKey.Claims["iss"].(string), k.kiteKey.Claims["kontrolKey"].(string))
		}
	}

	k.server.SetWrappers(wrapMethodArgs, wrapCallbackArgs, runMethod, runCallback, onError)
	k.server.Properties()["localKite"] = k

	k.server.OnConnect(func(c *rpc.Client) {
		k.Log.Info("New connection from: %s", c.Conn.Request().RemoteAddr)
	})

	// Call registered handlers when a client has disconnected.
	k.server.OnDisconnect(func(c *rpc.Client) {
		if r, ok := c.Properties()["remoteKite"]; ok {
			// Run OnDisconnect handlers.
			k.notifyRemoteKiteDisconnected(r.(*RemoteKite))
		}
	})

	// Every kite should be able to authenticate the user from token.
	k.Authenticators["token"] = k.AuthenticateFromToken
	// A kite accepts requests from Kontrol.
	k.Authenticators["kiteKey"] = k.AuthenticateFromKiteKey

	// Register our internal methods
	k.HandleFunc("systemInfo", func(r *Request) (interface{}, error) { return systeminfo.New() })
	k.HandleFunc("heartbeat", k.handleHeartbeat)
	k.HandleFunc("log", k.handleLog)

	return k
}

// Normally, each incoming request is processed in a new goroutine.
// If you disable concurrency, requests will be processed synchronously.
func (k *Kite) DisableConcurrency() {
	k.server.SetConcurrent(false)
}

// EnableTLS enables "wss://" protocol".
// It uses the same port and disables "ws://".
func (k *Kite) EnableTLS(certFile, keyFile string) {
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		k.Log.Fatal(err.Error())
	}

	k.server.TlsConfig = &tls.Config{
		Certificates: []tls.Certificate{cert},
	}

	k.Kite.URL.Scheme = "wss"
}

// Put this kite behind a reverse-proxy. Useful under firewall or NAT.
func (k *Kite) EnableProxy(username string) {
	k.proxyEnabled = true
	k.proxyUsername = username
}

// Trust a Kontrol key for validating tokens.
func (k *Kite) TrustKontrolKey(issuer, key string) {
	k.trustedKontrolKeys[issuer] = key
}

// Add new trusted root certificate for TLS.
func (k *Kite) AddRootCertificate(cert string) {
	k.tlsCertificates = append(k.tlsCertificates, cert)
}

// Run is a blocking method. It runs the kite server and then accepts requests
// asynchronously.
func (k *Kite) Run() {
	if os.Getenv("KITE_VERSION") != "" {
		fmt.Println(k.Version)
		os.Exit(0)
	}

	if err := k.listenAndServe(); err != nil {
		k.Log.Fatal(err)
	}
}

// Start is like Run(), but does not wait for it to complete. It's nonblocking.
func (k *Kite) Start() {
	go k.Run()
	<-k.ready // wait until we are ready
}

// Close stops the server.
func (k *Kite) Close() {
	k.Log.Notice("Closing server...")
	k.listener.Close()
}

func (k *Kite) handleHeartbeat(r *Request) (interface{}, error) {
	args := r.Args.MustSliceOfLength(2)
	seconds := args[0].MustFloat64()
	ping := args[1].MustFunction()

	go func() {
		for {
			time.Sleep(time.Duration(seconds) * time.Second)
			if ping() != nil {
				return
			}
		}
	}()

	return nil, nil
}

// handleLog prints a log message to stdout.
func (k *Kite) handleLog(r *Request) (interface{}, error) {
	msg := r.Args.One().MustString()
	k.Log.Info(fmt.Sprintf("%s: %s", r.RemoteKite.Name, msg))
	return nil, nil
}

func init() {
	// These logging related stuff needs to be called once because stupid
	// logging library uses global variables and resets the backends every time.
	logging.SetFormatter(logging.MustStringFormatter("%{level:-8s} ▶ %{message}"))
	stderrBackend := logging.NewLogBackend(os.Stderr, "", log.LstdFlags)
	stderrBackend.Color = true
	syslogBackend, _ := logging.NewSyslogBackend("")
	logging.SetBackend(stderrBackend, syslogBackend)
}

// newLogger returns a new logger object for desired name and level.
func newLogger(name string) *logging.Logger {
	logger := logging.MustGetLogger(name)

	var level logging.Level
	switch strings.ToUpper(os.Getenv("KITE_LOG_LEVEL")) {
	case "DEBUG":
		level = logging.DEBUG
	case "INFO":
		level = logging.INFO
	case "NOTICE":
		level = logging.NOTICE
	case "WARNING":
		level = logging.WARNING
	case "ERROR":
		level = logging.ERROR
	case "CRITICAL":
		level = logging.CRITICAL
	default:
		level = logging.INFO
	}

	logging.SetLevel(level, name)
	return logger
}

// listenAndServe starts our rpc server with the given addr.
func (k *Kite) listenAndServe() error {
	var err error

	k.listener, err = net.Listen("tcp4", k.Kite.URL.Host)
	if err != nil {
		return err
	}

	k.Log.Notice("Listening: %s", k.listener.Addr().String())

	// Enable TLS
	if k.server.TlsConfig != nil {
		k.listener = tls.NewListener(k.listener, k.server.TlsConfig)
	}

	// Port is known here if "0" is used as port number
	host, _, _ := net.SplitHostPort(k.Kite.URL.Host)
	_, port, _ := net.SplitHostPort(k.listener.Addr().String())
	k.Kite.URL.Host = net.JoinHostPort(host, port)

	// Kontrol will register to the URLs sent to this channel.
	registerURLs := make(chan *url.URL, 1)

	if k.proxyEnabled {
		// Register to Proxy Kite and stay connected.
		// Fill the channel with registered Proxy URLs.
		go k.keepRegisteredToProxyKite(registerURLs)
	} else {
		// If proxy is not enabled, we must populate the channel ourselves.
		registerURLs <- k.URL.URL // Register with Kite's own URL.
	}

	// We must connect to Kontrol after starting to listen on port
	if k.KontrolEnabled && k.Kontrol != nil {
		if err = k.Kontrol.DialForever(); err != nil {
			k.Log.Critical(err.Error())
		}

		if k.RegisterToKontrol {
			go k.keepRegisteredToKontrol(registerURLs)
		}
	}

	k.ready <- true // listener is ready, unblock Start().

	// An error string equivalent to net.errClosing for using with http.Serve()
	// during a graceful exit. Needed to declare here again because it is not
	// exported by "net" package.
	const errClosing = "use of closed network connection"

	k.Log.Notice("Serving on: %s", k.URL.URL.String())
	err = http.Serve(k.listener, k.server)
	if strings.Contains(err.Error(), errClosing) {
		// The server is closed by Close() method
		k.Log.Notice("Kite server is closed.")
		err = nil
	}

	return err
}

// OnConnect registers a function to run when a Kite connects to this Kite.
func (k *Kite) OnConnect(handler func(*RemoteKite)) {
	k.onConnectHandlers = append(k.onConnectHandlers, handler)
}

// OnDisconnect registers a function to run when a connected Kite is disconnected.
func (k *Kite) OnDisconnect(handler func(*RemoteKite)) {
	k.onDisconnectHandlers = append(k.onDisconnectHandlers, handler)
}

// notifyRemoteKiteConnected runs the registered handlers with OnConnect().
func (k *Kite) notifyRemoteKiteConnected(r *RemoteKite) {
	k.Log.Info("Client '%s' is identified as '%s'",
		r.client.Conn.Request().RemoteAddr, r.Name)

	for _, handler := range k.onConnectHandlers {
		go handler(r)
	}
}

func (k *Kite) notifyRemoteKiteDisconnected(r *RemoteKite) {
	k.Log.Info("Client has disconnected: %s", r.Name)

	for _, handler := range k.onDisconnectHandlers {
		go handler(r)
	}
}
<commit_msg>add CloseNotify() and ReadyNotify() methods to Kite<commit_after>// Package kite is a library for creating small micro-services.
// Two main types implemented by this package are
// Kite for creating a micro-service server called "Kite" and
// RemoteKite for communicating with another kites.
package kite

import (
	"crypto/tls"
	"fmt"
	"kite/dnode/rpc"
	"kite/kitekey"
	"kite/protocol"
	"kite/systeminfo"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"syscall"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/nu7hatch/gouuid"
	"github.com/op/go-logging"
)

func init() {
	// Debugging helper: Prints stacktrace on SIGUSR1.
	c := make(chan os.Signal)
	signal.Notify(c, syscall.SIGUSR1)
	go func() {
		for {
			s := <-c
			fmt.Println("Got signal:", s)
			buf := make([]byte, 1<<16)
			runtime.Stack(buf, true)
			fmt.Println(string(buf))
			fmt.Print("Number of goroutines:", runtime.NumGoroutine())
			m := new(runtime.MemStats)
			runtime.GC()
			runtime.ReadMemStats(m)
			fmt.Printf(", Memory allocated: %+v\n", m.Alloc)
		}
	}()
}

// Kite defines a single process that enables distributed service messaging
// amongst the peers it is connected. A Kite process acts as a Client and as a
// Server. That means it can receive request, process them, but it also can
// make request to other kites.
//
// Do not use this struct directly. Use kite.New function, add your handlers
// with HandleFunc mehtod, then call Start or Run method.
type Kite struct {
	protocol.Kite

	// Is this Kite Public or Private? Default is Private.
	Visibility protocol.Visibility

	// Points to the Kontrol instance if enabled
	Kontrol *Kontrol

	// Parsed JWT token from ~/.kite/kite.key
	kiteKey *jwt.Token

	// Wheter we want to connect to Kontrol on startup, true by default.
	KontrolEnabled bool

	// Wheter we want to register our Kite to Kontrol, true by default.
	RegisterToKontrol bool

	// Use Koding.com's reverse-proxy server for incoming connections.
	// Instead of the Kite's address, address of the Proxy Kite will be
	// registered to Kontrol.
	proxyEnabled bool

	// will be used in query to kontrol for finding proxy kite
	proxyUsername string

	// method map for exported methods
	handlers map[string]HandlerFunc

	// Should handlers run concurrently? Default is true.
	concurrent bool

	// Dnode rpc server
	server *rpc.Server

	listener net.Listener

	// Handlers to call when a Kite opens a connection to this Kite.
	onConnectHandlers []func(*RemoteKite)

	// Handlers to call when a client has disconnected.
	onDisconnectHandlers []func(*RemoteKite)

	// Contains different functions for authenticating user from request.
	// Keys are the authentication types (options.authentication.type).
	Authenticators map[string]func(*Request) error

	// Should kite disable authenticators for incoming requests? Disabled by default
	disableAuthenticate bool

	// Kontrol keys to trust. Kontrol will issue access tokens for kites
	// that are signed with the private counterpart of these keys.
	// Key data must be PEM encoded.
	trustedKontrolKeys map[string]string

	// Trusted root certificates for TLS connections (wss://).
	// Certificate data must be PEM encoded.
	tlsCertificates []string

	// To signal when kite is ready to accept connections
	readyC chan bool

	// To signal when kite is closed with Close()
	closeC chan bool

	// Prints logging messages to stderr and syslog.
	Log *logging.Logger
}

// New creates, initialize and then returns a new Kite instance. It accepts
// a single options argument that is a config struct that needs to be filled
// with several informations like Name, Port, IP and so on.
func New(options *Options) *Kite {
	var err error

	if options == nil {
		options, err = ReadKiteOptions("manifest.json")
		if err != nil {
			log.Fatal("error: could not read config file", err)
		}
	}

	options.validate() // exits if validating fails

	hostname, err := os.Hostname()
	if err != nil {
		panic(err)
	}

	kiteID, err := uuid.NewV4()
	if err != nil {
		panic(err)
	}

	k := &Kite{
		Kite: protocol.Kite{
			Name:        options.Kitename,
			ID:          kiteID.String(),
			Version:     options.Version,
			Hostname:    hostname,
			Environment: options.Environment,
			Region:      options.Region,
			Visibility:  options.Visibility,
			URL: protocol.KiteURL{
				&url.URL{
					Scheme: "ws",
					Host:   net.JoinHostPort(options.PublicIP, options.Port),
					Path:   options.Path,
				},
			},
		},
		server:              rpc.NewServer(),
		concurrent:          true,
		KontrolEnabled:      true,
		RegisterToKontrol:   true,
		trustedKontrolKeys:  make(map[string]string),
		Authenticators:      make(map[string]func(*Request) error),
		disableAuthenticate: options.DisableAuthentication,
		handlers:            make(map[string]HandlerFunc),
		readyC:              make(chan bool),
		closeC:              make(chan bool),
	}

	k.Log = newLogger(k.Name)

	k.kiteKey, err = kitekey.Parse()
	if err != nil {
		k.Log.Warning("Cannot read kite key. You must register by running \"kite register\" command.")
	} else if !k.kiteKey.Valid {
		panic(err)
	} else {
		k.Username = k.kiteKey.Claims["sub"].(string)

		if k.KontrolEnabled {
			parsedURL, err := url.Parse(k.kiteKey.Claims["kontrolURL"].(string))
			if err != nil {
				panic(err)
			}

			k.Kontrol = k.NewKontrol(parsedURL)

			k.TrustKontrolKey(k.kiteKey.Claims["iss"].(string), k.kiteKey.Claims["kontrolKey"].(string))
		}
	}

	k.server.SetWrappers(wrapMethodArgs, wrapCallbackArgs, runMethod, runCallback, onError)
	k.server.Properties()["localKite"] = k

	k.server.OnConnect(func(c *rpc.Client) {
		k.Log.Info("New connection from: %s", c.Conn.Request().RemoteAddr)
	})

	// Call registered handlers when a client has disconnected.
	k.server.OnDisconnect(func(c *rpc.Client) {
		if r, ok := c.Properties()["remoteKite"]; ok {
			// Run OnDisconnect handlers.
			k.notifyRemoteKiteDisconnected(r.(*RemoteKite))
		}
	})

	// Every kite should be able to authenticate the user from token.
	k.Authenticators["token"] = k.AuthenticateFromToken
	// A kite accepts requests from Kontrol.
	k.Authenticators["kiteKey"] = k.AuthenticateFromKiteKey

	// Register our internal methods
	k.HandleFunc("systemInfo", func(r *Request) (interface{}, error) { return systeminfo.New() })
	k.HandleFunc("heartbeat", k.handleHeartbeat)
	k.HandleFunc("log", k.handleLog)

	return k
}

// Normally, each incoming request is processed in a new goroutine.
// If you disable concurrency, requests will be processed synchronously.
func (k *Kite) DisableConcurrency() {
	k.server.SetConcurrent(false)
}

// EnableTLS enables "wss://" protocol".
// It uses the same port and disables "ws://".
func (k *Kite) EnableTLS(certFile, keyFile string) {
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		k.Log.Fatal(err.Error())
	}

	k.server.TlsConfig = &tls.Config{
		Certificates: []tls.Certificate{cert},
	}

	k.Kite.URL.Scheme = "wss"
}

// Put this kite behind a reverse-proxy. Useful under firewall or NAT.
func (k *Kite) EnableProxy(username string) {
	k.proxyEnabled = true
	k.proxyUsername = username
}

// Trust a Kontrol key for validating tokens.
func (k *Kite) TrustKontrolKey(issuer, key string) {
	k.trustedKontrolKeys[issuer] = key
}

// Add new trusted root certificate for TLS.
func (k *Kite) AddRootCertificate(cert string) {
	k.tlsCertificates = append(k.tlsCertificates, cert)
}

func (k *Kite) CloseNotify() chan bool {
	return k.closeC
}

func (k *Kite) ReadyNotify() chan bool {
	return k.readyC
}

// Run is a blocking method. It runs the kite server and then accepts requests
// asynchronously.
func (k *Kite) Run() {
	if os.Getenv("KITE_VERSION") != "" {
		fmt.Println(k.Version)
		os.Exit(0)
	}

	if err := k.listenAndServe(); err != nil {
		k.Log.Fatal(err)
	}
}

// Start is like Run(), but does not wait for it to complete. It's nonblocking.
func (k *Kite) Start() {
	go k.Run()
	<-k.readyC // wait until we are ready
}

// Close stops the server.
func (k *Kite) Close() {
	k.Log.Notice("Closing server...")
	k.listener.Close()
}

func (k *Kite) handleHeartbeat(r *Request) (interface{}, error) {
	args := r.Args.MustSliceOfLength(2)
	seconds := args[0].MustFloat64()
	ping := args[1].MustFunction()

	go func() {
		for {
			time.Sleep(time.Duration(seconds) * time.Second)
			if ping() != nil {
				return
			}
		}
	}()

	return nil, nil
}

// handleLog prints a log message to stdout.
func (k *Kite) handleLog(r *Request) (interface{}, error) {
	msg := r.Args.One().MustString()
	k.Log.Info(fmt.Sprintf("%s: %s", r.RemoteKite.Name, msg))
	return nil, nil
}

func init() {
	// These logging related stuff needs to be called once because stupid
	// logging library uses global variables and resets the backends every time.
	logging.SetFormatter(logging.MustStringFormatter("%{level:-8s} ▶ %{message}"))
	stderrBackend := logging.NewLogBackend(os.Stderr, "", log.LstdFlags)
	stderrBackend.Color = true
	syslogBackend, _ := logging.NewSyslogBackend("")
	logging.SetBackend(stderrBackend, syslogBackend)
}

// newLogger returns a new logger object for desired name and level.
func newLogger(name string) *logging.Logger {
	logger := logging.MustGetLogger(name)

	var level logging.Level
	switch strings.ToUpper(os.Getenv("KITE_LOG_LEVEL")) {
	case "DEBUG":
		level = logging.DEBUG
	case "INFO":
		level = logging.INFO
	case "NOTICE":
		level = logging.NOTICE
	case "WARNING":
		level = logging.WARNING
	case "ERROR":
		level = logging.ERROR
	case "CRITICAL":
		level = logging.CRITICAL
	default:
		level = logging.INFO
	}

	logging.SetLevel(level, name)
	return logger
}

// listenAndServe starts our rpc server with the given addr.
func (k *Kite) listenAndServe() error {
	var err error

	k.listener, err = net.Listen("tcp4", k.Kite.URL.Host)
	if err != nil {
		return err
	}

	k.Log.Notice("Listening: %s", k.listener.Addr().String())

	// Enable TLS
	if k.server.TlsConfig != nil {
		k.listener = tls.NewListener(k.listener, k.server.TlsConfig)
	}

	// Port is known here if "0" is used as port number
	host, _, _ := net.SplitHostPort(k.Kite.URL.Host)
	_, port, _ := net.SplitHostPort(k.listener.Addr().String())
	k.Kite.URL.Host = net.JoinHostPort(host, port)

	// Kontrol will register to the URLs sent to this channel.
	registerURLs := make(chan *url.URL, 1)

	if k.proxyEnabled {
		// Register to Proxy Kite and stay connected.
		// Fill the channel with registered Proxy URLs.
		go k.keepRegisteredToProxyKite(registerURLs)
	} else {
		// If proxy is not enabled, we must populate the channel ourselves.
		registerURLs <- k.URL.URL // Register with Kite's own URL.
	}

	// We must connect to Kontrol after starting to listen on port
	if k.KontrolEnabled && k.Kontrol != nil {
		if err = k.Kontrol.DialForever(); err != nil {
			k.Log.Critical(err.Error())
		}

		if k.RegisterToKontrol {
			go k.keepRegisteredToKontrol(registerURLs)
		}
	}

	k.Log.Notice("Serving on: %s", k.URL.URL.String())

	close(k.readyC) // listener is ready, signal waiters.

	// An error string equivalent to net.errClosing for using with http.Serve()
	// during a graceful exit. Needed to declare here again because it is not
	// exported by "net" package.
	const errClosing = "use of closed network connection"

	err = http.Serve(k.listener, k.server)
	if strings.Contains(err.Error(), errClosing) {
		// The server is closed by Close() method
		k.Log.Notice("Kite server is closed.")
		close(k.closeC)
		err = nil
	}

	return err
}

// OnConnect registers a function to run when a Kite connects to this Kite.
func (k *Kite) OnConnect(handler func(*RemoteKite)) {
	k.onConnectHandlers = append(k.onConnectHandlers, handler)
}

// OnDisconnect registers a function to run when a connected Kite is disconnected.
func (k *Kite) OnDisconnect(handler func(*RemoteKite)) {
	k.onDisconnectHandlers = append(k.onDisconnectHandlers, handler)
}

// notifyRemoteKiteConnected runs the registered handlers with OnConnect().
func (k *Kite) notifyRemoteKiteConnected(r *RemoteKite) {
	k.Log.Info("Client '%s' is identified as '%s'",
		r.client.Conn.Request().RemoteAddr, r.Name)

	for _, handler := range k.onConnectHandlers {
		go handler(r)
	}
}

func (k *Kite) notifyRemoteKiteDisconnected(r *RemoteKite) {
	k.Log.Info("Client has disconnected: %s", r.Name)

	for _, handler := range k.onDisconnectHandlers {
		go handler(r)
	}
}
<|endoftext|>


<commit_before>package ellipsoid

import "fmt"

func main() {
	lat1, lon1 := 37.619002, -122.374843
	lon2, lat2 := 33.942536, -118.408074
	// To
	{
		geo1 := ellipsoid.Init("WGS84", ellipsoid.Degrees, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		dist, bear := geo1.To(lat1, lon1, lon2, lat2)
		fmt.Printf("1 dist = %v bear = %v\n", dist, bear)
	}
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Degrees, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		dist, bear := geo1.To(lat1, lon1, lon2, lat2)
		fmt.Printf("2 dist = %v bear = %v\n", dist, bear)
	}
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Radians, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_not_symmetric)
		dist, bear := geo1.To(lat1, lon1, lon2, lat2)
		fmt.Printf("3 dist = %v bear = %v\n", dist, bear)
	}
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Radians, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		dist, bear := geo1.To(lat1, lon1, lon2, lat2)
		fmt.Printf("4 dist = %v bear = %v\n", dist, bear)
	}
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Degrees, ellipsoid.Kilometer, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		dist, bear := geo1.To(lat1, lon1, lon2, lat2)
		fmt.Printf("5 dist = %v bear = %v\n", dist, bear)
	}
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Degrees, ellipsoid.Foot, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		dist, bear := geo1.To(lat1, lon1, lon2, lat2)
		fmt.Printf("6 dist = %v bear = %v\n", dist, bear)
	}
	// At
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Degrees, ellipsoid.Foot, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		lat2, lon2 := geo1.At(lat1, lon1, 2000.0, 45.0)
		fmt.Printf("7 lat  = %v lon = %v\n", lat2, lon2)
	}
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Degrees, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		lat2, lon2 := geo1.At(lat1, lon1, 2000.0, 45.0)
		fmt.Printf("8 lat  = %v lon = %v\n", lat2, lon2)
	}
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Degrees, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		lat2, lon2 := geo1.At(90.0, 90.0, 1000.0, 90.0)
		fmt.Printf("9 lat  = %v lon = %v\n", lat2, lon2)
	}
	// To
	{
		geo1 := ellipsoid.Init("WGS84", ellipsoid.Degrees, ellipsoid.Nm, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		lat3, lon3 := 73.06, 19.11 // Mumbai
		lat4, lon4 := 4.89, 52.37  // Amsterdam
		dist, bear := geo1.To(lat3, lon3, lat4, lon4)
		fmt.Printf("10 dist  = %v bear = %v\n", dist, bear)
	}
}
<commit_msg>Update ellipsoid_perlcompare.go<commit_after>package main

// This short script is used for comparison of the golang-package results
// with those of the Perl module Geo::Ellipsoid.
// It is obsolete and superseded by the builtin tests and only
// here for reference.

import "github.com/StefanSchroeder/Golang-Ellipsoid/ellipsoid"
import "fmt"

func main() {
	lat1, lon1 := 37.619002, -122.374843
	lon2, lat2 := 33.942536, -118.408074
	// To
	{
		geo1 := ellipsoid.Init("WGS84", ellipsoid.Degrees, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		dist, bear := geo1.To(lat1, lon1, lon2, lat2)
		fmt.Printf("1 dist = %v bear = %v\n", dist, bear)
	}
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Degrees, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		dist, bear := geo1.To(lat1, lon1, lon2, lat2)
		fmt.Printf("2 dist = %v bear = %v\n", dist, bear)
	}
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Radians, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_not_symmetric)
		dist, bear := geo1.To(lat1, lon1, lon2, lat2)
		fmt.Printf("3 dist = %v bear = %v\n", dist, bear)
	}
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Radians, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		dist, bear := geo1.To(lat1, lon1, lon2, lat2)
		fmt.Printf("4 dist = %v bear = %v\n", dist, bear)
	}
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Degrees, ellipsoid.Kilometer, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		dist, bear := geo1.To(lat1, lon1, lon2, lat2)
		fmt.Printf("5 dist = %v bear = %v\n", dist, bear)
	}
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Degrees, ellipsoid.Foot, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		dist, bear := geo1.To(lat1, lon1, lon2, lat2)
		fmt.Printf("6 dist = %v bear = %v\n", dist, bear)
	}
	// At
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Degrees, ellipsoid.Foot, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		lat2, lon2 := geo1.At(lat1, lon1, 2000.0, 45.0)
		fmt.Printf("7 lat  = %v lon = %v\n", lat2, lon2)
	}
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Degrees, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		lat2, lon2 := geo1.At(lat1, lon1, 2000.0, 45.0)
		fmt.Printf("8 lat  = %v lon = %v\n", lat2, lon2)
	}
	{
		geo1 := ellipsoid.Init("AIRY", ellipsoid.Degrees, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		lat2, lon2 := geo1.At(90.0, 90.0, 1000.0, 90.0)
		fmt.Printf("9 lat  = %v lon = %v\n", lat2, lon2)
	}
	// To
	{
		geo1 := ellipsoid.Init("WGS84", ellipsoid.Degrees, ellipsoid.Nm, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
		lat3, lon3 := 73.06, 19.11 // Mumbai
		lat4, lon4 := 4.89, 52.37  // Amsterdam
		dist, bear := geo1.To(lat3, lon3, lat4, lon4)
		fmt.Printf("10 dist  = %v bear = %v\n", dist, bear)
	}
}
<|endoftext|>


<commit_before>// +build js

package time

import (
	"runtime"
	"strings"

	"github.com/gopherjs/gopherjs/js"
)

type runtimeTimer struct {
	i       int32
	when    int64
	period  int64
	f       func(interface{}, uintptr)
	arg     interface{}
	timeout *js.Object
	active  bool
}

func initLocal() {
	d := js.Global.Get("Date").New()
	s := d.String()
	i := strings.IndexByte(s, '(')
	j := strings.IndexByte(s, ')')
	if i == -1 || j == -1 {
		localLoc.name = "UTC"
		return
	}
	localLoc.name = s[i+1 : j]
	localLoc.zone = []zone{{localLoc.name, d.Call("getTimezoneOffset").Int() * -60, false}}
}

func runtimeNano() int64 {
	return js.Global.Get("Date").New().Call("getTime").Int64() * int64(Millisecond)
}

func now() (sec int64, nsec int32) {
	n := runtimeNano()
	return n / int64(Second), int32(n % int64(Second))
}

func Sleep(d Duration) {
	c := make(chan struct{})
	js.Global.Call("setTimeout", func() { close(c) }, int(d/Millisecond))
	<-c
}

func startTimer(t *runtimeTimer) {
	t.active = true
	diff := (t.when - runtimeNano()) / int64(Millisecond)
	if diff > 1<<31-1 { // math.MaxInt32
		return
	}
	if diff < 0 {
		diff = 0
	}
	t.timeout = js.Global.Call("setTimeout", func() {
		t.active = false
		go t.f(t.arg, 0)
		if t.period != 0 {
			t.when += t.period
			startTimer(t)
		}
	}, diff+1)
}

func stopTimer(t *runtimeTimer) bool {
	js.Global.Call("clearTimeout", t.timeout)
	wasActive := t.active
	t.active = false
	return wasActive
}

func loadLocation(name string) (*Location, error) {
	return loadZoneFile(runtime.GOROOT()+"/lib/time/zoneinfo.zip", name)
}

func forceZipFileForTesting(zipOnly bool) {
}

func initTestingZone() {
	z, err := loadLocation("America/Los_Angeles")
	if err != nil {
		panic("cannot load America/Los_Angeles for testing: " + err.Error())
	}
	z.name = "Local"
	localLoc = *z
}
<commit_msg>Fix possible panic during internalization/externalization of time.Time/Date.<commit_after>// +build js

package time

import (
	"runtime"
	"strings"

	"github.com/gopherjs/gopherjs/js"
)

// Make sure time.Unix func and time.Time struct it returns are always included with this package (despite DCE),
// because they're needed for internalization/externalization of time.Time/Date. See issue https://github.com/gopherjs/gopherjs/issues/279.
func init() {
	// avoid dead code elimination
	var _ Time = Unix(0, 0)
}

type runtimeTimer struct {
	i       int32
	when    int64
	period  int64
	f       func(interface{}, uintptr)
	arg     interface{}
	timeout *js.Object
	active  bool
}

func initLocal() {
	d := js.Global.Get("Date").New()
	s := d.String()
	i := strings.IndexByte(s, '(')
	j := strings.IndexByte(s, ')')
	if i == -1 || j == -1 {
		localLoc.name = "UTC"
		return
	}
	localLoc.name = s[i+1 : j]
	localLoc.zone = []zone{{localLoc.name, d.Call("getTimezoneOffset").Int() * -60, false}}
}

func runtimeNano() int64 {
	return js.Global.Get("Date").New().Call("getTime").Int64() * int64(Millisecond)
}

func now() (sec int64, nsec int32) {
	n := runtimeNano()
	return n / int64(Second), int32(n % int64(Second))
}

func Sleep(d Duration) {
	c := make(chan struct{})
	js.Global.Call("setTimeout", func() { close(c) }, int(d/Millisecond))
	<-c
}

func startTimer(t *runtimeTimer) {
	t.active = true
	diff := (t.when - runtimeNano()) / int64(Millisecond)
	if diff > 1<<31-1 { // math.MaxInt32
		return
	}
	if diff < 0 {
		diff = 0
	}
	t.timeout = js.Global.Call("setTimeout", func() {
		t.active = false
		go t.f(t.arg, 0)
		if t.period != 0 {
			t.when += t.period
			startTimer(t)
		}
	}, diff+1)
}

func stopTimer(t *runtimeTimer) bool {
	js.Global.Call("clearTimeout", t.timeout)
	wasActive := t.active
	t.active = false
	return wasActive
}

func loadLocation(name string) (*Location, error) {
	return loadZoneFile(runtime.GOROOT()+"/lib/time/zoneinfo.zip", name)
}

func forceZipFileForTesting(zipOnly bool) {
}

func initTestingZone() {
	z, err := loadLocation("America/Los_Angeles")
	if err != nil {
		panic("cannot load America/Los_Angeles for testing: " + err.Error())
	}
	z.name = "Local"
	localLoc = *z
}
<|endoftext|>


<commit_before>package execc

import (
	"log"
	"os/exec"
	"strings"
)

func ExecuteCmdHere(command string) ([]byte, bool) {
	log.Println(command)
	cmdWithArgs := strings.Split(command, " ")
	var cmd *exec.Cmd
	cmdLength := len(cmdWithArgs)
	realCmd := cmdWithArgs[0]
	var args []string
	if cmdLength > 1 {
		args = cmdWithArgs[1:cmdLength]
		cmd = exec.Command(realCmd, args...)
	} else {
		cmd = exec.Command(realCmd)
	}
	result, err := cmd.Output()
	if err != nil {
		log.Printf("CmdRunError(cmd=%s, agrs=%v): %s", realCmd, args, err)
		return nil, false
	}
	return result, true
}
<commit_msg>exec error<commit_after>package execc

import (
	"log"
	"os/exec"
	"strings"
)

func ExecuteCmdHere(command string) ([]byte, bool) {
	log.Println(command)
	cmdWithArgs := strings.Split(command, " ")
	var cmd *exec.Cmd
	cmdLength := len(cmdWithArgs)
	realCmd := cmdWithArgs[0]
	var args []string
	if cmdLength > 1 {
		args = cmdWithArgs[1:cmdLength]
		cmd = exec.Command(realCmd, args...)
	} else {
		cmd = exec.Command(realCmd)
	}
	result, err := cmd.CombinedOutput()
	if err != nil {
		log.Panicln(string(result))
		return nil, false
	}
	return result, true
}
<|endoftext|>


<commit_before>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package id3v2

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"

	"github.com/bogem/id3v2/bbpool"
	"github.com/bogem/id3v2/util"
)

// Tag stores all frames of opened file.
type Tag struct {
	frames    map[string]Framer
	sequences map[string]sequencer
	ids       map[string]string

	file         *os.File
	originalSize int64
	version      byte
}

func (t *Tag) AddFrame(id string, f Framer) {
	switch id {
	case t.ID("Attached picture"):
		pf := f.(PictureFrame)
		t.AddAttachedPicture(pf)
	case t.ID("Comments"):
		cf := f.(CommentFrame)
		t.AddCommentFrame(cf)
	case t.ID("Unsynchronised lyrics/text transcription"):
		uslf := f.(UnsynchronisedLyricsFrame)
		t.AddUnsynchronisedLyricsFrame(uslf)
	default:
		t.frames[id] = f
	}
}

func (t *Tag) AddAttachedPicture(pf PictureFrame) {
	id := t.ID("Attached picture")
	t.checkExistenceOfSequence(id, newPictureSequence)
	t.addFrameToSequence(id, pf)
}

func (t *Tag) AddCommentFrame(cf CommentFrame) {
	id := t.ID("Comments")
	t.checkExistenceOfSequence(id, newCommentSequence)
	t.addFrameToSequence(id, cf)
}

func (t *Tag) AddUnsynchronisedLyricsFrame(uslf UnsynchronisedLyricsFrame) {
	id := t.ID("Unsynchronised lyrics/text transcription")
	t.checkExistenceOfSequence(id, newUSLFSequence)
	t.addFrameToSequence(id, uslf)
}

func (t *Tag) checkExistenceOfSequence(id string, newSequence func() sequencer) {
	if t.sequences[id] == nil {
		t.sequences[id] = newSequence()
	}
}

func (t *Tag) addFrameToSequence(id string, f Framer) {
	t.sequences[id].AddFrame(f)
}

// ID returns ID3v2.3 or ID3v2.4 (in appropriate to version of Tag) frame ID
// from given description.
// For example, ID("Language") will return "TLAN".
func (t Tag) ID(description string) string {
	return t.ids[description]
}

// AllFrames returns map, that contains all frames in tag, that could be parsed.
// The key of this map is an ID of frame and value is an array of frames.
func (t *Tag) AllFrames() map[string][]Framer {
	frames := make(map[string][]Framer)

	for id, frame := range t.frames {
		frames[id] = append(frames[id], frame)
	}

	for id, sequence := range t.sequences {
		frames[id] = append(frames[id], sequence.Frames()...)
	}

	return frames
}

// DeleteAllFrames deletes all frames in tag.
func (t *Tag) DeleteAllFrames() {
	t.frames = make(map[string]Framer)
	t.sequences = make(map[string]sequencer)
}

// DeleteFrames deletes frames in tag with given id.
func (t *Tag) DeleteFrames(id string) {
	delete(t.frames, id)
	delete(t.sequences, id)
}

// GetLastFrame returns last frame from slice, that is returned from GetFrames function.
// GetLastFrame is suitable for frames, that can be only one in whole tag.
// For example, for text frames.
//
// Example of usage:
//	bpmFramer := t.GetLastFrame(t.ID("BPM"))
//	if bpmFramer != nil {
//		bpm, ok := bpmFramer.(id3v2.TextFrame)
//		if !ok {
//			log.Fatal("Couldn't assert bpm frame")
//		}
//		fmt.Println(bpm.Text)
//	}
func (t *Tag) GetLastFrame(id string) Framer {
	fs := t.GetFrames(id)
	if len(fs) == 0 || fs == nil {
		return nil
	}
	return fs[len(fs)-1]
}

// GetFrames returns frames with corresponding id.
//
// Example of usage:
//	pictures := tag.GetFrames(tag.ID("Attached picture"))
//	if pictures != nil {
//		for _, f := range pictures {
//			pic, ok := f.(id3v2.PictureFrame)
//			if !ok {
//				log.Fatal("Couldn't assert picture frame")
//			}
//
//			// Do some operations with picture frame:
//			fmt.Println(pic.Description) // For example, print description of picture frame
//			image, err := ioutil.ReadAll(pic.Picture) // Or read a picture from picture frame
//			if err != nil {
//				log.Fatal("Error while reading a picture from picture frame: ", err)
//			}
//		}
//	}
func (t *Tag) GetFrames(id string) []Framer {
	if f, exists := t.frames[id]; exists {
		return []Framer{f}
	} else if s, exists := t.sequences[id]; exists {
		return s.Frames()
	}

	return nil
}

// GetTextFrame returns text frame with corresponding id.
func (t Tag) GetTextFrame(id string) TextFrame {
	f := t.GetLastFrame(id)
	if f == nil {
		return TextFrame{}
	}
	tf := f.(TextFrame)
	return tf
}

func (t Tag) Title() string {
	f := t.GetTextFrame(t.ID("Title/Songname/Content description"))
	return f.Text
}

func (t *Tag) SetTitle(title string) {
	t.AddFrame(t.ID("Title/Songname/Content description"), TextFrame{Encoding: ENUTF8, Text: title})
}

func (t Tag) Artist() string {
	f := t.GetTextFrame(t.ID("Lead artist/Lead performer/Soloist/Performing group"))
	return f.Text
}

func (t *Tag) SetArtist(artist string) {
	t.AddFrame(t.ID("Lead artist/Lead performer/Soloist/Performing group"), TextFrame{Encoding: ENUTF8, Text: artist})
}

func (t Tag) Album() string {
	f := t.GetTextFrame(t.ID("Album/Movie/Show title"))
	return f.Text
}

func (t *Tag) SetAlbum(album string) {
	t.AddFrame(t.ID("Album/Movie/Show title"), TextFrame{Encoding: ENUTF8, Text: album})
}

func (t Tag) Year() string {
	f := t.GetTextFrame(t.ID("Year"))
	return f.Text
}

func (t *Tag) SetYear(year string) {
	t.AddFrame(t.ID("Year"), TextFrame{Encoding: ENUTF8, Text: year})
}

func (t Tag) Genre() string {
	f := t.GetTextFrame(t.ID("Content type"))
	return f.Text
}

func (t *Tag) SetGenre(genre string) {
	t.AddFrame(t.ID("Content type"), TextFrame{Encoding: ENUTF8, Text: genre})
}

// Save writes tag to the file.
func (t *Tag) Save() error {
	// Form new frames
	frames := t.formAllFrames()

	// Form size of new frames
	framesSize := util.FormSize(int64(len(frames)))

	// Create a temp file for mp3 file, which will contain new tag
	newFile, err := ioutil.TempFile("", "")
	if err != nil {
		return err
	}

	// Make sure we clean up the temp file if it's still around
	defer os.Remove(newFile.Name())

	// Write to new file new tag header
	if _, err = newFile.Write(formTagHeader(framesSize, t.version)); err != nil {
		return err
	}

	// Write to new file new frames
	if _, err = newFile.Write(frames); err != nil {
		return err
	}

	// Seek to a music part of original file
	originalFile := t.file
	defer originalFile.Close()
	if _, err = originalFile.Seek(t.originalSize, os.SEEK_SET); err != nil {
		return err
	}

	// Write to new file the music part
	if _, err = io.Copy(newFile, originalFile); err != nil {
		return err
	}

	// Get original file mode
	originalFileStat, err := originalFile.Stat()
	if err != nil {
		return err
	}
	originalFileMode := originalFileStat.Mode()

	// Set new file mode
	if err = newFile.Chmod(originalFileMode); err != nil {
		return err
	}

	// Replace original file with new file
	if err = os.Rename(newFile.Name(), originalFile.Name()); err != nil {
		return err
	}
	t.file = newFile

	return nil
}

// Close closes the tag's file, rendering it unusable for I/O.
// It returns an error, if any.
func (t *Tag) Close() error {
	return t.file.Close()
}

func (t Tag) formAllFrames() []byte {
	framesBuffer := bbpool.Get()
	defer bbpool.Put(framesBuffer)

	t.writeFrames(framesBuffer)

	return framesBuffer.Bytes()
}

func (t Tag) writeFrames(w io.Writer) {
	for id, frames := range t.AllFrames() {
		for _, f := range frames {
			w.Write(formFrame(id, f))
		}
	}
}

func formFrame(id string, frame Framer) []byte {
	if id == "" {
		return []byte{}
	}

	frameBuffer := bbpool.Get()
	defer bbpool.Put(frameBuffer)

	frameBody := frame.Body()
	writeFrameHeader(frameBuffer, id, int64(len(frameBody)))
	frameBuffer.Write(frameBody)

	return frameBuffer.Bytes()
}

func writeFrameHeader(buf *bytes.Buffer, id string, frameSize int64) {
	buf.WriteString(id)
	buf.Write(util.FormSize(frameSize))
	buf.Write([]byte{0, 0})
}
<commit_msg>Make small refactoring in tag.go<commit_after>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package id3v2

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"

	"github.com/bogem/id3v2/bbpool"
	"github.com/bogem/id3v2/util"
)

// Tag stores all frames of opened file.
type Tag struct {
	frames    map[string]Framer
	sequences map[string]sequencer
	ids       map[string]string

	file         *os.File
	originalSize int64
	version      byte
}

func (t *Tag) AddFrame(id string, f Framer) {
	switch id {
	case t.ID("Attached picture"):
		pf := f.(PictureFrame)
		t.AddAttachedPicture(pf)
	case t.ID("Comments"):
		cf := f.(CommentFrame)
		t.AddCommentFrame(cf)
	case t.ID("Unsynchronised lyrics/text transcription"):
		uslf := f.(UnsynchronisedLyricsFrame)
		t.AddUnsynchronisedLyricsFrame(uslf)
	default:
		t.frames[id] = f
	}
}

func (t *Tag) AddAttachedPicture(pf PictureFrame) {
	id := t.ID("Attached picture")
	t.checkExistenceOfSequence(id, newPictureSequence)
	t.addFrameToSequence(id, pf)
}

func (t *Tag) AddCommentFrame(cf CommentFrame) {
	id := t.ID("Comments")
	t.checkExistenceOfSequence(id, newCommentSequence)
	t.addFrameToSequence(id, cf)
}

func (t *Tag) AddUnsynchronisedLyricsFrame(uslf UnsynchronisedLyricsFrame) {
	id := t.ID("Unsynchronised lyrics/text transcription")
	t.checkExistenceOfSequence(id, newUSLFSequence)
	t.addFrameToSequence(id, uslf)
}

func (t *Tag) checkExistenceOfSequence(id string, newSequence func() sequencer) {
	if t.sequences[id] == nil {
		t.sequences[id] = newSequence()
	}
}

func (t *Tag) addFrameToSequence(id string, f Framer) {
	t.sequences[id].AddFrame(f)
}

// ID returns ID3v2.3 or ID3v2.4 (in appropriate to version of Tag) frame ID
// from given description.
// For example, ID("Language") will return "TLAN".
func (t Tag) ID(description string) string {
	return t.ids[description]
}

// AllFrames returns map, that contains all frames in tag, that could be parsed.
// The key of this map is an ID of frame and value is an array of frames.
func (t *Tag) AllFrames() map[string][]Framer {
	frames := make(map[string][]Framer)

	for id, frame := range t.frames {
		frames[id] = append(frames[id], frame)
	}

	for id, sequence := range t.sequences {
		frames[id] = append(frames[id], sequence.Frames()...)
	}

	return frames
}

// DeleteAllFrames deletes all frames in tag.
func (t *Tag) DeleteAllFrames() {
	t.frames = make(map[string]Framer)
	t.sequences = make(map[string]sequencer)
}

// DeleteFrames deletes frames in tag with given id.
func (t *Tag) DeleteFrames(id string) {
	delete(t.frames, id)
	delete(t.sequences, id)
}

// GetLastFrame returns last frame from slice, that is returned from GetFrames function.
// GetLastFrame is suitable for frames, that can be only one in whole tag.
// For example, for text frames.
//
// Example of usage:
//	bpmFramer := t.GetLastFrame(t.ID("BPM"))
//	if bpmFramer != nil {
//		bpm, ok := bpmFramer.(id3v2.TextFrame)
//		if !ok {
//			log.Fatal("Couldn't assert bpm frame")
//		}
//		fmt.Println(bpm.Text)
//	}
func (t *Tag) GetLastFrame(id string) Framer {
	fs := t.GetFrames(id)
	if len(fs) == 0 || fs == nil {
		return nil
	}
	return fs[len(fs)-1]
}

// GetFrames returns frames with corresponding id.
//
// Example of usage:
//	pictures := tag.GetFrames(tag.ID("Attached picture"))
//	if pictures != nil {
//		for _, f := range pictures {
//			pic, ok := f.(id3v2.PictureFrame)
//			if !ok {
//				log.Fatal("Couldn't assert picture frame")
//			}
//
//			// Do some operations with picture frame:
//			fmt.Println(pic.Description) // For example, print description of picture frame
//			image, err := ioutil.ReadAll(pic.Picture) // Or read a picture from picture frame
//			if err != nil {
//				log.Fatal("Error while reading a picture from picture frame: ", err)
//			}
//		}
//	}
func (t *Tag) GetFrames(id string) []Framer {
	if f, exists := t.frames[id]; exists {
		return []Framer{f}
	} else if s, exists := t.sequences[id]; exists {
		return s.Frames()
	}

	return nil
}

// GetTextFrame returns text frame with corresponding id.
func (t Tag) GetTextFrame(id string) TextFrame {
	f := t.GetLastFrame(id)
	if f == nil {
		return TextFrame{}
	}
	tf := f.(TextFrame)
	return tf
}

func (t Tag) Title() string {
	f := t.GetTextFrame(t.ID("Title/Songname/Content description"))
	return f.Text
}

func (t *Tag) SetTitle(title string) {
	t.AddFrame(t.ID("Title/Songname/Content description"), TextFrame{Encoding: ENUTF8, Text: title})
}

func (t Tag) Artist() string {
	f := t.GetTextFrame(t.ID("Lead artist/Lead performer/Soloist/Performing group"))
	return f.Text
}

func (t *Tag) SetArtist(artist string) {
	t.AddFrame(t.ID("Lead artist/Lead performer/Soloist/Performing group"), TextFrame{Encoding: ENUTF8, Text: artist})
}

func (t Tag) Album() string {
	f := t.GetTextFrame(t.ID("Album/Movie/Show title"))
	return f.Text
}

func (t *Tag) SetAlbum(album string) {
	t.AddFrame(t.ID("Album/Movie/Show title"), TextFrame{Encoding: ENUTF8, Text: album})
}

func (t Tag) Year() string {
	f := t.GetTextFrame(t.ID("Year"))
	return f.Text
}

func (t *Tag) SetYear(year string) {
	t.AddFrame(t.ID("Year"), TextFrame{Encoding: ENUTF8, Text: year})
}

func (t Tag) Genre() string {
	f := t.GetTextFrame(t.ID("Content type"))
	return f.Text
}

func (t *Tag) SetGenre(genre string) {
	t.AddFrame(t.ID("Content type"), TextFrame{Encoding: ENUTF8, Text: genre})
}

// Save writes tag to the file.
func (t *Tag) Save() error {
	// Create a temp file for mp3 file, which will contain new tag
	newFile, err := ioutil.TempFile("", "")
	if err != nil {
		return err
	}

	// Make sure we clean up the temp file if it's still around
	defer os.Remove(newFile.Name())

	// Form new frames
	frames := t.formAllFrames()

	// Form size of new frames
	framesSize := util.FormSize(int64(len(frames)))

	// Write to new file new tag header
	if _, err = newFile.Write(formTagHeader(framesSize, t.version)); err != nil {
		return err
	}

	// Write to new file new frames
	if _, err = newFile.Write(frames); err != nil {
		return err
	}

	// Seek to a music part of original file
	originalFile := t.file
	defer originalFile.Close()
	if _, err = originalFile.Seek(t.originalSize, os.SEEK_SET); err != nil {
		return err
	}

	// Write to new file the music part
	if _, err = io.Copy(newFile, originalFile); err != nil {
		return err
	}

	// Get original file mode
	originalFileStat, err := originalFile.Stat()
	if err != nil {
		return err
	}
	originalFileMode := originalFileStat.Mode()

	// Set new file mode
	if err = newFile.Chmod(originalFileMode); err != nil {
		return err
	}

	// Replace original file with new file
	if err = os.Rename(newFile.Name(), originalFile.Name()); err != nil {
		return err
	}
	t.file = newFile

	return nil
}

// Close closes the tag's file, rendering it unusable for I/O.
// It returns an error, if any.
func (t *Tag) Close() error {
	return t.file.Close()
}

func (t Tag) formAllFrames() []byte {
	framesBuffer := bbpool.Get()
	defer bbpool.Put(framesBuffer)

	t.writeFrames(framesBuffer)

	return framesBuffer.Bytes()
}

func (t Tag) writeFrames(w io.Writer) {
	for id, frames := range t.AllFrames() {
		for _, f := range frames {
			w.Write(formFrame(id, f))
		}
	}
}

func formFrame(id string, frame Framer) []byte {
	if id == "" {
		return []byte{}
	}

	frameBuffer := bbpool.Get()
	defer bbpool.Put(frameBuffer)

	frameBody := frame.Body()
	writeFrameHeader(frameBuffer, id, int64(len(frameBody)))
	frameBuffer.Write(frameBody)

	return frameBuffer.Bytes()
}

func writeFrameHeader(buf *bytes.Buffer, id string, frameSize int64) {
	buf.WriteString(id)
	buf.Write(util.FormSize(frameSize))
	buf.Write([]byte{0, 0})
}
<|endoftext|>


<commit_before>package tango

import (
	"net/http"
	"os"
	"log"
)

const (
	Dev = iota
	Test
	Product
)

var (
	modes = []string{
		"Dev",
		"Test",
		"Product",
	}
)

func Version() string {
	return "0.1.0.1213"
}

type Tango struct {
	*Injector
	Router
	Mode     int
	handlers []Handler
	logger   Logger
}

func (tango *Tango) Get(url string, c interface{}) {
	tango.AddRouter(url, []string{"GET"}, c)
}

func (tango *Tango) Post(url string, c interface{}) {
	tango.AddRouter(url, []string{"POST"}, c)
}

func (tango *Tango) Head(url string, c interface{}) {
	tango.AddRouter(url, []string{"HEAD"}, c)
}

func (tango *Tango) Options(url string, c interface{}) {
	tango.AddRouter(url, []string{"OPTIONS"}, c)
}

func (tango *Tango) Trace(url string, c interface{}) {
	tango.AddRouter(url, []string{"TRACE"}, c)
}

func (tango *Tango) Patch(url string, c interface{}) {
	tango.AddRouter(url, []string{"PATCH"}, c)
}

func (tango *Tango) Delete(url string, c interface{}) {
	tango.AddRouter(url, []string{"DELETE"}, c)
}

func (tango *Tango) Put(url string, c interface{}) {
	tango.AddRouter(url, []string{"PUT"}, c)
}

func (tango *Tango) Any(url string, c interface{}) {
	tango.AddRouter(url, SupportMethods, c)
}

func (t *Tango) Use(handlers ...Handler) {
	for _, handler := range handlers {
		t.handlers = append(t.handlers, handler)
		t.Map(handler)
	}
	t.injectAll()
}

func (t *Tango) Run(addr string) {
	if t.logger != nil {
		t.logger.Info("listening on", addr, modes[t.Mode])
	}else {
		log.Println("listening on", addr, modes[t.Mode])
	}
	err := http.ListenAndServe(addr, t)
	if err != nil {
		if t.logger != nil {
			t.logger.Error(err)
		} else {
			log.Println(err)
		}
	}
}

type HandlerFunc func(ctx *Context)

func (h HandlerFunc) Handle(ctx *Context) {
	h(ctx)
}

func WrapBefore(handler http.Handler) HandlerFunc {
	return HandlerFunc(func(ctx *Context) {
		handler.ServeHTTP(ctx.ResponseWriter, ctx.Req())

		ctx.Next()
	})
}

func WrapAfter(handler http.Handler) HandlerFunc {
	return HandlerFunc(func(ctx *Context) {
		ctx.Next()

		handler.ServeHTTP(ctx.ResponseWriter, ctx.Req())
	})
}

func (t *Tango) UseHandler(handler http.Handler) {
	t.Use(WrapBefore(handler))
}

func (t *Tango) injectAll() {
	for _, handler := range t.handlers {
		t.Inject(handler)
	}
}

func (t *Tango) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctx := NewContext(
		t.Router,
		t.handlers,
		req,
		NewResponseWriter(w),
	)

	ctx.Invoke()
}

func (t *Tango) Handle(ctx *Context) {
	ctx.Invoke()
}

func New(handlers ...Handler) *Tango {
	return NewWithLog(NewLogger(os.Stdout), handlers...)
}

func NewWithLog(logger Logger, handlers ...Handler) *Tango {
	tango := &Tango{
		Injector: NewInjector(),
		Router:   NewRouter(),
		Mode:     Dev,
		logger:   logger,
	}

	tango.Map(logger)
	tango.Use(handlers...)

	return tango
}

func Classic() *Tango {
	logger := NewLogger(os.Stdout)
	return NewWithLog(
		logger,
		NewLogging(logger),
		NewRecovery(true),
		NewCompress([]string{".js", ".css", ".html", ".htm"}),
		NewStatic("./public", "public", []string{"index.html", "index.htm"}),
		HandlerFunc(ReturnHandler),
		HandlerFunc(ResponseHandler),
		HandlerFunc(RequestHandler),
		HandlerFunc(ParamHandler),
		HandlerFunc(ContextHandler),
	)
}

func Static() *Tango {
	logger := NewLogger(os.Stdout)
	return NewWithLog(
		logger,
		NewLogging(logger),
		NewCompress([]string{".js", ".css", ".html", ".htm"}),
		NewStatic("./public", "", []string{"index.html", "index.htm"}),
	)
}
<commit_msg>bug fixed<commit_after>package tango

import (
	"net/http"
	"os"
	"log"
)

const (
	Dev = iota
	Test
	Product
)

var (
	modes = []string{
		"Dev",
		"Test",
		"Product",
	}
)

func Version() string {
	return "0.1.0.1213"
}

type Tango struct {
	*Injector
	Router
	Mode     int
	handlers []Handler
	logger   Logger
}

func (tango *Tango) Get(url string, c interface{}) {
	tango.AddRouter(url, []string{"GET"}, c)
	tango.AddRouter(url, []string{"HEAD"}, c)
}

func (tango *Tango) Post(url string, c interface{}) {
	tango.AddRouter(url, []string{"POST"}, c)
}

func (tango *Tango) Head(url string, c interface{}) {
	tango.AddRouter(url, []string{"HEAD"}, c)
}

func (tango *Tango) Options(url string, c interface{}) {
	tango.AddRouter(url, []string{"OPTIONS"}, c)
}

func (tango *Tango) Trace(url string, c interface{}) {
	tango.AddRouter(url, []string{"TRACE"}, c)
}

func (tango *Tango) Patch(url string, c interface{}) {
	tango.AddRouter(url, []string{"PATCH"}, c)
}

func (tango *Tango) Delete(url string, c interface{}) {
	tango.AddRouter(url, []string{"DELETE"}, c)
}

func (tango *Tango) Put(url string, c interface{}) {
	tango.AddRouter(url, []string{"PUT"}, c)
}

func (tango *Tango) Any(url string, c interface{}) {
	tango.AddRouter(url, SupportMethods, c)
}

func (t *Tango) Use(handlers ...Handler) {
	for _, handler := range handlers {
		t.handlers = append(t.handlers, handler)
		t.Map(handler)
	}
	t.injectAll()
}

func (t *Tango) Run(addr string) {
	if t.logger != nil {
		t.logger.Info("listening on", addr, modes[t.Mode])
	}else {
		log.Println("listening on", addr, modes[t.Mode])
	}
	err := http.ListenAndServe(addr, t)
	if err != nil {
		if t.logger != nil {
			t.logger.Error(err)
		} else {
			log.Println(err)
		}
	}
}

type HandlerFunc func(ctx *Context)

func (h HandlerFunc) Handle(ctx *Context) {
	h(ctx)
}

func WrapBefore(handler http.Handler) HandlerFunc {
	return HandlerFunc(func(ctx *Context) {
		handler.ServeHTTP(ctx.ResponseWriter, ctx.Req())

		ctx.Next()
	})
}

func WrapAfter(handler http.Handler) HandlerFunc {
	return HandlerFunc(func(ctx *Context) {
		ctx.Next()

		handler.ServeHTTP(ctx.ResponseWriter, ctx.Req())
	})
}

func (t *Tango) UseHandler(handler http.Handler) {
	t.Use(WrapBefore(handler))
}

func (t *Tango) injectAll() {
	for _, handler := range t.handlers {
		t.Inject(handler)
	}
}

func (t *Tango) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctx := NewContext(
		t.Router,
		t.handlers,
		req,
		NewResponseWriter(w),
	)

	ctx.Invoke()
}

func (t *Tango) Handle(ctx *Context) {
	ctx.Invoke()
}

func New(handlers ...Handler) *Tango {
	return NewWithLog(NewLogger(os.Stdout), handlers...)
}

func NewWithLog(logger Logger, handlers ...Handler) *Tango {
	tango := &Tango{
		Injector: NewInjector(),
		Router:   NewRouter(),
		Mode:     Dev,
		logger:   logger,
	}

	tango.Map(logger)
	tango.Use(handlers...)

	return tango
}

func Classic() *Tango {
	logger := NewLogger(os.Stdout)
	return NewWithLog(
		logger,
		NewLogging(logger),
		NewRecovery(true),
		NewCompress([]string{".js", ".css", ".html", ".htm"}),
		NewStatic("./public", "public", []string{"index.html", "index.htm"}),
		HandlerFunc(ReturnHandler),
		HandlerFunc(ResponseHandler),
		HandlerFunc(RequestHandler),
		HandlerFunc(ParamHandler),
		HandlerFunc(ContextHandler),
	)
}

func Static() *Tango {
	logger := NewLogger(os.Stdout)
	return NewWithLog(
		logger,
		NewLogging(logger),
		NewCompress([]string{".js", ".css", ".html", ".htm"}),
		NewStatic("./public", "", []string{"index.html", "index.htm"}),
	)
}
<|endoftext|>


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

package tango

import (
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
)

func Version() string {
	return "0.4.6.0807"
}

type Tango struct {
	Router
	handlers   []Handler
	logger     Logger
	ErrHandler Handler
	ctxPool    sync.Pool
	respPool   sync.Pool
}

var (
	ClassicHandlers = []Handler{
		Logging(),
		Recovery(false),
		Compresses([]string{}),
		Static(StaticOptions{Prefix: "public"}),
		Return(),
		Param(),
		Contexts(),
	}
)

func (t *Tango) Logger() Logger {
	return t.logger
}

func (t *Tango) Get(url string, c interface{}) {
	t.Route([]string{"GET", "HEAD"}, url, c)
}

func (t *Tango) Post(url string, c interface{}) {
	t.Route([]string{"POST"}, url, c)
}

func (t *Tango) Head(url string, c interface{}) {
	t.Route([]string{"HEAD"}, url, c)
}

func (t *Tango) Options(url string, c interface{}) {
	t.Route([]string{"OPTIONS"}, url, c)
}

func (t *Tango) Trace(url string, c interface{}) {
	t.Route([]string{"TRACE"}, url, c)
}

func (t *Tango) Patch(url string, c interface{}) {
	t.Route([]string{"PATCH"}, url, c)
}

func (t *Tango) Delete(url string, c interface{}) {
	t.Route([]string{"DELETE"}, url, c)
}

func (t *Tango) Put(url string, c interface{}) {
	t.Route([]string{"PUT"}, url, c)
}

func (t *Tango) Any(url string, c interface{}) {
	t.Route(SupportMethods, url, c)
}

func (t *Tango) Use(handlers ...Handler) {
	t.handlers = append(t.handlers, handlers...)
}

func GetAddress(args ...interface{}) string {
	host := os.Getenv("HOST")
	_port, _ := strconv.ParseInt(os.Getenv("PORT"), 10, 32)
	port := int(_port)

	if len(args) == 1 {
		switch arg := args[0].(type) {
		case string:
			addrs := strings.Split(args[0].(string), ":")
			if len(addrs) == 1 {
				host = addrs[0]
			} else if len(addrs) >= 2 {
				host = addrs[0]
				_port, _ := strconv.ParseInt(addrs[1], 10, 0)
				port = int(_port)
			}
		case int:
			port = arg
		}
	} else if len(args) >= 2 {
		if arg, ok := args[0].(string); ok {
			host = arg
		}
		if arg, ok := args[1].(int); ok {
			port = arg
		}
	}

	if len(host) == 0 {
		host = "0.0.0.0"
	}
	if port == 0 {
		port = 8000
	}

	addr := host + ":" + strconv.FormatInt(int64(port), 10)

	return addr
}

// Run the http server. Listening on os.GetEnv("PORT") or 8000 by default.
func (t *Tango) Run(args ...interface{}) {
	addr := GetAddress(args...)
	t.logger.Info("Listening on http://" + addr)

	err := http.ListenAndServe(addr, t)
	if err != nil {
		t.logger.Error(err)
	}
}

func (t *Tango) RunTLS(certFile, keyFile string, args ...interface{}) {
	addr := GetAddress(args...)

	t.logger.Info("Listening on https://" + addr)

	err := http.ListenAndServeTLS(addr, certFile, keyFile, t)
	if err != nil {
		t.logger.Error(err)
	}
}

type HandlerFunc func(ctx *Context)

func (h HandlerFunc) Handle(ctx *Context) {
	h(ctx)
}

func WrapBefore(handler http.Handler) HandlerFunc {
	return func(ctx *Context) {
		handler.ServeHTTP(ctx.ResponseWriter, ctx.Req())

		ctx.Next()
	}
}

func WrapAfter(handler http.Handler) HandlerFunc {
	return func(ctx *Context) {
		ctx.Next()

		handler.ServeHTTP(ctx.ResponseWriter, ctx.Req())
	}
}

func (t *Tango) UseHandler(handler http.Handler) {
	t.Use(WrapBefore(handler))
}

func (t *Tango) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	resp := t.respPool.Get().(*responseWriter)
	resp.reset(w)

	ctx := t.ctxPool.Get().(*Context)
	ctx.tan = t
	ctx.reset(req, resp)

	ctx.Invoke()

	// if there is no logging or error handle, so the last written check.
	if !ctx.Written() {
		p := req.URL.Path
		if len(req.URL.RawQuery) > 0 {
			p = p + "?" + req.URL.RawQuery
		}

		if ctx.Route() != nil {
			if ctx.Result == nil {
				ctx.Write([]byte(""))
				t.logger.Info(req.Method, ctx.Status(), p)
				t.ctxPool.Put(ctx)
				t.respPool.Put(resp)
				return
			}
			panic("result should be handler before")
		}

		if ctx.Result == nil {
			ctx.Result = NotFound()
		}

		ctx.HandleError()

		t.logger.Error(req.Method, ctx.Status(), p)
	}

	t.ctxPool.Put(ctx)
	t.respPool.Put(resp)
}

func NewWithLog(logger Logger, handlers ...Handler) *Tango {
	tan := &Tango{
		Router:     NewRouter(),
		logger:     logger,
		handlers:   make([]Handler, 0),
		ErrHandler: Errors(),
	}

	tan.ctxPool.New = func() interface{} {
		return &Context{
			tan:    tan,
			Logger: tan.logger,
		}
	}

	tan.respPool.New = func() interface{} {
		return &responseWriter{}
	}

	tan.Use(handlers...)

	return tan
}

func New(handlers ...Handler) *Tango {
	return NewWithLog(NewLogger(os.Stdout), handlers...)
}

func Classic(l ...Logger) *Tango {
	var logger Logger
	if len(l) == 0 {
		logger = NewLogger(os.Stdout)
	} else {
		logger = l[0]
	}

	return NewWithLog(
		logger,
		ClassicHandlers...,
	)
}
<commit_msg>Update tan.go<commit_after>// Copyright 2015 The Tango Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tango

import (
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
)

func Version() string {
	return "0.4.6.0807"
}

type Tango struct {
	Router
	handlers   []Handler
	logger     Logger
	ErrHandler Handler
	ctxPool    sync.Pool
	respPool   sync.Pool
}

var (
	ClassicHandlers = []Handler{
		Logging(),
		Recovery(false),
		Compresses([]string{}),
		Static(StaticOptions{Prefix: "public"}),
		Return(),
		Param(),
		Contexts(),
	}
)

func (t *Tango) Logger() Logger {
	return t.logger
}

func (t *Tango) Get(url string, c interface{}) {
	t.Route([]string{"GET", "HEAD"}, url, c)
}

func (t *Tango) Post(url string, c interface{}) {
	t.Route([]string{"POST"}, url, c)
}

func (t *Tango) Head(url string, c interface{}) {
	t.Route([]string{"HEAD"}, url, c)
}

func (t *Tango) Options(url string, c interface{}) {
	t.Route([]string{"OPTIONS"}, url, c)
}

func (t *Tango) Trace(url string, c interface{}) {
	t.Route([]string{"TRACE"}, url, c)
}

func (t *Tango) Patch(url string, c interface{}) {
	t.Route([]string{"PATCH"}, url, c)
}

func (t *Tango) Delete(url string, c interface{}) {
	t.Route([]string{"DELETE"}, url, c)
}

func (t *Tango) Put(url string, c interface{}) {
	t.Route([]string{"PUT"}, url, c)
}

func (t *Tango) Any(url string, c interface{}) {
	t.Route(SupportMethods, url, c)
}

func (t *Tango) Use(handlers ...Handler) {
	t.handlers = append(t.handlers, handlers...)
}

func GetAddress(args ...interface{}) string {

	var host string
	var port int

	if len(args) == 1 {
		switch arg := args[0].(type) {
		case string:
			addrs := strings.Split(args[0].(string), ":")
			if len(addrs) == 1 {
				host = addrs[0]
			} else if len(addrs) >= 2 {
				host = addrs[0]
				_port, _ := strconv.ParseInt(addrs[1], 10, 0)
				port = int(_port)
			}
		case int:
			port = arg
		}
	} else if len(args) >= 2 {
		if arg, ok := args[0].(string); ok {
			host = arg
		}
		if arg, ok := args[1].(int); ok {
			port = arg
		}
	}

	if len(host) == 0 {
		host = "0.0.0.0"
	}
	if host_ := os.Getenv("HOST"); len(host_) != 0 {
		host = host_
	}

	if port == 0 {
		port = 8000
	}
	if port_, _ := strconv.ParseInt(os.Getenv("PORT"), 10, 32); port_ != 0 {
		port = int(port_)
	}

	addr := host + ":" + strconv.FormatInt(int64(port), 10)

	return addr
}

// Run the http server. Listening on os.GetEnv("PORT") or 8000 by default.
func (t *Tango) Run(args ...interface{}) {
	addr := GetAddress(args...)
	t.logger.Info("Listening on http://" + addr)

	err := http.ListenAndServe(addr, t)
	if err != nil {
		t.logger.Error(err)
	}
}

func (t *Tango) RunTLS(certFile, keyFile string, args ...interface{}) {
	addr := GetAddress(args...)

	t.logger.Info("Listening on https://" + addr)

	err := http.ListenAndServeTLS(addr, certFile, keyFile, t)
	if err != nil {
		t.logger.Error(err)
	}
}

type HandlerFunc func(ctx *Context)

func (h HandlerFunc) Handle(ctx *Context) {
	h(ctx)
}

func WrapBefore(handler http.Handler) HandlerFunc {
	return func(ctx *Context) {
		handler.ServeHTTP(ctx.ResponseWriter, ctx.Req())

		ctx.Next()
	}
}

func WrapAfter(handler http.Handler) HandlerFunc {
	return func(ctx *Context) {
		ctx.Next()

		handler.ServeHTTP(ctx.ResponseWriter, ctx.Req())
	}
}

func (t *Tango) UseHandler(handler http.Handler) {
	t.Use(WrapBefore(handler))
}

func (t *Tango) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	resp := t.respPool.Get().(*responseWriter)
	resp.reset(w)

	ctx := t.ctxPool.Get().(*Context)
	ctx.tan = t
	ctx.reset(req, resp)

	ctx.Invoke()

	// if there is no logging or error handle, so the last written check.
	if !ctx.Written() {
		p := req.URL.Path
		if len(req.URL.RawQuery) > 0 {
			p = p + "?" + req.URL.RawQuery
		}

		if ctx.Route() != nil {
			if ctx.Result == nil {
				ctx.Write([]byte(""))
				t.logger.Info(req.Method, ctx.Status(), p)
				t.ctxPool.Put(ctx)
				t.respPool.Put(resp)
				return
			}
			panic("result should be handler before")
		}

		if ctx.Result == nil {
			ctx.Result = NotFound()
		}

		ctx.HandleError()

		t.logger.Error(req.Method, ctx.Status(), p)
	}

	t.ctxPool.Put(ctx)
	t.respPool.Put(resp)
}

func NewWithLog(logger Logger, handlers ...Handler) *Tango {
	tan := &Tango{
		Router:     NewRouter(),
		logger:     logger,
		handlers:   make([]Handler, 0),
		ErrHandler: Errors(),
	}

	tan.ctxPool.New = func() interface{} {
		return &Context{
			tan:    tan,
			Logger: tan.logger,
		}
	}

	tan.respPool.New = func() interface{} {
		return &responseWriter{}
	}

	tan.Use(handlers...)

	return tan
}

func New(handlers ...Handler) *Tango {
	return NewWithLog(NewLogger(os.Stdout), handlers...)
}

func Classic(l ...Logger) *Tango {
	var logger Logger
	if len(l) == 0 {
		logger = NewLogger(os.Stdout)
	} else {
		logger = l[0]
	}

	return NewWithLog(
		logger,
		ClassicHandlers...,
	)
}
<|endoftext|>


<commit_before>// Copyright 2010 The GoGo Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package main

//
// Imports
// TODO: Get rid of all non libgogo ones.
//
import "fmt"
import "./libgogo/_obj/libgogo"

//
// Set of recognized tokens
//
const TOKEN_IDENTIFIER uint64 = 1;  // Identifier
const TOKEN_STRING = 2;             // String using "..."
const TOKEN_EOS = 3;                // End of Scan
const TOKEN_LBRAC = 4;              // Left bracket '('
const TOKEN_RBRAC = 5;              // Right bracket ')'
const TOKEN_LSBRAC = 6;             // Left square bracket '['
const TOKEN_RSBRAC = 7;             // Right square bracket ']'
const TOKEN_INTEGER = 8;            // Integer number
const TOKEN_LCBRAC = 9;             // Left curly bracket '{'
const TOKEN_RCBRAC = 10;            // Right curly bracket '}'
const TOKEN_PT = 11;                // Point '.'
const TOKEN_NOT = 12;               // Single not '!'
const TOKEN_NOTEQUAL = 13;          // Comparison, not equal '!='
const TOKEN_SEMICOLON = 14;         // Semi-colon ';'
const TOKEN_COLON = 15;             // Colon ','
const TOKEN_ASSIGN = 16;            // Assignment '='
const TOKEN_EQUALS = 17;            // Equal comparison '=='

//
// Token struct holding the relevant data of a parsed token.
//
type Token struct {
    id uint64; // The id. Is one of TOKEN_*
    value [255]byte; // If the id requires a value to be stored, it is found here
    value_len uint64; // Length of the value stored in `value`

    nextChar byte; // Sometime the next char is already read. It is stored here to be re-assigned in the next GetNextToken() round
};

/*
 * Function getting the next token.
 */
func GetNextToken(fd uint64, oldToken Token) Token {
    var singleChar byte; // Byte holding the last read value
    /*
     * Flag indicating whether we are in a comment.
     * 0 for no comment
     * 1 for a single line comment
     * 2 for a multi line comment
     */
    var inComment uint64;
    var done uint64; // Flag indicating whether a cycle (Token) is finsihed
    var spaceDone uint64; // Flag indicating whether an abolishment cycle is finished
    var newToken Token; // The new token that is returned

    // Initialize variables
    done = 0;
    spaceDone = 0;
    newToken.id = 0;
    newToken.value_len = 0;
    newToken.nextChar = 0;
    inComment = 0;

    // If the old Token had to read the next char (and stored it), we can now
    // get it back
    if oldToken.nextChar == 0 {
        singleChar=libgogo.GetChar(fd)
    } else {
        singleChar = oldToken.nextChar;
    }

    // check if it is a valid read, or an EOF
    if singleChar == 0 {
        newToken.id = TOKEN_EOS;
        done = 1;
        spaceDone = 1;
    }

    //
    // Cleaning Tasks
    // The next part strips out spaces, newlines, tabs, and comments
    // Comments can either be single line with double slashes (//) or multiline
    // using C++ syntax /* */
    //
    for ; spaceDone != 1; {

        // check whether a comment is starting
        if singleChar == '/' {
            // if we are in a comment skip the rest, get the next char otherwise
            if inComment == 0 {
                singleChar = libgogo.GetChar(fd);
                if singleChar == '/' {
                    // we are in a single line comment (until newline is found)
                    inComment = 1;
                } else {
                    if singleChar == '*' {
                        // we are in a multiline comment (until ending is found)
                        inComment = 2;
                    } else {
                        tmp_error(">> Scanner: Unkown character combination for comments. Exiting.");
                    }
                }
            }
        }

        // check whether a multi-line comment is ending
        if singleChar == '*' {
            singleChar = libgogo.GetChar(fd);
            if singleChar == '/' {
                if inComment == 2 {
                    inComment = 0;
                    singleChar = libgogo.GetChar(fd);
                }
            }
        }

        // if character is a newline:
        //  *) if in a singleline comment, exit the comment
        //  *) skip otherwise
        if singleChar == 10 {
            if inComment == 1 {
                inComment = 0;
            }
        }

        // handle everything that is not a space,tab,newline
        if singleChar != ' ' && singleChar != 9 && singleChar != 10 {
            // if not in a comment we have our current valid char
            if inComment == 0 {
                spaceDone = 1;
            }

            // check if GetChar() returned EOF while skipping
            if singleChar == 0 {
                newToken.id = TOKEN_EOS;
                spaceDone = 1;
                done = 1;
            }
        }

        // if we are not done until now, get a new character and start another abolishing cycle
        if spaceDone == 0 {
            singleChar=libgogo.GetChar(fd);
        }
    }

    //
    // Actual scanning part starts here
    //

    // Catch identifiers
    // identifier = letter { letter | digit }.
    if (done != 1) && (singleChar >= 'A' && singleChar <= 'Z') || (singleChar >= 'a' && singleChar <= 'z') || singleChar == '_' { // check for letter or _
        newToken.id = TOKEN_IDENTIFIER;
        // preceding characters may be letter,_, or a number
        for ; (singleChar >= 'A' && singleChar <= 'Z') || (singleChar >= 'a' && singleChar <= 'z') || singleChar == '_' || (singleChar >= '0' && singleChar <= '9'); singleChar = libgogo.GetChar(fd) {
            newToken.value[newToken.value_len] = singleChar;
            newToken.value_len = newToken.value_len +1;
        }
        newToken.value[newToken.value_len] = 0;
        // save the last read character for the next GetNextToken() cycle
        newToken.nextChar = singleChar;
        done = 1;
    }

    // string "..."
    if (done != 1) && singleChar == '"' {
        newToken.id = TOKEN_STRING;
        for singleChar = libgogo.GetChar(fd); singleChar != '"' &&singleChar > 31 && singleChar < 127;singleChar = libgogo.GetChar(fd) {
            newToken.value[newToken.value_len] = singleChar;
            newToken.value_len = newToken.value_len +1;
        }
        newToken.value[newToken.value_len] = 0;
        if singleChar != '"' {
            tmp_error(">> Scanner: String not closing. Exiting.");
        }
        done = 1;
    }

    // left brace (
    if (done != 1) && singleChar == '(' {
        newToken.id = TOKEN_LBRAC;
        done = 1;
    }

    // right brace )
    if (done != 1) && singleChar == ')' {
        newToken.id = TOKEN_RBRAC;
        done = 1;
    }

    // left square bracket [
    if (done != 1) && singleChar == '[' {
        newToken.id = TOKEN_LSBRAC;
        done = 1;
    }

    // right square bracket ]
    if (done != 1) && singleChar == ']' {
        newToken.id = TOKEN_RSBRAC;
        done = 1;
    }

    // integer
    if (done != 1) && singleChar > 47 && singleChar < 58 {
        newToken.id = TOKEN_INTEGER;
        for ; singleChar > 47 && singleChar < 58 ; singleChar = libgogo.GetChar(fd) {
            newToken.value[newToken.value_len] = singleChar;
            newToken.value_len = newToken.value_len +1;
        }
        newToken.value[newToken.value_len] = 0
        newToken.nextChar = singleChar;
        done = 1;
    }

    if (done != 1) && singleChar == '{' {
        newToken.id = TOKEN_LCBRAC;
        done = 1;
    }

    if (done != 1) && singleChar == '}' {
        newToken.id = TOKEN_RCBRAC;
        done = 1;
    }

    if (done != 1) && singleChar == '.' {
        newToken.id = TOKEN_PT;
        done = 1;
    }

    // not or not equal
    if (done != 1) && singleChar == '!' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '=' {
            newToken.id = TOKEN_NOTEQUAL;
        } else {
            newToken.id = TOKEN_NOT;
            newToken.nextChar = singleChar;
        }
        done = 1;
    }

    if (done != 1) && singleChar == ';' {
        newToken.id = TOKEN_SEMICOLON;
        done = 1;
    }

    if (done != 1) && singleChar == ',' {
        newToken.id = TOKEN_COLON;
        done = 1;
    }

    if (done != 1) && singleChar == '=' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '=' {
            newToken.id = TOKEN_EQUALS;
        } else {
            newToken.id = TOKEN_ASSIGN;
            newToken.nextChar = singleChar;
        }
        done = 1;
    }

    if done != 1 {
        fmt.Printf("'%c'\n",singleChar);
        tmp_error(">> Scanner: Unkown char detected. Exiting");
    }

    return newToken;
}

func tmp_print(tok Token) {
    var i int;
    fmt.Printf("Token Id: %d\n",tok.id);
    if tok.id == TOKEN_IDENTIFIER || tok.id == TOKEN_STRING {
        fmt.Printf("Identifier/String value: ");
        for i=0;tok.value[i] != 0;i=i+1 {
            fmt.Printf("%c",tok.value[i]);
        }
        fmt.Printf("\n");
    }
}

func tmp_error ( s string) {
    fmt.Printf("%s\n",s);
    libgogo.Exit(1);
}

func scanner_test(fd uint64) {
    var tok Token;
    tok.id = 0;
    tok.nextChar = 0;

    for tok = GetNextToken(fd,tok); tok.id != TOKEN_EOS; tok = GetNextToken(fd,tok) {
        tmp_print(tok);
    }
}
<commit_msg>scanner: Add some scanner tokens.<commit_after>// Copyright 2010 The GoGo Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package main

//
// Imports
// TODO: Get rid of all non libgogo ones.
//
import "fmt"
import "./libgogo/_obj/libgogo"

//
// Set of recognized tokens
//
const TOKEN_IDENTIFIER uint64 = 1;  // Identifier
const TOKEN_STRING = 2;             // String using "..."
const TOKEN_EOS = 3;                // End of Scan
const TOKEN_LBRAC = 4;              // Left bracket '('
const TOKEN_RBRAC = 5;              // Right bracket ')'
const TOKEN_LSBRAC = 6;             // Left square bracket '['
const TOKEN_RSBRAC = 7;             // Right square bracket ']'
const TOKEN_INTEGER = 8;            // Integer number
const TOKEN_LCBRAC = 9;             // Left curly bracket '{'
const TOKEN_RCBRAC = 10;            // Right curly bracket '}'
const TOKEN_PT = 11;                // Point '.'
const TOKEN_NOT = 12;               // Single not '!'
const TOKEN_NOTEQUAL = 13;          // Comparison, not equal '!='
const TOKEN_SEMICOLON = 14;         // Semi-colon ';'
const TOKEN_COLON = 15;             // Colon ','
const TOKEN_ASSIGN = 16;            // Assignment '='
const TOKEN_EQUALS = 17;            // Equal comparison '=='
const TOKEN_CHAR = 18;              // Single Quoted Character
const TOKEN_REL_AND = 19;           // AND Relation
const TOKEN_REL_OR = 20;            // OR Relation
const TOKEN_REL_GTOE = 21;          // Greather-Than or Equal
const TOKEN_REL_GT = 22;            // Greather-Than
const TOKEN_REL_LTOE = 23;          // Less-Than or Equal
const TOKEN_REL_LT = 24;            // Less-Than
const TOKEN_ARITH_PLUS = 25;        // Arith. Plus
const TOKEN_ARITH_MINUS = 26;       // Arith. Minus

//
// Token struct holding the relevant data of a parsed token.
//
type Token struct {
    id uint64; // The id. Is one of TOKEN_*
    value [255]byte; // If the id requires a value to be stored, it is found here
    value_len uint64; // Length of the value stored in `value`

    nextChar byte; // Sometime the next char is already read. It is stored here to be re-assigned in the next GetNextToken() round
};

/*
 * Function getting the next token.
 */
func GetNextToken(fd uint64, oldToken Token) Token {
    var singleChar byte; // Byte holding the last read value
    /*
     * Flag indicating whether we are in a comment.
     * 0 for no comment
     * 1 for a single line comment
     * 2 for a multi line comment
     */
    var inComment uint64;
    var done uint64; // Flag indicating whether a cycle (Token) is finsihed
    var spaceDone uint64; // Flag indicating whether an abolishment cycle is finished
    var newToken Token; // The new token that is returned

    // Initialize variables
    done = 0;
    spaceDone = 0;
    newToken.id = 0;
    newToken.value_len = 0;
    newToken.nextChar = 0;
    inComment = 0;

    // If the old Token had to read the next char (and stored it), we can now
    // get it back
    if oldToken.nextChar == 0 {
        singleChar=libgogo.GetChar(fd)
    } else {
        singleChar = oldToken.nextChar;
    }

    // check if it is a valid read, or an EOF
    if singleChar == 0 {
        newToken.id = TOKEN_EOS;
        done = 1;
        spaceDone = 1;
    }

    //
    // Cleaning Tasks
    // The next part strips out spaces, newlines, tabs, and comments
    // Comments can either be single line with double slashes (//) or multiline
    // using C++ syntax /* */
    //
    for ; spaceDone != 1; {

        // check whether a comment is starting
        if singleChar == '/' {
            // if we are in a comment skip the rest, get the next char otherwise
            if inComment == 0 {
                singleChar = libgogo.GetChar(fd);
                if singleChar == '/' {
                    // we are in a single line comment (until newline is found)
                    inComment = 1;
                } else {
                    if singleChar == '*' {
                        // we are in a multiline comment (until ending is found)
                        inComment = 2;
                    } else {
                        tmp_error(">> Scanner: Unkown character combination for comments. Exiting.");
                    }
                }
            }
        }

        // check whether a multi-line comment is ending
        if singleChar == '*' {
            singleChar = libgogo.GetChar(fd);
            if singleChar == '/' {
                if inComment == 2 {
                    inComment = 0;
                    singleChar = libgogo.GetChar(fd);
                }
            }
        }

        // if character is a newline:
        //  *) if in a singleline comment, exit the comment
        //  *) skip otherwise
        if singleChar == 10 {
            if inComment == 1 {
                inComment = 0;
            }
        }

        // handle everything that is not a space,tab,newline
        if singleChar != ' ' && singleChar != 9 && singleChar != 10 {
            // if not in a comment we have our current valid char
            if inComment == 0 {
                spaceDone = 1;
            }

            // check if GetChar() returned EOF while skipping
            if singleChar == 0 {
                newToken.id = TOKEN_EOS;
                spaceDone = 1;
                done = 1;
            }
        }

        // if we are not done until now, get a new character and start another abolishing cycle
        if spaceDone == 0 {
            singleChar=libgogo.GetChar(fd);
        }
    }

    //
    // Actual scanning part starts here
    //

    // Catch identifiers
    // identifier = letter { letter | digit }.
    if (done != 1) && (singleChar >= 'A' && singleChar <= 'Z') || (singleChar >= 'a' && singleChar <= 'z') || singleChar == '_' { // check for letter or _
        newToken.id = TOKEN_IDENTIFIER;
        // preceding characters may be letter,_, or a number
        for ; (singleChar >= 'A' && singleChar <= 'Z') || (singleChar >= 'a' && singleChar <= 'z') || singleChar == '_' || (singleChar >= '0' && singleChar <= '9'); singleChar = libgogo.GetChar(fd) {
            newToken.value[newToken.value_len] = singleChar;
            newToken.value_len = newToken.value_len +1;
        }
        newToken.value[newToken.value_len] = 0;
        // save the last read character for the next GetNextToken() cycle
        newToken.nextChar = singleChar;
        done = 1;
    }

    // string "..."
    if (done != 1) && singleChar == '"' {
        newToken.id = TOKEN_STRING;
        for singleChar = libgogo.GetChar(fd); singleChar != '"' &&singleChar > 31 && singleChar < 127;singleChar = libgogo.GetChar(fd) {
            newToken.value[newToken.value_len] = singleChar;
            newToken.value_len = newToken.value_len +1;
        }
        newToken.value[newToken.value_len] = 0;
        if singleChar != '"' {
            tmp_error(">> Scanner: String not closing. Exiting.");
        }
        done = 1;
    }

    // Single Quoted Character
    // Needs to be converted later on
    if (done != 1) && singleChar == 39 {
        newToken.id = TOKEN_CHAR;
        for singleChar = libgogo.GetChar(fd); singleChar != 39 && singleChar > 31 && singleChar < 127;singleChar = libgogo.GetChar(fd) {
            newToken.value[newToken.value_len] = singleChar;
            newToken.value_len = newToken.value_len +1;
        }
        newToken.value[newToken.value_len] = 0;
        if singleChar != 39 {
            tmp_error(">> Scanner: Character not closing. Exiting.");
        }
        done = 1;
    }

    // left brace (
    if (done != 1) && singleChar == '(' {
        newToken.id = TOKEN_LBRAC;
        done = 1;
    }

    // right brace )
    if (done != 1) && singleChar == ')' {
        newToken.id = TOKEN_RBRAC;
        done = 1;
    }

    // left square bracket [
    if (done != 1) && singleChar == '[' {
        newToken.id = TOKEN_LSBRAC;
        done = 1;
    }

    // right square bracket ]
    if (done != 1) && singleChar == ']' {
        newToken.id = TOKEN_RSBRAC;
        done = 1;
    }

    // integer
    if (done != 1) && singleChar > 47 && singleChar < 58 {
        newToken.id = TOKEN_INTEGER;
        for ; singleChar > 47 && singleChar < 58 ; singleChar = libgogo.GetChar(fd) {
            newToken.value[newToken.value_len] = singleChar;
            newToken.value_len = newToken.value_len +1;
        }
        newToken.value[newToken.value_len] = 0
        newToken.nextChar = singleChar;
        done = 1;
    }

    // Left curly bracket '{'
    if (done != 1) && singleChar == '{' {
        newToken.id = TOKEN_LCBRAC;
        done = 1;
    }

    // Right curly bracket '}'
    if (done != 1) && singleChar == '}' {
        newToken.id = TOKEN_RCBRAC;
        done = 1;
    }

    // Point '.'
    if (done != 1) && singleChar == '.' {
        newToken.id = TOKEN_PT;
        done = 1;
    }

    // Not ('!') or Not Equal ('!=')
    if (done != 1) && singleChar == '!' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '=' {
            newToken.id = TOKEN_NOTEQUAL;
        } else {
            newToken.id = TOKEN_NOT;
            newToken.nextChar = singleChar;
        }
        done = 1;
    }

    // Semicolon ';'
    if (done != 1) && singleChar == ';' {
        newToken.id = TOKEN_SEMICOLON;
        done = 1;
    }

    // Colon ','
    if (done != 1) && singleChar == ',' {
        newToken.id = TOKEN_COLON;
        done = 1;
    }

    // Assignment '=' or Equals comparison '=='
    if (done != 1) && singleChar == '=' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '=' {
            newToken.id = TOKEN_EQUALS;
        } else {
            newToken.id = TOKEN_ASSIGN;
            newToken.nextChar = singleChar;
        }
        done = 1;
    }

    // AND Relation '&&'
    if (done != 1) && singleChar == '&' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '&' {
            newToken.id = TOKEN_REL_AND;
        } else {
            tmp_error(">> Scanner: No address operator supported.");
        }
        done = 1;
    }

    // OR Relation '||'
    if (done != 1) && singleChar == '|' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '|' {
            newToken.id = TOKEN_REL_OR;
        } else {
            tmp_error(">> Scanner: No binary OR (|) supported. Only ||.");
        }
        done = 1;
    }

    // Greater and Greater-Than relation
    if (done != 1) && singleChar == '>' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '=' {
            newToken.id = TOKEN_REL_GTOE;
        } else {
            newToken.id = TOKEN_REL_GT;
            newToken.nextChar = singleChar;
        }
        done = 1;
    }

    // Less and Less-Than relation
    if (done != 1) && singleChar == '<' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '=' {
            newToken.id = TOKEN_REL_LTOE;
        } else {
            newToken.id = TOKEN_REL_LT;
            newToken.nextChar = singleChar;
        }
        done = 1;
    }

    if (done != 1) && singleChar == '+' {
        newToken.id = TOKEN_ARITH_PLUS;
        done = 1;
    }

    if (done != 1) && singleChar == '-' {
        newToken.id = TOKEN_ARITH_MINUS;
        done = 1;
    }

    if done != 1 {
        fmt.Printf("'%c'\n",singleChar);
        tmp_error(">> Scanner: Unkown char detected. Exiting");
    }

    return newToken;
}

func tmp_print(tok Token) {
    var i int;
    fmt.Printf("Token Id: %d\n",tok.id);
    if tok.id == TOKEN_IDENTIFIER || tok.id == TOKEN_STRING {
        fmt.Printf("Identifier/String value: ");
        for i=0;tok.value[i] != 0;i=i+1 {
            fmt.Printf("%c",tok.value[i]);
        }
        fmt.Printf("\n");
    }
}

func tmp_error ( s string) {
    fmt.Printf("%s\n",s);
    libgogo.Exit(1);
}

func scanner_test(fd uint64) {
    var tok Token;
    tok.id = 0;
    tok.nextChar = 0;

    for tok = GetNextToken(fd,tok); tok.id != TOKEN_EOS; tok = GetNextToken(fd,tok) {
        tmp_print(tok);
    }
}
<|endoftext|>