<commit_before>package whproxy

import (
	"bufio"
	"io"
	"net/http"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/gorilla/websocket"
	"github.com/taskcluster/webhooktunnel/util"
	"github.com/taskcluster/webhooktunnel/wsmux"
)

const (
	monthUnix = 31 * 24 * time.Hour
)

// Config contains the run time parameters for the proxy
type Config struct {
	// Upgrader is a websocket.Upgrader instance which is used to upgrade incoming
	// websocket connections from Clients.
	Upgrader websocket.Upgrader

	// Logger is used to log proxy events. Refer util.Logger.
	Logger util.Logger

	// JWTSecretA and JWTSecretB are used by the proxy to verify JWTs from Clients.
	JWTSecretA []byte
	JWTSecretB []byte
	// Domain where proxy will be hosted
	Domain string
	// set to true if serving with TLS
	TLS bool

	DomainHosted bool
}

// proxy is used to send http and ws requests to a registered client.
// New proxy can be created by using whproxy.New()
type proxy struct {
	m               sync.RWMutex
	pool            map[string]*wsmux.Session
	upgrader        websocket.Upgrader
	logger          util.Logger
	onSessionRemove func(string)
	jwtSecretA      []byte
	jwtSecretB      []byte
	domain          string
	domainHosted    bool
	tls             bool
}

// regex for parsing requests
var (
	registerRe = regexp.MustCompile("^/register/([a-z0-9_-]+)/?$")
	serveRe    = regexp.MustCompile("^/([a-z0-9_-]+)/(.*)$")
)

// New creates a new proxy instance and wraps it as an http.Handler.
func New(conf Config) (http.Handler, error) {
	return newProxy(conf)
}

// ServeHTTP implements http.Handler so that the proxy may be used as a handler in a Mux or http.Server
func (p *proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	// check for path style
	// eg. domain = "tcproxy.net", req url = "https://tcproxy.net/.../"
	// host may be of the form "host:port"
	// register requests can only be path style
	p.logf("", r.RemoteAddr, "Host=%s Path=%s", r.Host, r.URL.Path)
	if strings.HasPrefix(r.Host, p.domain) {
		// register will be matched first
		if registerRe.MatchString(r.URL.Path) { // matches "/register/(\w+)/?$"
			id := registerRe.FindStringSubmatch(r.URL.Path)[1]
			tokenString := util.ExtractJWT(r.Header.Get("Authorization"))
			p.register(w, r, id, tokenString)
			return
		}

		s := strings.TrimPrefix(r.URL.Path, "/")
		index := strings.Index(s, "/")
		id, path := "", "/"
		if index < 0 {
			id = s
		} else {
			id = s[:index]
			path = s[index:]
		}
		if id == "" {
			http.NotFound(w, r)
			return
		}
		p.serveRequest(w, r, id, path)
		return
	}

	// if address has port, strip port
	host := r.Host
	index := strings.Index(host, ":")
	if index > 0 {
		host = host[:index]
	}
	p.logf("", r.RemoteAddr, "rewritten host=%s", host)
	if strings.HasSuffix(host, "."+p.domain) {
		index := strings.Index(r.Host, ".")
		id := r.Host[:index]
		path := r.URL.Path
		if id == "" {
			http.NotFound(w, r)
			return
		}
		p.serveRequest(w, r, id, path)
		return
	}

	http.NotFound(w, r)
}

func newProxy(conf Config) (*proxy, error) {
	p := &proxy{
		pool:         make(map[string]*wsmux.Session),
		upgrader:     conf.Upgrader,
		logger:       conf.Logger,
		jwtSecretA:   conf.JWTSecretA,
		jwtSecretB:   conf.JWTSecretB,
		domain:       conf.Domain,
		domainHosted: conf.DomainHosted,
		tls:          conf.TLS,
	}

	if len(p.jwtSecretA) == 0 || len(p.jwtSecretB) == 0 {
		panic("whproxy: missing secrets")
	}

	if p.logger == nil {
		p.logger = &util.NilLogger{}
	}

	return p, nil

}

// setSessionRemoveHandler sets a function which is called when a wsmux Session is removed from
// the proxy due to closure or error.
func (p *proxy) setSessionRemoveHandler(h func(string)) {
	p.m.Lock()
	defer p.m.Unlock()
	p.onSessionRemove = h
}

// getWorkerSession returns true if a session with the given id is present
func (p *proxy) getWorkerSession(id string) (*wsmux.Session, bool) {
	p.m.RLock()
	defer p.m.RUnlock()
	s, ok := p.pool[id]
	return s, ok
}

// removeTunnel is an idempotent operation which deletes a client session from the proxy's
// pool
func (p *proxy) removeTunnel(id string) {
	p.m.Lock()
	defer p.m.Unlock()
	delete(p.pool, id)
	p.logf(id, "", "session removed")
}

// register is used to connect a client to the proxy so that it can start serving API endpoints.
// The request must contain the tunnel ID in the url.
// The request is validated by the proxy and the http connection is upgraded to websocket.
func (p *proxy) register(w http.ResponseWriter, r *http.Request, id, tokenString string) {

	p.logf(id, r.RemoteAddr, "requesting client registration")
	if tokenString == "" {
		// No jwt. Connection not authorized
		p.logerrorf(id, r.RemoteAddr, "could not retreive auth token")
		http.Error(w, http.StatusText(400), 400)
		return
	}

	if !websocket.IsWebSocketUpgrade(r) {
		p.logerrorf(id, r.RemoteAddr, "request must be websocket upgrade")
		http.NotFound(w, r)
		return
	}

	// validation does not require lock
	if err := p.validateJWT(id, tokenString); err != nil {
		p.logerrorf(id, r.RemoteAddr, "unable to validate token: %v", err)
		http.Error(w, http.StatusText(400), 400)
		return
	}

	// we should lock while upgrading
	// otherwise there could be a possible race condition
	p.m.Lock()
	defer p.m.Unlock()

	// remove old session and allow new connection
	session := p.pool[id]

	// remove the close callback so that functions are not accidentally called
	if session != nil {
		p.logf(id, r.RemoteAddr, "removed previous session")
		session.SetCloseCallback(nil)
	}
	// unlock and close old session while upgrading
	if session != nil {
		_ = session.Close()
	}

	delete(p.pool, id)

	header := make(http.Header)

	urlScheme := "http://"
	if p.tls {
		urlScheme = "https://"
	}

	url := urlScheme + p.domain + "/" + id
	if p.domainHosted {
		url = urlScheme + id + "." + p.domain
	}
	header.Set("x-webhooktunnel-client-url", url)
	p.logf(id, r.RemoteAddr, "sending url= %s", url)
	conn, err := p.upgrader.Upgrade(w, r, header)
	if err != nil {
		p.logger.Print(err)
		return
	}

	// generate config
	conf := wsmux.Config{
		StreamBufferSize: 4 * 1024,
		CloseCallback: func() {
			p.removeTunnel(id)
			if p.onSessionRemove != nil {
				p.onSessionRemove(id)
			}
		},
		// Log: p.logger,
	}

	p.pool[id] = wsmux.Server(conn, conf)
	p.logf(id, r.RemoteAddr, "added new tunnel")
}

// serveRequest serves tunnel endpoints to viewers
func (p *proxy) serveRequest(w http.ResponseWriter, r *http.Request, id string, path string) {
	// log new request arrival
	p.logf(id, r.RemoteAddr, "request: host=%s path=%s", r.Host, path)

	session, ok := p.getWorkerSession(id)

	// 404 if tunnel is not registered on this proxy
	if !ok {
		// DHT code will be added here
		p.logerrorf(id, r.RemoteAddr, "could not find requested tunnel")
		http.Error(w, http.StatusText(404), 404)
		return
	}

	// set original path as header
	r.Header.Set("x-webhooktunnel-original-path", r.URL.Path)

	// check for a websocket request
	if websocket.IsWebSocketUpgrade(r) {
		_ = p.websocketProxy(w, r, session, id)
		return
	}

	// Open a stream to the tunnel session
	r.URL.Path = path
	p.logf(id, r.RemoteAddr, "attempting to open new stream")
	reqStream, streamID, err := session.Open()
	if err != nil {
		p.logerrorf(id, r.RemoteAddr, "could not open stream: path=%s", path)
		http.Error(w, http.StatusText(500), 500)
		return
	}
	p.logf(id, r.RemoteAddr, "opened new stream: ID=%d", streamID)

	// rewrite path for tunnel and write request
	err = r.Write(reqStream)
	if err != nil {
		p.logerrorf(id, r.RemoteAddr, "could not write request: path=%s", path)
		http.Error(w, http.StatusText(500), 500)
		return
	}

	// read response from tunnel
	bufReader := bufio.NewReader(reqStream)
	resp, err := http.ReadResponse(bufReader, r)
	if err != nil {
		p.logerrorf(id, r.RemoteAddr, "could not read response: path=%s", path)
		http.Error(w, http.StatusText(500), 500)
		return
	}

	// manually proxy response
	// clear responseWriter headers and write response headers instead
	for k := range w.Header() {
		w.Header().Del(k)
	}
	for k, v := range resp.Header {
		w.Header()[k] = v
	}

	// dump headers
	w.WriteHeader(resp.StatusCode)
	if resp.Body == nil {
		return
	}

	// stream body to viewer and close wsmux stream
	defer func() {
		_ = reqStream.Close()
	}()

	flusher, ok := w.(http.Flusher)
	// flusher may not be implemented by a ResponseWriter wrapper
	// simple copy
	if !ok {
		n, err := io.Copy(w, resp.Body)
		p.logf(id, r.RemoteAddr, "data transfered over request: %d bytes, error: %v", n, err)
		// log here
		return
	}

	p.logf(id, r.RemoteAddr, "streaming http")
	wf := &threadSafeWriteFlusher{w: w, f: flusher}
	n, err := copyAndFlush(wf, resp.Body, 100*time.Millisecond)
	p.logf(id, r.RemoteAddr, "data transfered over request: %d bytes, error: %v", n, err)
}

// validate jwt
// jwt signing and verification algorithm must be HMAC
func (p *proxy) validateJWT(id string, tokenString string) error {
	// parse jwt token
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, ErrUnexpectedSigningMethod
		}
		return p.jwtSecretA, nil
	})

	if err != nil {
		// log first error
		p.logerrorf(id, "", "%v: trying with second secret", err)

		token, err = jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, ErrUnexpectedSigningMethod
			}
			return p.jwtSecretB, nil
		})
	}

	if err != nil {
		p.logerrorf(id, "", "%v: auth failed", err)
		return ErrAuthFailed
	}

	// check claims
	now := time.Now().Unix()
	claims, ok := token.Claims.(jwt.MapClaims)

	if !ok {
		p.logerrorf(id, "", "%v: could not parse claims", err)
		return ErrTokenNotValid
	}
	p.logf(id, "", "claims: %v", claims)

	if !claims.VerifyExpiresAt(now, true) {
		p.logerrorf(id, "", "%v", err)
		return ErrAuthFailed
	}
	if !claims.VerifyNotBefore(now, true) {
		p.logerrorf(id, "", "%v", err)
		return ErrAuthFailed
	}
	if claims["tid"] != id {
		p.logerrorf(id, "", "%v", err)
		return ErrAuthFailed
	}

	if claims["exp"].(float64)-claims["nbf"].(float64) > float64(monthUnix) {
		p.logerrorf(id, "", "jwt should not be valid for more than 31 days")
		return ErrAuthFailed
	}

	return nil
}

// proxy logging utilities
const (
	fmtString      = "[PROXY] INFO: id=%s remote_ip=%s "
	fmtErrorString = "[PROXY] ERROR: id=%s remote_ip=%s "
)

// NOTE: cannot use logrus methods
func (p *proxy) logf(id string, remoteAddr string, format string, v ...interface{}) {
	args := []interface{}{id, remoteAddr}
	args = append(args, v...)
	p.logger.Printf(fmtString+format, args...)
}

func (p *proxy) logerrorf(id string, remoteAddr string, format string, v ...interface{}) {
	args := []interface{}{id, remoteAddr}
	args = append(args, v...)
	p.logger.Printf(fmtErrorString+format, args...)
}
<commit_msg>auth failure returns 401<commit_after>package whproxy

import (
	"bufio"
	"io"
	"net/http"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/gorilla/websocket"
	"github.com/taskcluster/webhooktunnel/util"
	"github.com/taskcluster/webhooktunnel/wsmux"
)

const (
	monthUnix = 31 * 24 * time.Hour
)

// Config contains the run time parameters for the proxy
type Config struct {
	// Upgrader is a websocket.Upgrader instance which is used to upgrade incoming
	// websocket connections from Clients.
	Upgrader websocket.Upgrader

	// Logger is used to log proxy events. Refer util.Logger.
	Logger util.Logger

	// JWTSecretA and JWTSecretB are used by the proxy to verify JWTs from Clients.
	JWTSecretA []byte
	JWTSecretB []byte
	// Domain where proxy will be hosted
	Domain string
	// set to true if serving with TLS
	TLS bool

	DomainHosted bool
}

// proxy is used to send http and ws requests to a registered client.
// New proxy can be created by using whproxy.New()
type proxy struct {
	m               sync.RWMutex
	pool            map[string]*wsmux.Session
	upgrader        websocket.Upgrader
	logger          util.Logger
	onSessionRemove func(string)
	jwtSecretA      []byte
	jwtSecretB      []byte
	domain          string
	domainHosted    bool
	tls             bool
}

