<commit_before>package main

import (
	"gopkg.in/v1/yaml"

	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
)

// Headers represents a simplified HTTP header dict
type Headers map[string]string

// ServerConfig represents a server configuration.
type ServerConfig struct {
	Listeners []Listener `yaml:"listeners"`
	Serves    []Serve    `yaml:"serves"`
	Errors    []Error    `yaml:"errors"`
	Redirects []Redirect `yaml:"redirects"`
}

func (c ServerConfig) sanitise() {
	for _, l := range c.Listeners {
		l.sanitise()
	}
	for _, s := range c.Serves {
		s.sanitise()
	}
	for _, r := range c.Redirects {
		r.sanitise()
	}
	for _, e := range c.Errors {
		e.sanitise()
	}
}

func (c ServerConfig) check() (ok bool) {
	ok = true
	if len(c.Listeners) == 0 {
		log.Printf("No listeners defined!")
		ok = false
	}
	for i, l := range c.Listeners {
		ok = l.check(fmt.Sprintf("Listener #%d:", i)) && ok
	}
	if len(c.Serves) == 0 {
		log.Printf("No serves defined!")
		ok = false
	}
	for i, s := range c.Serves {
		ok = s.check(fmt.Sprintf("Serve #%d:", i)) && ok
	}
	for i, r := range c.Redirects {
		ok = r.check(fmt.Sprintf("Redirect #%d:", i)) && ok
	}
	return
}

// Listener describes how connections are accepted and the protocol used.
type Listener struct {
	Protocol string  `yaml:"protocol"`
	Addr     string  `yaml:"addr"`
	CertFile string  `yaml:"cert"`
	KeyFile  string  `yaml:"key"`
	Headers  Headers `yaml:"headers"` // custom headers
	Gzip     bool    `yaml:"gzip"`
}

func (l *Listener) sanitise() {
	if l.Protocol == "" {
		l.Protocol = "http"
	}
	if l.Addr == "" {
		l.Addr = ":http"
	}
}

func (l *Listener) check(label string) (ok bool) {
	ok = true
	if l.Protocol == "http" {
		if l.CertFile != "" || l.KeyFile != "" {
			log.Printf(label + ": certificate supplied for non-HTTPS listener")
			ok = false
		}
	} else if l.Protocol == "https" {
		if _, err := os.Stat(l.CertFile); os.IsNotExist(err) {
			log.Printf(label+": cert file `%s` does not exist", l.CertFile)
			ok = false
		}
		if _, err := os.Stat(l.KeyFile); os.IsNotExist(err) {
			log.Printf(label+": key file `%s` does not exist", l.KeyFile)
			ok = false
		}
	} else {
		log.Printf(label+": invalid protocol `%s`", l.Protocol)
		ok = false
	}
	return
}

// Serve represents a path that will be served.
type Serve struct {
	Target         string  `yaml:"target"`          // where files are stored on the file system
	Path           string  `yaml:"path"`            // HTTP path to serve files under
	Error          int     `yaml:"error"`           // HTTP error to return (0=disabled)
	PreventListing bool    `yaml:"prevent-listing"` // prevent file listing
	Headers        Headers `yaml:"headers"`         // custom headers
}

func (s *Serve) sanitise() {
	if s.Path == "" {
		s.Path = "/"
	}
}

func (s Serve) check(label string) (ok bool) {
	ok = true
	if s.Path == "" {
		log.Println(label + ": no path specified")
		ok = false
	}
	if s.Error == 0 && s.Target == "" {
		log.Println(label + ": no target path specified")
		ok = false
	}
	if s.Error != 0 && s.Target != "" {
		log.Println(label + ": error specified with target path")
		ok = false
	}
	return
}

// Redirect represents a redirect from one path to another.
type Redirect struct {
	From string `yaml:"from"`
	To   string `yaml:"to"`
	With int    `yaml:"status"`
}

func (r *Redirect) sanitise() {
	if r.With == 0 {
		r.With = 301
		log.Printf("Defaulting status code %d for redirect %s\n", r.With, r.From)
	}
}

func (r Redirect) check(label string) (ok bool) {
	if r.From == "" {
		log.Printf(label + ": no `from` path")
		ok = false
	}
	if r.To == "" {
		log.Printf(label + ": no `to` path")
		ok = false
	}
	return true
}

// Error represents what to do when a particular HTTP status is encountered.
type Error struct {
	Status int    `yaml:"status"`
	Target string `yaml:"target"`
}

func (e *Error) sanitise() {
}

func (e Error) check() (ok bool) {
	return true
}

func (e Error) handler() http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Clear content-type as set by `http.Error` to force re-detection
		w.Header().Del("Content-Type")

		// Serve error page with a specific status code
		http.ServeFile(w, r, e.Target)
	})
}

// StaticServeMux wraps ServeMux but allows for the interception of errors.
type StaticServeMux struct {
	*http.ServeMux
	errors map[int]http.Handler
}

// NewStaticServeMux allocates and returns a new StaticServeMux
func NewStaticServeMux() *StaticServeMux {
	return &StaticServeMux{
		ServeMux: http.NewServeMux(),
		errors:   make(map[int]http.Handler),
	}
}

// HandleError registers a handler for the given response code.
func (s *StaticServeMux) HandleError(status int, handler http.Handler) {
	if s.errors[status] != nil {
		panic("Handler for error already registered")
	}
	s.errors[status] = handler
}

func (s StaticServeMux) intercept(status int, w http.ResponseWriter, req *http.Request) bool {
	// Get error handler if there is one
	if h, f := s.errors[status]; f {
		h.ServeHTTP(statusResponseWriter{w, status}, req)
		return true
	}
	// Ignore non-error status codes
	if status < 400 {
		return false
	}
	http.Error(w, http.StatusText(status), status)
	return true
}

func (s *StaticServeMux) interceptHandler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		irw := &interceptResponseWriter{
			ResponseWriter: w,
			r:              r,
			m:              s,
		}

		// If intercept occurred, originating call would have been panic'd.
		// Recover here once error has been dealt with.
		defer func() {
			if p := recover(); p != nil {
				if p == irw {
					return
				}
				panic(p)
			}
		}()

		handler.ServeHTTP(irw, r)
	})
}

func (s *StaticServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.RequestURI == "*" {
		if r.ProtoAtLeast(1, 1) {
			w.Header().Set("Connection", "close")
		}
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	h, _ := s.Handler(r)
	h = s.interceptHandler(h)
	h.ServeHTTP(w, r)
}

type interceptResponseWriter struct {
	http.ResponseWriter
	r *http.Request
	m *StaticServeMux
}

func (h *interceptResponseWriter) WriteHeader(status int) {
	if h.m.intercept(status, h.ResponseWriter, h.r) {
		panic(h)
	} else {
		h.ResponseWriter.WriteHeader(status)
	}
}

type statusResponseWriter struct {
	http.ResponseWriter
	Status int
}

func (h statusResponseWriter) WriteHeader(status int) {
	if h.Status < 0 {
		return
	}
	if h.Status > 0 {
		h.ResponseWriter.WriteHeader(h.Status)
		return
	}
	h.ResponseWriter.WriteHeader(status)
}

// PreventListingDir panics whenever a file open fails, allowing index
// requests to be intercepted.
type PreventListingDir struct {
	http.Dir
}

// Open panics whenever opening a file fails.
func (dir *PreventListingDir) Open(name string) (f http.File, err error) {
	f, err = dir.Dir.Open(name)
	if f == nil {
		panic(dir)
	}
	return
}

// CustomHeadersHandler creates a new handler that includes the provided
// headers in each response.
func CustomHeadersHandler(h http.Handler, headers Headers) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		wh := w.Header()
		for k, v := range headers {
			if wh.Get(k) == "" {
				wh.Set(k, v)
			}
		}
		h.ServeHTTP(w, r)
	})
}

// GzipResponseWriter gzips content written to it
type GzipResponseWriter struct {
	io.Writer
	http.ResponseWriter
	gotContentType bool
}

func (w *GzipResponseWriter) Write(b []byte) (int, error) {
	if !w.gotContentType {
		if w.Header().Get("Content-Type") == "" {
			w.Header().Set("Content-Type", http.DetectContentType(b))
		}
		w.gotContentType = true
	}
	return w.Writer.Write(b)
}

// GzipHandler gzips the HTTP response if supported by the client. Based on
// the implementation of `go.httpgzip`
func GzipHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Serve normally to clients that don't express gzip support
		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
			h.ServeHTTP(w, r)
			return
		}

		w.Header().Set("Content-Encoding", "gzip")
		gz := gzip.NewWriter(w)
		defer gz.Close()
		h.ServeHTTP(&GzipResponseWriter{Writer: gz, ResponseWriter: w}, r)
	})
}

var configPath string
var checkConfig bool
var defaultAddr string

func init() {
	flag.StringVar(&configPath, "config", "", "Path to configuration")
	flag.BoolVar(&checkConfig, "check", false, "Only check config")
	flag.StringVar(&defaultAddr, "addr", ":8080", "Default listen address")

	flag.Parse()
}

func readServerConfig(filename string) (cfg ServerConfig, err error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}
	err = yaml.Unmarshal(data, &cfg)
	return
}

func defaultServerConfig() ServerConfig {
	c := ServerConfig{}
	c.Listeners = []Listener{
		Listener{Protocol: "http", Addr: defaultAddr},
	}
	target := flag.Arg(0)
	if target == "" {
		target = "."
	}
	c.Serves = []Serve{
		Serve{Path: "/", Target: target},
	}
	return c
}

func main() {
	cfg := defaultServerConfig()
	if configPath != "" {
		var err error
		cfg, err = readServerConfig(configPath)
		if err != nil {
			log.Fatalln("Couldn't load config:", err)
		}
	}
	cfg.sanitise()

	if !cfg.check() {
		log.Fatalln("Invalid config.")
	}
	if checkConfig {
		log.Println("Config check passed")
		os.Exit(0)
	}

	mux := NewStaticServeMux()
	for _, e := range cfg.Errors {
		mux.HandleError(e.Status, e.handler())
	}

	// Setup serves
	for _, serve := range cfg.Serves {
		var h http.Handler
		if serve.Error > 0 {
			errStatus := serve.Error
			h = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				http.Error(w, http.StatusText(errStatus), errStatus)
			})
		} else if serve.PreventListing {
			// Prevent listing of directories lacking an index.html file
			target := serve.Target
			h = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				d := &PreventListingDir{http.Dir(target)}
				h := http.FileServer(d)
				defer func() {
					if p := recover(); p != nil {
						if p == d {
							http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
							return
						}
						panic(p)
					}
				}()
				h.ServeHTTP(w, r)
			})
		} else {
			h = http.FileServer(http.Dir(serve.Target))
		}
		if len(serve.Headers) > 0 {
			h = CustomHeadersHandler(h, serve.Headers)
		}
		mux.Handle(serve.Path, http.StripPrefix(serve.Path, h))
	}

	// Setup redirects
	for _, redirect := range cfg.Redirects {
		mux.Handle(redirect.From, http.RedirectHandler(redirect.To, redirect.With))
	}

	// Start listeners
	for _, listener := range cfg.Listeners {
		var h http.Handler = mux
		if len(listener.Headers) > 0 {
			h = CustomHeadersHandler(h, listener.Headers)
		}
		if listener.Gzip {
			h = GzipHandler(h)
		}
		if listener.Protocol == "http" {
			go func() {
				err := http.ListenAndServe(listener.Addr, h)
				if err != nil {
					log.Fatalln(err)
				}
			}()
		} else if listener.Protocol == "https" {
			go func() {
				err := http.ListenAndServeTLS(listener.Addr, listener.CertFile, listener.KeyFile, h)
				if err != nil {
					log.Fatalln(err)
				}
			}()
		} else {
			log.Printf("Unsupported protocol %s\n", listener.Protocol)
		}
		log.Printf("listening on %s (%s)\n", listener.Addr, listener.Protocol)
	}

	// Since all the listeners are running in separate gorotines, we have to
	// wait here for a termination signal.
	exit := make(chan os.Signal, 1)
	signal.Notify(exit, os.Interrupt, syscall.SIGTERM)
	<-exit
	os.Exit(0)
}
<commit_msg>Move handler code to owners<commit_after>package main

