<commit_before>package tunnel

import (
	"crypto/sha256"
	"errors"
	"fmt"
	"sync"

	"github.com/google/uuid"
	"github.com/micro/go-micro/transport"
	"github.com/micro/go-micro/util/log"
)

// tun represents a network tunnel
type tun struct {
	options Options

	sync.RWMutex

	// to indicate if we're connected or not
	connected bool

	// the send channel for all messages
	send chan *message

	// close channel
	closed chan bool

	// a map of sockets based on Micro-Tunnel-Id
	sockets map[string]*socket

	// outbound links
	links map[string]*link

	// listener
	listener transport.Listener
}

type link struct {
	transport.Socket
	id string
}

// create new tunnel on top of a link
func newTunnel(opts ...Option) *tun {
	options := DefaultOptions()
	for _, o := range opts {
		o(&options)
	}

	return &tun{
		options: options,
		send:    make(chan *message, 128),
		closed:  make(chan bool),
		sockets: make(map[string]*socket),
		links:   make(map[string]*link),
	}
}

// getSocket returns a socket from the internal socket map.
// It does this based on the Micro-Tunnel-Id and Micro-Tunnel-Session
func (t *tun) getSocket(id, session string) (*socket, bool) {
	// get the socket
	t.RLock()
	s, ok := t.sockets[id+session]
	t.RUnlock()
	return s, ok
}

// newSocket creates a new socket and saves it
func (t *tun) newSocket(id, session string) (*socket, bool) {
	// hash the id
	h := sha256.New()
	h.Write([]byte(id))
	id = fmt.Sprintf("%x", h.Sum(nil))

	// new socket
	s := &socket{
		id:      id,
		session: session,
		closed:  make(chan bool),
		recv:    make(chan *message, 128),
		send:    t.send,
		wait:    make(chan bool),
	}

	// save socket
	t.Lock()
	_, ok := t.sockets[id+session]
	if ok {
		// socket already exists
		t.Unlock()
		return nil, false
	}
	t.sockets[id+session] = s
	t.Unlock()

	// return socket
	return s, true
}

// TODO: use tunnel id as part of the session
func (t *tun) newSession() string {
	return uuid.New().String()
}

// process outgoing messages sent by all local sockets
func (t *tun) process() {
	// manage the send buffer
	// all pseudo sockets throw everything down this
	for {
		select {
		case msg := <-t.send:
			newMsg := &transport.Message{
				Header: make(map[string]string),
				Body:   msg.data.Body,
			}

			for k, v := range msg.data.Header {
				newMsg.Header[k] = v
			}

			// set the tunnel id on the outgoing message
			newMsg.Header["Micro-Tunnel-Id"] = msg.id

			// set the session id
			newMsg.Header["Micro-Tunnel-Session"] = msg.session

			// send the message via the interface
			t.RLock()
			log.Debugf("Sending some shit %d links", len(t.links))
			for _, link := range t.links {
				log.Debugf("Sending %+v to %s", newMsg, link.Remote())
				link.Send(newMsg)
			}
			t.RUnlock()
		case <-t.closed:
			return
		}
	}
}

// process incoming messages
func (t *tun) listen(link transport.Socket, listener bool) {
	for {
		// process anything via the net interface
		msg := new(transport.Message)
		err := link.Recv(msg)
		if err != nil {
			return
		}

		switch msg.Header["Micro-Tunnel"] {
		case "connect", "close":
			continue
		}

		// the tunnel id
		id := msg.Header["Micro-Tunnel-Id"]

		// the session id
		session := msg.Header["Micro-Tunnel-Session"]

		// if the session id is blank there's nothing we can do
		// TODO: check this is the case, is there any reason
		// why we'd have a blank session? Is the tunnel
		// used for some other purpose?
		if len(id) == 0 || len(session) == 0 {
			continue
		}

		var s *socket
		var exists bool

		log.Debugf("Received %+v from %s", msg, link.Remote())
		// get the socket based on the tunnel id and session
		// this could be something we dialed in which case
		// we have a session for it otherwise its a listener
		s, exists = t.getSocket(id, session)
		if !exists {
			// try get it based on just the tunnel id
			// the assumption here is that a listener
			// has no session but its set a listener session
			s, exists = t.getSocket(id, "listener")
		}

		// no socket in existence
		if !exists {
			log.Debugf("Skipping")
			// drop it, we don't care about
			// messages we don't know about
			continue
		}
		log.Debugf("Using socket %s %s", s.id, s.session)

		// is the socket closed?
		select {
		case <-s.closed:
			// closed
			delete(t.sockets, id)
			continue
		default:
			// process
		}

		// is the socket new?
		select {
		// if its new the socket is actually blocked waiting
		// for a connection. so we check if its waiting.
		case <-s.wait:
		// if its waiting e.g its new then we close it
		default:
			// set remote address of the socket
			s.remote = msg.Header["Remote"]
			close(s.wait)
		}

		// construct a new transport message
		tmsg := &transport.Message{
			Header: msg.Header,
			Body:   msg.Body,
		}

		// construct the internal message
		imsg := &message{
			id:      id,
			session: session,
			data:    tmsg,
		}

		// append to recv backlog
		// we don't block if we can't pass it on
		select {
		case s.recv <- imsg:
		default:
		}
	}
}

func (t *tun) connect() error {
	l, err := t.options.Transport.Listen(t.options.Address)
	if err != nil {
		return err
	}

	// save the listener
	t.listener = l

	go func() {
		// accept inbound connections
		err := l.Accept(func(sock transport.Socket) {
			log.Debugf("Tunnel accepted connection from %s", sock.Remote())
			// save the link
			id := uuid.New().String()
			t.Lock()
			t.links[id] = &link{
				Socket: sock,
				id:     id,
			}
			t.Unlock()

			// delete the link
			defer func() {
				t.Lock()
				delete(t.links, id)
				t.Unlock()
			}()

			// listen for inbound messages
			t.listen(sock, true)
		})

		t.Lock()
		defer t.Unlock()

		// still connected but the tunnel died
		if err != nil && t.connected {
			log.Logf("Tunnel listener died: %v", err)
		}
	}()

	for _, node := range t.options.Nodes {
		// skip zero length nodes
		if len(node) == 0 {
			continue
		}

		log.Debugf("Tunnel dialing %s", node)
		c, err := t.options.Transport.Dial(node)
		if err != nil {
			log.Debugf("Tunnel failed to connect to %s: %v", node, err)
			continue
		}
		log.Debugf("Tunnel connected to %s", node)

		if err := c.Send(&transport.Message{
			Header: map[string]string{
				"Micro-Tunnel": "connect",
			},
		}); err != nil {
			continue
		}

		// process incoming messages
		go t.listen(c, false)

		// save the link
		id := uuid.New().String()
		t.links[id] = &link{
			Socket: c,
			id:     id,
		}
	}

	// process outbound messages to be sent
	// process sends to all links
	go t.process()

	return nil
}

func (t *tun) close() error {
	// close all the links
	for id, link := range t.links {
		link.Send(&transport.Message{
			Header: map[string]string{
				"Micro-Tunnel": "close",
			},
		})
		link.Close()
		delete(t.links, id)
	}

	// close the listener
	return t.listener.Close()
}

// Close the tunnel
func (t *tun) Close() error {
	t.Lock()
	defer t.Unlock()

	if !t.connected {
		return nil
	}

	select {
	case <-t.closed:
		return nil
	default:
		// close all the sockets
		for _, s := range t.sockets {
			s.Close()
		}
		// close the connection
		close(t.closed)
		t.connected = false

		// send a close message
		// we don't close the link
		// just the tunnel
		return t.close()
	}

	return nil
}

// Connect the tunnel
func (t *tun) Connect() error {
	t.Lock()
	defer t.Unlock()

	// already connected
	if t.connected {
		return nil
	}

	// send the connect message
	if err := t.connect(); err != nil {
		return err
	}

	// set as connected
	t.connected = true
	// create new close channel
	t.closed = make(chan bool)

	return nil
}

func (t *tun) Init(opts ...Option) error {
	for _, o := range opts {
		o(&t.options)
	}
	return nil
}

// Dial an address
func (t *tun) Dial(addr string) (Conn, error) {
	log.Debugf("Tunnel dialing %s", addr)
	c, ok := t.newSocket(addr, t.newSession())
	if !ok {
		return nil, errors.New("error dialing " + addr)
	}

	// set remote
	c.remote = addr
	// set local
	c.local = "local"

	return c, nil
}

// Accept a connection on the address
func (t *tun) Listen(addr string) (Listener, error) {
	log.Debugf("Tunnel listening on %s", addr)
	// create a new socket by hashing the address
	c, ok := t.newSocket(addr, "listener")
	if !ok {
		return nil, errors.New("already listening on " + addr)
	}

	// set remote. it will be replaced by the first message received
	c.remote = "remote"
	// set local
	c.local = addr

	tl := &tunListener{
		addr: addr,
		// the accept channel
		accept: make(chan *socket, 128),
		// the channel to close
		closed: make(chan bool),
		// tunnel closed channel
		tunClosed: t.closed,
		// the connection
		conn: c,
		// the listener socket
		socket: c,
	}

	// this kicks off the internal message processor
	// for the listener so it can create pseudo sockets
	// per session if they do not exist or pass messages
	// to the existign sessions
	go tl.process()

	// return the listener
	return tl, nil
}
<commit_msg>Strip logging<commit_after>package tunnel

import (
	"crypto/sha256"
	"errors"
	"fmt"
	"sync"

	"github.com/google/uuid"
	"github.com/micro/go-micro/transport"
	"github.com/micro/go-micro/util/log"
)

// tun represents a network tunnel
type tun struct {
	options Options

	sync.RWMutex

	// to indicate if we're connected or not
	connected bool

	// the send channel for all messages
	send chan *message

	// close channel
	closed chan bool

	// a map of sockets based on Micro-Tunnel-Id
	sockets map[string]*socket

	// outbound links
	links map[string]*link

	// listener
	listener transport.Listener
}

type link struct {
	transport.Socket
	id string
}

// create new tunnel on top of a link
func newTunnel(opts ...Option) *tun {
	options := DefaultOptions()
	for _, o := range opts {
		o(&options)
	}

	return &tun{
		options: options,
		send:    make(chan *message, 128),
		closed:  make(chan bool),
		sockets: make(map[string]*socket),
		links:   make(map[string]*link),
	}
}

// getSocket returns a socket from the internal socket map.
// It does this based on the Micro-Tunnel-Id and Micro-Tunnel-Session
func (t *tun) getSocket(id, session string) (*socket, bool) {
	// get the socket
	t.RLock()
	s, ok := t.sockets[id+session]
	t.RUnlock()
	return s, ok
}

// newSocket creates a new socket and saves it
func (t *tun) newSocket(id, session string) (*socket, bool) {
	// hash the id
	h := sha256.New()
	h.Write([]byte(id))
	id = fmt.Sprintf("%x", h.Sum(nil))

	// new socket
	s := &socket{
		id:      id,
		session: session,
		closed:  make(chan bool),
		recv:    make(chan *message, 128),
		send:    t.send,
		wait:    make(chan bool),
	}

	// save socket
	t.Lock()
	_, ok := t.sockets[id+session]
	if ok {
		// socket already exists
		t.Unlock()
		return nil, false
	}
	t.sockets[id+session] = s
	t.Unlock()

	// return socket
	return s, true
}

// TODO: use tunnel id as part of the session
func (t *tun) newSession() string {
	return uuid.New().String()
}

// process outgoing messages sent by all local sockets
func (t *tun) process() {
	// manage the send buffer
	// all pseudo sockets throw everything down this
	for {
		select {
		case msg := <-t.send:
			newMsg := &transport.Message{
				Header: make(map[string]string),
				Body:   msg.data.Body,
			}

			for k, v := range msg.data.Header {
				newMsg.Header[k] = v
			}

			// set the tunnel id on the outgoing message
			newMsg.Header["Micro-Tunnel-Id"] = msg.id

			// set the session id
			newMsg.Header["Micro-Tunnel-Session"] = msg.session

			// send the message via the interface
			t.RLock()
			for _, link := range t.links {
				log.Debugf("Sending %+v to %s", newMsg, link.Remote())
				link.Send(newMsg)
			}
			t.RUnlock()
		case <-t.closed:
			return
		}
	}
}