// regex for parsing requests
var (
	registerRe = regexp.MustCompile("^/register/([a-z0-9_-]+)/?$")
	serveRe    = regexp.MustCompile("^/([a-z0-9_-]+)/(.*)$")
)

// New creates a new proxy instance and wraps it as an http.Handler.
func New(conf Config) (http.Handler, error) {
	return newProxy(conf)
}

// ServeHTTP implements http.Handler so that the proxy may be used as a handler in a Mux or http.Server
func (p *proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	// check for path style
	// eg. domain = "tcproxy.net", req url = "https://tcproxy.net/.../"
	// host may be of the form "host:port"
	// register requests can only be path style
	p.logf("", r.RemoteAddr, "Host=%s Path=%s", r.Host, r.URL.Path)
	if strings.HasPrefix(r.Host, p.domain) {
		// register will be matched first
		if registerRe.MatchString(r.URL.Path) { // matches "/register/(\w+)/?$"
			id := registerRe.FindStringSubmatch(r.URL.Path)[1]
			tokenString := util.ExtractJWT(r.Header.Get("Authorization"))
			p.register(w, r, id, tokenString)
			return
		}

		s := strings.TrimPrefix(r.URL.Path, "/")
		index := strings.Index(s, "/")
		id, path := "", "/"
		if index < 0 {
			id = s
		} else {
			id = s[:index]
			path = s[index:]
		}
		if id == "" {
			http.NotFound(w, r)
			return
		}
		p.serveRequest(w, r, id, path)
		return
	}

	// if address has port, strip port
	host := r.Host
	index := strings.Index(host, ":")
	if index > 0 {
		host = host[:index]
	}
	p.logf("", r.RemoteAddr, "rewritten host=%s", host)
	if strings.HasSuffix(host, "."+p.domain) {
		index := strings.Index(r.Host, ".")
		id := r.Host[:index]
		path := r.URL.Path
		if id == "" {
			http.NotFound(w, r)
			return
		}
		p.serveRequest(w, r, id, path)
		return
	}

	http.NotFound(w, r)
}

func newProxy(conf Config) (*proxy, error) {
	p := &proxy{
		pool:         make(map[string]*wsmux.Session),
		upgrader:     conf.Upgrader,
		logger:       conf.Logger,
		jwtSecretA:   conf.JWTSecretA,
		jwtSecretB:   conf.JWTSecretB,
		domain:       conf.Domain,
		domainHosted: conf.DomainHosted,
		tls:          conf.TLS,
	}

	if len(p.jwtSecretA) == 0 || len(p.jwtSecretB) == 0 {
		panic("whproxy: missing secrets")
	}

	if p.logger == nil {
		p.logger = &util.NilLogger{}
	}

	return p, nil

}

// setSessionRemoveHandler sets a function which is called when a wsmux Session is removed from
// the proxy due to closure or error.
func (p *proxy) setSessionRemoveHandler(h func(string)) {
	p.m.Lock()
	defer p.m.Unlock()
	p.onSessionRemove = h
}

// getWorkerSession returns true if a session with the given id is present
func (p *proxy) getWorkerSession(id string) (*wsmux.Session, bool) {
	p.m.RLock()
	defer p.m.RUnlock()
	s, ok := p.pool[id]
	return s, ok
}

// removeTunnel is an idempotent operation which deletes a client session from the proxy's
// pool
func (p *proxy) removeTunnel(id string) {
	p.m.Lock()
	defer p.m.Unlock()
	delete(p.pool, id)
	p.logf(id, "", "session removed")
}

// register is used to connect a client to the proxy so that it can start serving API endpoints.
// The request must contain the tunnel ID in the url.
// The request is validated by the proxy and the http connection is upgraded to websocket.
func (p *proxy) register(w http.ResponseWriter, r *http.Request, id, tokenString string) {

	p.logf(id, r.RemoteAddr, "requesting client registration")
	if tokenString == "" {
		// No jwt. Connection not authorized
		p.logerrorf(id, r.RemoteAddr, "could not retreive auth token")
		http.Error(w, http.StatusText(400), 400)
		return
	}

	if !websocket.IsWebSocketUpgrade(r) {
		p.logerrorf(id, r.RemoteAddr, "request must be websocket upgrade")
		http.NotFound(w, r)
		return
	}

	// validation does not require lock
	if err := p.validateJWT(id, tokenString); err != nil {
		p.logerrorf(id, r.RemoteAddr, "unable to validate token: %v", err)
		http.Error(w, http.StatusText(401), 401)
		return
	}

	// we should lock while upgrading
	// otherwise there could be a possible race condition
	p.m.Lock()
	defer p.m.Unlock()

	// remove old session and allow new connection
	session := p.pool[id]

	// remove the close callback so that functions are not accidentally called
	if session != nil {
		p.logf(id, r.RemoteAddr, "removed previous session")
		session.SetCloseCallback(nil)
	}
	// unlock and close old session while upgrading
	if session != nil {
		_ = session.Close()
	}

	delete(p.pool, id)

	header := make(http.Header)

	urlScheme := "http://"
	if p.tls {
		urlScheme = "https://"
	}

	url := urlScheme + p.domain + "/" + id
	if p.domainHosted {
		url = urlScheme + id + "." + p.domain
	}
	header.Set("x-webhooktunnel-client-url", url)
	p.logf(id, r.RemoteAddr, "sending url= %s", url)
	conn, err := p.upgrader.Upgrade(w, r, header)
	if err != nil {
		p.logger.Print(err)
		return
	}

	// generate config
	conf := wsmux.Config{
		StreamBufferSize: 4 * 1024,
		CloseCallback: func() {
			p.removeTunnel(id)
			if p.onSessionRemove != nil {
				p.onSessionRemove(id)
			}
		},
		// Log: p.logger,
	}

	p.pool[id] = wsmux.Server(conn, conf)
	p.logf(id, r.RemoteAddr, "added new tunnel")
}

// serveRequest serves tunnel endpoints to viewers
func (p *proxy) serveRequest(w http.ResponseWriter, r *http.Request, id string, path string) {
	// log new request arrival
	p.logf(id, r.RemoteAddr, "request: host=%s path=%s", r.Host, path)

	session, ok := p.getWorkerSession(id)

	// 404 if tunnel is not registered on this proxy
	if !ok {
		// DHT code will be added here
		p.logerrorf(id, r.RemoteAddr, "could not find requested tunnel")
		http.Error(w, http.StatusText(404), 404)
		return
	}

	// set original path as header
	r.Header.Set("x-webhooktunnel-original-path", r.URL.Path)

	// check for a websocket request
	if websocket.IsWebSocketUpgrade(r) {
		_ = p.websocketProxy(w, r, session, id)
		return
	}

	// Open a stream to the tunnel session
	r.URL.Path = path
	p.logf(id, r.RemoteAddr, "attempting to open new stream")
	reqStream, streamID, err := session.Open()
	if err != nil {
		p.logerrorf(id, r.RemoteAddr, "could not open stream: path=%s", path)
		http.Error(w, http.StatusText(500), 500)
		return
	}
	p.logf(id, r.RemoteAddr, "opened new stream: ID=%d", streamID)

	// rewrite path for tunnel and write request
	err = r.Write(reqStream)
	if err != nil {
		p.logerrorf(id, r.RemoteAddr, "could not write request: path=%s", path)
		http.Error(w, http.StatusText(500), 500)
		return
	}

	// read response from tunnel
	bufReader := bufio.NewReader(reqStream)
	resp, err := http.ReadResponse(bufReader, r)
	if err != nil {
		p.logerrorf(id, r.RemoteAddr, "could not read response: path=%s", path)
		http.Error(w, http.StatusText(500), 500)
		return
	}

	// manually proxy response
	// clear responseWriter headers and write response headers instead
	for k := range w.Header() {
		w.Header().Del(k)
	}
	for k, v := range resp.Header {
		w.Header()[k] = v
	}

	// dump headers
	w.WriteHeader(resp.StatusCode)
	if resp.Body == nil {
		return
	}

	// stream body to viewer and close wsmux stream
	defer func() {
		_ = reqStream.Close()
	}()

	flusher, ok := w.(http.Flusher)
	// flusher may not be implemented by a ResponseWriter wrapper
	// simple copy
	if !ok {
		n, err := io.Copy(w, resp.Body)
		p.logf(id, r.RemoteAddr, "data transfered over request: %d bytes, error: %v", n, err)
		// log here
		return
	}

	p.logf(id, r.RemoteAddr, "streaming http")
	wf := &threadSafeWriteFlusher{w: w, f: flusher}
	n, err := copyAndFlush(wf, resp.Body, 100*time.Millisecond)
	p.logf(id, r.RemoteAddr, "data transfered over request: %d bytes, error: %v", n, err)
}

// validate jwt
// jwt signing and verification algorithm must be HMAC
func (p *proxy) validateJWT(id string, tokenString string) error {
	// parse jwt token
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, ErrUnexpectedSigningMethod
		}
		return p.jwtSecretA, nil
	})

	if err != nil {
		// log first error
		p.logerrorf(id, "", "%v: trying with second secret", err)

		token, err = jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, ErrUnexpectedSigningMethod
			}
			return p.jwtSecretB, nil
		})
	}

	if err != nil {
		p.logerrorf(id, "", "%v: auth failed", err)
		return ErrAuthFailed
	}

	// check claims
	now := time.Now().Unix()
	claims, ok := token.Claims.(jwt.MapClaims)

	if !ok {
		p.logerrorf(id, "", "%v: could not parse claims", err)
		return ErrTokenNotValid
	}
	p.logf(id, "", "claims: %v", claims)

	if !claims.VerifyExpiresAt(now, true) {
		p.logerrorf(id, "", "%v", err)
		return ErrAuthFailed
	}
	if !claims.VerifyNotBefore(now, true) {
		p.logerrorf(id, "", "%v", err)
		return ErrAuthFailed
	}
	if claims["tid"] != id {
		p.logerrorf(id, "", "%v", err)
		return ErrAuthFailed
	}

	if claims["exp"].(float64)-claims["nbf"].(float64) > float64(monthUnix) {
		p.logerrorf(id, "", "jwt should not be valid for more than 31 days")
		return ErrAuthFailed
	}

	return nil
}

// proxy logging utilities
const (
	fmtString      = "[PROXY] INFO: id=%s remote_ip=%s "
	fmtErrorString = "[PROXY] ERROR: id=%s remote_ip=%s "
)

// NOTE: cannot use logrus methods
func (p *proxy) logf(id string, remoteAddr string, format string, v ...interface{}) {
	args := []interface{}{id, remoteAddr}
	args = append(args, v...)
	p.logger.Printf(fmtString+format, args...)
}

func (p *proxy) logerrorf(id string, remoteAddr string, format string, v ...interface{}) {
	args := []interface{}{id, remoteAddr}
	args = append(args, v...)
	p.logger.Printf(fmtErrorString+format, args...)
}
<|endoftext|>


<commit_before>package rabbitgo

import (
	//"errors"
	"errors"
	"fmt"
	"time"

	log "github.com/koding/logging"
	"github.com/streadway/amqp"
)

type Consumer struct {
	conn       *Connection
	ch         *amqp.Channel
	e          *Exchange
	q          *Queue
	deliveries <-chan amqp.Delivery
	handler    func(*Delivery)
	handlerRPC func(*Delivery)
	cc         *ConsumerConfig
	bc         *BindingConfig
	closed     bool
	// A notifiyng channel for publishings
	// will be used for sync. between close channel and consume handler
	done chan bool
	err  chan error
}

type ConsumerConfig struct {
	Tag           string
	PrefetchCount int
	PrefetchSize  int
	MaxDeliveries int
	Timeout       int
	AutoAck       bool
	Exclusive     bool
	NoLocal       bool
	NoWait        bool
	Args          amqp.Table
}

type BindingConfig struct {
	RoutingKey string
	NoWait     bool
	Args       amqp.Table
}

func (c *Consumer) Deliveries() <-chan amqp.Delivery {
	return c.deliveries
}

// NewConsumer is a constructor for consumer creation
// Accepts Exchange, Queue, BindingOptions and ConsumerOptions
func (c *Connection) NewConsumer(e *Exchange, q *Queue, bc *BindingConfig, cc *ConsumerConfig) (*Consumer, error) {
	ch, err := c.conn.Channel()
	if err != nil {
		return nil, err
	}
	consumer, err := c.newConsumerFromChannel(e, q, bc, cc, ch)
	if err != nil {
		return nil, err
	}
	return consumer, nil
}

func (c *Connection) newConsumerFromChannel(e *Exchange, q *Queue, bc *BindingConfig, cc *ConsumerConfig, ch *amqp.Channel) (*Consumer, error) {
	consumer := &Consumer{
		conn: c,
		ch:   ch,
		done: make(chan bool),
		cc:   cc,
		bc:   bc,
		e:    e,
		q:    q,
	}
	if err := consumer.connect(); err != nil {
		return nil, err
	}
	return consumer, nil
}

// connect internally declares the exchanges and queues
func (c *Consumer) connect() error {
	_, err := c.ch.QueueDeclare(
		c.q.Name,       // name of the queue
		c.q.Durable,    // durable
		c.q.AutoDelete, // delete when usused
		c.q.Exclusive,  // exclusive
		c.q.NoWait,     // noWait
		c.q.Args,       // arguments
	)
	if err != nil {
		return err
	}
	if c.cc.PrefetchCount > 0 || c.cc.PrefetchSize > 0 {
		err = c.ch.Qos(
			c.cc.PrefetchCount, // prefetch count
			c.cc.PrefetchSize,  // prefetch size
			false,              // global
		)
	}
	if err != nil {
		return err
	}
	if c.e != nil {
		err := c.ch.ExchangeDeclare(
			c.e.Name,       // name of the exchange
			c.e.Type,       // type
			c.e.Durable,    // durable
			c.e.AutoDelete, // delete when complete
			c.e.Internal,   // internal
			c.e.NoWait,     // noWait
			c.e.Args,       // arguments
		)
		if err != nil {
			return err
		}
		err = c.ch.QueueBind(
			// bind to real queue
			c.q.Name,        // name of the queue
			c.bc.RoutingKey, // bindingKey
			c.e.Name,        // sourceExchange
			c.bc.NoWait,     // noWait
			c.bc.Args,       // arguments
		)
		if err != nil {
			return err
		}
	}
	return nil
}