import (
	"gopkg.in/v1/yaml"

	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
)

// Headers represents a simplified HTTP header dict
type Headers map[string]string

// ServerConfig represents a server configuration.
type ServerConfig struct {
	Listeners []Listener `yaml:"listeners"`
	Serves    []Serve    `yaml:"serves"`
	Errors    []Error    `yaml:"errors"`
	Redirects []Redirect `yaml:"redirects"`
}

func (c ServerConfig) sanitise() {
	for _, l := range c.Listeners {
		l.sanitise()
	}
	for _, s := range c.Serves {
		s.sanitise()
	}
	for _, r := range c.Redirects {
		r.sanitise()
	}
	for _, e := range c.Errors {
		e.sanitise()
	}
}

func (c ServerConfig) check() (ok bool) {
	ok = true
	if len(c.Listeners) == 0 {
		log.Printf("No listeners defined!")
		ok = false
	}
	for i, l := range c.Listeners {
		ok = l.check(fmt.Sprintf("Listener #%d:", i)) && ok
	}
	if len(c.Serves) == 0 {
		log.Printf("No serves defined!")
		ok = false
	}
	for i, s := range c.Serves {
		ok = s.check(fmt.Sprintf("Serve #%d:", i)) && ok
	}
	for i, r := range c.Redirects {
		ok = r.check(fmt.Sprintf("Redirect #%d:", i)) && ok
	}
	return
}

// Listener describes how connections are accepted and the protocol used.
type Listener struct {
	Protocol string  `yaml:"protocol"`
	Addr     string  `yaml:"addr"`
	CertFile string  `yaml:"cert"`
	KeyFile  string  `yaml:"key"`
	Headers  Headers `yaml:"headers"` // custom headers
	Gzip     bool    `yaml:"gzip"`
}

func (l *Listener) sanitise() {
	if l.Protocol == "" {
		l.Protocol = "http"
	}
	if l.Addr == "" {
		l.Addr = ":http"
	}
}

func (l *Listener) check(label string) (ok bool) {
	ok = true
	if l.Protocol == "http" {
		if l.CertFile != "" || l.KeyFile != "" {
			log.Printf(label + ": certificate supplied for non-HTTPS listener")
			ok = false
		}
	} else if l.Protocol == "https" {
		if _, err := os.Stat(l.CertFile); os.IsNotExist(err) {
			log.Printf(label+": cert file `%s` does not exist", l.CertFile)
			ok = false
		}
		if _, err := os.Stat(l.KeyFile); os.IsNotExist(err) {
			log.Printf(label+": key file `%s` does not exist", l.KeyFile)
			ok = false
		}
	} else {
		log.Printf(label+": invalid protocol `%s`", l.Protocol)
		ok = false
	}
	return
}

// Serve represents a path that will be served.
type Serve struct {
	Target         string  `yaml:"target"`          // where files are stored on the file system
	Path           string  `yaml:"path"`            // HTTP path to serve files under
	Error          int     `yaml:"error"`           // HTTP error to return (0=disabled)
	PreventListing bool    `yaml:"prevent-listing"` // prevent file listing
	Headers        Headers `yaml:"headers"`         // custom headers
}

func (s *Serve) sanitise() {
	if s.Path == "" {
		s.Path = "/"
	}
}

func (s Serve) check(label string) (ok bool) {
	ok = true
	if s.Path == "" {
		log.Println(label + ": no path specified")
		ok = false
	}
	if s.Error == 0 && s.Target == "" {
		log.Println(label + ": no target path specified")
		ok = false
	}
	if s.Error != 0 && s.Target != "" {
		log.Println(label + ": error specified with target path")
		ok = false
	}
	return
}

func (s Serve) handler() http.Handler {
	var h http.Handler
	if s.Error > 0 {
		errStatus := s.Error
		h = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(errStatus), errStatus)
		})
	} else if s.PreventListing {
		// Prevent listing of directories lacking an index.html file
		target := s.Target
		h = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			d := &PreventListingDir{http.Dir(target)}
			h := http.FileServer(d)
			defer func() {
				if p := recover(); p != nil {
					if p == d {
						http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
						return
					}
					panic(p)
				}
			}()
			h.ServeHTTP(w, r)
		})
	} else {
		h = http.FileServer(http.Dir(s.Target))
	}
	if len(s.Headers) > 0 {
		h = CustomHeadersHandler(h, s.Headers)
	}
	return http.StripPrefix(s.Path, h)
}

// Redirect represents a redirect from one path to another.
type Redirect struct {
	From string `yaml:"from"`
	To   string `yaml:"to"`
	With int    `yaml:"status"`
}

func (r *Redirect) sanitise() {
	if r.With == 0 {
		r.With = 301
		log.Printf("Defaulting status code %d for redirect %s\n", r.With, r.From)
	}
}

func (r Redirect) check(label string) (ok bool) {
	if r.From == "" {
		log.Printf(label + ": no `from` path")
		ok = false
	}
	if r.To == "" {
		log.Printf(label + ": no `to` path")
		ok = false
	}
	return true
}

func (r Redirect) handler() http.Handler {
	return http.RedirectHandler(r.To, r.With)
}

// Error represents what to do when a particular HTTP status is encountered.
type Error struct {
	Status int    `yaml:"status"`
	Target string `yaml:"target"`
}

func (e *Error) sanitise() {
}

func (e Error) check() (ok bool) {
	return true
}

func (e Error) handler() http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Clear content-type as set by `http.Error` to force re-detection
		w.Header().Del("Content-Type")

		// Serve error page with a specific status code
		http.ServeFile(w, r, e.Target)
	})
}

// StaticServeMux wraps ServeMux but allows for the interception of errors.
type StaticServeMux struct {
	*http.ServeMux
	errors map[int]http.Handler
}

// NewStaticServeMux allocates and returns a new StaticServeMux
func NewStaticServeMux() *StaticServeMux {
	return &StaticServeMux{
		ServeMux: http.NewServeMux(),
		errors:   make(map[int]http.Handler),
	}
}

// HandleError registers a handler for the given response code.
func (s *StaticServeMux) HandleError(status int, handler http.Handler) {
	if s.errors[status] != nil {
		panic("Handler for error already registered")
	}
	s.errors[status] = handler
}

func (s StaticServeMux) intercept(status int, w http.ResponseWriter, req *http.Request) bool {
	// Get error handler if there is one
	if h, f := s.errors[status]; f {
		h.ServeHTTP(statusResponseWriter{w, status}, req)
		return true
	}
	// Ignore non-error status codes
	if status < 400 {
		return false
	}
	http.Error(w, http.StatusText(status), status)
	return true
}

func (s *StaticServeMux) interceptHandler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		irw := &interceptResponseWriter{
			ResponseWriter: w,
			r:              r,
			m:              s,
		}

		// If intercept occurred, originating call would have been panic'd.
		// Recover here once error has been dealt with.
		defer func() {
			if p := recover(); p != nil {
				if p == irw {
					return
				}
				panic(p)
			}
		}()

		handler.ServeHTTP(irw, r)
	})
}

func (s *StaticServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.RequestURI == "*" {
		if r.ProtoAtLeast(1, 1) {
			w.Header().Set("Connection", "close")
		}
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	h, _ := s.Handler(r)
	h = s.interceptHandler(h)
	h.ServeHTTP(w, r)
}

type interceptResponseWriter struct {
	http.ResponseWriter
	r *http.Request
	m *StaticServeMux
}

func (h *interceptResponseWriter) WriteHeader(status int) {
	if h.m.intercept(status, h.ResponseWriter, h.r) {
		panic(h)
	} else {
		h.ResponseWriter.WriteHeader(status)
	}
}

type statusResponseWriter struct {
	http.ResponseWriter
	Status int
}

func (h statusResponseWriter) WriteHeader(status int) {
	if h.Status < 0 {
		return
	}
	if h.Status > 0 {
		h.ResponseWriter.WriteHeader(h.Status)
		return
	}
	h.ResponseWriter.WriteHeader(status)
}

// PreventListingDir panics whenever a file open fails, allowing index
// requests to be intercepted.
type PreventListingDir struct {
	http.Dir
}

// Open panics whenever opening a file fails.
func (dir *PreventListingDir) Open(name string) (f http.File, err error) {
	f, err = dir.Dir.Open(name)
	if f == nil {
		panic(dir)
	}
	return
}

// CustomHeadersHandler creates a new handler that includes the provided
// headers in each response.
func CustomHeadersHandler(h http.Handler, headers Headers) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		wh := w.Header()
		for k, v := range headers {
			if wh.Get(k) == "" {
				wh.Set(k, v)
			}
		}
		h.ServeHTTP(w, r)
	})
}

// GzipResponseWriter gzips content written to it
type GzipResponseWriter struct {
	io.Writer
	http.ResponseWriter
	gotContentType bool
}

func (w *GzipResponseWriter) Write(b []byte) (int, error) {
	if !w.gotContentType {
		if w.Header().Get("Content-Type") == "" {
			w.Header().Set("Content-Type", http.DetectContentType(b))
		}
		w.gotContentType = true
	}
	return w.Writer.Write(b)
}

// GzipHandler gzips the HTTP response if supported by the client. Based on
// the implementation of `go.httpgzip`
func GzipHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Serve normally to clients that don't express gzip support
		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
			h.ServeHTTP(w, r)
			return
		}

		w.Header().Set("Content-Encoding", "gzip")
		gz := gzip.NewWriter(w)
		defer gz.Close()
		h.ServeHTTP(&GzipResponseWriter{Writer: gz, ResponseWriter: w}, r)
	})
}

var configPath string
var checkConfig bool
var defaultAddr string

func init() {
	flag.StringVar(&configPath, "config", "", "Path to configuration")
	flag.BoolVar(&checkConfig, "check", false, "Only check config")
	flag.StringVar(&defaultAddr, "addr", ":8080", "Default listen address")

	flag.Parse()
}

func readServerConfig(filename string) (cfg ServerConfig, err error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}
	err = yaml.Unmarshal(data, &cfg)
	return
}

func defaultServerConfig() ServerConfig {
	c := ServerConfig{}
	c.Listeners = []Listener{
		Listener{Protocol: "http", Addr: defaultAddr},
	}
	target := flag.Arg(0)
	if target == "" {
		target = "."
	}
	c.Serves = []Serve{
		Serve{Path: "/", Target: target},
	}
	return c
}

func main() {
	cfg := defaultServerConfig()
	if configPath != "" {
		var err error
		cfg, err = readServerConfig(configPath)
		if err != nil {
			log.Fatalln("Couldn't load config:", err)
		}
	}
	cfg.sanitise()

	if !cfg.check() {
		log.Fatalln("Invalid config.")
	}
	if checkConfig {
		log.Println("Config check passed")
		os.Exit(0)
	}

	// Setup handlers
	mux := NewStaticServeMux()
	for _, e := range cfg.Errors {
		mux.HandleError(e.Status, e.handler())
	}
	for _, serve := range cfg.Serves {
		mux.Handle(serve.Path, serve.handler())
	}
	for _, redirect := range cfg.Redirects {
		mux.Handle(redirect.From, redirect.handler())
	}

	// Start listeners
	for _, listener := range cfg.Listeners {
		var h http.Handler = mux
		if len(listener.Headers) > 0 {
			h = CustomHeadersHandler(h, listener.Headers)
		}
		if listener.Gzip {
			h = GzipHandler(h)
		}
		if listener.Protocol == "http" {
			go func() {
				err := http.ListenAndServe(listener.Addr, h)
				if err != nil {
					log.Fatalln(err)
				}
			}()
		} else if listener.Protocol == "https" {
			go func() {
				err := http.ListenAndServeTLS(listener.Addr, listener.CertFile, listener.KeyFile, h)
				if err != nil {
					log.Fatalln(err)
				}
			}()
		} else {
			log.Printf("Unsupported protocol %s\n", listener.Protocol)
		}
		log.Printf("listening on %s (%s)\n", listener.Addr, listener.Protocol)
	}

	// Since all the listeners are running in separate gorotines, we have to
	// wait here for a termination signal.
	exit := make(chan os.Signal, 1)
	signal.Notify(exit, os.Interrupt, syscall.SIGTERM)
	<-exit
	os.Exit(0)
}
<|endoftext|>