// process incoming messages
func (t *tun) listen(link transport.Socket, listener bool) {
	for {
		// process anything via the net interface
		msg := new(transport.Message)
		err := link.Recv(msg)
		if err != nil {
			return
		}

		switch msg.Header["Micro-Tunnel"] {
		case "connect", "close":
			continue
		}

		// the tunnel id
		id := msg.Header["Micro-Tunnel-Id"]

		// the session id
		session := msg.Header["Micro-Tunnel-Session"]

		// if the session id is blank there's nothing we can do
		// TODO: check this is the case, is there any reason
		// why we'd have a blank session? Is the tunnel
		// used for some other purpose?
		if len(id) == 0 || len(session) == 0 {
			continue
		}

		var s *socket
		var exists bool

		log.Debugf("Received %+v from %s", msg, link.Remote())
		// get the socket based on the tunnel id and session
		// this could be something we dialed in which case
		// we have a session for it otherwise its a listener
		s, exists = t.getSocket(id, session)
		if !exists {
			// try get it based on just the tunnel id
			// the assumption here is that a listener
			// has no session but its set a listener session
			s, exists = t.getSocket(id, "listener")
		}

		// no socket in existence
		if !exists {
			log.Debugf("Skipping")
			// drop it, we don't care about
			// messages we don't know about
			continue
		}
		log.Debugf("Using socket %s %s", s.id, s.session)

		// is the socket closed?
		select {
		case <-s.closed:
			// closed
			delete(t.sockets, id)
			continue
		default:
			// process
		}

		// is the socket new?
		select {
		// if its new the socket is actually blocked waiting
		// for a connection. so we check if its waiting.
		case <-s.wait:
		// if its waiting e.g its new then we close it
		default:
			// set remote address of the socket
			s.remote = msg.Header["Remote"]
			close(s.wait)
		}

		// construct a new transport message
		tmsg := &transport.Message{
			Header: msg.Header,
			Body:   msg.Body,
		}

		// construct the internal message
		imsg := &message{
			id:      id,
			session: session,
			data:    tmsg,
		}

		// append to recv backlog
		// we don't block if we can't pass it on
		select {
		case s.recv <- imsg:
		default:
		}
	}
}

func (t *tun) connect() error {
	l, err := t.options.Transport.Listen(t.options.Address)
	if err != nil {
		return err
	}

	// save the listener
	t.listener = l

	go func() {
		// accept inbound connections
		err := l.Accept(func(sock transport.Socket) {
			log.Debugf("Tunnel accepted connection from %s", sock.Remote())
			// save the link
			id := uuid.New().String()
			t.Lock()
			t.links[id] = &link{
				Socket: sock,
				id:     id,
			}
			t.Unlock()

			// delete the link
			defer func() {
				t.Lock()
				delete(t.links, id)
				t.Unlock()
			}()

			// listen for inbound messages
			t.listen(sock, true)
		})

		t.Lock()
		defer t.Unlock()

		// still connected but the tunnel died
		if err != nil && t.connected {
			log.Logf("Tunnel listener died: %v", err)
		}
	}()

	for _, node := range t.options.Nodes {
		// skip zero length nodes
		if len(node) == 0 {
			continue
		}

		log.Debugf("Tunnel dialing %s", node)
		c, err := t.options.Transport.Dial(node)
		if err != nil {
			log.Debugf("Tunnel failed to connect to %s: %v", node, err)
			continue
		}
		log.Debugf("Tunnel connected to %s", node)

		if err := c.Send(&transport.Message{
			Header: map[string]string{
				"Micro-Tunnel": "connect",
			},
		}); err != nil {
			continue
		}

		// process incoming messages
		go t.listen(c, false)

		// save the link
		id := uuid.New().String()
		t.links[id] = &link{
			Socket: c,
			id:     id,
		}
	}

	// process outbound messages to be sent
	// process sends to all links
	go t.process()

	return nil
}

func (t *tun) close() error {
	// close all the links
	for id, link := range t.links {
		link.Send(&transport.Message{
			Header: map[string]string{
				"Micro-Tunnel": "close",
			},
		})
		link.Close()
		delete(t.links, id)
	}

	// close the listener
	return t.listener.Close()
}

// Close the tunnel
func (t *tun) Close() error {
	t.Lock()
	defer t.Unlock()

	if !t.connected {
		return nil
	}

	select {
	case <-t.closed:
		return nil
	default:
		// close all the sockets
		for _, s := range t.sockets {
			s.Close()
		}
		// close the connection
		close(t.closed)
		t.connected = false

		// send a close message
		// we don't close the link
		// just the tunnel
		return t.close()
	}

	return nil
}

// Connect the tunnel
func (t *tun) Connect() error {
	t.Lock()
	defer t.Unlock()

	// already connected
	if t.connected {
		return nil
	}

	// send the connect message
	if err := t.connect(); err != nil {
		return err
	}

	// set as connected
	t.connected = true
	// create new close channel
	t.closed = make(chan bool)

	return nil
}

func (t *tun) Init(opts ...Option) error {
	for _, o := range opts {
		o(&t.options)
	}
	return nil
}

// Dial an address
func (t *tun) Dial(addr string) (Conn, error) {
	log.Debugf("Tunnel dialing %s", addr)
	c, ok := t.newSocket(addr, t.newSession())
	if !ok {
		return nil, errors.New("error dialing " + addr)
	}

	// set remote
	c.remote = addr
	// set local
	c.local = "local"

	return c, nil
}

// Accept a connection on the address
func (t *tun) Listen(addr string) (Listener, error) {
	log.Debugf("Tunnel listening on %s", addr)
	// create a new socket by hashing the address
	c, ok := t.newSocket(addr, "listener")
	if !ok {
		return nil, errors.New("already listening on " + addr)
	}

	// set remote. it will be replaced by the first message received
	c.remote = "remote"
	// set local
	c.local = addr

	tl := &tunListener{
		addr: addr,
		// the accept channel
		accept: make(chan *socket, 128),
		// the channel to close
		closed: make(chan bool),
		// tunnel closed channel
		tunClosed: t.closed,
		// the connection
		conn: c,
		// the listener socket
		socket: c,
	}

	// this kicks off the internal message processor
	// for the listener so it can create pseudo sockets
	// per session if they do not exist or pass messages
	// to the existign sessions
	go tl.process()

	// return the listener
	return tl, nil
}
<|endoftext|>


<commit_before>package typeinfer

// Functions for handling function call-like instructions
// i.e. builtin, call, closure, defer, go.

import (
	"fmt"
	"go/types"

	"github.com/nickng/dingo-hunter/typeinfer/migo"
	"golang.org/x/tools/go/ssa"
)

// Call performs call on a given unprepared call context.
func (caller *Function) Call(call *ssa.Call, infer *TypeInfer, b *Block, l *Loop) {
	if call == nil {
		infer.Logger.Fatal("Call is nil")
		return
	}
	common := call.Common()
	switch fn := common.Value.(type) {
	case *ssa.Builtin:
		switch fn.Name() {
		case "close":
			ch, ok := caller.locals[common.Args[0]]
			if !ok {
				infer.Logger.Fatalf("call close: %s: %s", common.Args[0].Name(), ErrUnknownValue)
				return
			}
			caller.FuncDef.AddStmts(&migo.CloseStatement{ch.String()})
			infer.Logger.Print(caller.Sprintf("close %s", common.Args[0]))
			return
		case "len":
			if l.State == Enter {
				len, err := caller.callLen(common, infer)
				if err == ErrRuntimeLen {
					l.Bound = Dynamic
					return
				}
				l.Bound, l.End = Static, len
				return
			}
			caller.locals[call] = &Instance{call, caller.InstanceID(), l.Index}
			infer.Logger.Printf(caller.Sprintf("  builtin.%s", common.String()))
		default:
			infer.Logger.Printf(caller.Sprintf("  builtin.%s", common.String()))
		}
	case *ssa.MakeClosure:
		infer.Logger.Printf(caller.Sprintf(SkipSymbol+" make closure %s", fn.String()))
		caller.callClosure(common, fn, infer, b, l)
	case *ssa.Function:
		if common.StaticCallee() == nil {
			infer.Logger.Fatal("Call with nil CallCommon")
		}
		callee := caller.callFn(common, infer, b, l)
		if callee != nil {
			caller.storeRetvals(infer, call.Value(), callee)
		}
	default:
		if !common.IsInvoke() {
			infer.Logger.Print("Unknown call type", common.String(), common.Description())
			return
		}
		callee := caller.invoke(common, infer, b, l)
		if callee != nil {
			caller.storeRetvals(infer, call.Value(), callee)
		}
	}
}

// Go handles Go statements.
func (caller *Function) Go(instr *ssa.Go, infer *TypeInfer) {
	common := instr.Common()
	callee := caller.prepareCallFn(common, common.StaticCallee(), nil)
	spawnStmt := &migo.SpawnStatement{Name: callee.Fn.String(), Params: []string{}}
	for _, c := range common.Args {
		if _, ok := c.Type().(*types.Chan); ok {
			ch := getChan(c, infer)
			spawnStmt.AddParams(ch.Name())
		}
	}
	caller.FuncDef.AddStmts(spawnStmt)
	// Don't actually call/visit the function but enqueue it.
	infer.GQueue = append(infer.GQueue, callee)
}

// callLen computes the length of a given data structure (if statically known).
func (caller *Function) callLen(common *ssa.CallCommon, infer *TypeInfer) (int64, error) {
	arg0 := common.Args[0]
	switch t := arg0.Type().(type) {
	case *types.Array:
		infer.Logger.Printf(caller.Sprintf("  len(%s %s) = %d", arg0.Name(), arg0.Type(), t.Len()))
		return t.Len(), nil
	default:
		// String = runtime length of string
		// Map    = runtime size of map
		// Slice  = runtime size of slice
		// Chan   = elements in queue
		infer.Logger.Printf(caller.Sprintf("  len(%s %s) = ?", arg0.Name(), arg0.Type()))
	}
	return 0, ErrRuntimeLen
}

// storeRetvals takes retval (SSA value from caller storing return value(s)) and
// stores the return value of function (callee).
func (caller *Function) storeRetvals(infer *TypeInfer, retval ssa.Value, callee *Function) {
	if !callee.HasBody() {
		switch callee.Fn.Signature.Results().Len() {
		case 0:
			// Nothing.
		case 1:
			// Creating external instance because return value may be used.
			caller.locals[retval] = &ExtInstance{caller.Fn, caller.InstanceID(), int64(0)}
			infer.Logger.Print(caller.Sprintf("retvals ext"))
		default:
			caller.locals[retval] = &ExtInstance{caller.Fn, caller.InstanceID(), int64(0)}
			caller.tuples[caller.locals[retval]] = make(Tuples, callee.Fn.Signature.Results().Len())
			infer.Logger.Print(caller.Sprintf("retvals ext len=%d", callee.Fn.Signature.Results().Len()))
		}
		return
	}
	switch len(callee.retvals) {
	case 0:
		// Nothing.
	case 1:
		caller.locals[retval] = callee.retvals[0]
		if a, ok := callee.arrays[caller.locals[retval]]; ok {
			caller.arrays[caller.locals[retval]] = a
		}
		if s, ok := callee.structs[caller.locals[retval]]; ok {
			caller.structs[caller.locals[retval]] = s
		}
		if instance, ok := caller.locals[retval].(*Instance); ok {
			infer.Logger.Print(caller.Sprintf("retvals: type=%s", instance.Type().String()))
		} else {
			infer.Logger.Print(caller.Sprintf("retvals: (external)"))
		}
	default:
		caller.locals[retval] = &Instance{retval, caller.InstanceID(), int64(0)}
		caller.tuples[caller.locals[retval]] = make(Tuples, len(callee.retvals))
		for i := range callee.retvals {
			caller.tuples[caller.locals[retval]][i] = callee.retvals[i]
		}
		infer.Logger.Print(caller.Sprintf("retvals len=%d", len(callee.retvals)))
	}
}

// IsRecursiveCall checks if current function context is a recursive call and marks
// the context recursive (with pointer to the original context).
func (caller *Function) IsRecursiveCall() bool {
	for parentCtx := caller.Caller; parentCtx.Caller != nil; parentCtx = parentCtx.Caller {
		if caller.Fn == parentCtx.Fn { // is identical function?
			return true
		}
	}
	return false
}