func (c *Consumer) consume() error {
	deliveries, err := c.ch.Consume(
		c.q.Name,       // name
		c.cc.Tag,       // consumerTag,
		c.cc.AutoAck,   // autoAck
		c.cc.Exclusive, // exclusive
		c.cc.NoLocal,   // noLocal
		c.cc.NoWait,    // noWait
		c.cc.Args,      // arguments
	)
	// should we stop streaming, in order not to consume from server?
	c.deliveries = deliveries
	return err
}

// Consume accepts a handler function for every message streamed from RabbitMq
// will be called within this handler func
func (c *Consumer) Consume(handler func(delivery *Delivery)) error {
	count := 0
	err := c.consume()
	if err != nil {
		return err
	}

	go func() {
		if timeout := c.cc.Timeout; timeout > 0 {
			ticker := time.NewTicker(100 * time.Millisecond)
			var tickerCount uint
			defer ticker.Stop()
			select {
			case <-ticker.C:
				tickerCount = tickerCount + 1
				if tickerCount*100 >= uint(timeout) {
					c.err <- errors.New(fmt.Sprintf("%s timeout", time.Duration(timeout)*time.Millisecond))
					return
				}
			}
		}
	}()
	c.handler = handler
	log.Info("handle: deliveries channel starting")
	defer log.Info("handle: deliveries channel closed")
	// handle all consumer errors, if required re-connect
	// there are problems with reconnection logic for now
	select {
	case c.err <- err:
		c.Shutdown()
		return err
	case d := <-c.deliveries:
		delivery := &Delivery{&d, c, false, nil, nil, false}
		handler(delivery)
		count++
		if c.cc.MaxDeliveries > 0 && count >= c.cc.MaxDeliveries {
			c.done <- true
			return nil
		}
	}
	//This was blocking the flow. Not sure how needed is.
	//c.done <- nil
	return nil
}

// ConsumeRPC accepts a handler function for every message streamed from RabbitMq
// will be called within this handler func.
// It returns the message to send and the content type.
func (c *Consumer) ConsumeRPC(handler func(delivery *Delivery)) error {
	err := c.consume()
	if err != nil {
		return err
	}
	c.handlerRPC = handler

	log.Info("handle: deliveries channel starting")

	// handle all consumer errors, if required re-connect
	// there are problems with reconnection logic for now
	for d := range c.deliveries {
		delivery := &Delivery{&d, c, false, nil, nil, false}
		replyTo := delivery.ReplyTo
		handler(delivery)
		if delivery.Delegated == true {
			if err = delivery.AckError; err != nil {
				log.Error("Unable to delegate an acknowledgement: " + err.Error())
			}
			continue
		}
		response := delivery.Response
		if response != nil {
			if replyTo == "" {
				log.Error("Response was ready, but received an empty routing key. Delivery was rejected.")
				delivery.RejectOrSkip(false)
				continue
			}
			publishing := &amqp.Publishing{
				Body:          response.Body,
				CorrelationId: delivery.CorrelationId,
			}
			err := c.ch.Publish(
				"",
				replyTo,
				false,
				false,
				*publishing,
			)
			if err != nil {
				log.Error("Unable to reply back: " + err.Error())
			}
		}
		delivery.AckOrSkip(delivery.preAckMultiple)
	}

	log.Info("handle: deliveries channel closed")
	return nil
}

// QOS controls how many messages the server will try to keep on the network for
// consumers before receiving delivery acks.  The intent of Qos is to make sure
// the network buffers stay full between the server and client.
func (c *Consumer) QOS(messageCount int) error {
	return c.ch.Qos(messageCount, 0, false)
}

// ConsumeMessage accepts a handler function and only consumes one message
// stream from RabbitMq
func (c *Consumer) Get(handler func(delivery *Delivery)) error {
	m, ok, err := c.ch.Get(c.q.Name, c.cc.AutoAck)
	message := &Delivery{&m, c, false, nil, nil, false}
	if err != nil {
		return err
	}
	c.handler = handler
	if ok {
		fmt.Println("Message received")
		handler(message)
	} else {
		fmt.Println("No message received")
	}
	// TODO maybe we should return ok too?
	return nil
}

func (c *Consumer) Shutdown() error {
	co := c.cc
	if err := shutdownChannel(c.ch, co.Tag); err != nil {
		return err
	}
	fmt.Println("Waiting for Consumer handler to exit")
	// if we have not called the Consume yet, we can return here
	if c.deliveries == nil {
		close(c.done)
	}
	fmt.Printf("deliveries %v", c.deliveries)
	c.closed = true
	fmt.Println("Consumer shutdown OK")
	// this channel is here for finishing the consumer's ranges of
	// delivery chans.  We need every delivery to be processed, here make
	// sure to wait for all consumers goroutines to finish before exiting our
	// process.
	return nil
}
<commit_msg>Stop ticker when deliveries are done.<commit_after>package rabbitgo

import (
	//"errors"
	"errors"
	"fmt"
	"time"

	log "github.com/koding/logging"
	"github.com/streadway/amqp"
)

type Consumer struct {
	conn       *Connection
	ch         *amqp.Channel
	e          *Exchange
	q          *Queue
	deliveries <-chan amqp.Delivery
	handler    func(*Delivery)
	handlerRPC func(*Delivery)
	cc         *ConsumerConfig
	bc         *BindingConfig
	closed     bool
	// A notifiyng channel for publishings
	// will be used for sync. between close channel and consume handler
	done chan bool
	err  chan error
}

type ConsumerConfig struct {
	Tag           string
	PrefetchCount int
	PrefetchSize  int
	MaxDeliveries int
	Timeout       int
	AutoAck       bool
	Exclusive     bool
	NoLocal       bool
	NoWait        bool
	Args          amqp.Table
}

type BindingConfig struct {
	RoutingKey string
	NoWait     bool
	Args       amqp.Table
}

func (c *Consumer) Deliveries() <-chan amqp.Delivery {
	return c.deliveries
}

// NewConsumer is a constructor for consumer creation
// Accepts Exchange, Queue, BindingOptions and ConsumerOptions
func (c *Connection) NewConsumer(e *Exchange, q *Queue, bc *BindingConfig, cc *ConsumerConfig) (*Consumer, error) {
	ch, err := c.conn.Channel()
	if err != nil {
		return nil, err
	}
	consumer, err := c.newConsumerFromChannel(e, q, bc, cc, ch)
	if err != nil {
		return nil, err
	}
	return consumer, nil
}

func (c *Connection) newConsumerFromChannel(e *Exchange, q *Queue, bc *BindingConfig, cc *ConsumerConfig, ch *amqp.Channel) (*Consumer, error) {
	consumer := &Consumer{
		conn: c,
		ch:   ch,
		done: make(chan bool),
		cc:   cc,
		bc:   bc,
		e:    e,
		q:    q,
	}
	if err := consumer.connect(); err != nil {
		return nil, err
	}
	return consumer, nil
}

// connect internally declares the exchanges and queues
func (c *Consumer) connect() error {
	_, err := c.ch.QueueDeclare(
		c.q.Name,       // name of the queue
		c.q.Durable,    // durable
		c.q.AutoDelete, // delete when usused
		c.q.Exclusive,  // exclusive
		c.q.NoWait,     // noWait
		c.q.Args,       // arguments
	)
	if err != nil {
		return err
	}
	if c.cc.PrefetchCount > 0 || c.cc.PrefetchSize > 0 {
		err = c.ch.Qos(
			c.cc.PrefetchCount, // prefetch count
			c.cc.PrefetchSize,  // prefetch size
			false,              // global
		)
	}
	if err != nil {
		return err
	}
	if c.e != nil {
		err := c.ch.ExchangeDeclare(
			c.e.Name,       // name of the exchange
			c.e.Type,       // type
			c.e.Durable,    // durable
			c.e.AutoDelete, // delete when complete
			c.e.Internal,   // internal
			c.e.NoWait,     // noWait
			c.e.Args,       // arguments
		)
		if err != nil {
			return err
		}
		err = c.ch.QueueBind(
			// bind to real queue
			c.q.Name,        // name of the queue
			c.bc.RoutingKey, // bindingKey
			c.e.Name,        // sourceExchange
			c.bc.NoWait,     // noWait
			c.bc.Args,       // arguments
		)
		if err != nil {
			return err
		}
	}
	return nil
}

func (c *Consumer) consume() error {
	deliveries, err := c.ch.Consume(
		c.q.Name,       // name
		c.cc.Tag,       // consumerTag,
		c.cc.AutoAck,   // autoAck
		c.cc.Exclusive, // exclusive
		c.cc.NoLocal,   // noLocal
		c.cc.NoWait,    // noWait
		c.cc.Args,      // arguments
	)
	// should we stop streaming, in order not to consume from server?
	c.deliveries = deliveries
	return err
}

// Consume accepts a handler function for every message streamed from RabbitMq
// will be called within this handler func
func (c *Consumer) Consume(handler func(delivery *Delivery)) error {
	count := 0
	err := c.consume()
	if err != nil {
		return err
	}

	go func() {
		if timeout := c.cc.Timeout; timeout > 0 {
			ticker := time.NewTicker(100 * time.Millisecond)
			var tickerCount uint
			defer ticker.Stop()
			select {
			case <-c.done:
				return
			case <-ticker.C:
				tickerCount = tickerCount + 1
				if tickerCount*100 >= uint(timeout) {
					c.err <- errors.New(fmt.Sprintf("%s timeout", time.Duration(timeout)*time.Millisecond))
					return
				}
			}
		}
	}()
	c.handler = handler
	log.Info("handle: deliveries channel starting")
	defer log.Info("handle: deliveries channel closed")
	// handle all consumer errors, if required re-connect
	// there are problems with reconnection logic for now
	select {
	case c.err <- err:
		c.Shutdown()
		return err
	case d := <-c.deliveries:
		delivery := &Delivery{&d, c, false, nil, nil, false}
		handler(delivery)
		count++
		if c.cc.MaxDeliveries > 0 && count >= c.cc.MaxDeliveries {
			c.done <- true
			return nil
		}
	}
	//This was blocking the flow. Not sure how needed is.
	//c.done <- nil
	return nil
}

// ConsumeRPC accepts a handler function for every message streamed from RabbitMq
// will be called within this handler func.
// It returns the message to send and the content type.
func (c *Consumer) ConsumeRPC(handler func(delivery *Delivery)) error {
	err := c.consume()
	if err != nil {
		return err
	}
	c.handlerRPC = handler

	log.Info("handle: deliveries channel starting")

	// handle all consumer errors, if required re-connect
	// there are problems with reconnection logic for now
	for d := range c.deliveries {
		delivery := &Delivery{&d, c, false, nil, nil, false}
		replyTo := delivery.ReplyTo
		handler(delivery)
		if delivery.Delegated == true {
			if err = delivery.AckError; err != nil {
				log.Error("Unable to delegate an acknowledgement: " + err.Error())
			}
			continue
		}
		response := delivery.Response
		if response != nil {
			if replyTo == "" {
				log.Error("Response was ready, but received an empty routing key. Delivery was rejected.")
				delivery.RejectOrSkip(false)
				continue
			}
			publishing := &amqp.Publishing{
				Body:          response.Body,
				CorrelationId: delivery.CorrelationId,
			}
			err := c.ch.Publish(
				"",
				replyTo,
				false,
				false,
				*publishing,
			)
			if err != nil {
				log.Error("Unable to reply back: " + err.Error())
			}
		}
		delivery.AckOrSkip(delivery.preAckMultiple)
	}

	log.Info("handle: deliveries channel closed")
	return nil
}

// QOS controls how many messages the server will try to keep on the network for
// consumers before receiving delivery acks.  The intent of Qos is to make sure
// the network buffers stay full between the server and client.
func (c *Consumer) QOS(messageCount int) error {
	return c.ch.Qos(messageCount, 0, false)
}

// ConsumeMessage accepts a handler function and only consumes one message
// stream from RabbitMq
func (c *Consumer) Get(handler func(delivery *Delivery)) error {
	m, ok, err := c.ch.Get(c.q.Name, c.cc.AutoAck)
	message := &Delivery{&m, c, false, nil, nil, false}
	if err != nil {
		return err
	}
	c.handler = handler
	if ok {
		fmt.Println("Message received")
		handler(message)
	} else {
		fmt.Println("No message received")
	}
	// TODO maybe we should return ok too?
	return nil
}

func (c *Consumer) Shutdown() error {
	co := c.cc
	if err := shutdownChannel(c.ch, co.Tag); err != nil {
		return err
	}
	fmt.Println("Waiting for Consumer handler to exit")
	// if we have not called the Consume yet, we can return here
	if c.deliveries == nil {
		close(c.done)
	}
	fmt.Printf("deliveries %v", c.deliveries)
	c.closed = true
	fmt.Println("Consumer shutdown OK")
	// this channel is here for finishing the consumer's ranges of
	// delivery chans.  We need every delivery to be processed, here make
	// sure to wait for all consumers goroutines to finish before exiting our
	// process.
	return nil
}
<|endoftext|>


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