<commit_before>package pages

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

	"github.com/datasektionen/taitan/anchor"

	log "github.com/Sirupsen/logrus"
	"github.com/russross/blackfriday"
)

// Resp is the response we serve for file queries.
type Resp struct {
	Title     string          `json:"title"`      // Human-readable title.
	Slug      string          `json:"slug"`       // URL-slug.
	UpdatedAt string          `json:"updated_at"` // Body update time.
	Image     string          `json:"image"`      // Path/URL/Placeholder to image.
	Body      string          `json:"body"`       // Main content of the page.
	Sidebar   string          `json:"sidebar"`    // The sidebar of the page.
	Anchors   []anchor.Anchor `json:"anchors"`    // The list of anchors to headers in the body.
}

// Load intializes a root directory and serves all sub-folders.
func Load(root string) (pages map[string]*Resp, err error) {
	var dirs []string
	err = filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
		// We only search for article directories.
		if !fi.IsDir() {
			return nil
		}
		// Ignore our .git folder.
		if fi.IsDir() && fi.Name() == ".git" {
			return filepath.SkipDir
		}
		dirs = append(dirs, path)
		return nil
	})
	if err != nil {
		return nil, err
	}
	return parseDirs(root, dirs)
}

// stripRoot removes root level of a directory.
// This is because when a user requests:
// `/sektionen/om-oss` the actual path is: `root/sektionen/om-oss`
func stripRoot(root string, dir string) string {
	return filepath.Clean(strings.Replace(dir, root, "/", 1))
}

// parseDirs parses each directory into a response. Returns a map from requested
// urls into responses.
func parseDirs(root string, dirs []string) (pages map[string]*Resp, err error) {
	pages = map[string]*Resp{}
	for _, dir := range dirs {
		r, err := parseDir(dir)
		if err != nil {
			return nil, err
		}
		pages[stripRoot(root, dir)] = r
		log.WithFields(log.Fields{
			"Resp": r,
			"dir":  dir,
		}).Debug("Our parsed response\n")
	}
	return pages, nil
}

// toHTML reads a markdown file and returns a HTML string.
func toHTML(filename string) (string, error) {
	buf, err := ioutil.ReadFile(filename)
	if err != nil {
		return "", err
	}
	// Use standard HTML rendering.
	renderer := blackfriday.HtmlRenderer(blackfriday.HTML_USE_XHTML, "", "")
	// Parse markdown where all id's are created from the values inside
	// the element tag.
	buf = blackfriday.MarkdownOptions(buf, renderer, blackfriday.Options{
		Extensions: blackfriday.EXTENSION_AUTO_HEADER_IDS,
	})
	return string(buf), nil
}

// parseDir creates a response for a directory.
func parseDir(dir string) (*Resp, error) {
	log.WithField("dir", dir).Debug("Parsing directory:")

	// Our content files.
	bodyPath := filepath.Join(dir, "body.md")
	sidebarPath := filepath.Join(dir, "sidebar.md")

	// Parse markdown to HTML.
	body, err := toHTML(bodyPath)
	if err != nil {
		return nil, err
	}
	log.WithField("body", body).Debug("HTML of body.md")

	// Parse sidebar to HTML.
	sidebar, err := toHTML(sidebarPath)
	if err != nil {
		return nil, err
	}
	log.WithField("sidebar", sidebar).Debug("HTML of sidebar.md")

	// Parse modified at.
	fi, err := os.Stat(bodyPath)
	if err != nil {
		return nil, err
	}
	// Parse anchors in the body.
	anchs, err := anchor.Anchors(body)
	if err != nil {
		return nil, err
	}
	// Title of the page (first anchor value).
	title := ""
	if len(anchs) > 0 {
		title = anchs[0].Value
	}
	const iso8601DateTime = "2006-01-02T15:04:05Z"
	return &Resp{
		Title:     title,
		Slug:      filepath.Base(dir),
		UpdatedAt: fi.ModTime().Format(iso8601DateTime),
		Image:     "unimplemented",
		Body:      body,
		Sidebar:   sidebar,
		Anchors:   anchs,
	}, nil
}
<commit_msg>more resiliant with broken dirs<commit_after>package pages

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

	"github.com/datasektionen/taitan/anchor"

	log "github.com/Sirupsen/logrus"
	"github.com/russross/blackfriday"
)

// Resp is the response we serve for file queries.
type Resp struct {
	Title     string          `json:"title"`      // Human-readable title.
	Slug      string          `json:"slug"`       // URL-slug.
	UpdatedAt string          `json:"updated_at"` // Body update time.
	Image     string          `json:"image"`      // Path/URL/Placeholder to image.
	Body      string          `json:"body"`       // Main content of the page.
	Sidebar   string          `json:"sidebar"`    // The sidebar of the page.
	Anchors   []anchor.Anchor `json:"anchors"`    // The list of anchors to headers in the body.
}

// Load intializes a root directory and serves all sub-folders.
func Load(root string) (pages map[string]*Resp, err error) {
	var dirs []string
	err = filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
		// We only search for article directories.
		if !fi.IsDir() {
			return nil
		}
		// Ignore our .git folder.
		if fi.IsDir() && fi.Name() == ".git" {
			return filepath.SkipDir
		}
		dirs = append(dirs, path)
		return nil
	})
	if err != nil {
		return nil, err
	}
	return parseDirs(root, dirs)
}

// stripRoot removes root level of a directory.
// This is because when a user requests:
// `/sektionen/om-oss` the actual path is: `root/sektionen/om-oss`
func stripRoot(root string, dir string) string {
	return filepath.Clean(strings.Replace(dir, root, "/", 1))
}

// parseDirs parses each directory into a response. Returns a map from requested
// urls into responses.
func parseDirs(root string, dirs []string) (pages map[string]*Resp, err error) {
	pages = map[string]*Resp{}
	for _, dir := range dirs {
		r, err := parseDir(dir)
		if err != nil {
			log.Warnln(err)
			return nil, nil
		}
		pages[stripRoot(root, dir)] = r
		log.WithFields(log.Fields{
			"Resp": r,
			"dir":  dir,
		}).Debug("Our parsed response\n")
	}
	return pages, nil
}

// toHTML reads a markdown file and returns a HTML string.
func toHTML(filename string) (string, error) {
	buf, err := ioutil.ReadFile(filename)
	if err != nil {
		return "", err
	}
	// Use standard HTML rendering.
	renderer := blackfriday.HtmlRenderer(blackfriday.HTML_USE_XHTML, "", "")
	// Parse markdown where all id's are created from the values inside
	// the element tag.
	buf = blackfriday.MarkdownOptions(buf, renderer, blackfriday.Options{
		Extensions: blackfriday.EXTENSION_AUTO_HEADER_IDS,
	})
	return string(buf), nil
}

// parseDir creates a response for a directory.
func parseDir(dir string) (*Resp, error) {
	log.WithField("dir", dir).Debug("Parsing directory:")

	// Our content files.
	bodyPath := filepath.Join(dir, "body.md")
	sidebarPath := filepath.Join(dir, "sidebar.md")

	// Parse markdown to HTML.
	body, err := toHTML(bodyPath)
	if err != nil {
		return nil, err
	}
	log.WithField("body", body).Debug("HTML of body.md")

	// Parse sidebar to HTML.
	sidebar, err := toHTML(sidebarPath)
	if err != nil {
		return nil, err
	}
	log.WithField("sidebar", sidebar).Debug("HTML of sidebar.md")

	// Parse modified at.
	fi, err := os.Stat(bodyPath)
	if err != nil {
		return nil, err
	}
	// Parse anchors in the body.
	anchs, err := anchor.Anchors(body)
	if err != nil {
		return nil, err
	}
	// Title of the page (first anchor value).
	title := ""
	if len(anchs) > 0 {
		title = anchs[0].Value
	}
	const iso8601DateTime = "2006-01-02T15:04:05Z"
	return &Resp{
		Title:     title,
		Slug:      filepath.Base(dir),
		UpdatedAt: fi.ModTime().Format(iso8601DateTime),
		Image:     "unimplemented",
		Body:      body,
		Sidebar:   sidebar,
		Anchors:   anchs,
	}, nil
}
<|endoftext|>


<commit_before>// Copyright (C) 2012 Numerotron Inc.
// Use of this source code is governed by an MIT-style license
// that can be found in the LICENSE file.

package bingo

import (
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"runtime/debug"
	"strings"
	"time"
)

type ContextBuilder func(*http.Request, http.ResponseWriter, *Session) Context
type ContextHandlerFunc func(Context) *AppError

func newHandler(fn func(http.ResponseWriter, *http.Request, *Session)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		session := loadSession(r)

		fn(w, r, session)
		elapsed := float64(time.Since(start))
		fmt.Printf("%s - request time: %.3f ms", r.URL.Path, elapsed/1000000.0)
	}
}

func newContext(fn ContextHandlerFunc, builder ContextBuilder) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		session := loadSession(r)
		context := builder(r, w, session)

		defer func(c Context) {
			if err := recover(); err != nil {
				serr := fmt.Sprintf("runtime error: %s", err)
				apperr := new(AppError)
				apperr.Message = serr
				apperr.Code = 500
				apperr.Err = errors.New(serr)
				handleError(c, apperr)
			}
		}(context)

		proceed, err := context.Before()
		if err != nil {
			handleError(context, ServerError(err, "before error occurred"))
			return
		}
		if !proceed {
			return
		}

		if e := fn(context); e != nil {
			switch e.Code {
			case 404:
				renderNotFound(context)
			default:
				handleError(context, e)
			}
		}

		context.After()

		elapsed := time.Since(start)

		fmt.Printf("%s - request time: %s\n", r.URL.Path, elapsed)
		LogAccess(context.Request(), elapsed)
	}
}

func newReflect(pattern string, handler interface{}, builder ContextBuilder) http.HandlerFunc {
	methods := make(map[string]reflect.Method)
	t := reflect.TypeOf(handler)
	for i := 0; i < t.NumMethod(); i++ {
		m := t.Method(i)
		methods[strings.ToLower(m.Name)] = m
	}

	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		session := loadSession(r)
		context := builder(r, w, session)

		defer func(c Context) {
			if err := recover(); err != nil {
				serr := fmt.Sprintf("runtime error: %s", err)
				apperr := new(AppError)
				apperr.Message = serr
				apperr.Code = 500
				apperr.Err = errors.New(serr)
				handleError(c, apperr)
			}
		}(context)

		context.Before()

		pieces := Path(r, len(pattern))
		var result []reflect.Value
		switch len(pieces) {
		case 0:
			m, ok := methods["index"]
			if !ok {
				renderNotFound(context)
				return
			}
			result = m.Func.Call([]reflect.Value{reflect.ValueOf(handler), reflect.ValueOf(context)})
		default:
			m, ok := methods[pieces[0]]
			if !ok {
				renderNotFound(context)
				return
			}

			switch m.Type.NumIn() {
			case 3:
				result = m.Func.Call([]reflect.Value{reflect.ValueOf(handler), reflect.ValueOf(context), reflect.ValueOf(pieces[1:])})
			case 2:
				result = m.Func.Call([]reflect.Value{reflect.ValueOf(handler), reflect.ValueOf(context)})
			default:
				renderNotFound(context)
				return
			}
		}

		if len(result) != 1 {
			panic("result should be len(1)")
		}
		e := result[0].Interface().(*AppError)
		if e != nil {
			switch e.Code {
			case 404:
				renderNotFound(context)
			default:
				handleError(context, e)
			}
			return
		}

		context.After()

		elapsed := float64(time.Since(start))
		fmt.Printf("%s - request time: %.3f ms\n", r.URL.Path, elapsed/1000000.0)
	}
}