func (caller *Function) invoke(common *ssa.CallCommon, infer *TypeInfer, b *Block, l *Loop) *Function {
	iface, ok := common.Value.Type().Underlying().(*types.Interface)
	if !ok {
		infer.Logger.Fatalf("invoke: %s is not an interface", common.String())
		return nil
	}
	ifaceInst, ok := caller.locals[common.Value] // SSA value initialised
	if !ok {
		infer.Logger.Fatalf("invoke: %s: %s", common.Value.Name(), ErrUnknownValue)
		return nil
	}
	inst, ok := ifaceInst.(*Instance) // Interface is concrete
	if !ok {
		infer.Logger.Fatalf("invoke: %s is not concrete", ifaceInst)
		return nil
	}
	meth, _ := types.MissingMethod(inst.Var().Type(), iface, true) // static
	if meth != nil {
		meth, _ = types.MissingMethod(inst.Var().Type(), iface, false) // non-static
		if meth != nil {
			infer.Logger.Printf("invoke: missing method %s: %s", meth.String(), ErrIfaceIncomplete)
			return nil
		}
	}
	fn := findMethod(common.Value.Parent().Prog, common.Method, inst.Var().Type(), infer)
	if fn == nil {
		infer.Logger.Fatalf("invoke: cannot locate concrete method: %s", meth.String())
		return nil
	}
	return caller.call(common, fn, common.Value, infer, b, l)
}

func (caller *Function) callFn(common *ssa.CallCommon, infer *TypeInfer, b *Block, l *Loop) *Function {
	return caller.call(common, common.StaticCallee(), nil, infer, b, l)
}

func (caller *Function) call(common *ssa.CallCommon, fn *ssa.Function, rcvr ssa.Value, infer *TypeInfer, b *Block, l *Loop) *Function {
	callee := caller.prepareCallFn(common, fn, rcvr)
	if callee.IsRecursiveCall() {
		return callee
	}
	visitFunc(callee.Fn, infer, callee)
	if callee.HasBody() {
		callStmt := &migo.CallStatement{Name: fmt.Sprintf("%s_%d", callee.Fn.String(), callee.InstanceID()), Params: []*migo.Parameter{}}
		for i, c := range common.Args {
			if _, ok := c.Type().(*types.Chan); ok {
				callStmt.AddParams(&migo.Parameter{Caller: c, Callee: callee.Fn.Params[i]})
			}
		}
		caller.FuncDef.AddStmts(callStmt)
	}
	return callee
}

func (caller *Function) callClosure(common *ssa.CallCommon, closure *ssa.MakeClosure, infer *TypeInfer, b *Block, l *Loop) {
	// closure.Bindings
	// closure.Fn
	infer.Logger.Fatalf("call closure: %s", ErrUnimplemented)
}

func findMethod(prog *ssa.Program, meth *types.Func, typ types.Type, infer *TypeInfer) *ssa.Function {
	if meth != nil {
		return prog.LookupMethod(typ, meth.Pkg(), meth.Name())
	}
	infer.Logger.Fatal(ErrMethodNotFound)
	return nil
}
<commit_msg>Prevent panic by possible nil meth<commit_after>package typeinfer

// Functions for handling function call-like instructions
// i.e. builtin, call, closure, defer, go.

import (
	"fmt"
	"go/types"

	"github.com/nickng/dingo-hunter/typeinfer/migo"
	"golang.org/x/tools/go/ssa"
)

// Call performs call on a given unprepared call context.
func (caller *Function) Call(call *ssa.Call, infer *TypeInfer, b *Block, l *Loop) {
	if call == nil {
		infer.Logger.Fatal("Call is nil")
		return
	}
	common := call.Common()
	switch fn := common.Value.(type) {
	case *ssa.Builtin:
		switch fn.Name() {
		case "close":
			ch, ok := caller.locals[common.Args[0]]
			if !ok {
				infer.Logger.Fatalf("call close: %s: %s", common.Args[0].Name(), ErrUnknownValue)
				return
			}
			caller.FuncDef.AddStmts(&migo.CloseStatement{ch.String()})
			infer.Logger.Print(caller.Sprintf("close %s", common.Args[0]))
			return
		case "len":
			if l.State == Enter {
				len, err := caller.callLen(common, infer)
				if err == ErrRuntimeLen {
					l.Bound = Dynamic
					return
				}
				l.Bound, l.End = Static, len
				return
			}
			caller.locals[call] = &Instance{call, caller.InstanceID(), l.Index}
			infer.Logger.Printf(caller.Sprintf("  builtin.%s", common.String()))
		default:
			infer.Logger.Printf(caller.Sprintf("  builtin.%s", common.String()))
		}
	case *ssa.MakeClosure:
		infer.Logger.Printf(caller.Sprintf(SkipSymbol+" make closure %s", fn.String()))
		caller.callClosure(common, fn, infer, b, l)
	case *ssa.Function:
		if common.StaticCallee() == nil {
			infer.Logger.Fatal("Call with nil CallCommon")
		}
		callee := caller.callFn(common, infer, b, l)
		if callee != nil {
			caller.storeRetvals(infer, call.Value(), callee)
		}
	default:
		if !common.IsInvoke() {
			infer.Logger.Print("Unknown call type", common.String(), common.Description())
			return
		}
		callee := caller.invoke(common, infer, b, l)
		if callee != nil {
			caller.storeRetvals(infer, call.Value(), callee)
		}
	}
}

// Go handles Go statements.
func (caller *Function) Go(instr *ssa.Go, infer *TypeInfer) {
	common := instr.Common()
	callee := caller.prepareCallFn(common, common.StaticCallee(), nil)
	spawnStmt := &migo.SpawnStatement{Name: callee.Fn.String(), Params: []string{}}
	for _, c := range common.Args {
		if _, ok := c.Type().(*types.Chan); ok {
			ch := getChan(c, infer)
			spawnStmt.AddParams(ch.Name())
		}
	}
	caller.FuncDef.AddStmts(spawnStmt)
	// Don't actually call/visit the function but enqueue it.
	infer.GQueue = append(infer.GQueue, callee)
}

// callLen computes the length of a given data structure (if statically known).
func (caller *Function) callLen(common *ssa.CallCommon, infer *TypeInfer) (int64, error) {
	arg0 := common.Args[0]
	switch t := arg0.Type().(type) {
	case *types.Array:
		infer.Logger.Printf(caller.Sprintf("  len(%s %s) = %d", arg0.Name(), arg0.Type(), t.Len()))
		return t.Len(), nil
	default:
		// String = runtime length of string
		// Map    = runtime size of map
		// Slice  = runtime size of slice
		// Chan   = elements in queue
		infer.Logger.Printf(caller.Sprintf("  len(%s %s) = ?", arg0.Name(), arg0.Type()))
	}
	return 0, ErrRuntimeLen
}

// storeRetvals takes retval (SSA value from caller storing return value(s)) and
// stores the return value of function (callee).
func (caller *Function) storeRetvals(infer *TypeInfer, retval ssa.Value, callee *Function) {
	if !callee.HasBody() {
		switch callee.Fn.Signature.Results().Len() {
		case 0:
			// Nothing.
		case 1:
			// Creating external instance because return value may be used.
			caller.locals[retval] = &ExtInstance{caller.Fn, caller.InstanceID(), int64(0)}
			infer.Logger.Print(caller.Sprintf("retvals ext"))
		default:
			caller.locals[retval] = &ExtInstance{caller.Fn, caller.InstanceID(), int64(0)}
			caller.tuples[caller.locals[retval]] = make(Tuples, callee.Fn.Signature.Results().Len())
			infer.Logger.Print(caller.Sprintf("retvals ext len=%d", callee.Fn.Signature.Results().Len()))
		}
		return
	}
	switch len(callee.retvals) {
	case 0:
		// Nothing.
	case 1:
		caller.locals[retval] = callee.retvals[0]
		if a, ok := callee.arrays[caller.locals[retval]]; ok {
			caller.arrays[caller.locals[retval]] = a
		}
		if s, ok := callee.structs[caller.locals[retval]]; ok {
			caller.structs[caller.locals[retval]] = s
		}
		if instance, ok := caller.locals[retval].(*Instance); ok {
			infer.Logger.Print(caller.Sprintf("retvals: type=%s", instance.Type().String()))
		} else {
			infer.Logger.Print(caller.Sprintf("retvals: (external)"))
		}
	default:
		caller.locals[retval] = &Instance{retval, caller.InstanceID(), int64(0)}
		caller.tuples[caller.locals[retval]] = make(Tuples, len(callee.retvals))
		for i := range callee.retvals {
			caller.tuples[caller.locals[retval]][i] = callee.retvals[i]
		}
		infer.Logger.Print(caller.Sprintf("retvals len=%d", len(callee.retvals)))
	}
}

// IsRecursiveCall checks if current function context is a recursive call and marks
// the context recursive (with pointer to the original context).
func (caller *Function) IsRecursiveCall() bool {
	for parentCtx := caller.Caller; parentCtx.Caller != nil; parentCtx = parentCtx.Caller {
		if caller.Fn == parentCtx.Fn { // is identical function?
			return true
		}
	}
	return false
}

func (caller *Function) invoke(common *ssa.CallCommon, infer *TypeInfer, b *Block, l *Loop) *Function {
	iface, ok := common.Value.Type().Underlying().(*types.Interface)
	if !ok {
		infer.Logger.Fatalf("invoke: %s is not an interface", common.String())
		return nil
	}
	ifaceInst, ok := caller.locals[common.Value] // SSA value initialised
	if !ok {
		infer.Logger.Fatalf("invoke: %s: %s", common.Value.Name(), ErrUnknownValue)
		return nil
	}
	inst, ok := ifaceInst.(*Instance) // Interface is concrete
	if !ok {
		infer.Logger.Fatalf("invoke: %s is not concrete", ifaceInst)
		return nil
	}
	meth, _ := types.MissingMethod(inst.Var().Type(), iface, true) // static
	if meth != nil {
		meth, _ = types.MissingMethod(inst.Var().Type(), iface, false) // non-static
		if meth != nil {
			infer.Logger.Printf("invoke: missing method %s: %s", meth.String(), ErrIfaceIncomplete)
			return nil
		}
	}
	fn := findMethod(common.Value.Parent().Prog, common.Method, inst.Var().Type(), infer)
	if fn == nil {
		if meth == nil {
			infer.Logger.Printf("invoke: cannot locate concrete method")
		} else {
			infer.Logger.Printf("invoke: cannot locate concrete method: %s", meth.String())
		}
		return nil
	}
	return caller.call(common, fn, common.Value, infer, b, l)
}

func (caller *Function) callFn(common *ssa.CallCommon, infer *TypeInfer, b *Block, l *Loop) *Function {
	return caller.call(common, common.StaticCallee(), nil, infer, b, l)
}

func (caller *Function) call(common *ssa.CallCommon, fn *ssa.Function, rcvr ssa.Value, infer *TypeInfer, b *Block, l *Loop) *Function {
	callee := caller.prepareCallFn(common, fn, rcvr)
	if callee.IsRecursiveCall() {
		return callee
	}
	visitFunc(callee.Fn, infer, callee)
	if callee.HasBody() {
		callStmt := &migo.CallStatement{Name: fmt.Sprintf("%s_%d", callee.Fn.String(), callee.InstanceID()), Params: []*migo.Parameter{}}
		for i, c := range common.Args {
			if _, ok := c.Type().(*types.Chan); ok {
				callStmt.AddParams(&migo.Parameter{Caller: c, Callee: callee.Fn.Params[i]})
			}
		}
		caller.FuncDef.AddStmts(callStmt)
	}
	return callee
}

func (caller *Function) callClosure(common *ssa.CallCommon, closure *ssa.MakeClosure, infer *TypeInfer, b *Block, l *Loop) {
	// closure.Bindings
	// closure.Fn
	infer.Logger.Fatalf("call closure: %s", ErrUnimplemented)
}

func findMethod(prog *ssa.Program, meth *types.Func, typ types.Type, infer *TypeInfer) *ssa.Function {
	if meth != nil {
		return prog.LookupMethod(typ, meth.Pkg(), meth.Name())
	}
	infer.Logger.Fatal(ErrMethodNotFound)
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
	"strings"
)

var verbose bool
var force bool
var copy bool

var ext string = ".was"

func init() {
	flag.BoolVar(&copy, "c", false, "copy instead of move")
	flag.BoolVar(&force, "f", false, "clobber any conflicting files")
	flag.BoolVar(&verbose, "v", false, "verbose output")
}

//swiped this from a gist:
//https://gist.github.com/albrow/5882501
func askForConfirmation() bool {
	consolereader := bufio.NewReader(os.Stdin)

	response, err := consolereader.ReadString('\n')
	if err != nil {
		fmt.Printf("%v\n", err)
		os.Exit(2)
	}
	okayResponses := []string{"y", "Y", "yes", "Yes", "YES"}
	nokayResponses := []string{"n", "N", "no", "No", "NO"}
	if containsString(okayResponses, response[:len(response)-1]) {
		return true
	} else if containsString(nokayResponses, response[:len(response)-1]) {
		return false
	} else {
		fmt.Println("Please type yes or no and then press enter:")
		return askForConfirmation()
	}
}