package dlshared

import (
	"sync"
	"time"
)

// The consumer is a generic component that is meant to be embedded into your applications. It
// allows you to create a specific number of goroutines to handle the realtime data/msg processing.
// The contact is that you must call the NewConsumer method to create the struct. After that, you
// must call the Start method before attempting to pass in a msg. To stop the consumer, close the
// receive channel and THEN call the Stop method.
type Consumer struct {

	Logger

	name string

	consumeFunc func(msg interface{})

	receiveChannel chan interface{}

	maxGoroutines int

	maxWaitOnStopInMs int64

	waitGroup *sync.WaitGroup
}

// Create the consumer. You must call this method to create a consumer. If you set the maxGoroutines
// to zero (or less) then it will spawn a new goroutine for each request and there is no limit. If maxGoroutines
// is set, these goroutines are created and added to a pool when Start is called. The maxWaitOnStopInMs is the amount of time the Stop
// method will wait for the goroutines to finish clearing out what they are processing. A maxWaitOnStopInMs value of zero
// indicates an unlimited wait. You MUST close the receive channel before calling stop.
func NewConsumer(	name string,
					receiveChannel chan interface{},
					consumeFunc func(msg interface{}),
					maxGoroutines,
					maxWaitOnStopInMs int,
					logger Logger) *Consumer {

	if maxWaitOnStopInMs < 0 { maxWaitOnStopInMs = 0 }
	if maxGoroutines <= 0 { maxGoroutines = 1 }

	consumer := &Consumer{
		Logger: logger,
		name: name,
		receiveChannel: receiveChannel,
		maxGoroutines: maxGoroutines,
		maxWaitOnStopInMs: int64(maxWaitOnStopInMs),
		waitGroup: new(sync.WaitGroup),
	}

	consumer.consumeFunc = func(msg interface{}) {
		defer func() {
			if r := recover(); r != nil {
				consumer.Logf(Warn, "Consume func in consumer: %s - panicked - err: %v", consumer.name, r)
			}
		}()
		consumeFunc(msg)
	}

	return consumer
}

// This method listens to the receive channel and then
// calls the consume function passed. The passed consume
// function should never panic. Don't Panic!
func (self *Consumer) msgProcessor() {
	defer self.waitGroup.Done()
	for msg := range self.receiveChannel { self.consumeFunc(msg) }
}

func (self *Consumer) Start() error {

	// Create the goroutine pool
	for idx := 0; idx < self.maxGoroutines; idx++ { self.waitGroup.Add(1); go self.msgProcessor() }

	return nil
}

// This method will block until all goroutines exit. It is up to the
// caller to clear the receiveChannel.
func (self *Consumer) Stop() error {

	// If the max wait on stop in ms equals zero, we will wait indefinitely before
	// stopping.
	if self.maxWaitOnStopInMs == 0 {
		self.waitGroup.Wait()
	} else {

		stopNotification := make(chan bool, 1)
		var stopWaitGroup sync.WaitGroup
		stopWaitGroup.Add(1)

		go func() {
			defer func() { stopWaitGroup.Done() }()
			self.waitGroup.Wait()
			stopNotification <- true
		}()

		select {
			case <- stopNotification: // This is a clean shutdown, do nothing.
			case <- time.After(time.Duration(self.maxWaitOnStopInMs) * time.Millisecond):
				self.Logf(Warn, "Cunsumer : %s - unabled to shutdown cleanly - stopping", self.name)
		}
	}

	return nil
}

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

package dlshared

import (
	"sync"
	"time"
)

// The consumer is a generic component that is meant to be embedded into your applications. It
// allows you to create a specific number of goroutines to handle the realtime data/msg processing.
// The contact is that you must call the NewConsumer method to create the struct. After that, you
// must call the Start method before attempting to pass in a msg. To stop the consumer, close the
// receive channel and THEN call the Stop method.
type Consumer struct {

	Logger

	name string

	consumeFunc func(msg interface{})

	receiveChannel chan interface{}

	maxGoroutines int

	maxWaitOnStopInMs int64

	waitGroup *sync.WaitGroup
}

// Create the consumer. You must call this method to create a consumer. If you set the maxGoroutines
// to zero (or less) then it will spawn a new goroutine for each request and there is no limit. If maxGoroutines
// is set, these goroutines are created and added to a pool when Start is called. The maxWaitOnStopInMs is the amount of time the Stop
// method will wait for the goroutines to finish clearing out what they are processing. A maxWaitOnStopInMs value of zero
// indicates an unlimited wait. You MUST close the receive channel before calling stop.
func NewConsumer(	name string,
					receiveChannel chan interface{},
					consumeFunc func(msg interface{}),
					maxGoroutines,
					maxWaitOnStopInMs int,
					logger Logger) *Consumer {

	if maxWaitOnStopInMs < 0 { maxWaitOnStopInMs = 0 }
	if maxGoroutines <= 0 { maxGoroutines = 1 }

	consumer := &Consumer{
		Logger: logger,
		name: name,
		receiveChannel: receiveChannel,
		maxGoroutines: maxGoroutines,
		maxWaitOnStopInMs: int64(maxWaitOnStopInMs),
		waitGroup: new(sync.WaitGroup),
	}

	consumer.consumeFunc = func(msg interface{}) {
		defer func() {
			if r := recover(); r != nil {
				consumer.Logf(Warn, "Consume func in consumer: %s - panicked - err: %v", consumer.name, r)
			}
		}()
		consumeFunc(msg)
	}

	return consumer
}

// This method listens to the receive channel and then
// calls the consume function passed. The passed consume
// function should never panic. Don't Panic!
func (self *Consumer) msgProcessor() {
	defer self.waitGroup.Done()
	for msg := range self.receiveChannel { self.consumeFunc(msg) }
}

func (self *Consumer) Start() error {

	// Create the goroutine pool
	for idx := 0; idx < self.maxGoroutines; idx++ { self.waitGroup.Add(1); go self.msgProcessor() }

	return nil
}

// This method will block until all goroutines exit. It is up to the
// caller to clear the receiveChannel.
func (self *Consumer) Stop() error {

	// If the max wait on stop in ms equals zero, we will wait indefinitely before
	// stopping.
	if self.maxWaitOnStopInMs == 0 {
		self.waitGroup.Wait()
	} else {
		stopNotification := make(chan bool, 1)
		var stopWaitGroup sync.WaitGroup
		stopWaitGroup.Add(1)

		go func() {
			defer func() { stopWaitGroup.Done() }()
			self.waitGroup.Wait()
			stopNotification <- true
		}()

		select {
			case <- stopNotification: // This is a clean shutdown, do nothing.
			case <- time.After(time.Duration(self.maxWaitOnStopInMs) * time.Millisecond):
				self.Logf(Warn, "Cunsumer : %s - unabled to shutdown cleanly - stopping", self.name)
		}
	}

	return nil
}

<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

func songControl(millisecondWait int64, is_playing bool, text string, song string, start_next bool) {
	time.Sleep(time.Duration(millisecondWait) * time.Millisecond)
	if song == statevar.CurrentSong {
		log.Printf(song + " " + text)
		statevar.IsPlaying = is_playing
		if start_next == true {
			skipTrack(-1)
		}
	}
}

func getPlaylistHTML() (playlist_html string) {
	playlist_html = ""
	for i, k := range statevar.SongList {
		name := statevar.SongMap[k].Fullname
		names := strings.Split(name, "/")
		showName := names[len(names)-1]
		if statevar.CurrentSong != statevar.SongMap[k].Fullname {
			playlist_html += "" + showName + "
\n"
		} else {
			playlist_html += "" + showName + "
\n"

		}
	}
	return
}

func getPlaybackPositionInSeconds() float64 {
	position := float64(getTime()-statevar.SongStartTime) / 1000.0
	if statevar.IsPlaying == true && position > 0 {
		return position
	} else {
		return 0.0
	}
}

func SyncRequest(rw http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		// defer timeTrack(time.Now(), r.RemoteAddr+" /sync")
		//current_song := r.FormValue("current_song")
		client_timestamp_str := r.FormValue("client_timestamp")
		client_timestamp, _ := strconv.ParseUint(client_timestamp_str, 10, 64)
		is_muted, _ := strconv.ParseBool(r.FormValue("is_muted"))
		mute_button_clicked, _ := strconv.ParseBool(r.FormValue("mute_button_clicked"))
		if mute_button_clicked == true {
			statevar.LastMuted = getTime()
			statevar.IsMuted = is_muted
		}

		if getTime()-statevar.LastMuted < 3000 {
			mute_button_clicked = true
			is_muted = statevar.IsMuted
		}
		name := statevar.CurrentSong
		names := strings.Split(name, "/")
		showName := names[len(names)-1]
		data := SyncJSON{
			Current_song:        showName,
			Client_timestamp:    int64(client_timestamp),
			Server_timestamp:    getTime(),
			Is_playing:          statevar.IsPlaying,
			Song_time:           getPlaybackPositionInSeconds(),
			Song_start_time:     statevar.SongStartTime,
			Mute_button_clicked: mute_button_clicked,
			Is_muted:            statevar.IsMuted,
		}
		b, err := json.Marshal(data)
		if err != nil {
			panic(err)
		}

		rw.Header().Set("Content-Type", "application/json")
		rw.Write([]byte(b))
	}
}

func NextSongRequest(rw http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		defer timeTrack(time.Now(), r.RemoteAddr+" /nextsong")
		skip, _ := strconv.Atoi(r.FormValue("skip"))
		skipTrack(skip)
		data := SyncJSON{
			Current_song:     "None",
			Client_timestamp: 0,
			Server_timestamp: 0,
			Is_playing:       false,
			Song_time:        0,
			Song_start_time:  0,
		}

		b, err := json.Marshal(data)
		if err != nil {
			panic(err)
		}
		rw.Header().Set("Content-Type", "application/json")
		rw.Write([]byte(b))
	}
}

func skipTrack(song_index int) {
	if song_index < 0 {
		if song_index == -1 && conf.Server.Random {
			s1 := rand.NewSource(time.Now().UnixNano())
			r1 := rand.New(s1)
			statevar.CurrentSongIndex = r1.Intn(len(statevar.SongList))
		} else {
			statevar.CurrentSongIndex += song_index + 2
		}
	} else {
		statevar.CurrentSongIndex = song_index
	}
	if statevar.CurrentSongIndex >= len(statevar.SongList) {
		statevar.CurrentSongIndex = 0
	}
	fmt.Println(statevar.CurrentSongIndex, len(statevar.SongList))
	song := statevar.SongList[statevar.CurrentSongIndex]

	err := os.Remove("./static/sound.mp3")
	if err != nil {
		fmt.Println(err)
	}

	// To be served by Caddy
	CopyFile(statevar.SongMap[song].Path, "./static/sound.mp3")
	statevar.MusicExtension = "mp3"
	if conf.Server.Ffmpeg {
		err = os.Remove("./static/sound.webm")
		if err != nil {
			fmt.Println(err)
		}
		fmt.Println("Converting to webm...")
		cmd := "ffmpeg"
		args := []string{"-i", "./static/sound.mp3", "-dash", "1", "-c:a", "libopus", "-compression_level", "0", "-frame_duration", "5", "-application", "lowdelay", "-cutoff", "20000", "./static/sound.webm"}
		if err := exec.Command(cmd, args...).Run(); err != nil {
			// If unsuccessful, will defualt to sound.mp3
			fmt.Println(err)
		} else {
			// If successful get rid of sound.mp3 and use sound.webm
			statevar.MusicExtension = "webm"
			err := os.Remove("./static/sound.mp3")
			if err != nil {
				fmt.Println(err)
			}
		}

	}

	rawSongData, _ = ioutil.ReadFile(statevar.SongMap[song].Path)

	statevar.CurrentSong = statevar.SongMap[song].Fullname
	statevar.SongStartTime = getTime() + 9000
	statevar.IsPlaying = false
	b, _ := json.Marshal(statevar)
	ioutil.WriteFile("state.json", b, 0644)
	go songControl(statevar.SongStartTime-getTime()-3000, false, "3", statevar.SongMap[song].Fullname, false)
	go songControl(statevar.SongStartTime-getTime()-2000, false, "2", statevar.SongMap[song].Fullname, false)
	go songControl(statevar.SongStartTime-getTime()-1000, false, "1", statevar.SongMap[song].Fullname, false)
	go songControl(statevar.SongStartTime-getTime(), true, "Playing "+statevar.SongMap[song].Fullname, statevar.SongMap[song].Fullname, false)
	go songControl(statevar.SongStartTime-getTime()+statevar.SongMap[song].Length, false, "Stopping "+statevar.SongMap[song].Fullname, statevar.SongMap[song].Fullname, true)
}
<commit_msg>Can simply overwrite files in ffmpeg, no need to use golang to erase them.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

func songControl(millisecondWait int64, is_playing bool, text string, song string, start_next bool) {
	time.Sleep(time.Duration(millisecondWait) * time.Millisecond)
	if song == statevar.CurrentSong {
		log.Printf(song + " " + text)
		statevar.IsPlaying = is_playing
		if start_next == true {
			skipTrack(-1)
		}
	}
}

func getPlaylistHTML() (playlist_html string) {
	playlist_html = ""
	for i, k := range statevar.SongList {
		name := statevar.SongMap[k].Fullname
		names := strings.Split(name, "/")
		showName := names[len(names)-1]
		if statevar.CurrentSong != statevar.SongMap[k].Fullname {
			playlist_html += "" + showName + "
\n"
		} else {
			playlist_html += "" + showName + "
\n"

		}
	}
	return
}