func handle(pattern string, fn http.HandlerFunc) {
	// handle everything below this pattern
	http.HandleFunc(pattern+"/", fn)

	// this has to come second to override http pkg default, which will add a
	// permanent redirect for the non-slash pattern to the slash pattern
	http.HandleFunc(pattern, fn)
}

func HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request, *Session)) {
	handle(pattern, newHandler(handler))
}

func HandleContext(pattern string, handler ContextHandlerFunc, builder ContextBuilder) {
	handle(pattern, newContext(handler, builder))
}

func HandleReflect(pattern string, handler interface{}, builder ContextBuilder) {
	handle(pattern, newReflect(pattern, handler, builder))
}

// pass in something like "images" to serve /images
func HandleFiles(path string) {
	pattern := fmt.Sprintf("/%s/", path)
	prefix := fmt.Sprintf("/%s", path)
	local := fmt.Sprintf("%s/%s", ContentDir, path)
	http.Handle(pattern, http.StripPrefix(prefix, http.FileServer(http.Dir(local))))
}

func handleError(c Context, aerr *AppError) {
	if Environment == EnvDevel {
		handleErrorInDev(c, aerr)
		return
	}
	renderError(c, aerr.Message)
	if AfterErrorFunc != nil {
		AfterErrorFunc(c, aerr)
	}
}

func handleErrorInDev(c Context, aerr *AppError) {
	fmt.Fprintln(c.Writer(), "An error occurred handling:", c.Request().URL.Path)
	fmt.Fprintln(c.Writer(), "")
	fmt.Fprintln(c.Writer(), aerr.Message)
	fmt.Fprintln(c.Writer(), aerr.Err)
	fmt.Fprintln(c.Writer(), "")
	fmt.Fprintln(c.Writer(), "Stack trace:")
	fmt.Fprintln(c.Writer(), string(debug.Stack()))
}
<commit_msg>NotifyRequestTime function<commit_after>// Copyright (C) 2012 Numerotron Inc.
// Use of this source code is governed by an MIT-style license
// that can be found in the LICENSE file.

package bingo

import (
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"runtime/debug"
	"strings"
	"time"
)

type ContextBuilder func(*http.Request, http.ResponseWriter, *Session) Context
type ContextHandlerFunc func(Context) *AppError

var NotifyRequestTime func(time.Duration)

func newHandler(fn func(http.ResponseWriter, *http.Request, *Session)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		session := loadSession(r)

		fn(w, r, session)
		elapsed := time.Since(start)
		fmt.Printf("%s - request time: %.3f ms", r.URL.Path, float64(elapsed)/float64(time.Millisecond))
                if NotifyRequestTime != nil {
                        NotifyRequestTime(elapsed)
                }
	}
}

func newContext(fn ContextHandlerFunc, builder ContextBuilder) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		session := loadSession(r)
		context := builder(r, w, session)

		defer func(c Context) {
			if err := recover(); err != nil {
				serr := fmt.Sprintf("runtime error: %s", err)
				apperr := new(AppError)
				apperr.Message = serr
				apperr.Code = 500
				apperr.Err = errors.New(serr)
				handleError(c, apperr)
			}
		}(context)

		proceed, err := context.Before()
		if err != nil {
			handleError(context, ServerError(err, "before error occurred"))
			return
		}
		if !proceed {
			return
		}

		if e := fn(context); e != nil {
			switch e.Code {
			case 404:
				renderNotFound(context)
			default:
				handleError(context, e)
			}
		}

		context.After()

		elapsed := time.Since(start)

		LogAccess(context.Request(), elapsed)
                if NotifyRequestTime != nil {
                        NotifyRequestTime(elapsed)
                }
	}
}

func newReflect(pattern string, handler interface{}, builder ContextBuilder) http.HandlerFunc {
	methods := make(map[string]reflect.Method)
	t := reflect.TypeOf(handler)
	for i := 0; i < t.NumMethod(); i++ {
		m := t.Method(i)
		methods[strings.ToLower(m.Name)] = m
	}

	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		session := loadSession(r)
		context := builder(r, w, session)

		defer func(c Context) {
			if err := recover(); err != nil {
				serr := fmt.Sprintf("runtime error: %s", err)
				apperr := new(AppError)
				apperr.Message = serr
				apperr.Code = 500
				apperr.Err = errors.New(serr)
				handleError(c, apperr)
			}
		}(context)

		context.Before()

		pieces := Path(r, len(pattern))
		var result []reflect.Value
		switch len(pieces) {
		case 0:
			m, ok := methods["index"]
			if !ok {
				renderNotFound(context)
				return
			}
			result = m.Func.Call([]reflect.Value{reflect.ValueOf(handler), reflect.ValueOf(context)})
		default:
			m, ok := methods[pieces[0]]
			if !ok {
				renderNotFound(context)
				return
			}

			switch m.Type.NumIn() {
			case 3:
				result = m.Func.Call([]reflect.Value{reflect.ValueOf(handler), reflect.ValueOf(context), reflect.ValueOf(pieces[1:])})
			case 2:
				result = m.Func.Call([]reflect.Value{reflect.ValueOf(handler), reflect.ValueOf(context)})
			default:
				renderNotFound(context)
				return
			}
		}

		if len(result) != 1 {
			panic("result should be len(1)")
		}
		e := result[0].Interface().(*AppError)
		if e != nil {
			switch e.Code {
			case 404:
				renderNotFound(context)
			default:
				handleError(context, e)
			}
			return
		}

		context.After()

		elapsed := time.Since(start)
		fmt.Printf("%s - request time: %.3f ms\n", r.URL.Path, float64(elapsed)/float64(time.Millisecond))
                if NotifyRequestTime != nil {
                        NotifyRequestTime(elapsed)
                }
	}
}

func handle(pattern string, fn http.HandlerFunc) {
	// handle everything below this pattern
	http.HandleFunc(pattern+"/", fn)

	// this has to come second to override http pkg default, which will add a
	// permanent redirect for the non-slash pattern to the slash pattern
	http.HandleFunc(pattern, fn)
}

func HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request, *Session)) {
	handle(pattern, newHandler(handler))
}

func HandleContext(pattern string, handler ContextHandlerFunc, builder ContextBuilder) {
	handle(pattern, newContext(handler, builder))
}

func HandleReflect(pattern string, handler interface{}, builder ContextBuilder) {
	handle(pattern, newReflect(pattern, handler, builder))
}

// pass in something like "images" to serve /images
func HandleFiles(path string) {
	pattern := fmt.Sprintf("/%s/", path)
	prefix := fmt.Sprintf("/%s", path)
	local := fmt.Sprintf("%s/%s", ContentDir, path)
	http.Handle(pattern, http.StripPrefix(prefix, http.FileServer(http.Dir(local))))
}

func handleError(c Context, aerr *AppError) {
	if Environment == EnvDevel {
		handleErrorInDev(c, aerr)
		return
	}
	renderError(c, aerr.Message)
	if AfterErrorFunc != nil {
		AfterErrorFunc(c, aerr)
	}
}

func handleErrorInDev(c Context, aerr *AppError) {
	fmt.Fprintln(c.Writer(), "An error occurred handling:", c.Request().URL.Path)
	fmt.Fprintln(c.Writer(), "")
	fmt.Fprintln(c.Writer(), aerr.Message)
	fmt.Fprintln(c.Writer(), aerr.Err)
	fmt.Fprintln(c.Writer(), "")
	fmt.Fprintln(c.Writer(), "Stack trace:")
	fmt.Fprintln(c.Writer(), string(debug.Stack()))
}
<|endoftext|>


<commit_before>package cors

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/volatile/core"
)

const (
	headerAllowCredentials = "Access-Control-Allow-Credentials"
	headerAllowHeaders     = "Access-Control-Allow-Headers"
	headerAllowMethods     = "Access-Control-Allow-Methods"
	headerAllowOrigin      = "Access-Control-Allow-Origin"
	headerExposeHeaders    = "Access-Control-Expose-Headers"
	headerMaxAge           = "Access-Control-Max-Age"

	headerRequestHeaders = "Access-Control-Request-Headers"
	headerRequestMethod  = "Access-Control-Request-Method"
)

// Options represents access control options for an origin.
type Options struct {
	// AllowedHeaders indicates, in the case of a preflight request,
	// which headers can be used during the actual request.
	AllowedHeaders []string

	// AllowedMethods indicates, in the case of a preflight request,
	// which methods can be used during the actual request.
	//
	// If len(ExposedHeaders) == 0, all methods will be allowed.
	AllowedMethods []string

	// CredentialsAllowed indicates whether the request can include user
	// credentials like cookies, HTTP authentication or client side SSL certificates.
	CredentialsAllowed bool

	// ExposedHeaders whitelists headers that browsers are allowed to access.
	//
	// If len(ExposedHeaders) == 0, all headers on preflight requests will be exposed.
	ExposedHeaders []string

	// MaxAge indicates how long the results of a preflight request can be cached.
	MaxAge time.Duration
}

type formattedOptions struct {
	AllowedHeaders,
	AllowedMethods,
	CredentialsAllowed,
	ExposedHeaders,
	MaxAge *string
}

// Use tells the core to use this handler with the provided options.
func Use(options map[string]Options) {
	fmtOpt := formatCORS(options)
	core.Use(func(c *core.Context) {
		setCORS(c, fmtOpt, c.Next)
	})
}

// LocalUse allows to set CORS locally, for a single handler.
// Remember you can't set headers after Write or WriteHeader has been called.
func LocalUse(c *core.Context, options map[string]Options, handler func()) {
	setCORS(c, formatCORS(options), handler)
}

func formatCORS(opt map[string]Options) (fmtOpt map[string]formattedOptions) {
	// For requests without credentials, the server may specify "*" as a wildcard,
	// thereby allowing any origin to access the resource.
	if wild, ok := opt["*"]; ok && wild.CredentialsAllowed {
		panic("sending credentials via CORS is not permitted for wildcarded origins")
	}

	fmtOpt = make(map[string]formattedOptions, len(opt))

	for origin, item := range opt {
		result := formattedOptions{}
		if len(item.AllowedHeaders) > 0 {
			*result.AllowedHeaders = strings.Join(item.AllowedHeaders, ", ")
		}
		if len(item.AllowedMethods) > 0 {
			*result.AllowedMethods = strings.Join(item.AllowedMethods, ", ")
		}
		if item.CredentialsAllowed {
			*result.CredentialsAllowed = strconv.FormatBool(item.CredentialsAllowed)
		}
		if len(item.ExposedHeaders) > 0 {
			*result.ExposedHeaders = strings.Join(item.ExposedHeaders, ", ")
		}
		if item.MaxAge.Seconds() > 0.5 {
			*result.MaxAge = fmt.Sprintf("%.f", item.MaxAge.Seconds())
		}

		fmtOpt[origin] = result
	}

	return
}