// posString returns the first index of element in slice.
// If slice does not contain element, returns -1.
func posString(slice []string, element string) int {
	for index, elem := range slice {
		if elem == element {
			return index
		}
	}
	return -1
}

// containsString returns true iff slice contains element
func containsString(slice []string, element string) bool {
	return !(posString(slice, element) == -1)
}

func main() {

	flag.Usage = func() {

		fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
		fmt.Fprintf(os.Stderr, `

        Description:

Stupid simple but useful tool to move a file or directory and move it back later.
Was moves a list of files to files with a .was extension, and/or moves them back if they already have a .was extension.

	Examples:

was thisFile -> thisFile.was
was thisFile.was -> thisFile
was thisFile thatFile.was -> thisFile.was thatFile
was -c someFile -> someFile someFile.was

was filename1 [filename2 filename3 ...]

WIP

Make it return non-zero if there were any errors
Let user choose the extension.
Read file list from STDIN

`)

		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\n")
	}

	flag.Parse()

	wasFiles := flag.Args()

	if len(wasFiles) < 1 {

		flag.Usage()
		os.Exit(2)

	}

	flag.Parse()

	if verbose {
		fmt.Println("hello world:%v:%s:", verbose, wasFiles)
	}

	for _, file := range wasFiles {

		if verbose {
			fmt.Fprintf(os.Stderr, "handling file:%s:len(file):%d:\n", file, len(file))
		}

		//chop off slash from directories
		if file[len(file)-1] == '/' {
			file = file[0 : len(file)-1]
		}

		if file == ext {
			fmt.Fprintf(os.Stderr, "ignoring .was:%v\n")
			continue
		}

		if _, err := os.Stat(file); err != nil {
			fmt.Fprintf(os.Stderr, "skipping:%v\n", err)
			continue
		}

		targetFile := file + ext
		if strings.HasSuffix(file, ext) {

			if verbose {
				fmt.Fprintf(os.Stderr, "doing unwas on:%s\n", targetFile)
			}

			targetFile = file[0 : len(file)-len(ext)]

		}

		if _, err := os.Stat(targetFile); err == nil {

			if verbose {
				fmt.Fprintf(os.Stderr, "target is blocked:%s\n", targetFile)
			}

			if !force {

				fmt.Printf("There's a file in the way:%s:\n", targetFile)
				fmt.Printf("Delete %s? Please type yes or no and then press enter:\n", targetFile)
				if askForConfirmation() {
					if err := os.RemoveAll(targetFile); err != nil {
						fmt.Fprintf(os.Stderr, "could not clear the way for new was file:skipping:%v\n", err)
						continue
					}
				} else {
					fmt.Fprintf(os.Stderr, "user chose to not delete target:skipping:%s\n", targetFile)
					continue
				}

			}

		}

		if verbose {
			fmt.Fprintf(os.Stderr, "target is clear:%s\n", file)
		}

		if copy {

			copyFileHandle, err := os.Open(file)
			if err != nil {

				fmt.Fprintf(os.Stderr, "skipping:%v\n", err)
				continue

			}
			defer copyFileHandle.Close()

			finfo, err := copyFileHandle.Stat()
			if err != nil {

				fmt.Fprintf(os.Stderr, "skipping:%v\n", err)
				continue

			}

			if fmode := finfo.Mode(); fmode.IsDir() {

				fmt.Fprintf(os.Stderr, "skipping:copy is not supported for directories\n")
				continue

			}

			targetFileHandle, err := os.Create(targetFile)
			if err != nil {

				fmt.Fprintf(os.Stderr, "skipping:%v\n", err)
				continue

			}
			defer targetFileHandle.Close()

			_, err = io.Copy(targetFileHandle, copyFileHandle)
			if err != nil {

				fmt.Fprintf(os.Stderr, "skipping:%v\n", err)
				continue

			}

		} else {

			if err := os.Rename(file, targetFile); err != nil {
				fmt.Fprintf(os.Stderr, "failed to was:%v\n", err)
				continue
			}

		}

		if verbose {
			fmt.Fprintf(os.Stderr, "was'd:%s\n", file)
		}

	}

}
<commit_msg>add option for configurable file extensions<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
	"strings"
)

var verbose bool
var force bool
var copy bool

var ext string = ".was"

func init() {
	flag.BoolVar(&copy, "c", false, "copy instead of move")
	flag.StringVar(&ext, "e", ext, "file extension")
	flag.BoolVar(&force, "f", false, "clobber any conflicting files")
	flag.BoolVar(&verbose, "v", false, "verbose output")
}

//swiped this from a gist:
//https://gist.github.com/albrow/5882501
func askForConfirmation() bool {
	consolereader := bufio.NewReader(os.Stdin)

	response, err := consolereader.ReadString('\n')
	if err != nil {
		fmt.Printf("%v\n", err)
		os.Exit(2)
	}
	okayResponses := []string{"y", "Y", "yes", "Yes", "YES"}
	nokayResponses := []string{"n", "N", "no", "No", "NO"}
	if containsString(okayResponses, response[:len(response)-1]) {
		return true
	} else if containsString(nokayResponses, response[:len(response)-1]) {
		return false
	} else {
		fmt.Println("Please type yes or no and then press enter:")
		return askForConfirmation()
	}
}

// posString returns the first index of element in slice.
// If slice does not contain element, returns -1.
func posString(slice []string, element string) int {
	for index, elem := range slice {
		if elem == element {
			return index
		}
	}
	return -1
}

// containsString returns true iff slice contains element
func containsString(slice []string, element string) bool {
	return !(posString(slice, element) == -1)
}

func main() {

	flag.Usage = func() {

		fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
		fmt.Fprintf(os.Stderr, `

        Description:

Stupid simple but useful tool to move a file or directory and move it back later.
Was moves a list of files to files with a .was extension, and/or moves them back if they already have a .was extension.

	Examples:

was thisFile -> thisFile.was
was thisFile.was -> thisFile
was thisFile thatFile.was -> thisFile.was thatFile
was -c someFile -> someFile someFile.was
was -e=saw someFile -> someFile.saw

was filename1 [filename2 filename3 ...]

WIP

Make it return non-zero if there were any errors
Let user choose the extension.
Read file list from STDIN

`)

		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\n")
	}

	flag.Parse()

	wasFiles := flag.Args()

	if len(wasFiles) < 1 {

		flag.Usage()
		os.Exit(2)

	}

	flag.Parse()

	if !strings.HasPrefix(ext, ".") {
		ext = "." + ext
	}

	if verbose {
		fmt.Println("hello world:%v:%s:", verbose, wasFiles)
	}

	for _, file := range wasFiles {

		if verbose {
			fmt.Fprintf(os.Stderr, "handling file:%s:len(file):%d:\n", file, len(file))
		}

		//chop off slash from directories
		if file[len(file)-1] == '/' {
			file = file[0 : len(file)-1]
		}

		if file == ext {
			fmt.Fprintf(os.Stderr, "ignoring %s:%v\n", ext)
			continue
		}

		if _, err := os.Stat(file); err != nil {
			fmt.Fprintf(os.Stderr, "skipping:%v\n", err)
			continue
		}

		targetFile := file + ext
		if strings.HasSuffix(file, ext) {

			if verbose {
				fmt.Fprintf(os.Stderr, "doing unwas on:%s\n", targetFile)
			}

			targetFile = file[0 : len(file)-len(ext)]

		}

		if _, err := os.Stat(targetFile); err == nil {

			if verbose {
				fmt.Fprintf(os.Stderr, "target is blocked:%s\n", targetFile)
			}

			if !force {

				fmt.Printf("There's a file in the way:%s:\n", targetFile)
				fmt.Printf("Delete %s? Please type yes or no and then press enter:\n", targetFile)
				if askForConfirmation() {
					if err := os.RemoveAll(targetFile); err != nil {
						fmt.Fprintf(os.Stderr, "could not clear the way for new was file:skipping:%v\n", err)
						continue
					}
				} else {
					fmt.Fprintf(os.Stderr, "user chose to not delete target:skipping:%s\n", targetFile)
					continue
				}

			}

		}

		if verbose {
			fmt.Fprintf(os.Stderr, "target is clear:%s\n", file)
		}

		if copy {

			copyFileHandle, err := os.Open(file)
			if err != nil {

				fmt.Fprintf(os.Stderr, "skipping:%v\n", err)
				continue

			}
			defer copyFileHandle.Close()

			finfo, err := copyFileHandle.Stat()
			if err != nil {

				fmt.Fprintf(os.Stderr, "skipping:%v\n", err)
				continue

			}

			if fmode := finfo.Mode(); fmode.IsDir() {

				fmt.Fprintf(os.Stderr, "skipping:copy is not supported for directories\n")
				continue

			}

			targetFileHandle, err := os.Create(targetFile)
			if err != nil {

				fmt.Fprintf(os.Stderr, "skipping:%v\n", err)
				continue

			}
			defer targetFileHandle.Close()

			_, err = io.Copy(targetFileHandle, copyFileHandle)
			if err != nil {

				fmt.Fprintf(os.Stderr, "skipping:%v\n", err)
				continue

			}

		} else {

			if err := os.Rename(file, targetFile); err != nil {
				fmt.Fprintf(os.Stderr, "failed to was:%v\n", err)
				continue
			}

		}

		if verbose {
			fmt.Fprintf(os.Stderr, "was'd:%s\n", file)
		}

	}

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"
)

func main() {
	Start(os.Getenv("PORT"))
}

func Start(port string) {
	http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
		switch req.Method {
		case "GET":
			handleGet(res, req)
			//		case "POST":
			//			handlePost(res, req)
		default:
			http.Error(res, "Only GET and POST supported", http.StatusMethodNotAllowed)
		}
	})

	if port == "" {
		port = "5000"
	}
	log.Println("listening on port:", port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}

type FileInfoResponse struct {
	Name    string    `json:"name"`
	Size    int64     `json:"size"`
	Type    string    `json:"type"`
	Perm    int       `json:"permission"`
	ModTime time.Time `json:"updated_at"`
}

func handleGet(res http.ResponseWriter, req *http.Request) {
	path, err := os.Open(req.URL.Path)
	if err != nil {
		http.Error(res, "Error reading path: "+err.Error(), http.StatusBadRequest)
		return
	}

	pathInfo, err := path.Stat()
	if err != nil {
		http.Error(res, "Error reading path info: "+err.Error(), http.StatusInternalServerError)
		return
	}

	if pathInfo.Mode().IsDir() {
		fileInfos, err := ioutil.ReadDir(req.URL.Path)
		if err != nil {
			http.Error(res, "Error reading dir: "+err.Error(), http.StatusInternalServerError)
			return
		}

		fileResponses := []FileInfoResponse{}
		for _, fi := range fileInfos {
			fileResponses = append(fileResponses, FileInfoResponse{
				Name:    fi.Name(),
				Size:    fi.Size(),
				Type:    string(fi.Mode().String()[0]),
				Perm:    int(fi.Mode().Perm()),
				ModTime: fi.ModTime(),
			})
		}

		res.Header().Set("Content-Type", "application/json") // TODO allow others
		fileResponsesJson, err := json.Marshal(fileResponses)
		if err != nil {
			http.Error(res, "Error converting JSON: "+err.Error(), http.StatusInternalServerError)
			return
		}
		res.Write(fileResponsesJson)
	} else if pathInfo.Mode().IsRegular() {
		io.Copy(res, path)
	} else {
		http.Error(res, "Invalid file type: "+string(pathInfo.Mode().String()[0]), http.StatusBadRequest)
	}
}

//func handlePost(res http.ResponseWriter, req *http.Request) {
//	scheme := "http"
//	if req.TLS != nil || req.Header.Get("X-Forwarded-Proto") == "https" {
//		scheme = "https"
//	}
//
//	data, err := ioutil.ReadAll(req.Body)
//	if err != nil {
//		log.Println("post.error.body:", err)
//		http.Error(res, "Error reading request body: "+err.Error(), http.StatusBadRequest)
//		return
//	}
//
//	if err := handleStatusParam(res, req); err != nil {
//		return
//	}
//
//	res.Header().Set("Content-Type", "text/uri-list; charset=utf-8")
//	contentType := req.Header.Get("Content-Type")
//	uri := shaas.CreateUri(scheme, req.Host, req.URL.Path, contentType, data)
//	fmt.Fprintln(res, uri)
//}
<commit_msg>private fileInfoResponse<commit_after>package main

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"
)