func getPlaybackPositionInSeconds() float64 {
	position := float64(getTime()-statevar.SongStartTime) / 1000.0
	if statevar.IsPlaying == true && position > 0 {
		return position
	} else {
		return 0.0
	}
}

func SyncRequest(rw http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		// defer timeTrack(time.Now(), r.RemoteAddr+" /sync")
		//current_song := r.FormValue("current_song")
		client_timestamp_str := r.FormValue("client_timestamp")
		client_timestamp, _ := strconv.ParseUint(client_timestamp_str, 10, 64)
		is_muted, _ := strconv.ParseBool(r.FormValue("is_muted"))
		mute_button_clicked, _ := strconv.ParseBool(r.FormValue("mute_button_clicked"))
		if mute_button_clicked == true {
			statevar.LastMuted = getTime()
			statevar.IsMuted = is_muted
		}

		if getTime()-statevar.LastMuted < 3000 {
			mute_button_clicked = true
			is_muted = statevar.IsMuted
		}
		name := statevar.CurrentSong
		names := strings.Split(name, "/")
		showName := names[len(names)-1]
		data := SyncJSON{
			Current_song:        showName,
			Client_timestamp:    int64(client_timestamp),
			Server_timestamp:    getTime(),
			Is_playing:          statevar.IsPlaying,
			Song_time:           getPlaybackPositionInSeconds(),
			Song_start_time:     statevar.SongStartTime,
			Mute_button_clicked: mute_button_clicked,
			Is_muted:            statevar.IsMuted,
		}
		b, err := json.Marshal(data)
		if err != nil {
			panic(err)
		}

		rw.Header().Set("Content-Type", "application/json")
		rw.Write([]byte(b))
	}
}

func NextSongRequest(rw http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		defer timeTrack(time.Now(), r.RemoteAddr+" /nextsong")
		skip, _ := strconv.Atoi(r.FormValue("skip"))
		skipTrack(skip)
		data := SyncJSON{
			Current_song:     "None",
			Client_timestamp: 0,
			Server_timestamp: 0,
			Is_playing:       false,
			Song_time:        0,
			Song_start_time:  0,
		}

		b, err := json.Marshal(data)
		if err != nil {
			panic(err)
		}
		rw.Header().Set("Content-Type", "application/json")
		rw.Write([]byte(b))
	}
}

func skipTrack(song_index int) {
	if song_index < 0 {
		if song_index == -1 && conf.Server.Random {
			s1 := rand.NewSource(time.Now().UnixNano())
			r1 := rand.New(s1)
			statevar.CurrentSongIndex = r1.Intn(len(statevar.SongList))
		} else {
			statevar.CurrentSongIndex += song_index + 2
		}
	} else {
		statevar.CurrentSongIndex = song_index
	}
	if statevar.CurrentSongIndex >= len(statevar.SongList) {
		statevar.CurrentSongIndex = 0
	}
	fmt.Println(statevar.CurrentSongIndex, len(statevar.SongList))
	song := statevar.SongList[statevar.CurrentSongIndex]

	err := os.Remove("./static/sound.mp3")
	if err != nil {
		fmt.Println(err)
	}

	// To be served by Caddy
	CopyFile(statevar.SongMap[song].Path, "./static/sound.mp3")
	statevar.MusicExtension = "mp3"
	if conf.Server.Ffmpeg {

		start := time.Now()
		cmd := "ffmpeg"
		args := []string{"-i", "./static/sound.mp3", "-y", "-acodec", "pcm_u8", "-ar", "44100", "./static/sound.wav"}
		if err := exec.Command(cmd, args...).Run(); err != nil {
			// If unsuccessful, will defualt to sound.mp3
			fmt.Println("Error with mp3 -> wav", err)
		} else {
			elapsed := time.Since(start)
			fmt.Printf("mp3 -> wav done. (%s)\n", elapsed)
			start = time.Now()
			cmd = "ffmpeg"
			args = []string{"-i", "./static/sound.wav", "-y", "-dash", "1", "-c:a", "libopus", "-compression_level", "0", "-frame_duration", "60", "-application", "lowdelay", "-cutoff", "20000", "./static/sound.webm"}
			if err := exec.Command(cmd, args...).Run(); err != nil {
				// If unsuccessful, will defualt to sound.mp3
				fmt.Println("Error with wav -> webm", err)
			} else {
				// If successful use sound.webm
				elapsed = time.Since(start)
				fmt.Printf("wav -> webm done. (%s)\n", elapsed)
				statevar.MusicExtension = "webm"
			}
		}
	}

	rawSongData, _ = ioutil.ReadFile(statevar.SongMap[song].Path)

	statevar.CurrentSong = statevar.SongMap[song].Fullname
	statevar.SongStartTime = getTime() + 9000
	statevar.IsPlaying = false
	b, _ := json.Marshal(statevar)
	ioutil.WriteFile("state.json", b, 0644)
	go songControl(statevar.SongStartTime-getTime()-3000, false, "3", statevar.SongMap[song].Fullname, false)
	go songControl(statevar.SongStartTime-getTime()-2000, false, "2", statevar.SongMap[song].Fullname, false)
	go songControl(statevar.SongStartTime-getTime()-1000, false, "1", statevar.SongMap[song].Fullname, false)
	go songControl(statevar.SongStartTime-getTime(), true, "Playing "+statevar.SongMap[song].Fullname, statevar.SongMap[song].Fullname, false)
	go songControl(statevar.SongStartTime-getTime()+statevar.SongMap[song].Length, false, "Stopping "+statevar.SongMap[song].Fullname, statevar.SongMap[song].Fullname, true)
}
<|endoftext|>


<commit_before>package anylisp

import (
	"fmt"
	"math/big"
	"os"
)

type Lister interface {
	Car() interface{}
	SetCar(v interface{}) interface{}
	Cdr() Lister
	SetCdr(v Lister) Lister
	Last() Lister
}

type List struct {
	car interface{}
	cdr Lister
}

type Inter interface {
	Add(x, y *big.Int) *big.Int
	Cmp(y *big.Int) (r int)
	Int64() int64
}

var (
	Ps_      Lister
	C_       Lister
	TempRoot Lister
)

func Parse(code string) {
	TempRoot = &List{"sx'", nil}
	Ps_ = &List{TempRoot, nil}
	C_ = &List{&List{TempRoot, nil}, nil}
	tok := ""
	for i := 0; i < len(code); i++ {
		if code[i] == ' ' || code[i] == '\t' || code[i] == '\n' {
			if tok == ")" {
				Assert(Ps_.Cdr() != nil, "Parse WTF! Too many )s")
				Ps_ = Ps_.Cdr()
			} else if len(tok) > 0 {
				var ls Lister
				if tok == "(" { // list
					ls = &List{nil, nil}
				} else if tok[0] == '[' && tok[len(tok)-1] == ']' { // number
					for j := 1; j < len(tok)-1; j++ {
						Assert(tok[j] == '-' || (tok[j] >= '0' && tok[j] <= '9') || (tok[j] >= 'a' && tok[j] <= 'f'),
							"Parse WTF! Bad character in number")
					}
					bi := new(big.Int)
					_, err := fmt.Sscanf(tok[1:len(tok)-1], "%x", bi)
					Assert(err == nil, "Parse WTF! Bad number")
					ls = &List{bi, nil}
				} else { // symbol
					ls = &List{tok, nil}
				}
				if Ps_.Car() == nil {
					Ps_.Cdr().Car().(Lister).SetCar(ls) // 1st token in list
				} else {
					Ps_.Car().(Lister).SetCdr(ls)
				}
				Ps_.SetCar(ls)
				if tok == "(" {
					Ps_ = &List{nil, Ps_}
				}
			}
			tok = ""
		} else {
			tok += string(code[i])
		}
	}
	Assert(Ps_.Cdr() == nil, "Parse WTF! Too few )s")
}

func Run() {
	for C_ != nil {
		f, ok := C_.Car().(Lister)
		Assert(ok, "WTF! Bad stack frame")
		e, ok := f.Car().(Lister)
		if !ok {
			fmt.Print("0 ")
			Ret(f.Car())
		} else {
			switch t := e.Car().(type) {
			case nil:
				Assert(false, "WTF! Can't call the empty list")
			case Inter:
				Assert(false, "WTF! Can't call an int")
			case Lister:
				Assert(false, "WTF! Can't call a list")
				// I kind of like the behavior below, but it causes strange error messages if there's a bug
				/*if f.Cdr() == nil {
					fmt.Println("a")
					C_ = &List{&List{t, nil}, C_}
				} else {
					fmt.Println("b")
					f.SetCar(f.Cdr().Car())
				}*/
			case string:
				switch t {
				case "sx'": // sx', arg, ret
					if e.Cdr() == nil {
						fmt.Print("{0 ")
						Ret(nil)
					} else if f.Cdr() == nil {
						fmt.Print("{1 ")
						f.SetCdr(&List{e.Cdr(), nil})
					} else if f.Cdr().Car() != nil {
						fmt.Print("{2 ")
						C_ = &List{&List{NCarL(f, 1).Car(), nil}, C_}
						f.SetCdr(&List{NCarL(f, 1).Cdr(), nil})
					} else {
						fmt.Print("{3 ")
						Ret(NCar(f, 2))
					}
				case "q'":
					fmt.Print("' ")
					Assert(e.Cdr() != nil, "WTF! Missing argument to quote")
					Ret(e.Cdr().Car())
				case ":^'", ":>'", ":|'": // op, ret
					if f.Cdr() == nil {
						fmt.Print(":1 ")
						Assert(e.Cdr() != nil, "WTF! Missing argument to "+t)
						C_ = &List{&List{e.Cdr().Car(), nil}, C_}
					} else if f.Cdr().Car() == nil {
						fmt.Print(":2 ")
						Ret(nil)
					} else {
						fmt.Print(":3 ")
						arg := NCarLA(f, 1, "WTF! "+t+" takes a list")
						switch t {
						case ":^'":
							Ret(arg.Car())
						case ":>'":
							Ret(arg.Cdr())
						case ":|'":
							Ret(arg.Last())
						}
					}
				case "lt'": // lt', arg, ret...
					if e.Cdr() == nil {
						fmt.Print("lt0 ")
						Ret(nil)
					} else if f.Cdr() == nil {
						fmt.Print("lt1 ")
						f.SetCdr(&List{e.Cdr(), nil})
					} else if f.Cdr().Car() != nil {
						fmt.Print("lt2 ")
						C_ = &List{&List{NCarL(f, 1).Car(), nil}, C_}
						f.Cdr().SetCar(NCarL(f, 1).Cdr())
					} else {
						fmt.Print("lt3 ")
						SetCdrA(NCdr(f, -2), f.Last().Car(), "WTF! Last argument to lt' must be a list")
						Ret(NCdr(f, 2))
					}
				case "?'":
					// ?', if part, then part, ret
					// ?', then part, nil, ret
					if e.Cdr() == nil {
						fmt.Print("?0 ")
						Ret(nil)
					} else if NCdr(f, 1) == nil {
						fmt.Print("?1 ")
						f.SetCdr(&List{e.Cdr(), &List{e.Cdr().Cdr(), nil}})
					} else if NCdr(f, 3) == nil {
						fmt.Print("?2 ")
						C_ = &List{&List{NCarL(f, 1).Car(), nil}, C_}
					} else if NCar(f, 2) == nil {
						fmt.Print("?3 ")
						Ret(NCar(f, 3))
					} else if NCar(f, 3) != nil {
						fmt.Print("?4 ")
						f.SetCdr(&List{NCarL(f, 1).Cdr(), &List{nil, nil}})
					} else if NCarL(f, 2).Cdr() == nil {
						fmt.Print("?5 ")
						Ret(nil)
					} else {
						fmt.Print("?6 ")
						f.SetCdr(&List{NCarL(f, 2).Cdr(), &List{NCarL(f, 2).Cdr().Cdr(), nil}})
					}
				case "+'": // +', arg, sum, ret
					if f.Cdr() == nil {
						fmt.Print("+0 ")
						f.SetCdr(&List{e.Cdr(), &List{big.NewInt(0), nil}})
					} else if NCdr(f, 3) != nil {
						fmt.Print("+1 ")
						NCarI(f, 2).Add(NCarI(f, 2), NCarIA(f, 3, "WTF! +' takes numbers"))
						f.Cdr().Cdr().SetCdr(nil)
					} else if f.Cdr().Car() != nil {
						fmt.Print("+2 ")
						C_ = &List{&List{NCarL(f, 1).Car(), nil}, C_}
						f.Cdr().SetCar(NCarL(f, 1).Cdr())
					} else {
						fmt.Print("+3 ")
						Ret(NCar(f, 2))
					}
				case "pr'": // pr', arg, ret...
					if e.Cdr() == nil {
						fmt.Print("pr0 ")
						Ret(nil)
					} else if f.Cdr() == nil {
						fmt.Print("pr1 ")
						f.SetCdr(&List{e.Cdr(), nil})
					} else {
						if f.Cdr().Cdr() != nil {
							fmt.Print("pr2 ")
							SetCdrA(NCdr(f, -2), f.Last().Car(), "WTF! pr' takes a string")
						}
						if f.Cdr().Car() != nil {
							fmt.Print("pr3 ")
							C_ = &List{&List{NCarL(f, 1).Car(), nil}, C_}
							f.Cdr().SetCar(NCarL(f, 1).Cdr())
						} else {
							fmt.Print("pr4 ")
							s := make([]uint8, Len(f.Cdr().Cdr()))
							for i, arg := 0, f.Cdr().Cdr(); arg != nil; i, arg = i+1, arg.Cdr() {
								c, ok := arg.Car().(Inter)
								Assert(ok && c.Cmp(big.NewInt(-1)) == 1 && c.Cmp(big.NewInt(256)) == -1,
									"WTF! Bad byte passed to pr'")
								s[i] = uint8(c.Int64())
							}
							fmt.Print(string(s))
							Ret(f.Cdr().Cdr())
						}
					}
				default:
					Assert(false, "WTF! Can't call undefined function \""+t+"\"")
				}
			default:
				Assert(false, "WTF! Unrecognized function type (probably an interpreter bug)")
			}
		}
	}
}