// setCORS set the response headers and continue if it's not a preflight request.
func setCORS(c *core.Context, fmtOpts map[string]formattedOptions, handler func()) {
	origin := c.Request.Header.Get("Origin")

	// Request does not have an Origin header, therefore it is no CORS request
	// and control is passed to the handler.
	if origin == "" {
		handler()
		return
	}

	fmtOpt, ok := fmtOpts[origin]
	wildcard = false

	// Source origin is not known by name, so check for wildcard.
	if !ok {
		fmtOpt, wildcard = fmtOpts["*"]
	}

	// No origin matched and wildcard not accepted, reject the request.
	if !ok && !wildcard {
		http.Error(c.ResponseWriter, "Invalid CORS request", http.StatusForbidden)
		return
	}

	c.ResponseWriter.Header().Set(headerAllowOrigin, origin)

	if wildcard {
		c.ResponseWriter.Header().Set("Vary", "Origin")
	}

	if fmtOpt.CredentialsAllowed != nil {
		c.ResponseWriter.Header().Set(headerAllowCredentials, *fmtOpt.CredentialsAllowed)
	}

	if fmtOpt.ExposedHeaders != nil {
		c.ResponseWriter.Header().Set(headerExposeHeaders, *fmtOpt.ExposedHeaders)
	}

	if fmtOpt.MaxAge != nil {
		c.ResponseWriter.Header().Set(headerMaxAge, *fmtOpt.MaxAge)
	}

	if c.Request.Method != "OPTIONS" {
		handler()
		return
	}

	// OPTIONS is used for preflight requests.
	// For that, only the CORS handler must respond, so the handlers chain is broken.

	// If no allowed headers are set, accept all from the real request.
	if fmtOpt.AllowedHeaders == nil {
		c.ResponseWriter.Header().Set(headerAllowHeaders, c.Request.Header.Get(headerRequestHeaders))
	} else {
		c.ResponseWriter.Header().Set(headerAllowHeaders, *fmtOpt.AllowedHeaders)
	}

	// If no allowed methods are set, accept the method of the real request.
	if fmtOpt.AllowedMethods == nil {
		c.ResponseWriter.Header().Set(headerAllowMethods, c.Request.Header.Get(headerRequestMethod))
	} else {
		c.ResponseWriter.Header().Set(headerAllowMethods, *fmtOpt.AllowedMethods)
	}

	c.ResponseWriter.WriteHeader(http.StatusOK)
}
<commit_msg>Fix typo and new doc<commit_after>package cors

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/volatile/core"
)

const (
	// AllOrigins contains the wildcard symbol.
	AllOrigins = "*"

	// Response headers
	headerAllowCredentials = "Access-Control-Allow-Credentials"
	headerAllowHeaders     = "Access-Control-Allow-Headers"
	headerAllowMethods     = "Access-Control-Allow-Methods"
	headerAllowOrigin      = "Access-Control-Allow-Origin"
	headerExposeHeaders    = "Access-Control-Expose-Headers"
	headerMaxAge           = "Access-Control-Max-Age"

	// Request headers
	headerRequestHeaders = "Access-Control-Request-Headers"
	headerRequestMethod  = "Access-Control-Request-Method"
)

// Options represents access control options for an origin.
type Options struct {
	AllowedHeaders     []string      // AllowedHeaders indicates, in the case of a preflight request, which headers can be used during the actual request. If none are set, all are allowed.
	AllowedMethods     []string      // AllowedMethods indicates, in the case of a preflight request, which methods can be used during the actual request. If none are set, all are allowed.
	CredentialsAllowed bool          // CredentialsAllowed indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.
	ExposedHeaders     []string      // ExposedHeaders whitelists headers that browsers are allowed to access.
	MaxAge             time.Duration // MaxAge indicates how long the results of a preflight request can be cached.
}

type formattedOptions struct {
	AllowedHeaders     *string
	AllowedMethods     *string
	CredentialsAllowed *string
	ExposedHeaders     *string
	MaxAge             *string
}

// Use adds a handler that sets CORS with the provided options for all dowstream requests.
func Use(options map[string]Options) {
	fmtOpt := formatCORS(options)
	core.Use(func(c *core.Context) {
		setCORS(c, fmtOpt, c.Next)
	})
}

// LocalUse sets CORS with the provided options locally, for a single handler.
// The global options (if used) are overwritten in this situation.
func LocalUse(c *core.Context, options map[string]Options, handler func()) {
	setCORS(c, formatCORS(options), handler)
}

func formatCORS(opt map[string]Options) (fmtOpt map[string]formattedOptions) {
	// TODO: Remove this check as there is no other solution to accept credentials for all origins.
	if opt, ok := opt[AllOrigins]; ok && opt.CredentialsAllowed {
		panic("cors: sending credentials via CORS is not permitted for wildcarded origins")
	}

	fmtOpt = make(map[string]formattedOptions, len(opt))

	for origin, item := range opt {
		result := formattedOptions{}
		if len(item.AllowedHeaders) > 0 {
			*result.AllowedHeaders = strings.Join(item.AllowedHeaders, ", ")
		}
		if len(item.AllowedMethods) > 0 {
			*result.AllowedMethods = strings.Join(item.AllowedMethods, ", ")
		}
		if item.CredentialsAllowed {
			*result.CredentialsAllowed = strconv.FormatBool(item.CredentialsAllowed)
		}
		if len(item.ExposedHeaders) > 0 {
			*result.ExposedHeaders = strings.Join(item.ExposedHeaders, ", ")
		}
		if item.MaxAge.Seconds() > 0.5 {
			*result.MaxAge = fmt.Sprintf("%.f", item.MaxAge.Seconds())
		}

		fmtOpt[origin] = result
	}

	return
}

// setCORS sets the response headers and continues downstream if it's not a preflight request.
func setCORS(c *core.Context, fmtOpts map[string]formattedOptions, handler func()) {
	origin := c.Request.Header.Get("Origin")

	// Use CORS only if an Origin header is defined for the request.
	if origin != "" {
		fmtOpt, ok := fmtOpts[origin]
		wildcard := false

		// Unknown origin: check for wildcard.
		if !ok {
			fmtOpt, wildcard = fmtOpts[AllOrigins]
		}

		// No origin matched and wildcard not accepted: reject the request.
		if !ok && !wildcard {
			http.Error(c.ResponseWriter, "Invalid CORS request", http.StatusForbidden)
			return
		}

		c.ResponseWriter.Header().Set(headerAllowOrigin, origin)

		if wildcard {
			c.ResponseWriter.Header().Set("Vary", "Origin")
		}

		// Set credentials header only if they are allowed.
		if fmtOpt.CredentialsAllowed != nil && *fmtOpt.CredentialsAllowed == "true" {
			c.ResponseWriter.Header().Set(headerAllowCredentials, *fmtOpt.CredentialsAllowed)
		}

		if fmtOpt.ExposedHeaders != nil {
			c.ResponseWriter.Header().Set(headerExposeHeaders, *fmtOpt.ExposedHeaders)
		}

		if fmtOpt.MaxAge != nil {
			c.ResponseWriter.Header().Set(headerMaxAge, *fmtOpt.MaxAge)
		}

		// OPTIONS method is used for a preflight request.
		// In this case, other CORS headers still need to be set before sending all of them, without any other work.
		if c.Request.Method == "OPTIONS" {
			// If no allowed headers are set, all are allowed.
			if fmtOpt.AllowedHeaders == nil {
				c.ResponseWriter.Header().Set(headerAllowHeaders, c.Request.Header.Get(headerRequestHeaders))
			} else {
				c.ResponseWriter.Header().Set(headerAllowHeaders, *fmtOpt.AllowedHeaders)
			}

			// If no allowed methods are set, all are allowed.
			if fmtOpt.AllowedMethods == nil {
				c.ResponseWriter.Header().Set(headerAllowMethods, c.Request.Header.Get(headerRequestMethod))
			} else {
				c.ResponseWriter.Header().Set(headerAllowMethods, *fmtOpt.AllowedMethods)
			}

			c.ResponseWriter.WriteHeader(http.StatusOK)
			return
		}
	}

	handler()
}
<|endoftext|>


<commit_before>package version

import "fmt"

const Name = "XD"

const Major = "0"

const Minor = "0"

const Patch = "3"

var Git string

func Version() string {
	return fmt.Sprintf("%s-%s.%s.%s%s", Name, Major, Minor, Patch, Git)
}
<commit_msg>unbump version<commit_after>package version

import "fmt"

const Name = "XD"

const Major = "0"

const Minor = "0"

const Patch = "2"

var Git string

func Version() string {
	return fmt.Sprintf("%s-%s.%s.%s%s", Name, Major, Minor, Patch, Git)
}
<|endoftext|>


<commit_before>// Copyright 2011 Arne Roomann-Kurrik
//
// 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 twittergo

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"time"
)

const (
	H_LIMIT        = "X-RateLimit-Limit"
	H_LIMIT_REMAIN = "X-RateLimit-Remaining"
	H_LIMIT_RESET  = "X-RateLimit-Reset"
)

const (
	STATUS_LIMIT = 420
)

type RateLimitError struct {
	Limit     uint32
	Remaining uint32
	Reset     time.Time
}

func (e RateLimitError) Error() string {
	msg := "Rate limit: %v, Remaining: %v, Reset: %v"
	return fmt.Sprintf(msg, e.Limit, e.Remaining, e.Reset)
}

type APIResponse http.Response

func (r APIResponse) HasRateLimit() bool {
	return r.Header.Get(H_LIMIT) != ""
}

func (r APIResponse) RateLimit() uint32 {
	h := r.Header.Get(H_LIMIT)
	i, _ := strconv.ParseUint(h, 10, 32)
	return uint32(i)
}

func (r APIResponse) RateLimitRemaining() uint32 {
	h := r.Header.Get(H_LIMIT_REMAIN)
	i, _ := strconv.ParseUint(h, 10, 32)
	return uint32(i)
}

func (r APIResponse) RateLimitReset() time.Time {
	h := r.Header.Get(H_LIMIT_RESET)
	i, _ := strconv.ParseUint(h, 10, 32)
	t := time.Unix(int64(i), 0)
	return t
}

// Parses a JSON encoded HTTP response into the supplied interface.
func (r APIResponse) Parse(out interface{}) (err error) {
	switch r.StatusCode {
	case STATUS_LIMIT:
		err = RateLimitError{
			Limit:     r.RateLimit(),
			Remaining: r.RateLimitRemaining(),
			Reset:     r.RateLimitReset(),
		}
		return
	}
	defer r.Body.Close()
	err = json.NewDecoder(r.Body).Decode(out)
	if err == io.EOF {
		err = nil
	}
	return
}

type User map[string]interface{}

func (u User) Id() uint64 {
	id, _ := strconv.ParseUint(u["id_str"].(string), 10, 64)
	return id
}

func (u User) IdStr() string {
	return u["id_str"].(string)
}

func (u User) Name() string {
	return u["name"].(string)
}
<commit_msg>Update models to handle invalid request errors<commit_after>// Copyright 2011 Arne Roomann-Kurrik
//
// 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 twittergo

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"time"
)

const (
	H_LIMIT        = "X-RateLimit-Limit"
	H_LIMIT_REMAIN = "X-RateLimit-Remaining"
	H_LIMIT_RESET  = "X-RateLimit-Reset"
)

const (
	STATUS_LIMIT   = 420
	STATUS_INVALID = 400
)

type Error struct {
	Code int
	Message string
}

func (e Error) Error() string {
	msg := "Error %v: %v"
	return fmt.Sprintf(msg, e.Code, e.Message)
}

type Errors struct {
	Errors []Error
}

func (e Errors) Error() string {
	msg := ""
	for _, err := range e.Errors {
		msg += err.Error() + ". "
	}
	return msg
}

type RateLimitError struct {
	Limit     uint32
	Remaining uint32
	Reset     time.Time
}

func (e RateLimitError) Error() string {
	msg := "Rate limit: %v, Remaining: %v, Reset: %v"
	return fmt.Sprintf(msg, e.Limit, e.Remaining, e.Reset)
}

type APIResponse http.Response

func (r APIResponse) HasRateLimit() bool {
	return r.Header.Get(H_LIMIT) != ""
}

func (r APIResponse) RateLimit() uint32 {
	h := r.Header.Get(H_LIMIT)
	i, _ := strconv.ParseUint(h, 10, 32)
	return uint32(i)
}

func (r APIResponse) RateLimitRemaining() uint32 {
	h := r.Header.Get(H_LIMIT_REMAIN)
	i, _ := strconv.ParseUint(h, 10, 32)
	return uint32(i)
}