func main() {
	Start(os.Getenv("PORT"))
}

func Start(port string) {
	http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
		switch req.Method {
		case "GET":
			handleGet(res, req)
			//		case "POST":
			//			handlePost(res, req)
		default:
			http.Error(res, "Only GET and POST supported", http.StatusMethodNotAllowed)
		}
	})

	if port == "" {
		port = "5000"
	}
	log.Println("listening on port:", port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}

type fileInfoResponse struct {
	Name    string    `json:"name"`
	Size    int64     `json:"size"`
	Type    string    `json:"type"`
	Perm    int       `json:"permission"`
	ModTime time.Time `json:"updated_at"`
}

func handleGet(res http.ResponseWriter, req *http.Request) {
	path, err := os.Open(req.URL.Path)
	if err != nil {
		http.Error(res, "Error reading path: "+err.Error(), http.StatusBadRequest)
		return
	}

	pathInfo, err := path.Stat()
	if err != nil {
		http.Error(res, "Error reading path info: "+err.Error(), http.StatusInternalServerError)
		return
	}

	if pathInfo.Mode().IsDir() {
		fileInfos, err := ioutil.ReadDir(req.URL.Path)
		if err != nil {
			http.Error(res, "Error reading dir: "+err.Error(), http.StatusInternalServerError)
			return
		}

		fileResponses := []fileInfoResponse{}
		for _, fi := range fileInfos {
			fileResponses = append(fileResponses, fileInfoResponse{
				Name:    fi.Name(),
				Size:    fi.Size(),
				Type:    string(fi.Mode().String()[0]),
				Perm:    int(fi.Mode().Perm()),
				ModTime: fi.ModTime(),
			})
		}

		res.Header().Set("Content-Type", "application/json") // TODO allow others
		fileResponsesJson, err := json.Marshal(fileResponses)
		if err != nil {
			http.Error(res, "Error converting JSON: "+err.Error(), http.StatusInternalServerError)
			return
		}
		res.Write(fileResponsesJson)
	} else if pathInfo.Mode().IsRegular() {
		io.Copy(res, path)
	} else {
		http.Error(res, "Invalid file type: "+string(pathInfo.Mode().String()[0]), http.StatusBadRequest)
	}
}

//func handlePost(res http.ResponseWriter, req *http.Request) {
//	scheme := "http"
//	if req.TLS != nil || req.Header.Get("X-Forwarded-Proto") == "https" {
//		scheme = "https"
//	}
//
//	data, err := ioutil.ReadAll(req.Body)
//	if err != nil {
//		log.Println("post.error.body:", err)
//		http.Error(res, "Error reading request body: "+err.Error(), http.StatusBadRequest)
//		return
//	}
//
//	if err := handleStatusParam(res, req); err != nil {
//		return
//	}
//
//	res.Header().Set("Content-Type", "text/uri-list; charset=utf-8")
//	contentType := req.Header.Get("Content-Type")
//	uri := shaas.CreateUri(scheme, req.Host, req.URL.Path, contentType, data)
//	fmt.Fprintln(res, uri)
//}
<|endoftext|>


<commit_before>package web

import (
    "bytes"
    "http"
    "io/ioutil"
    "log"
    "os"
    "path"
    "reflect"
    "regexp"
    "strings"
    "template"
)

type Request http.Request

func (r *Request) ParseForm() (err os.Error) {
    req := (*http.Request)(r)
    return req.ParseForm()
}

type Conn interface {
    StartResponse(status int)
    SetHeader(hdr string, val string)
    Write(data []byte) (n int, err os.Error)
    WriteString(content string)
    Close()
}

type Context struct {
    *Request
    Conn
}

func (ctx *Context) Error(status int, body string) {
    //send an error
}

var contextType reflect.Type
var templateDir string
var staticDir string

func init() {
    contextType = reflect.Typeof(Context{})
    SetTemplateDir("templates")
    SetStaticDir("static")
}

type route struct {
    r       string
    cr      *regexp.Regexp
    method  string
    handler *reflect.FuncValue
}

var routes = make(map[*regexp.Regexp]route)

func addRoute(r string, method string, handler interface{}) {
    cr, err := regexp.Compile(r)
    if err != nil {
        log.Stderrf("Error in route regex %q\n", r)
        return
    }
    fv := reflect.NewValue(handler).(*reflect.FuncValue)
    routes[cr] = route{r, cr, method, fv}
}

type httpConn struct {
    conn *http.Conn
}

func (c *httpConn) StartResponse(status int) { c.conn.WriteHeader(status) }

func (c *httpConn) SetHeader(hdr string, val string) {
    c.conn.SetHeader(hdr, val)
}

func (c *httpConn) WriteString(content string) {
    buf := bytes.NewBufferString(content)
    c.conn.Write(buf.Bytes())
}

func (c *httpConn) Write(content []byte) (n int, err os.Error) {
    return c.conn.Write(content)
}

func (c *httpConn) Close() {
    rwc, buf, _ := c.conn.Hijack()
    if buf != nil {
        buf.Flush()
    }

    if rwc != nil {
        rwc.Close()
    }
}

func httpHandler(c *http.Conn, req *http.Request) {
    conn := httpConn{c}
    routeHandler((*Request)(req), &conn)
}

func error(conn Conn, code int, body string) {
    conn.StartResponse(code)
    conn.WriteString(body)
}

func routeHandler(req *Request, conn Conn) {
    requestPath := req.URL.Path

    //log the request
    if len(req.URL.RawQuery) == 0 {
        log.Stdout(requestPath)
    } else {
        log.Stdout(requestPath + "?" + req.URL.RawQuery)
    }

    //parse the form data (if it exists)
    perr := req.ParseForm()
    if perr != nil {
        log.Stderrf("Failed to parse form data %q", perr.String())
    }

    ctx := Context{req, conn}

    //try to serve a static file
    staticFile := path.Join(staticDir, requestPath)
    if fileExists(staticFile) {
        serveFile(&ctx, staticFile)
        return
    }

    //set default encoding
    conn.SetHeader("Content-Type", "text/html; charset=utf-8")

    for cr, route := range routes {
        if !cr.MatchString(requestPath) {
            continue
        }
        match := cr.MatchStrings(requestPath)
        if len(match) > 0 {
            if len(match[0]) != len(requestPath) {
                continue
            }
            if req.Method != route.method {
                continue
            }
            ai := 0
            handlerType := route.handler.Type().(*reflect.FuncType)
            expectedIn := handlerType.NumIn()
            args := make([]reflect.Value, expectedIn)

            if expectedIn > 0 {
                a0 := handlerType.In(0)
                ptyp, ok := a0.(*reflect.PtrType)
                if ok {
                    typ := ptyp.Elem()
                    if typ == contextType {
                        args[ai] = reflect.NewValue(&ctx)
                        ai += 1
                        expectedIn -= 1
                    }
                }
            }

            actualIn := len(match) - 1
            if expectedIn != actualIn {
                log.Stderrf("Incorrect number of arguments for %s\n", requestPath)
                error(conn, 500, "Server Error")
                return
            }

            for _, arg := range match[1:] {
                args[ai] = reflect.NewValue(arg)
            }
            ret := route.handler.Call(args)[0].(*reflect.StringValue).Get()
            conn.StartResponse(200)
            conn.WriteString(ret)
            return
        }
    }

    error(conn, 404, "Page not found")
}

func render(tmplString string, context interface{}) (string, os.Error) {

    var tmpl *template.Template
    var err os.Error

    if tmpl, err = template.Parse(tmplString, nil); err != nil {
        return "", err
    }

    var buf bytes.Buffer

    tmpl.Execute(context, &buf)
    return buf.String(), nil
}

func Render(filename string, context interface{}) (string, os.Error) {
    var templateBytes []uint8
    var err os.Error

    if !strings.HasPrefix(filename, "/") {
        filename = path.Join(templateDir, filename)
    }

    if templateBytes, err = ioutil.ReadFile(filename); err != nil {
        return "", err
    }

    return render(string(templateBytes), context)
}

func RenderString(tmplString string, context interface{}) (string, os.Error) {
    return render(tmplString, context)
}

func Run(addr string) {
    http.Handle("/", http.HandlerFunc(httpHandler))

    log.Stdoutf("web.go serving %s", addr)
    err := http.ListenAndServe(addr, nil)
    if err != nil {
        log.Exit("ListenAndServe:", err)
    }
}

func RunScgi(addr string) {
    log.Stdoutf("web.go serving scgi %s", addr)
    listenAndServeScgi(addr)
}

func RunFcgi(addr string) {
    log.Stdoutf("web.go serving fcgi %s", addr)
    listenAndServeFcgi(addr)
}

func Get(route string, handler interface{}) { addRoute(route, "GET", handler) }

func Post(route string, handler interface{}) { addRoute(route, "POST", handler) }

func Head(route string, handler interface{}) { addRoute(route, "HEAD", handler) }

func Put(route string, handler interface{}) { addRoute(route, "PUT", handler) }

func Delete(route string, handler interface{}) {
    addRoute(route, "DELETE", handler)
}

func dirExists(dir string) bool {
    d, e := os.Stat(dir)
    switch {
    case e != nil:
        return false
    case !d.IsDirectory():
        return false
    }

    return true
}

func fileExists(dir string) bool {
    d, e := os.Stat(dir)
    switch {
    case e != nil:
        return false
    case !d.IsRegular():
        return false
    }

    return true
}

type dirError string

func (path dirError) String() string { return "Failed to set directory " + string(path) }

func getCwd() string { return os.Getenv("PWD") }

func SetStaticDir(dir string) os.Error {
    cwd := getCwd()
    sd := path.Join(cwd, dir)
    if !dirExists(sd) {
        return dirError(sd)

    }
    staticDir = sd

    return nil
}

func SetTemplateDir(dir string) os.Error {
    cwd := getCwd()
    td := path.Join(cwd, dir)
    if !dirExists(td) {
        return dirError(td)
    }
    templateDir = td
    return nil
}

//copied from go's http package, because it's not public
var statusText = map[int]string{
    http.StatusContinue: "Continue",
    http.StatusSwitchingProtocols: "Switching Protocols",

    http.StatusOK: "OK",
    http.StatusCreated: "Created",
    http.StatusAccepted: "Accepted",
    http.StatusNonAuthoritativeInfo: "Non-Authoritative Information",
    http.StatusNoContent: "No Content",
    http.StatusResetContent: "Reset Content",
    http.StatusPartialContent: "Partial Content",

    http.StatusMultipleChoices: "Multiple Choices",
    http.StatusMovedPermanently: "Moved Permanently",
    http.StatusFound: "Found",
    http.StatusSeeOther: "See Other",
    http.StatusNotModified: "Not Modified",
    http.StatusUseProxy: "Use Proxy",
    http.StatusTemporaryRedirect: "Temporary Redirect",

    http.StatusBadRequest: "Bad Request",
    http.StatusUnauthorized: "Unauthorized",
    http.StatusPaymentRequired: "Payment Required",
    http.StatusForbidden: "Forbidden",
    http.StatusNotFound: "Not Found",
    http.StatusMethodNotAllowed: "Method Not Allowed",
    http.StatusNotAcceptable: "Not Acceptable",
    http.StatusProxyAuthRequired: "Proxy Authentication Required",
    http.StatusRequestTimeout: "Request Timeout",
    http.StatusConflict: "Conflict",
    http.StatusGone: "Gone",
    http.StatusLengthRequired: "Length Required",
    http.StatusPreconditionFailed: "Precondition Failed",
    http.StatusRequestEntityTooLarge: "Request Entity Too Large",
    http.StatusRequestURITooLong: "Request URI Too Long",
    http.StatusUnsupportedMediaType: "Unsupported Media Type",
    http.StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable",
    http.StatusExpectationFailed: "Expectation Failed",

    http.StatusInternalServerError: "Internal Server Error",
    http.StatusNotImplemented: "Not Implemented",
    http.StatusBadGateway: "Bad Gateway",
    http.StatusServiceUnavailable: "Service Unavailable",
    http.StatusGatewayTimeout: "Gateway Timeout",
    http.StatusHTTPVersionNotSupported: "HTTP Version Not Supported",
}
<commit_msg>Removed Render functions<commit_after>package web

import (
    "bytes"
    "http"
    "log"
    "os"
    "path"
    "reflect"
    "regexp"
)