func PrintTree(ls interface{}) {
	switch t := ls.(type) {
	case nil:
		fmt.Print("( ) ")
	case Inter:
		fmt.Printf("[%x] ", t)
	case Lister:
		fmt.Print("( ")
		for ls != nil {
			PrintTree(ls.(Lister).Car())
			ls = ls.(Lister).Cdr()
		}
		fmt.Print(") ")
	case string:
		fmt.Print(t + " ")
	default:
		Assert(false, "Unrecognized object in tree")
	}
}

func Ret(v interface{}) {
	if C_.Cdr() != nil {
		C_.Cdr().Car().(Lister).Last().SetCdr(&List{v, nil})
	}
	C_ = C_.Cdr()
}

func Len(ls Lister) int {
	if ls == nil {
		return 0
	}
	if ls.Cdr() == nil {
		return 1
	}
	return Len(ls.Cdr()) + 1
}

func NCar(ls Lister, n int) interface{} {
	nCdr := NCdr(ls, n)
	Assert(nCdr != nil, "WTF! Out of bounds when calling :'")
	return nCdr.Car()
}

func NCarL(ls Lister, n int) Lister {
	return NCarLA(ls, n, "WTF! Requested list element isn't a list")
}

func NCarLA(ls Lister, n int, msg string) Lister {
	nCar, ok := NCar(ls, n).(Lister)
	Assert(ok, msg)
	return nCar
}

func NCarI(ls Lister, n int) *big.Int {
	return NCarIA(ls, n, "WTF! Requested list element isn't an int")
}

func NCarIA(ls Lister, n int, msg string) *big.Int {
	nCar, ok := NCar(ls, n).(*big.Int)
	Assert(ok, msg)
	return nCar
}

func NCdr(ls Lister, n int) Lister {
	if ls == nil {
		return nil
	}
	if n > 0 {
		return NCdr(ls.Cdr(), n-1)
	}
	if n < 0 {
		return NCdr(ls, Len(ls)+n)
	}
	return ls
}

func SetCdrA(ls Lister, v interface{}, msg string) Lister {
	switch t := v.(type) {
	case nil:
		return ls.SetCdr(nil)
	case Lister:
		return ls.SetCdr(t)
	}
	Assert(false, msg)
	return nil
}

func (ls *List) Car() interface{} {
	return ls.car
}

func (ls *List) SetCar(v interface{}) interface{} {
	ls.car = v
	return v
}

func (ls *List) Cdr() Lister {
	return ls.cdr
}

func (ls *List) SetCdr(v Lister) Lister {
	ls.cdr = v
	return v
}

func (ls *List) Last() Lister {
	if ls.cdr == nil {
		return ls
	}
	return ls.cdr.Last()
}

func Assert(cond bool, msg string) {
	if !cond {
		fmt.Println(msg)
		os.Exit(2)
	}
}
<commit_msg>remove unnecessary 0 argument checks<commit_after>package anylisp

import (
	"fmt"
	"math/big"
	"os"
)

type Lister interface {
	Car() interface{}
	SetCar(v interface{}) interface{}
	Cdr() Lister
	SetCdr(v Lister) Lister
	Last() Lister
}

type List struct {
	car interface{}
	cdr Lister
}

type Inter interface {
	Add(x, y *big.Int) *big.Int
	Cmp(y *big.Int) (r int)
	Int64() int64
}

var (
	Ps_      Lister
	C_       Lister
	TempRoot Lister
)

func Parse(code string) {
	TempRoot = &List{"sx'", nil}
	Ps_ = &List{TempRoot, nil}
	C_ = &List{&List{TempRoot, nil}, nil}
	tok := ""
	for i := 0; i < len(code); i++ {
		if code[i] == ' ' || code[i] == '\t' || code[i] == '\n' {
			if tok == ")" {
				Assert(Ps_.Cdr() != nil, "Parse WTF! Too many )s")
				Ps_ = Ps_.Cdr()
			} else if len(tok) > 0 {
				var ls Lister
				if tok == "(" { // list
					ls = &List{nil, nil}
				} else if tok[0] == '[' && tok[len(tok)-1] == ']' { // number
					for j := 1; j < len(tok)-1; j++ {
						Assert(tok[j] == '-' || (tok[j] >= '0' && tok[j] <= '9') || (tok[j] >= 'a' && tok[j] <= 'f'),
							"Parse WTF! Bad character in number")
					}
					bi := new(big.Int)
					_, err := fmt.Sscanf(tok[1:len(tok)-1], "%x", bi)
					Assert(err == nil, "Parse WTF! Bad number")
					ls = &List{bi, nil}
				} else { // symbol
					ls = &List{tok, nil}
				}
				if Ps_.Car() == nil {
					Ps_.Cdr().Car().(Lister).SetCar(ls) // 1st token in list
				} else {
					Ps_.Car().(Lister).SetCdr(ls)
				}
				Ps_.SetCar(ls)
				if tok == "(" {
					Ps_ = &List{nil, Ps_}
				}
			}
			tok = ""
		} else {
			tok += string(code[i])
		}
	}
	Assert(Ps_.Cdr() == nil, "Parse WTF! Too few )s")
}

func Run() {
	for C_ != nil {
		f, ok := C_.Car().(Lister)
		Assert(ok, "WTF! Bad stack frame")
		e, ok := f.Car().(Lister)
		if !ok {
			fmt.Print("0 ")
			Ret(f.Car())
		} else {
			switch t := e.Car().(type) {
			case nil:
				Assert(false, "WTF! Can't call the empty list")
			case Inter:
				Assert(false, "WTF! Can't call an int")
			case Lister:
				Assert(false, "WTF! Can't call a list")
				// I kind of like the behavior below, but it causes strange error messages if there's a bug
				/*if f.Cdr() == nil {
					fmt.Println("a")
					C_ = &List{&List{t, nil}, C_}
				} else {
					fmt.Println("b")
					f.SetCar(f.Cdr().Car())
				}*/
			case string:
				switch t {
				case "sx'": // sx', arg, ret
					if f.Cdr() == nil {
						fmt.Print("{0 ")
						f.SetCdr(&List{e.Cdr(), nil})
					} else if f.Cdr().Car() != nil {
						fmt.Print("{1 ")
						C_ = &List{&List{NCarL(f, 1).Car(), nil}, C_}
						f.SetCdr(&List{NCarL(f, 1).Cdr(), nil})
					} else {
						fmt.Print("{2 ")
						Ret(f.Last().Car())
					}
				case "q'":
					fmt.Print("' ")
					Assert(e.Cdr() != nil, "WTF! Missing argument to quote")
					Ret(e.Cdr().Car())
				case ":^'", ":>'", ":|'": // op, ret
					if f.Cdr() == nil {
						fmt.Print(":1 ")
						Assert(e.Cdr() != nil, "WTF! Missing argument to "+t)
						C_ = &List{&List{e.Cdr().Car(), nil}, C_}
					} else if f.Cdr().Car() == nil {
						fmt.Print(":2 ")
						Ret(nil)
					} else {
						fmt.Print(":3 ")
						arg := NCarLA(f, 1, "WTF! "+t+" takes a list")
						switch t {
						case ":^'":
							Ret(arg.Car())
						case ":>'":
							Ret(arg.Cdr())
						case ":|'":
							Ret(arg.Last())
						}
					}
				case "lt'": // lt', arg, ret...
					if f.Cdr() == nil {
						fmt.Print("lt0 ")
						f.SetCdr(&List{e.Cdr(), nil})
					} else if f.Cdr().Car() != nil {
						fmt.Print("lt1 ")
						C_ = &List{&List{NCarL(f, 1).Car(), nil}, C_}
						f.Cdr().SetCar(NCarL(f, 1).Cdr())
					} else {
						fmt.Print("lt2 ")
						SetCdrA(NCdr(f, -2), f.Last().Car(), "WTF! Last argument to lt' must be a list")
						Ret(NCdr(f, 2))
					}
				case "?'":
					// ?', if part, then part, ret
					// ?', then part, nil, ret
					if e.Cdr() == nil {
						fmt.Print("?0 ")
						Ret(nil)
					} else if NCdr(f, 1) == nil {
						fmt.Print("?1 ")
						f.SetCdr(&List{e.Cdr(), &List{e.Cdr().Cdr(), nil}})
					} else if NCdr(f, 3) == nil {
						fmt.Print("?2 ")
						C_ = &List{&List{NCarL(f, 1).Car(), nil}, C_}
					} else if NCar(f, 2) == nil {
						fmt.Print("?3 ")
						Ret(NCar(f, 3))
					} else if NCar(f, 3) != nil {
						fmt.Print("?4 ")
						f.SetCdr(&List{NCarL(f, 1).Cdr(), &List{nil, nil}})
					} else if NCarL(f, 2).Cdr() == nil {
						fmt.Print("?5 ")
						Ret(nil)
					} else {
						fmt.Print("?6 ")
						f.SetCdr(&List{NCarL(f, 2).Cdr(), &List{NCarL(f, 2).Cdr().Cdr(), nil}})
					}
				case "+'": // +', arg, sum, ret
					if f.Cdr() == nil {
						fmt.Print("+0 ")
						f.SetCdr(&List{e.Cdr(), &List{big.NewInt(0), nil}})
					} else if NCdr(f, 3) != nil {
						fmt.Print("+1 ")
						NCarI(f, 2).Add(NCarI(f, 2), NCarIA(f, 3, "WTF! +' takes numbers"))
						f.Cdr().Cdr().SetCdr(nil)
					} else if f.Cdr().Car() != nil {
						fmt.Print("+2 ")
						C_ = &List{&List{NCarL(f, 1).Car(), nil}, C_}
						f.Cdr().SetCar(NCarL(f, 1).Cdr())
					} else {
						fmt.Print("+3 ")
						Ret(NCar(f, 2))
					}
				case "pr'": // pr', arg, ret...
					if f.Cdr() == nil {
						fmt.Print("pr0 ")
						f.SetCdr(&List{e.Cdr(), nil})
					} else {
						if f.Cdr().Cdr() != nil {
							fmt.Print("pr1 ")
							SetCdrA(NCdr(f, -2), f.Last().Car(), "WTF! pr' takes a string")
						}
						if f.Cdr().Car() != nil {
							fmt.Print("pr2 ")
							C_ = &List{&List{NCarL(f, 1).Car(), nil}, C_}
							f.Cdr().SetCar(NCarL(f, 1).Cdr())
						} else {
							fmt.Print("pr3 ")
							s := make([]uint8, Len(f.Cdr().Cdr()))
							for i, arg := 0, f.Cdr().Cdr(); arg != nil; i, arg = i+1, arg.Cdr() {
								c, ok := arg.Car().(Inter)
								Assert(ok && c.Cmp(big.NewInt(-1)) == 1 && c.Cmp(big.NewInt(256)) == -1,
									"WTF! Bad byte passed to pr'")
								s[i] = uint8(c.Int64())
							}
							fmt.Print(string(s))
							Ret(f.Cdr().Cdr())
						}
					}
				default:
					Assert(false, "WTF! Can't call undefined function \""+t+"\"")
				}
			default:
				Assert(false, "WTF! Unrecognized function type (probably an interpreter bug)")
			}
		}
	}
}

func PrintTree(ls interface{}) {
	switch t := ls.(type) {
	case nil:
		fmt.Print("( ) ")
	case Inter:
		fmt.Printf("[%x] ", t)
	case Lister:
		fmt.Print("( ")
		for ls != nil {
			PrintTree(ls.(Lister).Car())
			ls = ls.(Lister).Cdr()
		}
		fmt.Print(") ")
	case string:
		fmt.Print(t + " ")
	default:
		Assert(false, "Unrecognized object in tree")
	}
}

func Ret(v interface{}) {
	if C_.Cdr() != nil {
		C_.Cdr().Car().(Lister).Last().SetCdr(&List{v, nil})
	}
	C_ = C_.Cdr()
}

func Len(ls Lister) int {
	if ls == nil {
		return 0
	}
	if ls.Cdr() == nil {
		return 1
	}
	return Len(ls.Cdr()) + 1
}

func NCar(ls Lister, n int) interface{} {
	nCdr := NCdr(ls, n)
	Assert(nCdr != nil, "WTF! Out of bounds when calling :'")
	return nCdr.Car()
}

func NCarL(ls Lister, n int) Lister {
	return NCarLA(ls, n, "WTF! Requested list element isn't a list")
}

func NCarLA(ls Lister, n int, msg string) Lister {
	nCar, ok := NCar(ls, n).(Lister)
	Assert(ok, msg)
	return nCar
}

func NCarI(ls Lister, n int) *big.Int {
	return NCarIA(ls, n, "WTF! Requested list element isn't an int")
}

func NCarIA(ls Lister, n int, msg string) *big.Int {
	nCar, ok := NCar(ls, n).(*big.Int)
	Assert(ok, msg)
	return nCar
}