func (r APIResponse) RateLimitReset() time.Time {
	h := r.Header.Get(H_LIMIT_RESET)
	i, _ := strconv.ParseUint(h, 10, 32)
	t := time.Unix(int64(i), 0)
	return t
}

// Parses a JSON encoded HTTP response into the supplied interface.
func (r APIResponse) Parse(out interface{}) (err error) {
	switch r.StatusCode {
	case STATUS_INVALID:
		e := &Errors{}
		defer r.Body.Close()
		json.NewDecoder(r.Body).Decode(e)
		err = *e
		return
	case STATUS_LIMIT:
		err = RateLimitError{
			Limit:     r.RateLimit(),
			Remaining: r.RateLimitRemaining(),
			Reset:     r.RateLimitReset(),
		}
		return
	}
	defer r.Body.Close()
	err = json.NewDecoder(r.Body).Decode(out)
	if err == io.EOF {
		err = nil
	}
	return
}

type User map[string]interface{}

func (u User) Id() uint64 {
	id, _ := strconv.ParseUint(u["id_str"].(string), 10, 64)
	return id
}

func (u User) IdStr() string {
	return u["id_str"].(string)
}

func (u User) Name() string {
	return u["name"].(string)
}
<|endoftext|>


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

package src

import "fmt"

type FuncLit struct {
	ExprName string    `json:"expression_name"`
	Name     string    `json:"name"`
	Type     *FuncType `json:"type"`
	Body     []Stmt    `json:"body,omitempty"`
	LoC      int64     `json:"loc"` // Lines of Code
}

func newFuncLit(m map[string]interface{}) (*FuncLit, error) {
	var err error
	errPrefix := "src/func_lit"
	fct := FuncLit{}

	if typ, err := extractStringValue("expression_name", errPrefix, m); err != nil {
		// XXX It is not possible to add debug info on this error because it is
		// required that this error be en "errNotExist".
		return nil, errNotExist
	} else if typ != FuncLitName {
		return nil, fmt.Errorf("invalid type: expected 'FuncLit', found '%s'", typ)
	}

	fct.ExprName = FuncLitName

	if fct.Name, err = extractStringValue("name", errPrefix, m); err != nil {
		return nil, addDebugInfo(err)
	}

	fctTypeMap, err := extractMapValue("type", errPrefix, m)
	if err != nil {
		return nil, addDebugInfo(err)
	}

	if fct.Type, err = newFuncType(fctTypeMap); err != nil {
		return nil, addDebugInfo(err)
	}

	if fct.Body, err = newStmtsSlice("body", errPrefix, m); err != nil && isExist(err) {
		return nil, addDebugInfo(err)
	}

	if fct.LoC, err = extractInt64Value("loc", errPrefix, m); err != nil {
		return nil, addDebugInfo(err)
	}

	return &fct, nil
}
<commit_msg>src: remove field name from FuncLit<commit_after>// Copyright 2014-2015 The DevMine Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package src

import "fmt"

type FuncLit struct {
	ExprName string    `json:"expression_name"`
	Type     *FuncType `json:"type"`
	Body     []Stmt    `json:"body,omitempty"`
	LoC      int64     `json:"loc"` // Lines of Code
}

func newFuncLit(m map[string]interface{}) (*FuncLit, error) {
	var err error
	errPrefix := "src/func_lit"
	fct := FuncLit{}

	if typ, err := extractStringValue("expression_name", errPrefix, m); err != nil {
		// XXX It is not possible to add debug info on this error because it is
		// required that this error be en "errNotExist".
		return nil, errNotExist
	} else if typ != FuncLitName {
		return nil, fmt.Errorf("invalid type: expected 'FuncLit', found '%s'", typ)
	}

	fct.ExprName = FuncLitName

	fctTypeMap, err := extractMapValue("type", errPrefix, m)
	if err != nil {
		return nil, addDebugInfo(err)
	}

	if fct.Type, err = newFuncType(fctTypeMap); err != nil {
		return nil, addDebugInfo(err)
	}

	if fct.Body, err = newStmtsSlice("body", errPrefix, m); err != nil && isExist(err) {
		return nil, addDebugInfo(err)
	}

	if fct.LoC, err = extractInt64Value("loc", errPrefix, m); err != nil {
		return nil, addDebugInfo(err)
	}

	return &fct, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"github.com/kless/term"
	"io/ioutil"
	"log"
	"os"
	"path"
	"regexp"
	"strings"
	"syscall"
)

var argNum = 0

var filterPackageName = flag.Bool("p", false, "filter by package name; detect it by default")
var packageName = flag.String("package", "", "package name to filter by")
var args []string

type dotfileNotFoundError struct {
	name string
}

func (self dotfileNotFoundError) Error() string {
	return fmt.Sprintf("%s not found. Please ensure your project is configured properly.", self.name)
}

func main() {
	log.SetFlags(0)

	flag.Parse()
	args = flag.Args()

	var cluster string

	envCluster := getNextArg("environment not given")
	envClusterSplit := strings.Split(envCluster, "/")
	env := envClusterSplit[0]
	if len(envClusterSplit) > 1 {
		cluster = envClusterSplit[1]
	}

	cmd := getNextArg("command not given")

	projectName, err := detectProjectName()
	if err != nil {
		log.Fatalln(err)
	}

	if cmd == "deploy" {
		*filterPackageName = true
	}

	var packageNames []string

	if *filterPackageName {
		if *packageName == "" {
			packageNames, err = detectPackageNames()
			if err != nil {
				log.Fatalln(err)
			}
		} else {
			packageNames = []string{*packageName}
		}
	}

	awsConf, err := getAWSConf(projectName)
	if err != nil {
		log.Fatalln(err)
	}
	job, err := NewJob(awsConf, env, cluster, projectName, packageNames,
		os.Stdout, term.IsTerminal(syscall.Stdout))
	if err != nil {
		log.Fatalln(err)
	}

	switch cmd {
	case "deploy":
		showErrorsList(job.Deploy())
	case "exec":
		cmd := getRemainingArgsAsString("command not given")
		showErrorsList(job.Exec(cmd))
	case "ssh":
		hostName := getNextArg("")
		sshArgs := getRemainingArgsAsSlice("")
		err = job.Ssh(hostName, sshArgs)
	case "scp":
		if len(args) <= argNum {
			log.Fatalln("you must give at least one source file")
		}
		err = job.Scp(args[argNum:])
	case "ls":
		err = job.List()
	case "hostname":
		instanceName := getNextArg("instance name not given")
		err = job.Hostname(instanceName)
	default:
		log.Fatalf("command not recognised: %s\n", cmd)
	}

	if err != nil {
		log.Fatalln(err)
	}
}

func showErrorsList(errs []error) {
	if len(errs) > 0 {
		errStrings := make([]string, len(errs))
		for i, err := range errs {
			errStrings[i] = err.Error()
		}
		log.Fatalf(strings.Join(errStrings, "\n"))
	}
}

func fatalUsageError(errMsg string) {
	fmt.Fprintln(os.Stderr, "fatal: "+errMsg+"\n")
	usage()
	os.Exit(1)
}

func getNextArg(errMsg string) (val string) {
	if len(args) >= (argNum + 1) {
		val = args[argNum]
		argNum += 1
	} else if errMsg != "" {
		fatalUsageError(errMsg)
	}
	return
}

func getRemainingArgsAsString(errMsg string) (val string) {
	remainingArgs := args[argNum:]
	if len(remainingArgs) >= 1 {
		val = strings.Join(remainingArgs, " ")
	} else {
		log.Fatalln(errMsg)
	}
	return
}

func getRemainingArgsAsSlice(errMsg string) (val []string) {
	val = args[argNum:]
	if errMsg != "" && len(val) == 0 {
		log.Fatalln(errMsg)
	}
	return
}

func findDotfileAndRead(fn string, errName string) (value string, err error) {
	dir, err := os.Getwd()
	if err != nil {
		return
	}

	var newDir string
	for {
		if fBytes, err := ioutil.ReadFile(path.Join(dir, fn)); err == nil && len(fBytes) > 0 {
			return strings.TrimSpace(string(fBytes)), nil
		}

		newDir = path.Dir(dir)
		if dir == newDir {
			break
		}
		dir = newDir
	}

	return "", dotfileNotFoundError{name: errName}
}

func findDotfilesAndRead(fns []string, errName string) (value string, err error) {
	for _, fn := range fns {
		value, err = findDotfileAndRead(fn, errName)
		if err == nil {
			return value, nil
		} else {
			if _, ok := err.(dotfileNotFoundError); !ok {
				fmt.Printf("err: %#v\n", err)
				return "", err
			}
		}
	}
	return
}

func detectProjectName() (projectName string, err error) {
	return findDotfilesAndRead([]string{".project-name", ".moltar-project", ".mxm-project"}, "Project name")
}

var packageNamePattern = regexp.MustCompile(`\b[-\w]+\b`)

func detectPackageNames() (packageNames []string, err error) {
	ps, err := findDotfilesAndRead([]string{
		".package-name", ".moltar-package", ".mxm-package",
		".package-names", ".moltar-packages", ".mxm-packages",
	}, "Package name")
	if err == nil {
		return packageNamePattern.FindAllString(ps, -1), nil
	} else {
		return nil, err
	}
}

func formatTable(fields [][]string) (out string) {
	if len(fields) == 0 {
		return
	}
	outBuf := new(bytes.Buffer)
	numFields := len(fields[0])
	maxIndex := numFields - 1
	maxWidths := make([]int, numFields)
	for _, f := range fields {
		for i, c := range f {
			if lenc := len(c); lenc > maxWidths[i] {
				maxWidths[i] = lenc
			}
		}
	}

	for _, f := range fields {
		for i := 0; i < numFields; i++ {
			c := f[i]
			outBuf.WriteString(c)
			if i < maxIndex {
				outBuf.Write(
					bytes.Repeat([]byte(" "), maxWidths[i]-len(c)+2))
			}
		}
		outBuf.WriteRune('\n')
	}

	out = outBuf.String()

	return
}
<commit_msg>Accept deploy package names as command-line args<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"github.com/kless/term"
	"io/ioutil"
	"log"
	"os"
	"path"
	"regexp"
	"strings"
	"syscall"
)

var argNum = 0

var filterPackageName = flag.Bool("p", false, "filter by package name; detect it by default")
var packageName = flag.String("package", "", "package name to filter by")
var args []string

type dotfileNotFoundError struct {
	name string
}

func (self dotfileNotFoundError) Error() string {
	return fmt.Sprintf("%s not found. Please ensure your project is configured properly.", self.name)
}

func main() {
	log.SetFlags(0)

	flag.Parse()
	args = flag.Args()

	var cluster string

	envCluster := getNextArg("environment not given")
	envClusterSplit := strings.Split(envCluster, "/")
	env := envClusterSplit[0]
	if len(envClusterSplit) > 1 {
		cluster = envClusterSplit[1]
	}

	cmd := getNextArg("command not given")

	projectName, err := detectProjectName()
	if err != nil {
		log.Fatalln(err)
	}

	var packageNames []string

	if cmd == "deploy" {
		*filterPackageName = true
		packageNames = getRemainingArgsAsSlice("")
	}

	if *filterPackageName && len(packageNames) == 0 {
		if *packageName == "" {
			packageNames, err = detectPackageNames()
			if err != nil {
				log.Fatalln(err)
			}
		} else {
			packageNames = []string{*packageName}
		}
	}

	awsConf, err := getAWSConf(projectName)
	if err != nil {
		log.Fatalln(err)
	}
	job, err := NewJob(awsConf, env, cluster, projectName, packageNames,
		os.Stdout, term.IsTerminal(syscall.Stdout))
	if err != nil {
		log.Fatalln(err)
	}

	switch cmd {
	case "deploy":
		showErrorsList(job.Deploy())
	case "exec":
		cmd := getRemainingArgsAsString("command not given")
		showErrorsList(job.Exec(cmd))
	case "ssh":
		hostName := getNextArg("")
		sshArgs := getRemainingArgsAsSlice("")
		err = job.Ssh(hostName, sshArgs)
	case "scp":
		if len(args) <= argNum {
			log.Fatalln("you must give at least one source file")
		}
		err = job.Scp(args[argNum:])
	case "ls":
		err = job.List()
	case "hostname":
		instanceName := getNextArg("instance name not given")
		err = job.Hostname(instanceName)
	default:
		log.Fatalf("command not recognised: %s\n", cmd)
	}

	if err != nil {
		log.Fatalln(err)
	}
}