type Request http.Request

func (r *Request) ParseForm() (err os.Error) {
    req := (*http.Request)(r)
    return req.ParseForm()
}

type Conn interface {
    StartResponse(status int)
    SetHeader(hdr string, val string)
    Write(data []byte) (n int, err os.Error)
    WriteString(content string)
    Close()
}

type Context struct {
    *Request
    Conn
}

func (ctx *Context) Error(status int, body string) {
    //send an error
}

var contextType reflect.Type
var templateDir string
var staticDir string

func init() {
    contextType = reflect.Typeof(Context{})
    SetTemplateDir("templates")
    SetStaticDir("static")
}

type route struct {
    r       string
    cr      *regexp.Regexp
    method  string
    handler *reflect.FuncValue
}

var routes = make(map[*regexp.Regexp]route)

func addRoute(r string, method string, handler interface{}) {
    cr, err := regexp.Compile(r)
    if err != nil {
        log.Stderrf("Error in route regex %q\n", r)
        return
    }
    fv := reflect.NewValue(handler).(*reflect.FuncValue)
    routes[cr] = route{r, cr, method, fv}
}

type httpConn struct {
    conn *http.Conn
}

func (c *httpConn) StartResponse(status int) { c.conn.WriteHeader(status) }

func (c *httpConn) SetHeader(hdr string, val string) {
    c.conn.SetHeader(hdr, val)
}

func (c *httpConn) WriteString(content string) {
    buf := bytes.NewBufferString(content)
    c.conn.Write(buf.Bytes())
}

func (c *httpConn) Write(content []byte) (n int, err os.Error) {
    return c.conn.Write(content)
}

func (c *httpConn) Close() {
    rwc, buf, _ := c.conn.Hijack()
    if buf != nil {
        buf.Flush()
    }

    if rwc != nil {
        rwc.Close()
    }
}

func httpHandler(c *http.Conn, req *http.Request) {
    conn := httpConn{c}
    routeHandler((*Request)(req), &conn)
}

func error(conn Conn, code int, body string) {
    conn.StartResponse(code)
    conn.WriteString(body)
}

func routeHandler(req *Request, conn Conn) {
    requestPath := req.URL.Path

    //log the request
    if len(req.URL.RawQuery) == 0 {
        log.Stdout(requestPath)
    } else {
        log.Stdout(requestPath + "?" + req.URL.RawQuery)
    }

    //parse the form data (if it exists)
    perr := req.ParseForm()
    if perr != nil {
        log.Stderrf("Failed to parse form data %q", perr.String())
    }

    ctx := Context{req, conn}

    //try to serve a static file
    staticFile := path.Join(staticDir, requestPath)
    if fileExists(staticFile) {
        serveFile(&ctx, staticFile)
        return
    }

    //set default encoding
    conn.SetHeader("Content-Type", "text/html; charset=utf-8")

    for cr, route := range routes {
        if !cr.MatchString(requestPath) {
            continue
        }
        match := cr.MatchStrings(requestPath)
        if len(match) > 0 {
            if len(match[0]) != len(requestPath) {
                continue
            }
            if req.Method != route.method {
                continue
            }
            ai := 0
            handlerType := route.handler.Type().(*reflect.FuncType)
            expectedIn := handlerType.NumIn()
            args := make([]reflect.Value, expectedIn)

            if expectedIn > 0 {
                a0 := handlerType.In(0)
                ptyp, ok := a0.(*reflect.PtrType)
                if ok {
                    typ := ptyp.Elem()
                    if typ == contextType {
                        args[ai] = reflect.NewValue(&ctx)
                        ai += 1
                        expectedIn -= 1
                    }
                }
            }

            actualIn := len(match) - 1
            if expectedIn != actualIn {
                log.Stderrf("Incorrect number of arguments for %s\n", requestPath)
                error(conn, 500, "Server Error")
                return
            }

            for _, arg := range match[1:] {
                args[ai] = reflect.NewValue(arg)
            }
            ret := route.handler.Call(args)[0].(*reflect.StringValue).Get()
            conn.StartResponse(200)
            conn.WriteString(ret)
            return
        }
    }

    error(conn, 404, "Page not found")
}

func Run(addr string) {
    http.Handle("/", http.HandlerFunc(httpHandler))

    log.Stdoutf("web.go serving %s", addr)
    err := http.ListenAndServe(addr, nil)
    if err != nil {
        log.Exit("ListenAndServe:", err)
    }
}

func RunScgi(addr string) {
    log.Stdoutf("web.go serving scgi %s", addr)
    listenAndServeScgi(addr)
}

func RunFcgi(addr string) {
    log.Stdoutf("web.go serving fcgi %s", addr)
    listenAndServeFcgi(addr)
}

func Get(route string, handler interface{}) { addRoute(route, "GET", handler) }

func Post(route string, handler interface{}) { addRoute(route, "POST", handler) }

func Head(route string, handler interface{}) { addRoute(route, "HEAD", handler) }

func Put(route string, handler interface{}) { addRoute(route, "PUT", handler) }

func Delete(route string, handler interface{}) {
    addRoute(route, "DELETE", handler)
}

func dirExists(dir string) bool {
    d, e := os.Stat(dir)
    switch {
    case e != nil:
        return false
    case !d.IsDirectory():
        return false
    }

    return true
}

func fileExists(dir string) bool {
    d, e := os.Stat(dir)
    switch {
    case e != nil:
        return false
    case !d.IsRegular():
        return false
    }

    return true
}

type dirError string

func (path dirError) String() string { return "Failed to set directory " + string(path) }

func getCwd() string { return os.Getenv("PWD") }

func SetStaticDir(dir string) os.Error {
    cwd := getCwd()
    sd := path.Join(cwd, dir)
    if !dirExists(sd) {
        return dirError(sd)

    }
    staticDir = sd

    return nil
}

func SetTemplateDir(dir string) os.Error {
    cwd := getCwd()
    td := path.Join(cwd, dir)
    if !dirExists(td) {
        return dirError(td)
    }
    templateDir = td
    return nil
}

//copied from go's http package, because it's not public
var statusText = map[int]string{
    http.StatusContinue: "Continue",
    http.StatusSwitchingProtocols: "Switching Protocols",

    http.StatusOK: "OK",
    http.StatusCreated: "Created",
    http.StatusAccepted: "Accepted",
    http.StatusNonAuthoritativeInfo: "Non-Authoritative Information",
    http.StatusNoContent: "No Content",
    http.StatusResetContent: "Reset Content",
    http.StatusPartialContent: "Partial Content",

    http.StatusMultipleChoices: "Multiple Choices",
    http.StatusMovedPermanently: "Moved Permanently",
    http.StatusFound: "Found",
    http.StatusSeeOther: "See Other",
    http.StatusNotModified: "Not Modified",
    http.StatusUseProxy: "Use Proxy",
    http.StatusTemporaryRedirect: "Temporary Redirect",

    http.StatusBadRequest: "Bad Request",
    http.StatusUnauthorized: "Unauthorized",
    http.StatusPaymentRequired: "Payment Required",
    http.StatusForbidden: "Forbidden",
    http.StatusNotFound: "Not Found",
    http.StatusMethodNotAllowed: "Method Not Allowed",
    http.StatusNotAcceptable: "Not Acceptable",
    http.StatusProxyAuthRequired: "Proxy Authentication Required",
    http.StatusRequestTimeout: "Request Timeout",
    http.StatusConflict: "Conflict",
    http.StatusGone: "Gone",
    http.StatusLengthRequired: "Length Required",
    http.StatusPreconditionFailed: "Precondition Failed",
    http.StatusRequestEntityTooLarge: "Request Entity Too Large",
    http.StatusRequestURITooLong: "Request URI Too Long",
    http.StatusUnsupportedMediaType: "Unsupported Media Type",
    http.StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable",
    http.StatusExpectationFailed: "Expectation Failed",

    http.StatusInternalServerError: "Internal Server Error",
    http.StatusNotImplemented: "Not Implemented",
    http.StatusBadGateway: "Bad Gateway",
    http.StatusServiceUnavailable: "Service Unavailable",
    http.StatusGatewayTimeout: "Gateway Timeout",
    http.StatusHTTPVersionNotSupported: "HTTP Version Not Supported",
}
<|endoftext|>


<commit_before>// Package zlmgo provides Go bindings to the Zen License Manager (ZLM).
package zlmgo

/*
#cgo CFLAGS: -I/usr/local/include
#cgo LDFLAGS: /usr/local/lib/libzlm.a

#include <stdlib.h>
#include <zlm.h>

char zlmgo_errbuf_array[ZLM_ERRBUF];
char* zlmgo_errbuf = zlmgo_errbuf_array; // hack around cgo warning
*/
import "C"

import (
	"errors"
	"runtime"
	"unsafe"
)

// License wraps a ZLM license object.
type License struct {
	l *C.ZlmLicense
}

// LicenseNew returns a new license object (panics if not enough memory is available).
func LicenseNew() *License {
	license := &License{C.zlm_license_new(C.zlmgo_errbuf)}
	if license.l == nil {
		panic(C.GoString(C.zlmgo_errbuf))
	}
	runtime.SetFinalizer(license, (*License).free)
	return license
}

// Get wraps zlm_license_get(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) Get(product, version, argv0, path, licenseString string) error {
	var c_product, c_version, c_argv0, c_path, c_license_string *C.char
	// convert argument to C strings
	if product != "" {
		c_product = C.CString(product)
	}
	if version != "" {
		c_version = C.CString(version)
	}
	if argv0 != "" {
		c_argv0 = C.CString(argv0)
	}
	if path != "" {
		c_path = C.CString(path)
	}
	if licenseString != "" {
		c_license_string = C.CString(licenseString)
	}
	// call actual method
	if C.zlm_license_get(license.l, c_product, c_version, c_argv0, c_path, c_license_string, C.zlmgo_errbuf) != C.ZLM_OK {
		return errors.New(C.GoString(C.zlmgo_errbuf))
	}
	return nil
}

func (license *License) free() {
	C.zlm_license_free(license.l)
}

func (license *License) Product() string {
	return C.GoString(C.zlm_license_product(license.l))
}

func (license *License) Expiry() string {
	return C.GoString(C.zlm_license_expiry(license.l))
}

func (license *License) ExpiryDays() int {
	return int(C.zlm_license_expiry_days(license.l))
}

func (license *License) Customer() string {
	return C.GoString(C.zlm_license_customer(license.l))
}

func (license *License) Userdata() string {
	return C.GoString(C.zlm_license_userdata(license.l))
}

func (license *License) Next() error {
	if C.zlm_license_next(license.l, C.zlmgo_errbuf) != C.ZLM_OK {
		return errors.New(C.GoString(C.zlmgo_errbuf))
	}
	return nil
}

func Version() string {
	return C.GoString(C.zlm_version())
}

func HostidJSON() (string, error) {
	cs := C.zlm_hostid_json(C.zlmgo_errbuf)
	if cs == nil {
		return "", errors.New(C.GoString(C.zlmgo_errbuf))
	}
	hostid := C.GoString(cs)
	C.free(unsafe.Pointer(cs))
	return hostid, nil
}

func (license *License) CheckA() {
	C.zlm_license_check_a(license.l)
}

func (license *License) CheckB() {
	C.zlm_license_check_b(license.l)
}

func (license *License) CheckC() {
	C.zlm_license_check_c(license.l)
}

func (license *License) CheckD() {
	C.zlm_license_check_d(license.l)
}

func (license *License) CheckE() {
	C.zlm_license_check_e(license.l)
}

func (license *License) CheckF() {
	C.zlm_license_check_f(license.l)
}

func (license *License) CheckG() {
	C.zlm_license_check_g(license.l)
}

func (license *License) CheckH() {
	C.zlm_license_check_h(license.l)
}

func (license *License) CheckI() {
	C.zlm_license_check_i(license.l)
}

func (license *License) CheckJ() {
	C.zlm_license_check_j(license.l)
}

func (license *License) CheckK() {
	C.zlm_license_check_k(license.l)
}

func (license *License) CheckL() {
	C.zlm_license_check_l(license.l)
}

func (license *License) CheckM() {
	C.zlm_license_check_m(license.l)
}

func (license *License) CheckN() {
	C.zlm_license_check_n(license.l)
}

func (license *License) CheckO() {
	C.zlm_license_check_o(license.l)
}

func (license *License) CheckP() {
	C.zlm_license_check_p(license.l)
}

func (license *License) CheckQ() {
	C.zlm_license_check_q(license.l)
}