func NCdr(ls Lister, n int) Lister {
	if ls == nil {
		return nil
	}
	if n > 0 {
		return NCdr(ls.Cdr(), n-1)
	}
	if n < 0 {
		return NCdr(ls, Len(ls)+n)
	}
	return ls
}

func SetCdrA(ls Lister, v interface{}, msg string) Lister {
	switch t := v.(type) {
	case nil:
		return ls.SetCdr(nil)
	case Lister:
		return ls.SetCdr(t)
	}
	Assert(false, msg)
	return nil
}

func (ls *List) Car() interface{} {
	return ls.car
}

func (ls *List) SetCar(v interface{}) interface{} {
	ls.car = v
	return v
}

func (ls *List) Cdr() Lister {
	return ls.cdr
}

func (ls *List) SetCdr(v Lister) Lister {
	ls.cdr = v
	return v
}

func (ls *List) Last() Lister {
	if ls.cdr == nil {
		return ls
	}
	return ls.cdr.Last()
}

func Assert(cond bool, msg string) {
	if !cond {
		fmt.Println(msg)
		os.Exit(2)
	}
}
<|endoftext|>


<commit_before>package anylisp

import (
	"fmt"
	"math/big"
	"os"
)

type Lister interface {
	Car() interface{}
	SetCar(v interface{}) interface{}
	Cdr() Lister
	SetCdr(v Lister) Lister
	Last() Lister
}

type List struct {
	car interface{}
	cdr Lister
}

type Inter interface {
	Cmp(y *big.Int) (r int)
	Int64() int64
}

var (
	Ps_      Lister
	C_       Lister
	TempRoot Lister
)

func Parse(code string) {
	TempRoot = &List{"sx'", nil}
	Ps_ = &List{TempRoot, nil}
	C_ = &List{&List{TempRoot, nil}, nil}
	tok := ""
	for i := 0; i < len(code); i++ {
		if code[i] == ' ' || code[i] == '\t' || code[i] == '\n' {
			if tok == ")" {
				Assert(Ps_.Cdr() != nil, "Parse WTF! Too many )s")
				Ps_ = Ps_.Cdr()
			} else if len(tok) > 0 {
				var ls Lister
				if tok == "(" { // list
					ls = &List{nil, nil}
				} else if tok[0] == '[' && tok[len(tok)-1] == ']' { // number
					for j := 1; j < len(tok)-1; j++ {
						Assert(tok[j] == '-' || (tok[j] >= '0' && tok[j] <= '9') || (tok[j] >= 'a' && tok[j] <= 'f'),
							"Parse WTF! Bad character in number")
					}
					bi := new(big.Int)
					_, err := fmt.Sscanf(tok[1:len(tok)-1], "%x", bi)
					Assert(err == nil, "Parse WTF! Bad number")
					ls = &List{bi, nil}
				} else { // symbol
					ls = &List{tok, nil}
				}
				if Ps_.Car() == nil {
					Ps_.Cdr().Car().(Lister).SetCar(ls) // 1st token in list
				} else {
					Ps_.Car().(Lister).SetCdr(ls)
				}
				Ps_.SetCar(ls)
				if tok == "(" {
					Ps_ = &List{nil, Ps_}
				}
			}
			tok = ""
		} else {
			tok += string(code[i])
		}
	}
	Assert(Ps_.Cdr() == nil, "Parse WTF! Too few )s")
}

func Run() {
	for C_ != nil {
		frm, ok := C_.Car().(Lister)
		Assert(ok, "WTF! Bad stack frame")
		HasArg := func(n int) bool {
			return Nth(frm, n-1).Cdr() != nil
		}
		Arg := func(n int) interface{} {
			return Nth(frm, n).Car()
		}
		ArgL := func(n int) Lister {
			arg, ok := Arg(n).(Lister)
			Assert(ok, fmt.Sprintf("WTF! Stack frame argument %d isn't a list", n))
			return arg
		}
		exp, ok := frm.Car().(Lister)
		if !ok {
			fmt.Print("0 ")
			Ret(frm.Car())
		} else {
			switch t := exp.Car().(type) {
			case nil:
				Assert(false, "WTF! Can't call the empty list")
			case Inter:
				Assert(false, "WTF! Can't call an int")
			case Lister:
				Assert(false, "WTF! Can't call a list")
				// I kind of like the behavior below, but it causes strange error messages if there's a bug
				/*if frm.Cdr() == nil {
					fmt.Println("a")
					C_ = &List{&List{t, nil}, C_}
				} else {
					fmt.Println("b")
					frm.SetCar(frm.Cdr().Car())
				}*/
			case string:
				switch t {
				case "sx'": // sx', arg, ret
					if exp.Cdr() == nil {
						fmt.Print("{0 ")
						Ret(nil)
					} else if !HasArg(1) {
						fmt.Print("{1 ")
						frm.SetCdr(&List{exp.Cdr(), nil})
					} else if Arg(1) == nil {
						fmt.Print("{2 ")
						Ret(Arg(2))
					} else {
						fmt.Print("{3 ")
						C_ = &List{&List{ArgL(1).Car(), nil}, C_}
						frm.SetCdr(&List{ArgL(1).Cdr(), nil})
					}
				case "q'":
					if exp.Cdr() == nil {
						fmt.Print("'0 ")
						Ret(nil)
					} else {
						fmt.Print("'1 ")
						Ret(exp.Cdr().Car())
					}
				case "?'":
					// ?', if part, then part, ret
					// ?', then part, nil, ret
					if exp.Cdr() == nil {
						fmt.Print("?0 ")
						Ret(nil)
					} else if !HasArg(1) {
						fmt.Print("?1 ")
						frm.SetCdr(&List{exp.Cdr(), &List{exp.Cdr().Cdr(), nil}})
					} else if HasArg(3) {
						if Arg(2) == nil {
							fmt.Print("?2 ")
							Ret(Arg(3))
						} else if Arg(3) != nil {
							fmt.Print("?3 ")
							frm.SetCdr(&List{ArgL(1).Cdr(), &List{nil, nil}})
						} else if ArgL(2).Cdr() == nil {
							fmt.Print("?4 ")
							Ret(nil)
						} else {
							fmt.Print("?5 ")
							frm.SetCdr(&List{ArgL(2).Cdr(), &List{ArgL(2).Cdr().Cdr(), nil}})
						}
					} else {
						fmt.Print("?6 ")
						C_ = &List{&List{ArgL(1).Car(), nil}, C_}
					}
				case "pr'":
					// pr', ret
					// TODO: print all arguments
					if exp.Cdr() == nil {
						fmt.Print("pr0 ")
						Ret(nil)
					} else if !HasArg(1) {
						fmt.Print("pr1 ")
						C_ = &List{&List{exp.Cdr().Car(), nil}, C_}
					} else {
						fmt.Print("pr2 ")
						s := make([]uint8, Len(ArgL(1)))
						for i, arg := 0, ArgL(1); arg != nil; i, arg = i+1, arg.Cdr() {
							c, ok := arg.Car().(Inter)
							Assert(ok && c.Cmp(big.NewInt(-1)) == 1 && c.Cmp(big.NewInt(256)) == -1,
								"WTF! pr' takes a string")
							s[i] = uint8(c.Int64())
						}
						fmt.Print(string(s))
						Ret(Arg(1))
					}
				default:
					Assert(false, "WTF! Can't call undefined function \""+t+"\"")
				}
			default:
				Assert(false, "WTF! Unrecognized function type (probably an interpreter bug)")
			}
		}
	}
}

func PrintTree(ls interface{}) {
	switch t := ls.(type) {
	case nil:
		fmt.Print("( ) ")
	case Inter:
		fmt.Printf("[%x] ", t)
	case Lister:
		fmt.Print("( ")
		for ls != nil {
			PrintTree(ls.(Lister).Car())
			ls = ls.(Lister).Cdr()
		}
		fmt.Print(") ")
	case string:
		fmt.Print(t + " ")
	default:
		Assert(false, "Unrecognized object in tree")
	}
}

func Ret(v interface{}) {
	if C_.Cdr() != nil {
		C_.Cdr().Car().(Lister).Last().SetCdr(&List{v, nil})
	}
	C_ = C_.Cdr()
}

func Len(ls Lister) int {
	if ls == nil {
		return 0
	}
	if ls.Cdr() == nil {
		return 1
	}
	return Len(ls.Cdr()) + 1
}

func Nth(ls Lister, n int) Lister {
	Assert(ls != nil, "WTF! Out of bounds when calling nth'.")
	if n > 0 {
		return Nth(ls.Cdr(), n-1)
	}
	if n < 0 {
		return Nth(ls, Len(ls)-n)
	}
	return ls
}

func (ls *List) Car() interface{} {
	return ls.car
}

func (ls *List) SetCar(v interface{}) interface{} {
	ls.car = v
	return v
}

func (ls *List) Cdr() Lister {
	return ls.cdr
}

func (ls *List) SetCdr(v Lister) Lister {
	ls.cdr = v
	return v
}

func (ls *List) Last() Lister {
	if ls.cdr == nil {
		return ls
	}
	return ls.cdr.Last()
}

func Assert(cond bool, msg string) {
	if !cond {
		fmt.Println(msg)
		os.Exit(2)
	}
}
<commit_msg>first, rest, last<commit_after>package anylisp

import (
	"fmt"
	"math/big"
	"os"
)

type Lister interface {
	Car() interface{}
	SetCar(v interface{}) interface{}
	Cdr() Lister
	SetCdr(v Lister) Lister
	Last() Lister
}

type List struct {
	car interface{}
	cdr Lister
}

type Inter interface {
	Cmp(y *big.Int) (r int)
	Int64() int64
}

var (
	Ps_      Lister
	C_       Lister
	TempRoot Lister
)

func Parse(code string) {
	TempRoot = &List{"sx'", nil}
	Ps_ = &List{TempRoot, nil}
	C_ = &List{&List{TempRoot, nil}, nil}
	tok := ""
	for i := 0; i < len(code); i++ {
		if code[i] == ' ' || code[i] == '\t' || code[i] == '\n' {
			if tok == ")" {
				Assert(Ps_.Cdr() != nil, "Parse WTF! Too many )s")
				Ps_ = Ps_.Cdr()
			} else if len(tok) > 0 {
				var ls Lister
				if tok == "(" { // list
					ls = &List{nil, nil}
				} else if tok[0] == '[' && tok[len(tok)-1] == ']' { // number
					for j := 1; j < len(tok)-1; j++ {
						Assert(tok[j] == '-' || (tok[j] >= '0' && tok[j] <= '9') || (tok[j] >= 'a' && tok[j] <= 'f'),
							"Parse WTF! Bad character in number")
					}
					bi := new(big.Int)
					_, err := fmt.Sscanf(tok[1:len(tok)-1], "%x", bi)
					Assert(err == nil, "Parse WTF! Bad number")
					ls = &List{bi, nil}
				} else { // symbol
					ls = &List{tok, nil}
				}
				if Ps_.Car() == nil {
					Ps_.Cdr().Car().(Lister).SetCar(ls) // 1st token in list
				} else {
					Ps_.Car().(Lister).SetCdr(ls)
				}
				Ps_.SetCar(ls)
				if tok == "(" {
					Ps_ = &List{nil, Ps_}
				}
			}
			tok = ""
		} else {
			tok += string(code[i])
		}
	}
	Assert(Ps_.Cdr() == nil, "Parse WTF! Too few )s")
}