func showErrorsList(errs []error) {
	if len(errs) > 0 {
		errStrings := make([]string, len(errs))
		for i, err := range errs {
			errStrings[i] = err.Error()
		}
		log.Fatalf(strings.Join(errStrings, "\n"))
	}
}

func fatalUsageError(errMsg string) {
	fmt.Fprintln(os.Stderr, "fatal: "+errMsg+"\n")
	usage()
	os.Exit(1)
}

func getNextArg(errMsg string) (val string) {
	if len(args) >= (argNum + 1) {
		val = args[argNum]
		argNum += 1
	} else if errMsg != "" {
		fatalUsageError(errMsg)
	}
	return
}

func getRemainingArgsAsString(errMsg string) (val string) {
	remainingArgs := args[argNum:]
	if len(remainingArgs) >= 1 {
		val = strings.Join(remainingArgs, " ")
	} else {
		log.Fatalln(errMsg)
	}
	return
}

func getRemainingArgsAsSlice(errMsg string) (val []string) {
	val = args[argNum:]
	if errMsg != "" && len(val) == 0 {
		log.Fatalln(errMsg)
	}
	return
}

func findDotfileAndRead(fn string, errName string) (value string, err error) {
	dir, err := os.Getwd()
	if err != nil {
		return
	}

	var newDir string
	for {
		if fBytes, err := ioutil.ReadFile(path.Join(dir, fn)); err == nil && len(fBytes) > 0 {
			return strings.TrimSpace(string(fBytes)), nil
		}

		newDir = path.Dir(dir)
		if dir == newDir {
			break
		}
		dir = newDir
	}

	return "", dotfileNotFoundError{name: errName}
}

func findDotfilesAndRead(fns []string, errName string) (value string, err error) {
	for _, fn := range fns {
		value, err = findDotfileAndRead(fn, errName)
		if err == nil {
			return value, nil
		} else {
			if _, ok := err.(dotfileNotFoundError); !ok {
				fmt.Printf("err: %#v\n", err)
				return "", err
			}
		}
	}
	return
}

func detectProjectName() (projectName string, err error) {
	return findDotfilesAndRead([]string{".project-name", ".moltar-project", ".mxm-project"}, "Project name")
}

var packageNamePattern = regexp.MustCompile(`\b[-\w]+\b`)

func detectPackageNames() (packageNames []string, err error) {
	ps, err := findDotfilesAndRead([]string{
		".package-name", ".moltar-package", ".mxm-package",
		".package-names", ".moltar-packages", ".mxm-packages",
	}, "Package name")
	if err == nil {
		return packageNamePattern.FindAllString(ps, -1), nil
	} else {
		return nil, err
	}
}

func formatTable(fields [][]string) (out string) {
	if len(fields) == 0 {
		return
	}
	outBuf := new(bytes.Buffer)
	numFields := len(fields[0])
	maxIndex := numFields - 1
	maxWidths := make([]int, numFields)
	for _, f := range fields {
		for i, c := range f {
			if lenc := len(c); lenc > maxWidths[i] {
				maxWidths[i] = lenc
			}
		}
	}

	for _, f := range fields {
		for i := 0; i < numFields; i++ {
			c := f[i]
			outBuf.WriteString(c)
			if i < maxIndex {
				outBuf.Write(
					bytes.Repeat([]byte(" "), maxWidths[i]-len(c)+2))
			}
		}
		outBuf.WriteRune('\n')
	}

	out = outBuf.String()

	return
}
<|endoftext|>


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

//
// Expression related code generation
//

package main

import "./libgogo/_obj/libgogo"

type ExpressionDescriptor struct {
    ExpressionDepth uint64;
    RestCounter uint64; // depr.
    IncCnt uint64;
    GlobalCounter uint64; // depr.
    Prefix string; // depr.
    CurFile string;
    CurLine uint64;
    T uint64;
    F uint64;
    TDepth uint64;
    FDepth uint64;
};

//
// Called by parser (ParseSimpleExpression)
//
func GenerateSimpleExpressionArith(item1 *libgogo.Item, item2 *libgogo.Item, op uint64) {
    if Compile != 0 {
        if (item1.Itemtype != byte_t) && (item1.Itemtype != uint64_t) {
            SymbolTableError("Invalid left operand type for", "", "addition/subtraction:", item1.Itemtype.Name);
        }
        if (item2.Itemtype != byte_t) && (item2.Itemtype != uint64_t) {
            SymbolTableError("Invalid right operand type for", "", "addition/subtraction:", item2.Itemtype.Name);
        }
        if op == TOKEN_ARITH_PLUS { //Add
            AddSubInstruction("ADD", item1, item2, item1.A + item2.A, 0);
        } else { //Subtract
            AddSubInstruction("SUB", item1, item2, item1.A - item2.A, 0);
        }
    }
}

//
// Called by parser (ParseTerm)
//
func GenerateTermArith(item1 *libgogo.Item, item2 *libgogo.Item, op uint64) {
    if Compile != 0 {
        if (op == TOKEN_ARITH_DIV) || op == (TOKEN_ARITH_MUL) {
            if (item1.Itemtype != byte_t) && (item1.Itemtype != uint64_t) {
                SymbolTableError("Invalid left operand type for", "", "multiplication/division:", item1.Itemtype.Name);
            }
            if (item2.Itemtype != byte_t) && (item2.Itemtype != uint64_t) {
                SymbolTableError("Invalid right operand type for", "", "multiplication/division:", item2.Itemtype.Name);
            }
        }
        if op == TOKEN_ARITH_DIV { // Division
            if item2.Mode == libgogo.MODE_CONST {
                if item2.A == 0 {
                    GenErrorWeak("Division by zero.");
                }
            }
            if item2.A != 0 { //Avoid division by zero for constvalue parameter
                DivMulInstruction("DIV", item1, item2, item1.A / item2.A, 0);
            } else {
                DivMulInstruction("DIV", item1, item2, 0, 0);
            }
        }
        if op == TOKEN_ARITH_MUL { // Multiplication
            DivMulInstruction("MUL", item1, item2, item1.A * item2.A, 0);
        }
    }
}

func GenerateRelative(item *libgogo.Item, op uint64, ed *ExpressionDescriptor) {
    var labelString string;
    var jmp string;

    if Compile != 0 {
        if item.Mode != libgogo.MODE_COND {
            GenErrorWeak("Can use relative operators only with conditionals.");
        }
        if op == TOKEN_REL_AND {
            labelString = GenerateSubLabel(ed,0,"END");
            jmp = GetJump(item.C, 1);
            PrintJump(jmp, labelString);
            if ed.T != 0 {
                if ed.TDepth >= ed.ExpressionDepth {
                    labelString = GetSubLabel(ed,1,"END");
                    PrintLabel(labelString);
                    ed.T = 0;
                }
            }
        } else {
            if op == TOKEN_REL_OR {
                labelString = GenerateSubLabel(ed,1,"END");
                jmp = GetJump(item.C, 0);
                PrintJump(jmp, labelString);
                if ed.F != 0 {
                    if ed.FDepth >= ed.ExpressionDepth {
                        labelString = GetSubLabel(ed,0,"END");
                        PrintLabel(labelString);
                        ed.F = 0;
                    }
                }
            } else {
                GenErrorWeak("Relative AND or OR expected.");
            }
        }
    }
}

//
// Called by parser (ParseExpression)
// Note: This function can only handle operands with a maximum of 8 bytes in size
//
func GenerateComparison(item1 *libgogo.Item, item2 *libgogo.Item, op uint64) {
    if Compile != 0 {
        //if (item1.Itemtype != uint64_t) || (item2.Itemtype != uint64_t) {
        //    SymbolTableError("Cannot compare types", "", "other than", uint64_t.Name);
        //}
        if (item1.Itemtype != item2.Itemtype) && (item1.Itemtype != string_t) && (item2.Itemtype != string_t) {
            GenErrorWeak("Can only compare variables of same type.");
        }
        if (item1.Itemtype == string_t) || (item2.Itemtype == string_t) {
            GenErrorWeak("Cannot compare string types.");
        }
        if item1.PtrType == 1 {
            if item2.PtrType == 1 {
                if (op != TOKEN_EQUALS) && (op != TOKEN_NOTEQUAL) {
                    GenErrorWeak("Can only compare '==' or '!=' on pointers'");
                }
            } else {
                GenErrorWeak("Pointer to non-pointer comparison.");
            }
        }
        if (item2.PtrType == 1) && (item1.PtrType != 1) {
            GenErrorWeak("Non-pointer to pointer comparison.");
        }

        // Generate CMP statements depending on items
        if item1.Mode == libgogo.MODE_CONST {
            if item2.Mode == libgogo.MODE_CONST {
                item1.Itemtype = bool_t;
                item1.A = GetConditionalBool(op, item1.A, item2.A);
            } else {
                if item1.PtrType == 0 {
                    MakeRegistered(item1, 0);
                } else {
                    MakeRegistered(item1, 1);
                }
                if item2.Mode == libgogo.MODE_REG {
                    PrintInstruction_Reg_Reg("CMP", 8, "R", item1.R, 0, 0, 0, "", "R", item2.R, 0, 0, 0, "");
                }
                if item2.Mode == libgogo.MODE_VAR {
                    if item2.PtrType == 1 {
                        MakeRegistered(item2, 1);
                        PrintInstruction_Reg_Reg("CMP", 8, "R", item1.R, 0, 0, 0, "", "R", item2.R, 0, 0, 0, "");
                    } else {
                        PrintInstruction_Reg_Var("CMP", "R", item1.R, "", 0, item2);
                    }
                }
            }
        }
        if item1.Mode == libgogo.MODE_REG {
            DereferRegisterIfNecessary(item1);
            if item2.Mode == libgogo.MODE_CONST {
                PrintInstruction_Reg_Imm("CMP", 8, "R", item1.R, 0, 0, 0, "", item2.A);
            }
            if item2.Mode == libgogo.MODE_REG {
                PrintInstruction_Reg_Reg("CMP", 8, "R", item1.R, 0, 0, 0, "", "R", item2.R, 0, 0, 0, "");
            }
            if item2.Mode == libgogo.MODE_VAR {
                PrintInstruction_Reg_Var("CMP", "R", item1.R, "", 0, item2);
            }
        }
        if item1.Mode == libgogo.MODE_VAR {
            if item2.Mode == libgogo.MODE_CONST {
                PrintInstruction_Var_Imm("CMP", item1, item2.A);
            }
            if item2.Mode == libgogo.MODE_REG {
                DereferRegisterIfNecessary(item1);
                if item1.PtrType == 1 {
                    MakeRegistered(item1, 1);
                    PrintInstruction_Reg_Reg("CMP", 8, "R", item1.R, 0, 0, 0, "", "R", item2.R, 0, 0, 0, "");
                } else {
                    PrintInstruction_Var_Reg("CMP", item1, "R", item2.R, "", 0);
                }
            }
            if item2.Mode == libgogo.MODE_VAR {
                if item2.PtrType == 0 {
                    MakeRegistered(item2, 0);
                } else {
                    MakeRegistered(item2, 1);
                }
                if item1.PtrType == 1 {
                    MakeRegistered(item1, 1);
                    PrintInstruction_Reg_Reg("CMP", 8, "R", item1.R, 0, 0, 0, "", "R", item2.R, 0, 0, 0, "");
                } else {
                    PrintInstruction_Var_Reg("CMP", item1, "R", item2.R, "", 0);
                }
            }
        }

        // Prepare item
        item1.Itemtype = bool_t;
        item1.Mode = libgogo.MODE_COND;
        if op == TOKEN_EQUALS {
            item1.C = libgogo.REL_EQ;
        }
        if op == TOKEN_NOTEQUAL {
            item1.C = libgogo.REL_NEQ;
        }
        if op == TOKEN_REL_LT {
            item1.C = libgogo.REL_LT;
        }
        if op == TOKEN_REL_LTOE {
            item1.C = libgogo.REL_LTE;
        }
        if op == TOKEN_REL_GT {
            item1.C = libgogo.REL_GT;
        }
        if op == TOKEN_REL_GTOE {
            item1.C = libgogo.REL_GTE;
        }

        FreeRegisterIfRequired(item2);
    }
}