func (license *License) CheckR() {
	C.zlm_license_check_r(license.l)
}
<commit_msg>add documentation<commit_after>// Package zlmgo provides Go bindings to the Zen License Manager (ZLM).
package zlmgo

/*
#cgo CFLAGS: -I/usr/local/include
#cgo LDFLAGS: /usr/local/lib/libzlm.a

#include <stdlib.h>
#include <zlm.h>

char zlmgo_errbuf_array[ZLM_ERRBUF];
char* zlmgo_errbuf = zlmgo_errbuf_array; // hack around cgo warning
*/
import "C"

import (
	"errors"
	"runtime"
	"unsafe"
)

// License wraps a ZLM license object.
type License struct {
	l *C.ZlmLicense
}

// LicenseNew returns a new license object (panics if not enough memory is available).
func LicenseNew() *License {
	license := &License{C.zlm_license_new(C.zlmgo_errbuf)}
	if license.l == nil {
		panic(C.GoString(C.zlmgo_errbuf))
	}
	runtime.SetFinalizer(license, (*License).free)
	return license
}

// Get wraps zlm_license_get(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) Get(product, version, argv0, path, licenseString string) error {
	var c_product, c_version, c_argv0, c_path, c_license_string *C.char
	// convert argument to C strings
	if product != "" {
		c_product = C.CString(product)
	}
	if version != "" {
		c_version = C.CString(version)
	}
	if argv0 != "" {
		c_argv0 = C.CString(argv0)
	}
	if path != "" {
		c_path = C.CString(path)
	}
	if licenseString != "" {
		c_license_string = C.CString(licenseString)
	}
	// call actual method
	if C.zlm_license_get(license.l, c_product, c_version, c_argv0, c_path, c_license_string, C.zlmgo_errbuf) != C.ZLM_OK {
		return errors.New(C.GoString(C.zlmgo_errbuf))
	}
	return nil
}

func (license *License) free() {
	C.zlm_license_free(license.l)
}

// Product wraps zlm_license_product(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) Product() string {
	return C.GoString(C.zlm_license_product(license.l))
}

// Expiry wraps zlm_license_expiry(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) Expiry() string {
	return C.GoString(C.zlm_license_expiry(license.l))
}

// ExpiryDays wraps zlm_license_expiry_days(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) ExpiryDays() int {
	return int(C.zlm_license_expiry_days(license.l))
}

// Customer wraps zlm_license_customer(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) Customer() string {
	return C.GoString(C.zlm_license_customer(license.l))
}

// Userdata wraps zlm_license_userdata(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) Userdata() string {
	return C.GoString(C.zlm_license_userdata(license.l))
}

// Next wraps zlm_license_next(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) Next() error {
	if C.zlm_license_next(license.l, C.zlmgo_errbuf) != C.ZLM_OK {
		return errors.New(C.GoString(C.zlmgo_errbuf))
	}
	return nil
}

// Version wraps zlm_version(), see
// https://zenlicensemanager.com/documentation/#API
func Version() string {
	return C.GoString(C.zlm_version())
}

// HostidJSON wraps zlm_hostid_json(), see
// https://zenlicensemanager.com/documentation/#API
func HostidJSON() (string, error) {
	cs := C.zlm_hostid_json(C.zlmgo_errbuf)
	if cs == nil {
		return "", errors.New(C.GoString(C.zlmgo_errbuf))
	}
	hostid := C.GoString(cs)
	C.free(unsafe.Pointer(cs))
	return hostid, nil
}

// CheckA wraps zlm_license_check_a(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckA() {
	C.zlm_license_check_a(license.l)
}

// CheckB wraps zlm_license_check_b(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckB() {
	C.zlm_license_check_b(license.l)
}

// CheckC wraps zlm_license_check_c(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckC() {
	C.zlm_license_check_c(license.l)
}

// CheckD wraps zlm_license_check_d(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckD() {
	C.zlm_license_check_d(license.l)
}

// CheckE wraps zlm_license_check_e(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckE() {
	C.zlm_license_check_e(license.l)
}

// CheckF wraps zlm_license_check_f(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckF() {
	C.zlm_license_check_f(license.l)
}

// CheckG wraps zlm_license_check_g(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckG() {
	C.zlm_license_check_g(license.l)
}

// CheckH wraps zlm_license_check_h(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckH() {
	C.zlm_license_check_h(license.l)
}

// CheckI wraps zlm_license_check_i(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckI() {
	C.zlm_license_check_i(license.l)
}

// CheckJ wraps zlm_license_check_j(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckJ() {
	C.zlm_license_check_j(license.l)
}

// CheckK wraps zlm_license_check_k(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckK() {
	C.zlm_license_check_k(license.l)
}

// CheckL wraps zlm_license_check_l(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckL() {
	C.zlm_license_check_l(license.l)
}

// CheckM wraps zlm_license_check_m(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckM() {
	C.zlm_license_check_m(license.l)
}

// CheckN wraps zlm_license_check_n(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckN() {
	C.zlm_license_check_n(license.l)
}

// CheckO wraps zlm_license_check_o(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckO() {
	C.zlm_license_check_o(license.l)
}

// CheckP wraps zlm_license_check_p(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckP() {
	C.zlm_license_check_p(license.l)
}

// CheckQ wraps zlm_license_check_q(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckQ() {
	C.zlm_license_check_q(license.l)
}

// CheckR wraps zlm_license_check_r(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) CheckR() {
	C.zlm_license_check_r(license.l)
}
<|endoftext|>


<commit_before>package missinggo

import (
	"net"
	"strconv"
)

// Extracts the port as an integer from an address string.
func AddrPort(addr net.Addr) int {
	switch raw := addr.(type) {
	case *net.UDPAddr:
		return raw.Port
	default:
		_, port, err := net.SplitHostPort(addr.String())
		if err != nil {
			panic(err)
		}
		i64, err := strconv.ParseInt(port, 0, 0)
		if err != nil {
			panic(err)
		}
		return int(i64)
	}
}

func AddrIP(addr net.Addr) net.IP {
	if addr == nil {
		return nil
	}
	switch raw := addr.(type) {
	case *net.UDPAddr:
		return raw.IP
	case *net.TCPAddr:
		return raw.IP
	default:
		host, _, err := net.SplitHostPort(addr.String())
		if err != nil {
			panic(err)
		}
		return net.ParseIP(host)
	}
}
<commit_msg>AddrPort: Make *net.TCPAddr faster<commit_after>package missinggo

import (
	"net"
	"strconv"
)

// Extracts the port as an integer from an address string.
func AddrPort(addr net.Addr) int {
	switch raw := addr.(type) {
	case *net.UDPAddr:
		return raw.Port
	case *net.TCPAddr:
		return raw.Port
	default:
		_, port, err := net.SplitHostPort(addr.String())
		if err != nil {
			panic(err)
		}
		i64, err := strconv.ParseInt(port, 0, 0)
		if err != nil {
			panic(err)
		}
		return int(i64)
	}
}

func AddrIP(addr net.Addr) net.IP {
	if addr == nil {
		return nil
	}
	switch raw := addr.(type) {
	case *net.UDPAddr:
		return raw.IP
	case *net.TCPAddr:
		return raw.IP
	default:
		host, _, err := net.SplitHostPort(addr.String())
		if err != nil {
			panic(err)
		}
		return net.ParseIP(host)
	}
}
<|endoftext|>


<commit_before>package aeds

import (
	"bytes"
	"encoding/gob"
	"time"

	"appengine"
	"appengine/datastore"
	"appengine/memcache"
)

// interface for structures that can be stored in App Engine's datastore
type Entity interface {
	Kind() string
	StringId() string

	HookAfterGet()  // Calculate derived fields after fetching from datastore
	HookBeforePut() // Calculate derived fields before writing to datastore

	// CacheTtl indicates how long the entity should be cached in memcache.
	// Return zero to disable memcache.  If this method returns a non-zero
	// duration, the receiver should also implement the GobEncoder and
	// GobDecoder interfaces.
	CacheTtl() time.Duration
}

// Key returns a datastore key for this entity.
func Key(c appengine.Context, e Entity) *datastore.Key {
	return datastore.NewKey(c, e.Kind(), e.StringId(), 0, nil)
}

// Put stores an entity in the datastore.
func Put(c appengine.Context, e Entity) (*datastore.Key, error) {
	e.HookBeforePut()
	ttl := e.CacheTtl()

	// encode entity as a gob (before storing in datastore)
	var value bytes.Buffer
	if ttl > 0 {
		err := gob.NewEncoder(&value).Encode(e)
		if err != nil {
			return nil, err
		}
	}

	// store entity in the datastore
	lookupKey := Key(c, e)
	key, err := datastore.Put(c, lookupKey, e)
	if err != nil {
		return nil, err
	}

	// store entity in memcache too?
	if ttl > 0 {
		item := &memcache.Item{
			Key:        lookupKey.String(),
			Value:      value.Bytes(),
			Expiration: ttl,
		}
		err := memcache.Set(c, item)
		if err != nil {
			c.Errorf("aeds.Put memcache.Set error: %s", err)
		}
	}

	return key, nil
}

// Delete removes an entity from the datastore.
func Delete(c appengine.Context, e Entity) error {
	lookupKey := Key(c, e)

	// should the entity be removed from memcache too?
	if e.CacheTtl() > 0 {
		err := memcache.Delete(c, lookupKey.String())
		if err == memcache.ErrCacheMiss {
			// noop
		} else if err != nil {
			return err
		}
	}

	return datastore.Delete(c, lookupKey)
}

// FromId fetches an entity based on its ID.  The given entity
// should have enough data to calculate the entity's key.  On
// success, the entity is modified in place with all data from
// the datastore.
// Field mismatch errors are ignored.
func FromId(c appengine.Context, e Entity) (Entity, error) {
	lookupKey := Key(c, e)
	ttl := e.CacheTtl()

	// should we look in memcache too?
	cacheMiss := false
	if ttl > 0 {
		item, err := memcache.Get(c, lookupKey.String())
		if err == nil {
			buf := bytes.NewBuffer(item.Value)
			err := gob.NewDecoder(buf).Decode(e)
			e.HookAfterGet()
			return e, err
		}
		if err == memcache.ErrCacheMiss {
			cacheMiss = true
		}
		// ignore any memcache errors
	}

	// look in the datastore
	err := datastore.Get(c, lookupKey, e)
	if err == nil {
		e.HookAfterGet()

		// should we update memcache?
		if cacheMiss && ttl > 0 {
			e.HookBeforePut()

			// encode
			var value bytes.Buffer
			err := gob.NewEncoder(&value).Encode(e)
			if err != nil {
				return nil, err
			}

			// store
			item := &memcache.Item{
				Key:        lookupKey.String(),
				Value:      value.Bytes(),
				Expiration: ttl,
			}
			err = memcache.Set(c, item)
			_ = err // ignore memcache errors
		}

		return e, nil
	}
	if IsErrFieldMismatch(err) {
		e.HookAfterGet()
		return e, nil
	}
	return nil, err // unknown datastore error
}
<commit_msg>Make hook methods optional<commit_after>package aeds

import (
	"bytes"
	"encoding/gob"
	"time"

	"appengine"
	"appengine/datastore"
	"appengine/memcache"
)

// interface for structures that can be stored in App Engine's datastore
type Entity interface {
	Kind() string
	StringId() string

	// CacheTtl indicates how long the entity should be cached in memcache.
	// Return zero to disable memcache.  If this method returns a non-zero
	// duration, the receiver should also implement the GobEncoder and
	// GobDecoder interfaces.
	CacheTtl() time.Duration
}

// HasGetHook is implemented by any Entity that wants to execute
// specific code after fetching the raw entity from datastore.
// This is often used to calculate derived fields.
type HasGetHook interface {
	HookAfterGet()
}

// HasPutHook is implemented by any Entity that wants to execute
// specific code before writing the raw entity to datastore.
// This is often used to calculate derived fields.
type HasPutHook interface {
	HookBeforePut()
}

// Key returns a datastore key for this entity.
func Key(c appengine.Context, e Entity) *datastore.Key {
	return datastore.NewKey(c, e.Kind(), e.StringId(), 0, nil)
}