func Run() {
	for C_ != nil {
		frm, ok := C_.Car().(Lister)
		Assert(ok, "WTF! Bad stack frame")
		HasArg := func(n int) bool {
			return Nth(frm, n-1).Cdr() != nil
		}
		Arg := func(n int) interface{} {
			return Nth(frm, n).Car()
		}
		ArgL := func(n int) Lister {
			arg, ok := Arg(n).(Lister)
			Assert(ok, fmt.Sprintf("WTF! Stack frame argument %d isn't a list", n))
			return arg
		}
		exp, ok := frm.Car().(Lister)
		if !ok {
			fmt.Print("0 ")
			Ret(frm.Car())
		} else {
			switch t := exp.Car().(type) {
			case nil:
				Assert(false, "WTF! Can't call the empty list")
			case Inter:
				Assert(false, "WTF! Can't call an int")
			case Lister:
				Assert(false, "WTF! Can't call a list")
				// I kind of like the behavior below, but it causes strange error messages if there's a bug
				/*if frm.Cdr() == nil {
					fmt.Println("a")
					C_ = &List{&List{t, nil}, C_}
				} else {
					fmt.Println("b")
					frm.SetCar(frm.Cdr().Car())
				}*/
			case string:
				switch t {
				case "sx'": // sx', arg, ret
					if exp.Cdr() == nil {
						fmt.Print("{0 ")
						Ret(nil)
					} else if !HasArg(1) {
						fmt.Print("{1 ")
						frm.SetCdr(&List{exp.Cdr(), nil})
					} else if Arg(1) == nil {
						fmt.Print("{2 ")
						Ret(Arg(2))
					} else {
						fmt.Print("{3 ")
						C_ = &List{&List{ArgL(1).Car(), nil}, C_}
						frm.SetCdr(&List{ArgL(1).Cdr(), nil})
					}
				case "q'":
					if exp.Cdr() == nil {
						fmt.Print("'0 ")
						Ret(nil)
					} else {
						fmt.Print("'1 ")
						Ret(exp.Cdr().Car())
					}
				case ":^'", ":>'", ":|'":
					if exp.Cdr() == nil {
						fmt.Print(":0 ")
						Ret(nil)
					} else if !HasArg(1) {
						fmt.Print(":1 ")
						C_ = &List{&List{exp.Cdr().Car(), nil}, C_}
					} else if Arg(1) == nil {
						fmt.Print(":2 ")
						Ret(nil)
					} else { // TODO: better error message if not a list
						fmt.Print(":3 ")
						switch t {
						case ":^'":
							Ret(ArgL(1).Car())
						case ":>'":
							Ret(ArgL(1).Cdr())
						case ":|'":
							Ret(ArgL(1).Last())
						}
					}
				case "?'":
					// ?', if part, then part, ret
					// ?', then part, nil, ret
					if exp.Cdr() == nil {
						fmt.Print("?0 ")
						Ret(nil)
					} else if !HasArg(1) {
						fmt.Print("?1 ")
						frm.SetCdr(&List{exp.Cdr(), &List{exp.Cdr().Cdr(), nil}})
					} else if HasArg(3) {
						if Arg(2) == nil {
							fmt.Print("?2 ")
							Ret(Arg(3))
						} else if Arg(3) != nil {
							fmt.Print("?3 ")
							frm.SetCdr(&List{ArgL(1).Cdr(), &List{nil, nil}})
						} else if ArgL(2).Cdr() == nil {
							fmt.Print("?4 ")
							Ret(nil)
						} else {
							fmt.Print("?5 ")
							frm.SetCdr(&List{ArgL(2).Cdr(), &List{ArgL(2).Cdr().Cdr(), nil}})
						}
					} else {
						fmt.Print("?6 ")
						C_ = &List{&List{ArgL(1).Car(), nil}, C_}
					}
				case "pr'":
					// pr', ret
					// TODO: print all arguments
					if exp.Cdr() == nil {
						fmt.Print("pr0 ")
						Ret(nil)
					} else if !HasArg(1) {
						fmt.Print("pr1 ")
						C_ = &List{&List{exp.Cdr().Car(), nil}, C_}
					} else if Arg(1) == nil {
						fmt.Print("pr2 ")
						Ret(nil)
					} else {
						fmt.Print("pr3 ")
						s := make([]uint8, Len(ArgL(1))) // TODO: better error message if not a list
						for i, arg := 0, ArgL(1); arg != nil; i, arg = i+1, arg.Cdr() {
							c, ok := arg.Car().(Inter)
							Assert(ok && c.Cmp(big.NewInt(-1)) == 1 && c.Cmp(big.NewInt(256)) == -1,
								"WTF! pr' takes a string")
							s[i] = uint8(c.Int64())
						}
						fmt.Print(string(s))
						Ret(Arg(1))
					}
				default:
					Assert(false, "WTF! Can't call undefined function \""+t+"\"")
				}
			default:
				Assert(false, "WTF! Unrecognized function type (probably an interpreter bug)")
			}
		}
	}
}

func PrintTree(ls interface{}) {
	switch t := ls.(type) {
	case nil:
		fmt.Print("( ) ")
	case Inter:
		fmt.Printf("[%x] ", t)
	case Lister:
		fmt.Print("( ")
		for ls != nil {
			PrintTree(ls.(Lister).Car())
			ls = ls.(Lister).Cdr()
		}
		fmt.Print(") ")
	case string:
		fmt.Print(t + " ")
	default:
		Assert(false, "Unrecognized object in tree")
	}
}

func Ret(v interface{}) {
	if C_.Cdr() != nil {
		C_.Cdr().Car().(Lister).Last().SetCdr(&List{v, nil})
	}
	C_ = C_.Cdr()
}

func Len(ls Lister) int {
	if ls == nil {
		return 0
	}
	if ls.Cdr() == nil {
		return 1
	}
	return Len(ls.Cdr()) + 1
}

func Nth(ls Lister, n int) Lister {
	Assert(ls != nil, "WTF! Out of bounds when calling nth'.")
	if n > 0 {
		return Nth(ls.Cdr(), n-1)
	}
	if n < 0 {
		return Nth(ls, Len(ls)-n)
	}
	return ls
}

func (ls *List) Car() interface{} {
	return ls.car
}

func (ls *List) SetCar(v interface{}) interface{} {
	ls.car = v
	return v
}

func (ls *List) Cdr() Lister {
	return ls.cdr
}

func (ls *List) SetCdr(v Lister) Lister {
	ls.cdr = v
	return v
}

func (ls *List) Last() Lister {
	if ls.cdr == nil {
		return ls
	}
	return ls.cdr.Last()
}

func Assert(cond bool, msg string) {
	if !cond {
		fmt.Println(msg)
		os.Exit(2)
	}
}
<|endoftext|>


<commit_before>// Copyright © 2016 Thomas Rabaix <thomas.rabaix@gmail.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package commands

import (
	"flag"
	"fmt"
	"github.com/mitchellh/cli"
	gitlab "github.com/plouc/go-gitlab-client"
	helper "github.com/rande/gitlab-ci-helper"
	"io"
	"os"
	"strconv"
	"strings"
)

type ProjectBuildArtifactCommand struct {
	Ui            cli.Ui
	Verbose       bool
	ExtractPath   string
	ArtifactsFile string
	BuildId       string
	Job           string
	Ref           string
	Project       string
}

func (c *ProjectBuildArtifactCommand) Run(args []string) int {

	flags := flag.NewFlagSet("project:builds:artifacts", flag.ContinueOnError)
	flags.Usage = func() {
		c.Ui.Output(c.Help())
	}

	flags.BoolVar(&c.Verbose, "verbose", false, "")
	flags.StringVar(&c.ArtifactsFile, "file", "artifacts.zip", "Artifacts file name")
	flags.StringVar(&c.ExtractPath, "path", "", "The path to extract the artifacts")
	flags.StringVar(&c.BuildId, "build", "", "The build number to get the artifacts")

	flags.StringVar(&c.Job, "job", "", "The job to search the artifacts")
	flags.StringVar(&c.Ref, "ref", os.Getenv("CI_BUILD_REF"), "The reference (sha1) to search the artifacts")
	flags.StringVar(&c.Project, "project", os.Getenv("CI_PROJECT_ID"), "The project reference")

	if err := flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()

	if len(args) != 0 {
		flags.Usage()

		c.Ui.Error(fmt.Sprintf("Error: %s", "Invalid number of arguments"))

		return 1
	}

	config := helper.NewConfig()
	client := gitlab.NewGitlab(config.Gitlab.Host, config.Gitlab.ApiPath, config.Gitlab.Token)

	project, err := helper.GetProject(c.Project, client)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	c.Ui.Output(fmt.Sprintf("Found project: %s/%s (id: %d)", project.Namespace.Name, project.Name, project.Id))

	var build *gitlab.Build

	if len(c.BuildId) > 0 {
		build, err = helper.GetBuild(project, c.BuildId, client)

		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

			return 1
		}

	} else if len(c.Ref) > 0 {
		builds, err := client.ProjectCommitBuilds(strconv.FormatInt(int64(project.Id), 10), c.Ref)

		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

			return 1
		}

		for _, b := range builds {
			if b.Name == c.Job {
				build = b
				break
			}
		}
	}

	if build == nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", "Unable to found the build"))

		return 1
	}

	c.Ui.Output(fmt.Sprintf("Found build - stage:%s status:%s id:%d", build.Stage, build.Status, build.Id))

	fp, err := os.Create(c.ArtifactsFile)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	r, err := client.ProjectBuildArtifacts(strconv.FormatInt(int64(project.Id), 10), strconv.FormatInt(int64(build.Id), 10))

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	c.Ui.Output(fmt.Sprintf("Downloading artifacts... (%s)", fp.Name()))
	_, err = io.Copy(fp, r)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	if len(c.ExtractPath) > 0 {
		c.Ui.Output(fmt.Sprintf("Extracting package... (%s)", c.ExtractPath))

		err = helper.Unzip(c.ArtifactsFile, c.ExtractPath)

		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

			return 1
		}
	}

	c.Ui.Output(fmt.Sprintf("Done!"))

	return 0
}

func (c *ProjectBuildArtifactCommand) Synopsis() string {
	return "Download artifact from a job."
}

func (c *ProjectBuildArtifactCommand) Help() string {
	helpText := `
Usage: gitlab-ci-helper project:builds:artifacts [options]

  Download an artifacts and extract it if the 'path' option is provided

Options:

  -project=XX         The project reference (default: CI_PROJECT_ID)
  -build=XX           The build number used to retrieved the related artifact
  -job=XX             The job to search the build (must be used with -ref, default: package)
  -ref=XX             The sha1 linked to the build (must be used with -stage, default: CI_BUILD_REF)
  -file=artifacts.zip The path to the artifact file (default: artifacts.zip)
  -path=./package     The path to extract the command. If not set, the artifact will not
                      be extracted.
  -verbose            Add verbose information to the output

Credentials are retrieved from environment:

  GITLAB_HOST         The gitlab host
  GITLAB_TOKEN        The user's token
  GITLAB_API_PATH     (optional) the api path, default to: "/api/v3"
`

	return strings.TrimSpace(helpText)
}
<commit_msg>feat(artifacts): delete artifacts file when archiving<commit_after>// Copyright © 2016 Thomas Rabaix <thomas.rabaix@gmail.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package commands

import (
	"flag"
	"fmt"
	"github.com/mitchellh/cli"
	gitlab "github.com/plouc/go-gitlab-client"
	helper "github.com/rande/gitlab-ci-helper"
	"io"
	"os"
	"strconv"
	"strings"
)

type ProjectBuildArtifactCommand struct {
	Ui            cli.Ui
	Verbose       bool
	ExtractPath   string
	ArtifactsFile string
	BuildId       string
	Job           string
	Ref           string
	Project       string
}

func (c *ProjectBuildArtifactCommand) Run(args []string) int {

	flags := flag.NewFlagSet("project:builds:artifacts", flag.ContinueOnError)
	flags.Usage = func() {
		c.Ui.Output(c.Help())
	}

	flags.BoolVar(&c.Verbose, "verbose", false, "")
	flags.StringVar(&c.ArtifactsFile, "file", "artifacts.zip", "Artifacts file name")
	flags.StringVar(&c.ExtractPath, "path", "", "The path to extract the artifacts")
	flags.StringVar(&c.BuildId, "build", "", "The build number to get the artifacts")

	flags.StringVar(&c.Job, "job", "", "The job to search the artifacts")
	flags.StringVar(&c.Ref, "ref", os.Getenv("CI_BUILD_REF"), "The reference (sha1) to search the artifacts")
	flags.StringVar(&c.Project, "project", os.Getenv("CI_PROJECT_ID"), "The project reference")

	if err := flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()

	if len(args) != 0 {
		flags.Usage()

		c.Ui.Error(fmt.Sprintf("Error: %s", "Invalid number of arguments"))

		return 1
	}

	config := helper.NewConfig()
	client := gitlab.NewGitlab(config.Gitlab.Host, config.Gitlab.ApiPath, config.Gitlab.Token)

	project, err := helper.GetProject(c.Project, client)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	c.Ui.Output(fmt.Sprintf("Found project: %s/%s (id: %d)", project.Namespace.Name, project.Name, project.Id))

	var build *gitlab.Build

	if len(c.BuildId) > 0 {
		build, err = helper.GetBuild(project, c.BuildId, client)

		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

			return 1
		}

	} else if len(c.Ref) > 0 {
		builds, err := client.ProjectCommitBuilds(strconv.FormatInt(int64(project.Id), 10), c.Ref)

		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

			return 1
		}

		for _, b := range builds {
			if b.Name == c.Job {
				build = b
				break
			}
		}
	}

	if build == nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", "Unable to found the build"))

		return 1
	}

	c.Ui.Output(fmt.Sprintf("Found build - stage:%s status:%s id:%d", build.Stage, build.Status, build.Id))

	fp, err := os.Create(c.ArtifactsFile)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	r, err := client.ProjectBuildArtifacts(strconv.FormatInt(int64(project.Id), 10), strconv.FormatInt(int64(build.Id), 10))

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	c.Ui.Output(fmt.Sprintf("Downloading artifacts... (%s)", fp.Name()))
	_, err = io.Copy(fp, r)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	if len(c.ExtractPath) > 0 {
		c.Ui.Output(fmt.Sprintf("Extracting package... (%s)", c.ExtractPath))

		err = helper.Unzip(c.ArtifactsFile, c.ExtractPath)

		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

			return 1
		}

		// delete artifacts.zip
		os.Remove(c.ArtifactsFile);
	}

	c.Ui.Output(fmt.Sprintf("Done!"))

	return 0
}

func (c *ProjectBuildArtifactCommand) Synopsis() string {
	return "Download artifact from a job."
}

func (c *ProjectBuildArtifactCommand) Help() string {
	helpText := `
Usage: gitlab-ci-helper project:builds:artifacts [options]

  Download an artifacts and extract it if the 'path' option is provided

Options:

  -project=XX         The project reference (default: CI_PROJECT_ID)
  -build=XX           The build number used to retrieved the related artifact
  -job=XX             The job to search the build (must be used with -ref, default: package)
  -ref=XX             The sha1 linked to the build (must be used with -stage, default: CI_BUILD_REF)
  -file=artifacts.zip The path to the artifact file (default: artifacts.zip)
  -path=./package     The path to extract the command. If not set, the artifact will not
                      be extracted.
  -verbose            Add verbose information to the output

Credentials are retrieved from environment:

  GITLAB_HOST         The gitlab host
  GITLAB_TOKEN        The user's token
  GITLAB_API_PATH     (optional) the api path, default to: "/api/v3"
`

	return strings.TrimSpace(helpText)
}
<|endoftext|>