func GetConditionalBool(op uint64, val1 uint64, val2 uint64) uint64 {
    var ret uint64;
    if op == TOKEN_EQUALS {
        if val1 == val2 {
            ret = 1;
        } else {
            ret = 0;
        }
    }
    if op == TOKEN_NOTEQUAL {
        if val1 == val2 {
            ret = 0;
        } else {
            ret = 1;
        }
    }
    if op == TOKEN_REL_GTOE {
        if val1 >= val2 {
            ret = 1;
        } else {
            ret = 0;
        }
    }
    if op == TOKEN_REL_LTOE {
        if val1 <= val2 {
            ret = 1;
        } else {
            ret = 0;
        }
    }
    if op == TOKEN_REL_GT {
        if val1 > val2 {
            ret = 1;
        } else {
            ret = 0;
        }
    }
    if op == TOKEN_REL_LT {
        if val1 < val2 {
            ret = 1;
        } else {
            ret = 0;
        }
    }
    return ret;
}
<commit_msg>gen-expr: Support fancy (but useless, since not bounded to a var) const           expressions.<commit_after>// Copyright 2010 The GoGo Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

//
// Expression related code generation
//

package main

import "./libgogo/_obj/libgogo"

type ExpressionDescriptor struct {
    ExpressionDepth uint64;
    RestCounter uint64; // depr.
    IncCnt uint64;
    GlobalCounter uint64; // depr.
    Prefix string; // depr.
    CurFile string;
    CurLine uint64;
    T uint64;
    F uint64;
    TDepth uint64;
    FDepth uint64;
};

//
// Called by parser (ParseSimpleExpression)
//
func GenerateSimpleExpressionArith(item1 *libgogo.Item, item2 *libgogo.Item, op uint64) {
    if Compile != 0 {
        if (item1.Itemtype != byte_t) && (item1.Itemtype != uint64_t) {
            SymbolTableError("Invalid left operand type for", "", "addition/subtraction:", item1.Itemtype.Name);
        }
        if (item2.Itemtype != byte_t) && (item2.Itemtype != uint64_t) {
            SymbolTableError("Invalid right operand type for", "", "addition/subtraction:", item2.Itemtype.Name);
        }
        if op == TOKEN_ARITH_PLUS { //Add
            AddSubInstruction("ADD", item1, item2, item1.A + item2.A, 0);
        } else { //Subtract
            AddSubInstruction("SUB", item1, item2, item1.A - item2.A, 0);
        }
    }
}

//
// Called by parser (ParseTerm)
//
func GenerateTermArith(item1 *libgogo.Item, item2 *libgogo.Item, op uint64) {
    if Compile != 0 {
        if (op == TOKEN_ARITH_DIV) || op == (TOKEN_ARITH_MUL) {
            if (item1.Itemtype != byte_t) && (item1.Itemtype != uint64_t) {
                SymbolTableError("Invalid left operand type for", "", "multiplication/division:", item1.Itemtype.Name);
            }
            if (item2.Itemtype != byte_t) && (item2.Itemtype != uint64_t) {
                SymbolTableError("Invalid right operand type for", "", "multiplication/division:", item2.Itemtype.Name);
            }
        }
        if op == TOKEN_ARITH_DIV { // Division
            if item2.Mode == libgogo.MODE_CONST {
                if item2.A == 0 {
                    GenErrorWeak("Division by zero.");
                }
            }
            if item2.A != 0 { //Avoid division by zero for constvalue parameter
                DivMulInstruction("DIV", item1, item2, item1.A / item2.A, 0);
            } else {
                DivMulInstruction("DIV", item1, item2, 0, 0);
            }
        }
        if op == TOKEN_ARITH_MUL { // Multiplication
            DivMulInstruction("MUL", item1, item2, item1.A * item2.A, 0);
        }
    }
}

func GenerateRelative(item *libgogo.Item, op uint64, ed *ExpressionDescriptor) {
    var labelString string;
    var jmp string;

    if Compile != 0 {
        if item.Mode != libgogo.MODE_COND {
            GenErrorWeak("Can use relative operators only with conditionals.");
        }
        if op == TOKEN_REL_AND {
            labelString = GenerateSubLabel(ed,0,"END");
            jmp = GetJump(item.C, 1);
            PrintJump(jmp, labelString);
            if ed.T != 0 {
                if ed.TDepth >= ed.ExpressionDepth {
                    labelString = GetSubLabel(ed,1,"END");
                    PrintLabel(labelString);
                    ed.T = 0;
                }
            }
        } else {
            if op == TOKEN_REL_OR {
                labelString = GenerateSubLabel(ed,1,"END");
                jmp = GetJump(item.C, 0);
                PrintJump(jmp, labelString);
                if ed.F != 0 {
                    if ed.FDepth >= ed.ExpressionDepth {
                        labelString = GetSubLabel(ed,0,"END");
                        PrintLabel(labelString);
                        ed.F = 0;
                    }
                }
            } else {
                GenErrorWeak("Relative AND or OR expected.");
            }
        }
    }
}

//
// Called by parser (ParseExpression)
// Note: This function can only handle operands with a maximum of 8 bytes in size
//
func GenerateComparison(item1 *libgogo.Item, item2 *libgogo.Item, op uint64) {
    if Compile != 0 {
        // Type/Pointer checking
        if (item1.Itemtype != item2.Itemtype) && (item1.Itemtype != string_t) && (item2.Itemtype != string_t) {
            GenErrorWeak("Can only compare variables of same type.");
        }
        if (item1.Itemtype == string_t) || (item2.Itemtype == string_t) {
            GenErrorWeak("Cannot compare string types.");
        }
        if item1.PtrType == 1 {
            if item2.PtrType == 1 {
                if (op != TOKEN_EQUALS) && (op != TOKEN_NOTEQUAL) {
                    GenErrorWeak("Can only compare '==' or '!=' on pointers'");
                }
                if (item1.Mode == libgogo.MODE_CONST) || (item2.Mode == libgogo.MODE_CONST) {
                    GenErrorWeak("Const pointers not allowed. This should not happen.");
                }
            }
        }
        if (item2.PtrType == 1) && (item1.PtrType != 1) {
            GenErrorWeak("Non-pointer to pointer comparison.");
        }
        if (item1.PtrType ==1) && (item2.PtrType != 1) {
            GenErrorWeak("Pointer to non-pointer comparison.");
        }

        // Generate CMP statements depending on items
        if item1.Mode == libgogo.MODE_CONST {
            if item2.Mode == libgogo.MODE_CONST { // Values here, since Ptrs are not allowed
                // Move constvalue to register and compare it against 0
                item1.Itemtype = bool_t;
                item1.A = GetConditionalBool(op, item1.A, item2.A);
                MakeRegistered(item1, 0);
                // CMP is handled by other if branch (free optimization, yey)
                item2.A = 0;
                op = TOKEN_NOTEQUAL; // Force != for comparison against 0
            } else {
                if item1.PtrType == 0 {
                    MakeRegistered(item1, 0);
                } else {
                    MakeRegistered(item1, 1);
                }
                if item2.Mode == libgogo.MODE_REG {
                    PrintInstruction_Reg_Reg("CMP", 8, "R", item1.R, 0, 0, 0, "", "R", item2.R, 0, 0, 0, "");
                }
                if item2.Mode == libgogo.MODE_VAR {
                    if item2.PtrType == 1 {
                        MakeRegistered(item2, 1);
                        PrintInstruction_Reg_Reg("CMP", 8, "R", item1.R, 0, 0, 0, "", "R", item2.R, 0, 0, 0, "");
                    } else {
                        PrintInstruction_Reg_Var("CMP", "R", item1.R, "", 0, item2);
                    }
                }
            }
        }
        if item1.Mode == libgogo.MODE_REG {
            DereferRegisterIfNecessary(item1);
            if item2.Mode == libgogo.MODE_CONST {
                PrintInstruction_Reg_Imm("CMP", 8, "R", item1.R, 0, 0, 0, "", item2.A);
            }
            if item2.Mode == libgogo.MODE_REG {
                PrintInstruction_Reg_Reg("CMP", 8, "R", item1.R, 0, 0, 0, "", "R", item2.R, 0, 0, 0, "");
            }
            if item2.Mode == libgogo.MODE_VAR {
                PrintInstruction_Reg_Var("CMP", "R", item1.R, "", 0, item2);
            }
        }
        if item1.Mode == libgogo.MODE_VAR {
            if item2.Mode == libgogo.MODE_CONST {
                PrintInstruction_Var_Imm("CMP", item1, item2.A);
            }
            if item2.Mode == libgogo.MODE_REG {
                DereferRegisterIfNecessary(item1);
                if item1.PtrType == 1 {
                    MakeRegistered(item1, 1);
                    PrintInstruction_Reg_Reg("CMP", 8, "R", item1.R, 0, 0, 0, "", "R", item2.R, 0, 0, 0, "");
                } else {
                    PrintInstruction_Var_Reg("CMP", item1, "R", item2.R, "", 0);
                }
            }
            if item2.Mode == libgogo.MODE_VAR {
                if item2.PtrType == 0 {
                    MakeRegistered(item2, 0);
                } else {
                    MakeRegistered(item2, 1);
                }
                if item1.PtrType == 1 {
                    MakeRegistered(item1, 1);
                    PrintInstruction_Reg_Reg("CMP", 8, "R", item1.R, 0, 0, 0, "", "R", item2.R, 0, 0, 0, "");
                } else {
                    PrintInstruction_Var_Reg("CMP", item1, "R", item2.R, "", 0);
                }
            }
        }

        // Prepare item
        item1.Itemtype = bool_t;
        item1.Mode = libgogo.MODE_COND;
        if op == TOKEN_EQUALS {
            item1.C = libgogo.REL_EQ;
        }
        if op == TOKEN_NOTEQUAL {
            item1.C = libgogo.REL_NEQ;
        }
        if op == TOKEN_REL_LT {
            item1.C = libgogo.REL_LT;
        }
        if op == TOKEN_REL_LTOE {
            item1.C = libgogo.REL_LTE;
        }
        if op == TOKEN_REL_GT {
            item1.C = libgogo.REL_GT;
        }
        if op == TOKEN_REL_GTOE {
            item1.C = libgogo.REL_GTE;
        }

        FreeRegisterIfRequired(item2);
    }
}

func GetConditionalBool(op uint64, val1 uint64, val2 uint64) uint64 {
    var ret uint64;
    if op == TOKEN_EQUALS {
        if val1 == val2 {
            ret = 1;
        } else {
            ret = 0;
        }
    }
    if op == TOKEN_NOTEQUAL {
        if val1 == val2 {
            ret = 0;
        } else {
            ret = 1;
        }
    }
    if op == TOKEN_REL_GTOE {
        if val1 >= val2 {
            ret = 1;
        } else {
            ret = 0;
        }
    }
    if op == TOKEN_REL_LTOE {
        if val1 <= val2 {
            ret = 1;
        } else {
            ret = 0;
        }
    }
    if op == TOKEN_REL_GT {
        if val1 > val2 {
            ret = 1;
        } else {
            ret = 0;
        }
    }
    if op == TOKEN_REL_LT {
        if val1 < val2 {
            ret = 1;
        } else {
            ret = 0;
        }
    }
    return ret;
}
<|endoftext|>