// Put stores an entity in the datastore.
func Put(c appengine.Context, e Entity) (*datastore.Key, error) {
	if x, ok := e.(HasPutHook); ok {
		x.HookBeforePut()
	}
	ttl := e.CacheTtl()

	// encode entity as a gob (before storing in datastore)
	var value bytes.Buffer
	if ttl > 0 {
		err := gob.NewEncoder(&value).Encode(e)
		if err != nil {
			return nil, err
		}
	}

	// store entity in the datastore
	lookupKey := Key(c, e)
	key, err := datastore.Put(c, lookupKey, e)
	if err != nil {
		return nil, err
	}

	// store entity in memcache too?
	if ttl > 0 {
		item := &memcache.Item{
			Key:        lookupKey.String(),
			Value:      value.Bytes(),
			Expiration: ttl,
		}
		err := memcache.Set(c, item)
		if err != nil {
			c.Errorf("aeds.Put memcache.Set error: %s", err)
		}
	}

	return key, nil
}

// Delete removes an entity from the datastore.
func Delete(c appengine.Context, e Entity) error {
	lookupKey := Key(c, e)

	// should the entity be removed from memcache too?
	if e.CacheTtl() > 0 {
		err := memcache.Delete(c, lookupKey.String())
		if err == memcache.ErrCacheMiss {
			// noop
		} else if err != nil {
			return err
		}
	}

	return datastore.Delete(c, lookupKey)
}

// FromId fetches an entity based on its ID.  The given entity
// should have enough data to calculate the entity's key.  On
// success, the entity is modified in place with all data from
// the datastore.
// Field mismatch errors are ignored.
func FromId(c appengine.Context, e Entity) (Entity, error) {
	lookupKey := Key(c, e)
	ttl := e.CacheTtl()

	// should we look in memcache too?
	cacheMiss := false
	if ttl > 0 {
		item, err := memcache.Get(c, lookupKey.String())
		if err == nil {
			buf := bytes.NewBuffer(item.Value)
			err := gob.NewDecoder(buf).Decode(e)
			if x, ok := e.(HasGetHook); ok {
				x.HookAfterGet()
			}
			return e, err
		}
		if err == memcache.ErrCacheMiss {
			cacheMiss = true
		}
		// ignore any memcache errors
	}

	// look in the datastore
	err := datastore.Get(c, lookupKey, e)
	if err == nil {
		if x, ok := e.(HasGetHook); ok {
			x.HookAfterGet()
		}

		// should we update memcache?
		if cacheMiss && ttl > 0 {
			if x, ok := e.(HasPutHook); ok {
				x.HookBeforePut()
			}

			// encode
			var value bytes.Buffer
			err := gob.NewEncoder(&value).Encode(e)
			if err != nil {
				return nil, err
			}

			// store
			item := &memcache.Item{
				Key:        lookupKey.String(),
				Value:      value.Bytes(),
				Expiration: ttl,
			}
			err = memcache.Set(c, item)
			_ = err // ignore memcache errors
		}

		return e, nil
	}
	if IsErrFieldMismatch(err) {
		if x, ok := e.(HasGetHook); ok {
			x.HookAfterGet()
		}
		return e, nil
	}
	return nil, err // unknown datastore error
}
<|endoftext|>


<commit_before>package apib

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"regexp"
	"strings"

	"github.com/acsellers/inflections"
	"github.com/labstack/echo"
)

var calls = make(map[string]Resources)
var recording bool
var currentGroup string
var currentName string
var currentParams []*Param

type Call struct {
	Group    string
	Name     string
	Request  Request
	Response Response
}

type Resources map[string]Resource
type Resource struct {
	URI         string
	Calls       []Call
	Params      map[string][]string
	ExtraParams []*Param
}
type Request struct {
	URI        string
	Method     string
	Headers    map[string]string
	Params     map[string][]string
	Body       string
	PathParams map[string]string
}

type Response struct {
	Headers    map[string]string
	Body       string
	StatusCode int
}

type Param struct {
	Name        string
	Example     string
	Description string
	Type        string
	Required    bool
}

func Record() {
	recording = true
}

func Store() {
	spaces := regexp.MustCompile(" ")
	recording = false

	for group, rss := range calls {
		fileName := inflections.Underscore(group)
		fileName = spaces.ReplaceAllString(fileName, "_")
		f, err := os.Create(fileName + ".apib")
		if err != nil {
			log.Fatal("Can't create file.", err)
		}
		f.Write([]byte(fmt.Sprintf("# Group %s\n\n", group)))
		for uri, rs := range rss {

			f.Write([]byte(fmt.Sprintf("## %s\n\n", uri)))
			if len(rs.Params) > 0 {
				f.Write([]byte(fmt.Sprintf("+ Parameters\n\n")))
				for k, v := range rs.Params {
					for _, p := range v {
						f.Write([]byte(fmt.Sprintf("    + %s: `%s` (string)\n", k, p)))
					}
				}
				if len(rs.ExtraParams) > 0 {
					for _, p := range rs.ExtraParams {
						f.Write([]byte(fmt.Sprintf("    + %s: `%s` (%s)\n", p.Name, p.Example, p.Type)))
						if p.Description != "" {
							f.Write([]byte(fmt.Sprintf("      %s\n", p.Description)))
						}
					}
				}
			}
			f.Write([]byte("\n"))
			for _, c := range rs.Calls {

				f.Write([]byte(fmt.Sprintf("### %s [%s]\n\n\n", c.Name, c.Request.Method)))
				f.Write([]byte(fmt.Sprintf("+ Request (%s)\n\n", c.Request.Headers["Content-Type"])))
				if len(c.Request.Headers) > 0 {
					f.Write([]byte(fmt.Sprintf("    + Headers\n\n")))
					for k, v := range c.Request.Headers {
						f.Write([]byte(fmt.Sprintf("            %s: %s\n", k, v)))
					}
					f.Write([]byte("\n"))
				}

				if len(c.Request.Body) > 0 {
					f.Write([]byte(fmt.Sprintf("    + Body\n\n")))
					body := stringToLines(c.Request.Body)
					for i, s := range body {
						body[i] = "            " + s
					}
					f.Write([]byte(fmt.Sprintf("            \n%s\n            \n\n", strings.Join(body, "\n"))))
				}
				f.Write([]byte("\n"))
				f.Write([]byte(fmt.Sprintf("+ Response %d (%s)\n\n", c.Response.StatusCode, c.Response.Headers["Content-Type"])))
				if len(c.Response.Headers) > 0 {
					f.Write([]byte(fmt.Sprintf("    + Headers\n\n")))
					for k, v := range c.Response.Headers {
						f.Write([]byte(fmt.Sprintf("            %s: %s\n", k, v)))
					}
					f.Write([]byte("\n"))
				}
				if len(c.Response.Body) > 0 {
					f.Write([]byte(fmt.Sprintf("    + Body\n\n")))
					body := stringToLines(c.Response.Body)
					for i, s := range body {
						body[i] = "            " + s
					}
					f.Write([]byte(fmt.Sprintf("            \n%s\n            \n\n", strings.Join(body, "\n"))))
				}
				f.Write([]byte("\n"))
			}
		}
		f.Close()
	}
}

func stringToLines(s string) []string {
	var lines []string

	scanner := bufio.NewScanner(strings.NewReader(s))
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading standard input:", err)
	}

	return lines
}

func Group(group string) {
	currentGroup = group
}

func Name(name string) {
	currentName = name
}

func AddParam(name string, typ string, ex string, desc string, req bool) {
	if currentParams == nil {
		currentParams = []*Param{}
	}
	currentParams = append(currentParams, &Param{
		Name:        name,
		Type:        typ,
		Example:     ex,
		Description: desc,
		Required:    req,
	})
}

func Flush() {
	currentGroup = ""
	currentName = ""
	currentParams = []*Param{}
}

func params(c echo.Context) map[string]string {
	params := make(map[string]string)

	for _, p := range c.ParamNames() {
		params[p] = c.Param(p)
	}

	return params
}
<commit_msg>Extra params weren't printed if no params were present<commit_after>package apib

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"regexp"
	"strings"

	"github.com/acsellers/inflections"
	"github.com/labstack/echo"
)

var calls = make(map[string]Resources)
var recording bool
var currentGroup string
var currentName string
var currentParams []*Param

type Call struct {
	Group    string
	Name     string
	Request  Request
	Response Response
}

type Resources map[string]Resource
type Resource struct {
	URI         string
	Calls       []Call
	Params      map[string][]string
	ExtraParams []*Param
}
type Request struct {
	URI        string
	Method     string
	Headers    map[string]string
	Params     map[string][]string
	Body       string
	PathParams map[string]string
}

type Response struct {
	Headers    map[string]string
	Body       string
	StatusCode int
}

type Param struct {
	Name        string
	Example     string
	Description string
	Type        string
	Required    bool
}

func Record() {
	recording = true
}

func Store() {
	spaces := regexp.MustCompile(" ")
	recording = false

	for group, rss := range calls {
		fileName := inflections.Underscore(group)
		fileName = spaces.ReplaceAllString(fileName, "_")
		f, err := os.Create(fileName + ".apib")
		if err != nil {
			log.Fatal("Can't create file.", err)
		}
		f.Write([]byte(fmt.Sprintf("# Group %s\n\n", group)))
		for uri, rs := range rss {

			f.Write([]byte(fmt.Sprintf("## %s\n\n", uri)))
			if len(rs.Params) > 0 || len(rs.ExtraParams) > 0 {
				f.Write([]byte(fmt.Sprintf("+ Parameters\n\n")))
				if len(rs.Params) > 0 {
					for k, v := range rs.Params {
						for _, p := range v {
							f.Write([]byte(fmt.Sprintf("    + %s: `%s` (string)\n", k, p)))
						}
					}
				}
				if len(rs.ExtraParams) > 0 {
					for _, p := range rs.ExtraParams {
						f.Write([]byte(fmt.Sprintf("    + %s: `%s` (%s)\n", p.Name, p.Example, p.Type)))
						if p.Description != "" {
							f.Write([]byte(fmt.Sprintf("      %s\n", p.Description)))
						}
					}
				}
			}
			f.Write([]byte("\n"))
			for _, c := range rs.Calls {

				f.Write([]byte(fmt.Sprintf("### %s [%s]\n\n\n", c.Name, c.Request.Method)))
				f.Write([]byte(fmt.Sprintf("+ Request (%s)\n\n", c.Request.Headers["Content-Type"])))
				if len(c.Request.Headers) > 0 {
					f.Write([]byte(fmt.Sprintf("    + Headers\n\n")))
					for k, v := range c.Request.Headers {
						f.Write([]byte(fmt.Sprintf("            %s: %s\n", k, v)))
					}
					f.Write([]byte("\n"))
				}

				if len(c.Request.Body) > 0 {
					f.Write([]byte(fmt.Sprintf("    + Body\n\n")))
					body := stringToLines(c.Request.Body)
					for i, s := range body {
						body[i] = "            " + s
					}
					f.Write([]byte(fmt.Sprintf("            \n%s\n            \n\n", strings.Join(body, "\n"))))
				}
				f.Write([]byte("\n"))
				f.Write([]byte(fmt.Sprintf("+ Response %d (%s)\n\n", c.Response.StatusCode, c.Response.Headers["Content-Type"])))
				if len(c.Response.Headers) > 0 {
					f.Write([]byte(fmt.Sprintf("    + Headers\n\n")))
					for k, v := range c.Response.Headers {
						f.Write([]byte(fmt.Sprintf("            %s: %s\n", k, v)))
					}
					f.Write([]byte("\n"))
				}
				if len(c.Response.Body) > 0 {
					f.Write([]byte(fmt.Sprintf("    + Body\n\n")))
					body := stringToLines(c.Response.Body)
					for i, s := range body {
						body[i] = "            " + s
					}
					f.Write([]byte(fmt.Sprintf("            \n%s\n            \n\n", strings.Join(body, "\n"))))
				}
				f.Write([]byte("\n"))
			}
		}
		f.Close()
	}
}

func stringToLines(s string) []string {
	var lines []string

	scanner := bufio.NewScanner(strings.NewReader(s))
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading standard input:", err)
	}

	return lines
}

func Group(group string) {
	currentGroup = group
}

func Name(name string) {
	currentName = name
}

func AddParam(name string, typ string, ex string, desc string, req bool) {
	if currentParams == nil {
		currentParams = []*Param{}
	}
	currentParams = append(currentParams, &Param{
		Name:        name,
		Type:        typ,
		Example:     ex,
		Description: desc,
		Required:    req,
	})
}

func Flush() {
	currentGroup = ""
	currentName = ""
	currentParams = []*Param{}
}

func params(c echo.Context) map[string]string {
	params := make(map[string]string)

	for _, p := range c.ParamNames() {
		params[p] = c.Param(p)
	}

	return params
}
<|endoftext|>