<commit_before>// Package raft is an implementation of the Raft consensus protocol.
//
// Call NewConsensusModule with appropriate parameters to start an instance.
// Incoming RPC calls can then be sent to it using the ProcessRpc...Async
// methods.
//
// You will have to provide implementations of the following interfaces:
//
//  - raft.PersistentState
//  - raft.Log
//  - raft.RpcService
//
// Notes for implementers of these interfaces:
//
// - Concurrency: a ConsensusModule will only ever call the methods of these
// interfaces from it's single goroutine.
//
// - Errors: all errors should be checked and returned. This includes both
// invalid parameters sent by the consensus module and internal errors in the
// implementation. Note that any error will shutdown the ConsensusModule.
//
package raft

import (
	"errors"
	"sync/atomic"
	"time"
)

// A ConsensusModule is an active Raft consensus module implementation.
type ConsensusModule struct {
	passiveConsensusModule *passiveConsensusModule

	// -- External components - these fields meant to be immutable
	rpcService RpcService

	// -- State - these fields may be accessed concurrently
	stopped int32

	// -- Channels
	runnableChannel chan func() error
	ticker          *time.Ticker

	// -- Control
	stopSignal chan struct{}
	stopError  *atomic.Value
}

// Allocate and initialize a ConsensusModule with the given components and
// settings.
//
// A goroutine that handles consensus processing is created.
// All parameters are required.
// timeSettings is checked using ValidateTimeSettings().
func NewConsensusModule(
	persistentState PersistentState,
	lasm LogAndStateMachine,
	rpcService RpcService,
	clusterInfo *ClusterInfo,
	timeSettings TimeSettings,
) (*ConsensusModule, error) {
	runnableChannel := make(chan func() error, RPC_CHANNEL_BUFFER_SIZE)
	ticker := time.NewTicker(timeSettings.TickerDuration)
	now := time.Now()

	cm := &ConsensusModule{
		nil, // temp value, to be replaced before goroutine start

		// -- External components
		rpcService,

		// -- State
		0,

		// -- Channels
		runnableChannel,
		ticker,

		// -- Control
		make(chan struct{}),
		&atomic.Value{},
	}

	pcm, err := newPassiveConsensusModule(
		persistentState,
		lasm,
		cm,
		clusterInfo,
		timeSettings.ElectionTimeoutLow,
		now,
	)
	if err != nil {
		return nil, err
	}

	// we can only set the value here because it's a cyclic reference
	cm.passiveConsensusModule = pcm

	// Start the go routine
	go cm.processor()

	return cm, nil
}

// Check if the ConsensusModule is stopped.
func (cm *ConsensusModule) IsStopped() bool {
	return atomic.LoadInt32(&cm.stopped) != 0
}

// Stop the ConsensusModule asynchronously.
//
// This will stop the goroutine that does the processing.
// This is safe to call even if the goroutine has already stopped, but it
// will panic if called more than once.
func (cm *ConsensusModule) StopAsync() {
	close(cm.stopSignal)
}

// Get the error that stopped the ConsensusModule goroutine.
//
// The value will be nil if the goroutine is not stopped, or if it stopped
// without an error.
func (cm *ConsensusModule) GetStopError() error {
	stopErr := cm.stopError.Load()
	if stopErr != nil {
		return stopErr.(error)
	}
	return nil
}

// Get the current server state.
func (cm *ConsensusModule) GetServerState() ServerState {
	return cm.passiveConsensusModule.getServerState()
}

// Process the given RpcAppendEntries message from the given peer
// asynchronously.
//
// This method sends the RPC message to the ConsensusModule's goroutine.
// The RPC reply will be sent later on the returned channel.
//
// See RpcSender (in interfaces.go) for outgoing RPC.
//
// TODO: behavior when channel full?
func (cm *ConsensusModule) ProcessRpcAppendEntriesAsync(
	from ServerId,
	rpc *RpcAppendEntries,
) <-chan *RpcAppendEntriesReply {
	replyChan := make(chan *RpcAppendEntriesReply, 1)
	cm.runnableChannel <- func() error {
		now := time.Now()

		rpcReply, err := cm.passiveConsensusModule.rpc_RpcAppendEntries(from, rpc, now)
		if err != nil {
			return err
		}

		select {
		case replyChan <- rpcReply:
			return nil
		default:
			// theoretically unreachable as we make a buffered channel of
			// capacity 1 and this is the one send to it
			return errors.New("FATAL: replyChan is nil or wants to block")
		}
	}
	return replyChan
}

// Process the given RpcRequestVote message from the given peer
// asynchronously.
//
// This method sends the RPC message to the ConsensusModule's goroutine.
// The RPC reply will be sent later on the returned channel.
//
// See RpcSender (in interfaces.go) for outgoing RPC.
//
// TODO: behavior when channel full?
func (cm *ConsensusModule) ProcessRpcRequestVoteAsync(
	from ServerId,
	rpc *RpcRequestVote,
) <-chan *RpcRequestVoteReply {
	replyChan := make(chan *RpcRequestVoteReply, 1)
	cm.runnableChannel <- func() error {
		now := time.Now()

		rpcReply, err := cm.passiveConsensusModule.rpc_RpcRequestVote(from, rpc, now)
		if err != nil {
			return err
		}

		select {
		case replyChan <- rpcReply:
			return nil
		default:
			// theoretically unreachable as we make a buffered channel of
			// capacity 1 and this is the one send to it
			return errors.New("FATAL: replyChan is nil or wants to block")
		}
	}
	return replyChan
}

// Append the given command as an entry in the log.
//
// This can only be done if the ConsensusModule is in LEADER state.
//
// The command should already have been validated by this point to ensure that
// it will succeed when applied to the state machine.
// (both internal contents and other context/state checks)
//
// This method sends the RPC message to the ConsensusModule's goroutine.
// The reply will be sent later on the returned channel when the append is
// processed. The reply will contain the index of the new entry or an error.
//
// Here, we intentionally punt on some of the leader details, specifically
// most of:
//
// #RFS-L2: If command received from client: append entry to local log,
// respond after entry applied to state machine (#5.3)
//
// We choose not to deal with the client directly. You must implement the
// interaction with clients and waiting the entry to be applied to the state
// machine. (see delegation of lastApplied to raft.Log)
//
// TODO: behavior when channel full?
func (cm *ConsensusModule) AppendCommandAsync(
	command Command,
) <-chan AppendCommandResult {
	replyChan := make(chan AppendCommandResult, 1)
	cm.runnableChannel <- func() error {
		logIndex, err := cm.passiveConsensusModule.appendCommand(command)
		appendCommandResult := AppendCommandResult{logIndex, err}
		select {
		case replyChan <- appendCommandResult:
			return nil
		default:
			// theoretically unreachable as we make a buffered channel of
			// capacity 1 and this is the one send to it
			return errors.New("FATAL: replyChan is nil or wants to block")
		}
	}
	return replyChan
}

type AppendCommandResult struct {
	LogIndex
	error
}

// -- protected methods

// Implement rpcSender.sendRpcAppendEntriesAsync to bridge to
// RpcService.SendRpcAppendEntriesAsync() with a closure callback.
func (cm *ConsensusModule) sendRpcAppendEntriesAsync(toServer ServerId, rpc *RpcAppendEntries) error {
	replyAsync := func(rpcReply *RpcAppendEntriesReply) {
		// Process the given RPC reply message from the given peer
		// asynchronously.
		// TODO: behavior when channel full?
		cm.runnableChannel <- func() error {
			err := cm.passiveConsensusModule.rpcReply_RpcAppendEntriesReply(toServer, rpc, rpcReply)
			if err != nil {
				return err
			}
			return nil
		}
	}
	return cm.rpcService.SendRpcAppendEntriesAsync(toServer, rpc, replyAsync)
}

// Implement rpcSender.sendRpcRequestVoteAsync to bridge to
// RpcService.SendRpcRequestVoteAsync() with a closure callback.
func (cm *ConsensusModule) sendRpcRequestVoteAsync(toServer ServerId, rpc *RpcRequestVote) error {
	replyAsync := func(rpcReply *RpcRequestVoteReply) {
		// Process the given RPC reply message from the given peer
		// asynchronously.
		// TODO: behavior when channel full?
		cm.runnableChannel <- func() error {
			err := cm.passiveConsensusModule.rpcReply_RpcRequestVoteReply(toServer, rpc, rpcReply)
			if err != nil {
				return err
			}
			return nil
		}
	}
	return cm.rpcService.SendRpcRequestVoteAsync(toServer, rpc, replyAsync)
}

func (cm *ConsensusModule) processor() {
	var stopErr error = nil

	defer func() {
		// Save error if needed
		if stopErr != nil {
			cm.stopError.Store(stopErr)
		}
		// Mark the server as stopped
		atomic.StoreInt32(&cm.stopped, 1)
		// Clean up things
		cm.runnableChannel = nil // don't close channel - avoids sender panics (rpc callbacks)
		cm.ticker.Stop()
	}()

loop:
	for {
		select {
		case runnable, ok := <-cm.runnableChannel:
			if !ok {
				// theoretically unreachable as we don't close the channel
				// til shutdown
				stopErr = errors.New("FATAL: runnableChannel closed")
				break loop
			}
			err := runnable()
			if err != nil {
				stopErr = err
				break loop
			}
		case _, ok := <-cm.ticker.C:
			if !ok {
				// theoretically unreachable as we don't stop the timer til shutdown
				stopErr = errors.New("FATAL: ticker channel closed")
				break loop
			}
			// Get a fresh now since the ticker's now could have been waiting
			now := time.Now()
			err := cm.passiveConsensusModule.tick(now)
			if err != nil {
				stopErr = err
				break loop
			}
		case <-cm.stopSignal:
			break loop
		}
	}
}

type rpcTuple struct {
	from      ServerId
	rpc       interface{}
	replyChan chan interface{}
}
<commit_msg>ConsensusModule shutdown as regular code instead of in a defer<commit_after>// Package raft is an implementation of the Raft consensus protocol.
//
// Call NewConsensusModule with appropriate parameters to start an instance.
// Incoming RPC calls can then be sent to it using the ProcessRpc...Async
// methods.
//
// You will have to provide implementations of the following interfaces:
//
//  - raft.PersistentState
//  - raft.Log
//  - raft.RpcService
//
// Notes for implementers of these interfaces:
//
// - Concurrency: a ConsensusModule will only ever call the methods of these
// interfaces from it's single goroutine.
//
// - Errors: all errors should be checked and returned. This includes both
// invalid parameters sent by the consensus module and internal errors in the
// implementation. Note that any error will shutdown the ConsensusModule.
//
package raft

import (
	"errors"
	"sync/atomic"
	"time"
)

// A ConsensusModule is an active Raft consensus module implementation.
type ConsensusModule struct {
	passiveConsensusModule *passiveConsensusModule

	// -- External components - these fields meant to be immutable
	rpcService RpcService

	// -- State - these fields may be accessed concurrently
	stopped int32

	// -- Channels
	runnableChannel chan func() error
	ticker          *time.Ticker

	// -- Control
	stopSignal chan struct{}
	stopError  *atomic.Value
}

// Allocate and initialize a ConsensusModule with the given components and
// settings.
//
// A goroutine that handles consensus processing is created.
// All parameters are required.
// timeSettings is checked using ValidateTimeSettings().
func NewConsensusModule(
	persistentState PersistentState,
	lasm LogAndStateMachine,
	rpcService RpcService,
	clusterInfo *ClusterInfo,
	timeSettings TimeSettings,
) (*ConsensusModule, error) {
	runnableChannel := make(chan func() error, RPC_CHANNEL_BUFFER_SIZE)
	ticker := time.NewTicker(timeSettings.TickerDuration)
	now := time.Now()

	cm := &ConsensusModule{
		nil, // temp value, to be replaced before goroutine start

		// -- External components
		rpcService,

		// -- State
		0,

		// -- Channels
		runnableChannel,
		ticker,

		// -- Control
		make(chan struct{}),
		&atomic.Value{},
	}

	pcm, err := newPassiveConsensusModule(
		persistentState,
		lasm,
		cm,
		clusterInfo,
		timeSettings.ElectionTimeoutLow,
		now,
	)
	if err != nil {
		return nil, err
	}

	// we can only set the value here because it's a cyclic reference
	cm.passiveConsensusModule = pcm

	// Start the go routine
	go cm.processor()

	return cm, nil
}

// Check if the ConsensusModule is stopped.
func (cm *ConsensusModule) IsStopped() bool {
	return atomic.LoadInt32(&cm.stopped) != 0
}

// Stop the ConsensusModule asynchronously.
//
// This will stop the goroutine that does the processing.
// This is safe to call even if the goroutine has already stopped, but it
// will panic if called more than once.
func (cm *ConsensusModule) StopAsync() {
	close(cm.stopSignal)
}

// Get the error that stopped the ConsensusModule goroutine.
//
// The value will be nil if the goroutine is not stopped, or if it stopped
// without an error.
func (cm *ConsensusModule) GetStopError() error {
	stopErr := cm.stopError.Load()
	if stopErr != nil {
		return stopErr.(error)
	}
	return nil
}

// Get the current server state.
func (cm *ConsensusModule) GetServerState() ServerState {
	return cm.passiveConsensusModule.getServerState()
}

// Process the given RpcAppendEntries message from the given peer
// asynchronously.
//
// This method sends the RPC message to the ConsensusModule's goroutine.
// The RPC reply will be sent later on the returned channel.
//
// See RpcSender (in interfaces.go) for outgoing RPC.
//
// TODO: behavior when channel full?
func (cm *ConsensusModule) ProcessRpcAppendEntriesAsync(
	from ServerId,
	rpc *RpcAppendEntries,
) <-chan *RpcAppendEntriesReply {
	replyChan := make(chan *RpcAppendEntriesReply, 1)
	cm.runnableChannel <- func() error {
		now := time.Now()

		rpcReply, err := cm.passiveConsensusModule.rpc_RpcAppendEntries(from, rpc, now)
		if err != nil {
			return err
		}

		select {
		case replyChan <- rpcReply:
			return nil
		default:
			// theoretically unreachable as we make a buffered channel of
			// capacity 1 and this is the one send to it
			return errors.New("FATAL: replyChan is nil or wants to block")
		}
	}
	return replyChan
}

// Process the given RpcRequestVote message from the given peer
// asynchronously.
//
// This method sends the RPC message to the ConsensusModule's goroutine.
// The RPC reply will be sent later on the returned channel.
//
// See RpcSender (in interfaces.go) for outgoing RPC.
//
// TODO: behavior when channel full?
func (cm *ConsensusModule) ProcessRpcRequestVoteAsync(
	from ServerId,
	rpc *RpcRequestVote,
) <-chan *RpcRequestVoteReply {
	replyChan := make(chan *RpcRequestVoteReply, 1)
	cm.runnableChannel <- func() error {
		now := time.Now()

		rpcReply, err := cm.passiveConsensusModule.rpc_RpcRequestVote(from, rpc, now)
		if err != nil {
			return err
		}

		select {
		case replyChan <- rpcReply:
			return nil
		default:
			// theoretically unreachable as we make a buffered channel of
			// capacity 1 and this is the one send to it
			return errors.New("FATAL: replyChan is nil or wants to block")
		}
	}
	return replyChan
}

// Append the given command as an entry in the log.
//
// This can only be done if the ConsensusModule is in LEADER state.
//
// The command should already have been validated by this point to ensure that
// it will succeed when applied to the state machine.
// (both internal contents and other context/state checks)
//
// This method sends the RPC message to the ConsensusModule's goroutine.
// The reply will be sent later on the returned channel when the append is
// processed. The reply will contain the index of the new entry or an error.
//
// Here, we intentionally punt on some of the leader details, specifically
// most of:
//
// #RFS-L2: If command received from client: append entry to local log,
// respond after entry applied to state machine (#5.3)
//
// We choose not to deal with the client directly. You must implement the
// interaction with clients and waiting the entry to be applied to the state
// machine. (see delegation of lastApplied to raft.Log)
//
// TODO: behavior when channel full?
func (cm *ConsensusModule) AppendCommandAsync(
	command Command,
) <-chan AppendCommandResult {
	replyChan := make(chan AppendCommandResult, 1)
	cm.runnableChannel <- func() error {
		logIndex, err := cm.passiveConsensusModule.appendCommand(command)
		appendCommandResult := AppendCommandResult{logIndex, err}
		select {
		case replyChan <- appendCommandResult:
			return nil
		default:
			// theoretically unreachable as we make a buffered channel of
			// capacity 1 and this is the one send to it
			return errors.New("FATAL: replyChan is nil or wants to block")
		}
	}
	return replyChan
}

type AppendCommandResult struct {
	LogIndex
	error
}

// -- protected methods

// Implement rpcSender.sendRpcAppendEntriesAsync to bridge to
// RpcService.SendRpcAppendEntriesAsync() with a closure callback.
func (cm *ConsensusModule) sendRpcAppendEntriesAsync(toServer ServerId, rpc *RpcAppendEntries) error {
	replyAsync := func(rpcReply *RpcAppendEntriesReply) {
		// Process the given RPC reply message from the given peer
		// asynchronously.
		// TODO: behavior when channel full?
		cm.runnableChannel <- func() error {
			err := cm.passiveConsensusModule.rpcReply_RpcAppendEntriesReply(toServer, rpc, rpcReply)
			if err != nil {
				return err
			}
			return nil
		}
	}
	return cm.rpcService.SendRpcAppendEntriesAsync(toServer, rpc, replyAsync)
}

// Implement rpcSender.sendRpcRequestVoteAsync to bridge to
// RpcService.SendRpcRequestVoteAsync() with a closure callback.
func (cm *ConsensusModule) sendRpcRequestVoteAsync(toServer ServerId, rpc *RpcRequestVote) error {
	replyAsync := func(rpcReply *RpcRequestVoteReply) {
		// Process the given RPC reply message from the given peer
		// asynchronously.
		// TODO: behavior when channel full?
		cm.runnableChannel <- func() error {
			err := cm.passiveConsensusModule.rpcReply_RpcRequestVoteReply(toServer, rpc, rpcReply)
			if err != nil {
				return err
			}
			return nil
		}
	}
	return cm.rpcService.SendRpcRequestVoteAsync(toServer, rpc, replyAsync)
}

func (cm *ConsensusModule) processor() {
	var stopErr error = nil

loop:
	for {
		select {
		case runnable, ok := <-cm.runnableChannel:
			if !ok {
				// theoretically unreachable as we don't close the channel
				// til shutdown
				stopErr = errors.New("FATAL: runnableChannel closed")
				break loop
			}
			err := runnable()
			if err != nil {
				stopErr = err
				break loop
			}
		case _, ok := <-cm.ticker.C:
			if !ok {
				// theoretically unreachable as we don't stop the timer til shutdown
				stopErr = errors.New("FATAL: ticker channel closed")
				break loop
			}
			// Get a fresh now since the ticker's now could have been waiting
			now := time.Now()
			err := cm.passiveConsensusModule.tick(now)
			if err != nil {
				stopErr = err
				break loop
			}
		case <-cm.stopSignal:
			break loop
		}
	}

	// Save error if needed
	if stopErr != nil {
		cm.stopError.Store(stopErr)
	}
	// Mark the server as stopped
	atomic.StoreInt32(&cm.stopped, 1)
	// Clean up things
	cm.runnableChannel = nil // don't close channel - avoids sender panics (rpc callbacks)
	cm.ticker.Stop()

}

type rpcTuple struct {
	from      ServerId
	rpc       interface{}
	replyChan chan interface{}
}
<|endoftext|>


<commit_before>// +build !plan9

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	. "github.com/candid82/joker/core"
	"github.com/candid82/liner"
)

var qualifiedSymbolRe *regexp.Regexp = regexp.MustCompile(`([0-9A-Za-z_\-\+\*\'\.]+)/([0-9A-Za-z_\-\+\*\']*$)`)
var callRe *regexp.Regexp = regexp.MustCompile(`\(\s*([0-9A-Za-z_\-\+\*\'\.]*$)`)

func completer(line string, pos int) (head string, c []string, tail string) {
	head = line[:pos]
	tail = line[pos:]
	var match []string
	var prefix string
	var ns *Namespace
	var addNamespaces bool
	if match = qualifiedSymbolRe.FindStringSubmatch(head); match != nil {
		nsName := match[1]
		prefix = match[2]
		ns = GLOBAL_ENV.NamespaceFor(GLOBAL_ENV.CurrentNamespace(), MakeSymbol(nsName+"/"+prefix))
	} else if match = callRe.FindStringSubmatch(head); match != nil {
		prefix = match[1]
		ns = GLOBAL_ENV.CurrentNamespace()
		addNamespaces = true
	}
	if ns == nil {
		return
	}
	for k, _ := range ns.Mappings() {
		if strings.HasPrefix(*k, prefix) {
			c = append(c, *k)
		}
	}
	if addNamespaces {
		for k, _ := range GLOBAL_ENV.Namespaces {
			if strings.HasPrefix(*k, prefix) {
				c = append(c, *k)
			}
		}
		for k, _ := range ns.Aliases() {
			if strings.HasPrefix(*k, prefix) {
				c = append(c, *k)
			}
		}
	}
	if len(c) > 0 {
		head = head[:len(head)-len(prefix)]
	}
	return
}

func saveReplHistory(rl *liner.State, filename string) {
	if filename == "" {
		return
	}
	if f, err := os.Create(filename); err == nil {
		rl.WriteHistory(f)
		f.Close()
	}
}

func repl(phase Phase) {
	ProcessReplData()
	GLOBAL_ENV.FindNamespace(MakeSymbol("user")).ReferAll(GLOBAL_ENV.FindNamespace(MakeSymbol("joker.repl")))
	fmt.Printf("Welcome to joker %s. Use '(exit)', EOF (Ctrl-D) or SIGINT (Ctrl-C) to exit.\n", VERSION)
	parseContext := &ParseContext{GlobalEnv: GLOBAL_ENV}
	replContext := NewReplContext(parseContext.GlobalEnv)

	var runeReader io.RuneReader
	var rl *liner.State
	var historyFilename string
	if noReadline {
		runeReader = bufio.NewReader(Stdin)
	} else {
		home := HomeDir()
		jokerd := filepath.Join(home, ".jokerd")
		if _, err := os.Stat(jokerd); os.IsNotExist(err) {
			if err := os.MkdirAll(jokerd, 0777); err != nil {
				fmt.Fprintf(Stderr, "WARNING: could not create %s \n", jokerd)
			}
		}
		if !noReplHistory {
			historyFilename = filepath.Join(jokerd, ".repl_history")
		}
		rl = liner.NewLiner()
		OnExit(func() {
			saveReplHistory(rl, historyFilename)
		})
		defer rl.Close()
		rl.SetCtrlCAborts(true)
		rl.SetWordCompleter(completer)

		if !noReplHistory {
			if f, err := os.Open(historyFilename); err == nil {
				rl.ReadHistory(f)
				f.Close()
			}
		}

		runeReader = NewLineRuneReader(rl)

		for _, line := range strings.Split(string(dataRead), "\n") {
			if strings.TrimSpace(line) != "" {
				rl.AppendHistory(line)
			}
		}
		dataRead = []rune{}
	}

	reader := NewReader(runeReader, "<repl>")

	for {
		if noReadline {
			print(GLOBAL_ENV.CurrentNamespace().Name.ToString(false) + "=> ")
		} else {
			runeReader.(*LineRuneReader).Prompt = (GLOBAL_ENV.CurrentNamespace().Name.ToString(false) + "=> ")
		}
		if processReplCommand(reader, phase, parseContext, replContext) {
			saveReplHistory(rl, historyFilename)
			return
		}
	}
}
<commit_msg>Close liner on (exit).<commit_after>// +build !plan9

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	. "github.com/candid82/joker/core"
	"github.com/candid82/liner"
)

var qualifiedSymbolRe *regexp.Regexp = regexp.MustCompile(`([0-9A-Za-z_\-\+\*\'\.]+)/([0-9A-Za-z_\-\+\*\']*$)`)
var callRe *regexp.Regexp = regexp.MustCompile(`\(\s*([0-9A-Za-z_\-\+\*\'\.]*$)`)

func completer(line string, pos int) (head string, c []string, tail string) {
	head = line[:pos]
	tail = line[pos:]
	var match []string
	var prefix string
	var ns *Namespace
	var addNamespaces bool
	if match = qualifiedSymbolRe.FindStringSubmatch(head); match != nil {
		nsName := match[1]
		prefix = match[2]
		ns = GLOBAL_ENV.NamespaceFor(GLOBAL_ENV.CurrentNamespace(), MakeSymbol(nsName+"/"+prefix))
	} else if match = callRe.FindStringSubmatch(head); match != nil {
		prefix = match[1]
		ns = GLOBAL_ENV.CurrentNamespace()
		addNamespaces = true
	}
	if ns == nil {
		return
	}
	for k, _ := range ns.Mappings() {
		if strings.HasPrefix(*k, prefix) {
			c = append(c, *k)
		}
	}
	if addNamespaces {
		for k, _ := range GLOBAL_ENV.Namespaces {
			if strings.HasPrefix(*k, prefix) {
				c = append(c, *k)
			}
		}
		for k, _ := range ns.Aliases() {
			if strings.HasPrefix(*k, prefix) {
				c = append(c, *k)
			}
		}
	}
	if len(c) > 0 {
		head = head[:len(head)-len(prefix)]
	}
	return
}

func saveReplHistory(rl *liner.State, filename string) {
	if filename == "" {
		return
	}
	if f, err := os.Create(filename); err == nil {
		rl.WriteHistory(f)
		f.Close()
	}
}

func repl(phase Phase) {
	ProcessReplData()
	GLOBAL_ENV.FindNamespace(MakeSymbol("user")).ReferAll(GLOBAL_ENV.FindNamespace(MakeSymbol("joker.repl")))
	fmt.Printf("Welcome to joker %s. Use '(exit)', EOF (Ctrl-D) or SIGINT (Ctrl-C) to exit.\n", VERSION)
	parseContext := &ParseContext{GlobalEnv: GLOBAL_ENV}
	replContext := NewReplContext(parseContext.GlobalEnv)

	var runeReader io.RuneReader
	var rl *liner.State
	var historyFilename string
	if noReadline {
		runeReader = bufio.NewReader(Stdin)
	} else {
		home := HomeDir()
		jokerd := filepath.Join(home, ".jokerd")
		if _, err := os.Stat(jokerd); os.IsNotExist(err) {
			if err := os.MkdirAll(jokerd, 0777); err != nil {
				fmt.Fprintf(Stderr, "WARNING: could not create %s \n", jokerd)
			}
		}
		if !noReplHistory {
			historyFilename = filepath.Join(jokerd, ".repl_history")
		}
		rl = liner.NewLiner()
		OnExit(func() {
			saveReplHistory(rl, historyFilename)
			rl.Close()
		})
		defer rl.Close()
		rl.SetCtrlCAborts(true)
		rl.SetWordCompleter(completer)

		if !noReplHistory {
			if f, err := os.Open(historyFilename); err == nil {
				rl.ReadHistory(f)
				f.Close()
			}
		}

		runeReader = NewLineRuneReader(rl)

		for _, line := range strings.Split(string(dataRead), "\n") {
			if strings.TrimSpace(line) != "" {
				rl.AppendHistory(line)
			}
		}
		dataRead = []rune{}
	}

	reader := NewReader(runeReader, "<repl>")

	for {
		if noReadline {
			print(GLOBAL_ENV.CurrentNamespace().Name.ToString(false) + "=> ")
		} else {
			runeReader.(*LineRuneReader).Prompt = (GLOBAL_ENV.CurrentNamespace().Name.ToString(false) + "=> ")
		}
		if processReplCommand(reader, phase, parseContext, replContext) {
			saveReplHistory(rl, historyFilename)
			return
		}
	}
}
<|endoftext|>


<commit_before>package zoom

import (
	"fmt"
	"strings"
	"time"
)

// Time is a custom Time type that accounts for null values and empty strings // during JSON marshaling and unmarshaling
type Time struct {
	time.Time
}

// UnmarshalJSON describes JSON unmarshaling for custom Time objects, handling
// empty string values
func (t *Time) UnmarshalJSON(b []byte) (err error) {
	s := strings.Trim(string(b), "\"")
	if s == "null" || s == "" {
		t.Time = time.Time{}
		return
	}

	t.Time, err = time.Parse(time.RFC3339, s)
	return
}

// MarshalJSON describes JSON unmarshaling for custom Time objects, handling
// empty string values
func (t *Time) MarshalJSON() ([]byte, error) {
	return []byte(fmt.Sprintf("\"%s\"", t.Time.Format(time.RFC3339))), nil
}

// String defines how time is printed out
func (t *Time) String() string {
	return fmt.Sprintf("\"%s\"", t.Time.Format(time.RFC3339))
}
<commit_msg>Time type string should not include quotes<commit_after>package zoom

import (
	"fmt"
	"strings"
	"time"
)

// Time is a custom Time type that accounts for null values and empty strings // during JSON marshaling and unmarshaling
type Time struct {
	time.Time
}

// UnmarshalJSON describes JSON unmarshaling for custom Time objects, handling
// empty string values
func (t *Time) UnmarshalJSON(b []byte) (err error) {
	s := strings.Trim(string(b), "\"")
	if s == "null" || s == "" {
		t.Time = time.Time{}
		return
	}

	t.Time, err = time.Parse(time.RFC3339, s)
	return
}

// MarshalJSON describes JSON unmarshaling for custom Time objects, handling
// empty string values
func (t *Time) MarshalJSON() ([]byte, error) {
	return []byte(fmt.Sprintf("\"%s\"", t.Time.Format(time.RFC3339))), nil
}

// String defines how time is printed out
func (t *Time) String() string {
	return t.Time.Format(time.RFC3339)
}
<|endoftext|>


<commit_before>package dnsr

var root = `
;       This file holds the information on root name servers needed to
;       initialize cache of Internet domain name servers
;       (e.g. reference this file in the "cache  .  <file>"
;       configuration file of BIND domain name servers).
;
;       This file is made available by InterNIC
;       under anonymous FTP as
;           file                /domain/named.cache
;           on server           FTP.INTERNIC.NET
;       -OR-                    RS.INTERNIC.NET
;
;       last update:     June 29, 2022
;       related version of root zone:     2022062901
;
; FORMERLY NS.INTERNIC.NET
;
.                        3600000      NS    A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET.      3600000      A     198.41.0.4
A.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
.                        3600000      NS    B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET.      3600000      A     199.9.14.201
B.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:200::b
;
; FORMERLY C.PSI.NET
;
.                        3600000      NS    C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET.      3600000      A     192.33.4.12
C.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2::c
;
; FORMERLY TERP.UMD.EDU
;
.                        3600000      NS    D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET.      3600000      A     199.7.91.13
D.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2d::d
;
; FORMERLY NS.NASA.GOV
;
.                        3600000      NS    E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET.      3600000      A     192.203.230.10
E.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:a8::e
;
; FORMERLY NS.ISC.ORG
;
.                        3600000      NS    F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET.      3600000      A     192.5.5.241
F.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2f::f
;
; FORMERLY NS.NIC.DDN.MIL
;
.                        3600000      NS    G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET.      3600000      A     192.112.36.4
G.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:12::d0d
;
; FORMERLY AOS.ARL.ARMY.MIL
;
.                        3600000      NS    H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET.      3600000      A     198.97.190.53
H.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:1::53
;
; FORMERLY NIC.NORDU.NET
;
.                        3600000      NS    I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET.      3600000      A     192.36.148.17
I.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fe::53
;
; OPERATED BY VERISIGN, INC.
;
.                        3600000      NS    J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET.      3600000      A     192.58.128.30
J.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:c27::2:30
;
; OPERATED BY RIPE NCC
;
.                        3600000      NS    K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET.      3600000      A     193.0.14.129
K.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fd::1
;
; OPERATED BY ICANN
;
.                        3600000      NS    L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET.      3600000      A     199.7.83.42
L.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:9f::42
;
; OPERATED BY WIDE
;
.                        3600000      NS    M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET.      3600000      A     202.12.27.33
M.ROOT-SERVERS.NET.      3600000      AAAA  2001:dc3::35
; End of file`
<commit_msg>Automatic update for Thu Jul  7 08:30:27 UTC 2022<commit_after>package dnsr

var root = `
;       This file holds the information on root name servers needed to
;       initialize cache of Internet domain name servers
;       (e.g. reference this file in the "cache  .  <file>"
;       configuration file of BIND domain name servers).
;
;       This file is made available by InterNIC
;       under anonymous FTP as
;           file                /domain/named.cache
;           on server           FTP.INTERNIC.NET
;       -OR-                    RS.INTERNIC.NET
;
;       last update:     July 06, 2022
;       related version of root zone:     2022070601
;
; FORMERLY NS.INTERNIC.NET
;
.                        3600000      NS    A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET.      3600000      A     198.41.0.4
A.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
.                        3600000      NS    B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET.      3600000      A     199.9.14.201
B.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:200::b
;
; FORMERLY C.PSI.NET
;
.                        3600000      NS    C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET.      3600000      A     192.33.4.12
C.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2::c
;
; FORMERLY TERP.UMD.EDU
;
.                        3600000      NS    D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET.      3600000      A     199.7.91.13
D.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2d::d
;
; FORMERLY NS.NASA.GOV
;
.                        3600000      NS    E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET.      3600000      A     192.203.230.10
E.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:a8::e
;
; FORMERLY NS.ISC.ORG
;
.                        3600000      NS    F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET.      3600000      A     192.5.5.241
F.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2f::f
;
; FORMERLY NS.NIC.DDN.MIL
;
.                        3600000      NS    G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET.      3600000      A     192.112.36.4
G.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:12::d0d
;
; FORMERLY AOS.ARL.ARMY.MIL
;
.                        3600000      NS    H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET.      3600000      A     198.97.190.53
H.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:1::53
;
; FORMERLY NIC.NORDU.NET
;
.                        3600000      NS    I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET.      3600000      A     192.36.148.17
I.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fe::53
;
; OPERATED BY VERISIGN, INC.
;
.                        3600000      NS    J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET.      3600000      A     192.58.128.30
J.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:c27::2:30
;
; OPERATED BY RIPE NCC
;
.                        3600000      NS    K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET.      3600000      A     193.0.14.129
K.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fd::1
;
; OPERATED BY ICANN
;
.                        3600000      NS    L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET.      3600000      A     199.7.83.42
L.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:9f::42
;
; OPERATED BY WIDE
;
.                        3600000      NS    M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET.      3600000      A     202.12.27.33
M.ROOT-SERVERS.NET.      3600000      AAAA  2001:dc3::35
; End of file`
<|endoftext|>


<commit_before>package dnsr

var root = `
;       This file holds the information on root name servers needed to
;       initialize cache of Internet domain name servers
;       (e.g. reference this file in the "cache  .  <file>"
;       configuration file of BIND domain name servers).
;
;       This file is made available by InterNIC
;       under anonymous FTP as
;           file                /domain/named.cache
;           on server           FTP.INTERNIC.NET
;       -OR-                    RS.INTERNIC.NET
;
;       last update:     March 17, 2021
;       related version of root zone:     2021031701
;
; FORMERLY NS.INTERNIC.NET
;
.                        3600000      NS    A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET.      3600000      A     198.41.0.4
A.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
.                        3600000      NS    B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET.      3600000      A     199.9.14.201
B.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:200::b
;
; FORMERLY C.PSI.NET
;
.                        3600000      NS    C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET.      3600000      A     192.33.4.12
C.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2::c
;
; FORMERLY TERP.UMD.EDU
;
.                        3600000      NS    D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET.      3600000      A     199.7.91.13
D.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2d::d
;
; FORMERLY NS.NASA.GOV
;
.                        3600000      NS    E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET.      3600000      A     192.203.230.10
E.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:a8::e
;
; FORMERLY NS.ISC.ORG
;
.                        3600000      NS    F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET.      3600000      A     192.5.5.241
F.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2f::f
;
; FORMERLY NS.NIC.DDN.MIL
;
.                        3600000      NS    G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET.      3600000      A     192.112.36.4
G.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:12::d0d
;
; FORMERLY AOS.ARL.ARMY.MIL
;
.                        3600000      NS    H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET.      3600000      A     198.97.190.53
H.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:1::53
;
; FORMERLY NIC.NORDU.NET
;
.                        3600000      NS    I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET.      3600000      A     192.36.148.17
I.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fe::53
;
; OPERATED BY VERISIGN, INC.
;
.                        3600000      NS    J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET.      3600000      A     192.58.128.30
J.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:c27::2:30
;
; OPERATED BY RIPE NCC
;
.                        3600000      NS    K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET.      3600000      A     193.0.14.129
K.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fd::1
;
; OPERATED BY ICANN
;
.                        3600000      NS    L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET.      3600000      A     199.7.83.42
L.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:9f::42
;
; OPERATED BY WIDE
;
.                        3600000      NS    M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET.      3600000      A     202.12.27.33
M.ROOT-SERVERS.NET.      3600000      AAAA  2001:dc3::35
; End of file`
<commit_msg>Automatic update for Thu Apr 29 08:21:24 UTC 2021<commit_after>package dnsr

var root = `
;       This file holds the information on root name servers needed to
;       initialize cache of Internet domain name servers
;       (e.g. reference this file in the "cache  .  <file>"
;       configuration file of BIND domain name servers).
;
;       This file is made available by InterNIC
;       under anonymous FTP as
;           file                /domain/named.cache
;           on server           FTP.INTERNIC.NET
;       -OR-                    RS.INTERNIC.NET
;
;       last update:     April 28, 2021
;       related version of root zone:     2021042801
;
; FORMERLY NS.INTERNIC.NET
;
.                        3600000      NS    A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET.      3600000      A     198.41.0.4
A.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
.                        3600000      NS    B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET.      3600000      A     199.9.14.201
B.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:200::b
;
; FORMERLY C.PSI.NET
;
.                        3600000      NS    C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET.      3600000      A     192.33.4.12
C.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2::c
;
; FORMERLY TERP.UMD.EDU
;
.                        3600000      NS    D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET.      3600000      A     199.7.91.13
D.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2d::d
;
; FORMERLY NS.NASA.GOV
;
.                        3600000      NS    E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET.      3600000      A     192.203.230.10
E.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:a8::e
;
; FORMERLY NS.ISC.ORG
;
.                        3600000      NS    F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET.      3600000      A     192.5.5.241
F.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2f::f
;
; FORMERLY NS.NIC.DDN.MIL
;
.                        3600000      NS    G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET.      3600000      A     192.112.36.4
G.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:12::d0d
;
; FORMERLY AOS.ARL.ARMY.MIL
;
.                        3600000      NS    H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET.      3600000      A     198.97.190.53
H.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:1::53
;
; FORMERLY NIC.NORDU.NET
;
.                        3600000      NS    I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET.      3600000      A     192.36.148.17
I.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fe::53
;
; OPERATED BY VERISIGN, INC.
;
.                        3600000      NS    J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET.      3600000      A     192.58.128.30
J.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:c27::2:30
;
; OPERATED BY RIPE NCC
;
.                        3600000      NS    K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET.      3600000      A     193.0.14.129
K.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fd::1
;
; OPERATED BY ICANN
;
.                        3600000      NS    L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET.      3600000      A     199.7.83.42
L.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:9f::42
;
; OPERATED BY WIDE
;
.                        3600000      NS    M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET.      3600000      A     202.12.27.33
M.ROOT-SERVERS.NET.      3600000      AAAA  2001:dc3::35
; End of file`
<|endoftext|>


<commit_before>// Copyright (C) 2017 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

type templateParams map[string]interface{}

type fileParams struct {
	filename string
	params   templateParams
}

type pathnameTemplateMultiplier struct {
	paramName    string
	paramValues  []string
	continuation pathnameTemplateText
}

type pathnameTemplateText struct {
	text string
	next *pathnameTemplateMultiplier
}

// Subst updates the 'pathnameTemplateText' receiver by replacing all
// instances of 'name' surrounded by braces with 'value', which can be
// either a string or a slice of strings.  In the latter case, the text
// in the receiver structure gets truncated by the substitution and the
// receiver structure gets extended by a new pathnameTemplateMultiplier
// structure.  Subst returns the number of substitution values.
func (t *pathnameTemplateText) subst(name string, value interface{}) int {
	if textValue, ok := value.(string); ok {
		t.text = strings.Replace(t.text, "{"+name+"}",
			textValue, -1)
	} else if arrayValue, ok := value.([]string); !ok {
		t.text = strings.Replace(t.text, "{"+name+"}",
			fmt.Sprint(value), -1)
	} else if pos := strings.Index(t.text, "{"+name+"}"); pos >= 0 {
		t.next = &pathnameTemplateMultiplier{name, arrayValue,
			pathnameTemplateText{t.text[pos+len(name)+2:], t.next}}
		t.text = t.text[:pos]
		return len(arrayValue)
	}

	return 1
}

// ExpandPathnameTemplate takes a pathname template and substitutes
// template parameter names with their values. Parameter values can be
// either strings or slices of strings. Each template value that is a
// slice of strings multiplies the number of output strings by the number
// of strings in the slice.
func expandPathnameTemplate(pathname string,
	params templateParams) []fileParams {
	root := pathnameTemplateText{pathname, nil}

	resultSize := 1

	for name, value := range params {
		resultSize *= root.subst(name, value)

		for n := root.next; n != nil; n = n.continuation.next {
			resultSize *= n.continuation.subst(name, value)
		}
	}

	result := make([]fileParams, resultSize)

	for i := 0; i < resultSize; i++ {
		result[i].filename = root.text
		copyOfParams := templateParams{}
		for name, value := range params {
			copyOfParams[name] = value
		}
		result[i].params = copyOfParams
	}

	if resultSize == 0 {
		return result
	}

	sliceSize := 1

	for a := root.next; a != nil; a = a.continuation.next {
		numberOfValues := len(a.paramValues)

		continuationText := a.continuation.text

		for i := 0; i < resultSize; {
			for j := 0; j < numberOfValues; j++ {
				value := a.paramValues[j]
				filenameFragment := value + continuationText
				for k := 0; k < sliceSize; k++ {
					result[i].filename += filenameFragment
					result[i].params[a.paramName] = value
					i++
				}
			}
		}

		sliceSize *= numberOfValues
	}

	return result
}

func must(err error) {
	if err != nil {
		panic(err)
	}
}

func varName(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' ||
			r >= '0' && r <= '9' {
			return r
		} else if r == '+' {
			return 'x'
		}
		return '_'
	}, arg)
}

func varNameUC(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'a' && r <= 'z' {
			return r - 'a' + 'A'
		} else if r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' {
			return r
		} else if r == '+' {
			return 'X'
		}
		return '_'
	}, arg)
}

func libName(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' ||
			r >= '0' && r <= '9' ||
			r == '+' || r == '-' || r == '.' {
			return r
		}
		return '_'
	}, arg)
}

var funcMap = template.FuncMap{
	"VarName":   varName,
	"VarNameUC": varNameUC,
	"LibName":   libName,
}

func generateFileFromTemplate(projectDir, templatePathname string,
	templateContents []byte, templateFileMode os.FileMode,
	params templateParams) error {
	// Parse the template file. The parsed template will be
	// reused multiple times if expandPathnameTemplate()
	// returns more than one pathname expansion.
	t, err := template.New(filepath.Base(templatePathname)).Funcs(
		funcMap).Parse(string(templateContents))
	if err != nil {
		return err
	}

	for _, fp := range expandPathnameTemplate(templatePathname, params) {

		projectFile := filepath.Join(projectDir, fp.filename)

		if err = os.MkdirAll(filepath.Dir(projectFile),
			os.ModePerm); err != nil {
			return err
		}

		buffer := bytes.NewBufferString("")

		if err = t.Execute(buffer, fp.params); err != nil {
			return err
		}

		newContents := buffer.Bytes()

		oldContents, err := ioutil.ReadFile(projectFile)

		if os.IsNotExist(err) {
			fmt.Println("A", projectFile)
		} else if bytes.Compare(oldContents, newContents) != 0 {
			fmt.Println("U", projectFile)
		} else {
			return nil
		}

		if err = ioutil.WriteFile(projectFile, newContents,
			templateFileMode); err != nil {
			return err
		}
	}

	return nil
}

// GetTemplateWalkFunc returns a walker function for use with filepath.Walk().
// The returned function interprets each file it visits as a 'text/template'
// file and generates a new file with the same relative pathname in the output
// directory 'projectDir'.
func getTemplateWalkFunc(templateDir, projectDir string,
	params templateParams) filepath.WalkFunc {

	templateDir = filepath.Clean(templateDir) + string(filepath.Separator)

	return func(templateFile string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		// Panic if filepath.Walk() does not behave as expected.
		if !strings.HasPrefix(templateFile, templateDir) {
			panic(templateFile + " does not start with " +
				templateDir)
		}

		// Read the contents of the template file. Cannot use
		// template.ParseFiles() because a Funcs() call must be
		// made between New() and Parse().
		templateContents, err := ioutil.ReadFile(templateFile)
		if err != nil {
			return err
		}

		// Pathname of the template file relative to the
		// template directory.
		templateFile = templateFile[len(templateDir):]

		// Ignore package definition file for the template.
		if templateFile == packageDefinitionFilename {
			return nil
		}

		if err = generateFileFromTemplate(projectDir, templateFile,
			templateContents, info.Mode(), params); err != nil {
			return err
		}

		return nil
	}
}

// For each source file in 'templateDir', generateBuildFilesFromProjectTemplate
// generates an output file with the same relative pathname inside 'projectDir'.
func generateBuildFilesFromProjectTemplate(templateDir,
	projectDir string, pd *packageDefinition) error {

	if err := filepath.Walk(templateDir, getTemplateWalkFunc(templateDir,
		projectDir, pd.params)); err != nil {
		return err
	}

	return nil
}

// EmbeddedTemplateFile defines the file mode and the contents
// of a single file that is a part of an embedded project template.
type embeddedTemplateFile struct {
	mode     os.FileMode
	contents []byte
}

// EmbeddedTemplate defines a build-in project template.
type embeddedTemplate map[string]embeddedTemplateFile

// GenerateBuildFilesFromEmbeddedTemplate generates project build
// files from a built-in template pointed to by the 'template' parameter.
func generateBuildFilesFromEmbeddedTemplate(template *embeddedTemplate,
	projectDir string, pd *packageDefinition) error {
	for pathname, fileInfo := range *template {
		if err := generateFileFromTemplate(projectDir, pathname,
			fileInfo.contents, fileInfo.mode,
			pd.params); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Prepare for symlinking in template generation<commit_after>// Copyright (C) 2017 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

type templateParams map[string]interface{}

type fileParams struct {
	filename string
	params   templateParams
}

type pathnameTemplateMultiplier struct {
	paramName    string
	paramValues  []string
	continuation pathnameTemplateText
}

type pathnameTemplateText struct {
	text string
	next *pathnameTemplateMultiplier
}

// Subst updates the 'pathnameTemplateText' receiver by replacing all
// instances of 'name' surrounded by braces with 'value', which can be
// either a string or a slice of strings.  In the latter case, the text
// in the receiver structure gets truncated by the substitution and the
// receiver structure gets extended by a new pathnameTemplateMultiplier
// structure.  Subst returns the number of substitution values.
func (t *pathnameTemplateText) subst(name string, value interface{}) int {
	if textValue, ok := value.(string); ok {
		t.text = strings.Replace(t.text, "{"+name+"}",
			textValue, -1)
	} else if arrayValue, ok := value.([]string); !ok {
		t.text = strings.Replace(t.text, "{"+name+"}",
			fmt.Sprint(value), -1)
	} else if pos := strings.Index(t.text, "{"+name+"}"); pos >= 0 {
		t.next = &pathnameTemplateMultiplier{name, arrayValue,
			pathnameTemplateText{t.text[pos+len(name)+2:], t.next}}
		t.text = t.text[:pos]
		return len(arrayValue)
	}

	return 1
}

// ExpandPathnameTemplate takes a pathname template and substitutes
// template parameter names with their values. Parameter values can be
// either strings or slices of strings. Each template value that is a
// slice of strings multiplies the number of output strings by the number
// of strings in the slice.
func expandPathnameTemplate(pathname string,
	params templateParams) []fileParams {
	root := pathnameTemplateText{pathname, nil}

	resultSize := 1

	for name, value := range params {
		resultSize *= root.subst(name, value)

		for n := root.next; n != nil; n = n.continuation.next {
			resultSize *= n.continuation.subst(name, value)
		}
	}

	result := make([]fileParams, resultSize)

	for i := 0; i < resultSize; i++ {
		result[i].filename = root.text
		copyOfParams := templateParams{}
		for name, value := range params {
			copyOfParams[name] = value
		}
		result[i].params = copyOfParams
	}

	if resultSize == 0 {
		return result
	}

	sliceSize := 1

	for a := root.next; a != nil; a = a.continuation.next {
		numberOfValues := len(a.paramValues)

		continuationText := a.continuation.text

		for i := 0; i < resultSize; {
			for j := 0; j < numberOfValues; j++ {
				value := a.paramValues[j]
				filenameFragment := value + continuationText
				for k := 0; k < sliceSize; k++ {
					result[i].filename += filenameFragment
					result[i].params[a.paramName] = value
					i++
				}
			}
		}

		sliceSize *= numberOfValues
	}

	return result
}

func must(err error) {
	if err != nil {
		panic(err)
	}
}

func varName(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' ||
			r >= '0' && r <= '9' {
			return r
		} else if r == '+' {
			return 'x'
		}
		return '_'
	}, arg)
}

func varNameUC(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'a' && r <= 'z' {
			return r - 'a' + 'A'
		} else if r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' {
			return r
		} else if r == '+' {
			return 'X'
		}
		return '_'
	}, arg)
}

func libName(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' ||
			r >= '0' && r <= '9' ||
			r == '+' || r == '-' || r == '.' {
			return r
		}
		return '_'
	}, arg)
}

var funcMap = template.FuncMap{
	"VarName":   varName,
	"VarNameUC": varNameUC,
	"LibName":   libName,
}

func generateFileFromTemplate(projectDir, templatePathname string,
	templateContents []byte, templateFileMode os.FileMode,
	params templateParams) error {
	// Parse the template file. The parsed template will be
	// reused multiple times if expandPathnameTemplate()
	// returns more than one pathname expansion.
	t, err := template.New(filepath.Base(templatePathname)).Funcs(
		funcMap).Parse(string(templateContents))
	if err != nil {
		return err
	}

	for _, fp := range expandPathnameTemplate(templatePathname, params) {

		projectFile := filepath.Join(projectDir, fp.filename)

		if err = os.MkdirAll(filepath.Dir(projectFile),
			os.ModePerm); err != nil {
			return err
		}

		buffer := bytes.NewBufferString("")

		if err = t.Execute(buffer, fp.params); err != nil {
			return err
		}

		newContents := buffer.Bytes()

		oldContents, err := ioutil.ReadFile(projectFile)

		if os.IsNotExist(err) {
			fmt.Println("A", projectFile)
		} else if bytes.Compare(oldContents, newContents) != 0 {
			fmt.Println("U", projectFile)
		} else {
			return nil
		}

		if err = ioutil.WriteFile(projectFile, newContents,
			templateFileMode); err != nil {
			return err
		}
	}

	return nil
}

type fileProcessor func(sourcePathname, relativePathname string,
	sourceFileInfo os.FileInfo) error

func getFileGenerationWalkFunc(sourceDir, targetDir string,
	processFile fileProcessor) filepath.WalkFunc {

	sourceDir = filepath.Clean(sourceDir) + string(filepath.Separator)

	return func(sourcePathname string, sourceFileInfo os.FileInfo,
		err error) error {
		if err != nil {
			return err
		}

		if sourceFileInfo.IsDir() {
			return nil
		}

		// Panic if filepath.Walk() does not behave as expected.
		if !strings.HasPrefix(sourcePathname, sourceDir) {
			panic(sourcePathname + " does not start with " +
				sourceDir)
		}

		// Relative pathname of the source file in the source
		// directory (and the target file in the target directory).
		relativePathname := sourcePathname[len(sourceDir):]

		// Ignore the package definition file.
		if relativePathname == packageDefinitionFilename {
			return nil
		}

		return processFile(sourcePathname, relativePathname,
			sourceFileInfo)
	}
}

// GetTemplateWalkFunc returns a walker function for use with filepath.Walk().
// The returned function interprets each file it visits as a 'text/template'
// file and generates a new file with the same relative pathname in the output
// directory 'projectDir'.
func getTemplateWalkFunc(templateDir, projectDir string,
	params templateParams) filepath.WalkFunc {

	templateDir = filepath.Clean(templateDir) + string(filepath.Separator)

	return func(templateFile string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		// Panic if filepath.Walk() does not behave as expected.
		if !strings.HasPrefix(templateFile, templateDir) {
			panic(templateFile + " does not start with " +
				templateDir)
		}

		// Read the contents of the template file. Cannot use
		// template.ParseFiles() because a Funcs() call must be
		// made between New() and Parse().
		templateContents, err := ioutil.ReadFile(templateFile)
		if err != nil {
			return err
		}

		// Pathname of the template file relative to the
		// template directory.
		templateFile = templateFile[len(templateDir):]

		// Ignore package definition file for the template.
		if templateFile == packageDefinitionFilename {
			return nil
		}

		if err = generateFileFromTemplate(projectDir, templateFile,
			templateContents, info.Mode(), params); err != nil {
			return err
		}

		return nil
	}
}

var fileLinker = func(sourcePathname, relativePathname string,
	sourceFileInfo os.FileInfo) error {
	fmt.Println("L", sourcePathname, relativePathname)
	return nil
}

// For each source file in 'templateDir', generateBuildFilesFromProjectTemplate
// generates an output file with the same relative pathname inside 'projectDir'.
func generateBuildFilesFromProjectTemplate(templateDir,
	projectDir string, pd *packageDefinition) error {

	sourceDir := filepath.Dir(pd.pathname)

	if err := filepath.Walk(sourceDir, getFileGenerationWalkFunc(sourceDir,
		projectDir, fileLinker)); err != nil {
		return err
	}

	if err := filepath.Walk(templateDir, getTemplateWalkFunc(templateDir,
		projectDir, pd.params)); err != nil {
		return err
	}

	return nil
}

// EmbeddedTemplateFile defines the file mode and the contents
// of a single file that is a part of an embedded project template.
type embeddedTemplateFile struct {
	mode     os.FileMode
	contents []byte
}

// EmbeddedTemplate defines a build-in project template.
type embeddedTemplate map[string]embeddedTemplateFile

// GenerateBuildFilesFromEmbeddedTemplate generates project build
// files from a built-in template pointed to by the 'template' parameter.
func generateBuildFilesFromEmbeddedTemplate(template *embeddedTemplate,
	projectDir string, pd *packageDefinition) error {

	sourceDir := filepath.Dir(pd.pathname)

	if err := filepath.Walk(sourceDir, getFileGenerationWalkFunc(sourceDir,
		projectDir, fileLinker)); err != nil {
		return err
	}

	for pathname, fileInfo := range *template {
		if err := generateFileFromTemplate(projectDir, pathname,
			fileInfo.contents, fileInfo.mode,
			pd.params); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package parse

// Copyright © 2017 Dmitry Moskowski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import (
	"fmt"
	"sort"
	"strings"
)

const (
	newLine   = "\n"
	space     = " "
	delimiter = ", "
)

// Rule represents a general Rule interface.
type Rule interface {
	Treer
	// Parameters returns a KV rule parameters.
	GetParameters() map[string]interface{}

	// String returns rule as a string,
	// resolving recursion with `<circular>` placeholder.
	String() string
}

const (
	circularLabel = "<circular>"
)

// RuleFormatter is a configurable rule pretty
// printer.
type RuleFormatter struct {
	Pretty     bool
	Spaces     int
	MaxLineLen int
}

func (rs *RuleFormatter) indent(s string) string {
	if rs.Pretty {
		indent := strings.Repeat(space, rs.Spaces)
		lines := strings.Split(s, newLine)
		for k, v := range lines {
			lines[k] = indent + v
		}
		return strings.Join(lines, newLine)
	}
	return s
}

func (rs *RuleFormatter) single(rule Rule, depth int, childs string) string {
	var (
		parameters = rule.GetParameters()
		paramKeys  = []string{}
		params     = ""
	)
	for k, _ := range parameters {
		paramKeys = append(
			paramKeys,
			k,
		)
	}
	sort.Strings(paramKeys)
	for k, v := range paramKeys {
		if k > 0 {
			params += delimiter
		}
		params += fmt.Sprintf(
			"%s: %v",
			v,
			parameters[v],
		)
	}

	if rs.Pretty && len(childs) > 0 && len(childs) > rs.MaxLineLen {
		childs = newLine + rs.indent(childs) + newLine
	}
	return fmt.Sprintf(
		"%T(%s)(%s)",
		rule,
		params,
		childs,
	)
}

func (rs *RuleFormatter) format(visited map[Rule]bool, depth int, rule Rule) string {
	var (
		child string
	)

	if _, ok := visited[rule]; ok {
		child = circularLabel
	} else {
		visited[rule] = true

		childs := rule.GetChilds()
		if len(childs) > 0 {
			for k, v := range childs {
				if k > 0 {
					child += delimiter
					if rs.Pretty {
						child += newLine
					}
				}
				child += rs.format(
					visited,
					depth+1,
					v.(Rule),
				)
			}
		}
	}

	if depth == 0 {
		return rs.single(
			rule,
			depth,
			child,
		)
	}
	return rs.indent(
		rs.single(
			rule,
			depth,
			child,
		),
	)
}

// Format the Rule as string.
func (rs *RuleFormatter) Format(rule Rule) string {
	return rs.format(map[Rule]bool{}, 0, rule)
}

// RulesString folds a nested(maybe circular)
// rule into a string representation.
func RuleString(rule Rule) string {
	return (&RuleFormatter{}).
		Format(rule)
}

func RulePrettyString(rule Rule) string {
	return (&RuleFormatter{true, 2, 80}).
		Format(rule)
}
<commit_msg>Ignore rules with nil values<commit_after>package parse

// Copyright © 2017 Dmitry Moskowski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import (
	"fmt"
	"sort"
	"strings"
)

const (
	newLine   = "\n"
	space     = " "
	delimiter = ", "
)

// Rule represents a general Rule interface.
type Rule interface {
	Treer
	// Parameters returns a KV rule parameters.
	GetParameters() map[string]interface{}

	// String returns rule as a string,
	// resolving recursion with `<circular>` placeholder.
	String() string
}

const (
	circularLabel = "<circular>"
	nilLabel      = "<nil>"
)

// RuleFormatter is a configurable rule pretty
// printer.
type RuleFormatter struct {
	Pretty     bool
	Spaces     int
	MaxLineLen int
}

func (rs *RuleFormatter) indent(s string) string {
	if rs.Pretty {
		indent := strings.Repeat(space, rs.Spaces)
		lines := strings.Split(s, newLine)
		for k, v := range lines {
			lines[k] = indent + v
		}
		return strings.Join(lines, newLine)
	}
	return s
}

func (rs *RuleFormatter) single(rule Rule, depth int, childs string) string {
	var (
		parameters = rule.GetParameters()
		paramKeys  = []string{}
		params     = ""
	)
	for k, _ := range parameters {
		paramKeys = append(
			paramKeys,
			k,
		)
	}
	sort.Strings(paramKeys)
	for k, v := range paramKeys {
		if k > 0 {
			params += delimiter
		}
		params += fmt.Sprintf(
			"%s: %v",
			v,
			parameters[v],
		)
	}

	if rs.Pretty && len(childs) > 0 && len(childs) > rs.MaxLineLen {
		childs = newLine + rs.indent(childs) + newLine
	}
	return fmt.Sprintf(
		"%T(%s)(%s)",
		rule,
		params,
		childs,
	)
}

func (rs *RuleFormatter) format(visited map[Rule]bool, depth int, rule Rule) string {
	var (
		child string
	)

	if _, ok := visited[rule]; ok {
		child = circularLabel
	} else {
		visited[rule] = true

		childs := rule.GetChilds()
		if len(childs) > 0 {
			for k, v := range childs {
				if k > 0 {
					child += delimiter
					if rs.Pretty {
						child += newLine
					}
				}
				if v == nil {
					child += nilLabel
					continue
				}
				child += rs.format(
					visited,
					depth+1,
					v.(Rule),
				)
			}
		}
	}

	if depth == 0 {
		return rs.single(
			rule,
			depth,
			child,
		)
	}
	return rs.indent(
		rs.single(
			rule,
			depth,
			child,
		),
	)
}

// Format the Rule as string.
func (rs *RuleFormatter) Format(rule Rule) string {
	return rs.format(map[Rule]bool{}, 0, rule)
}

// RulesString folds a nested(maybe circular)
// rule into a string representation.
func RuleString(rule Rule) string {
	return (&RuleFormatter{}).
		Format(rule)
}

func RulePrettyString(rule Rule) string {
	return (&RuleFormatter{true, 2, 80}).
		Format(rule)
}
<|endoftext|>


<commit_before>package todo

import (
	"database/sql"
	"html"
	"html/template"
	"log"
	"net/http"
	"time"

	"github.com/dchest/uniuri"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"github.com/gorilla/securecookie"
	"golang.org/x/crypto/bcrypt"
)

const (
	PORT     = ":8080"
	LENGTH   = 12
	USERNAME = "root"
	// PASS database password
	PASS = ""
	// NAME database name
	NAME = ""
	// DATABASE connection String
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

var templates = template.Must(template.ParseFiles("static/index.html", "static/login.html", "static/register.html", "static/todo.html", "static/edit.html", "static/add.html"))

// generate new random cookie keys
var cookieHandler = securecookie.New(
	securecookie.GenerateRandomKey(64),
	securecookie.GenerateRandomKey(32),
)

type User struct {
	ID       int
	Email    string
	Password string
}

type Tasks struct {
	Name      string `json:"name"`
	Title     string `json:"title"`
	Task      string `json:"task"`
	Created   string `json:"created"`
	DueDate   string `json:"duedate"`
	Email     string `json:"email"`
	Completed bool   `json:"completed"`
	Public    bool   `json:"public"`
}

type Page struct {
	Tasks []Tasks `json:"tasks"`
}

func checkErr(err error) {
	if err != nil {
		log.Println(err)
	}
}

func genName() string {
	name := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	_, err = db.Query("select name from tasks where name=?", name)
	db.Close()
	if err == sql.ErrNoRows {
		genName()
	}
	checkErr(err)
	return name
}

func loggedIn(r *http.Request) bool {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return false
	}
	err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return false
	}
	return true

}

func getEmail(r *http.Request) (string, error) {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return "", err
	}
	err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return "", err
	}
	return cookieValue["email"], nil

}

func rootHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	email, err := getEmail(r)
	checkErr(err)

	rows, err := db.Query("select name, title, task, created, duedate from tasks where email=? order by duedate asc", email)
	checkErr(err)

	b := Page{Tasks: []Tasks{}}

	for rows.Next() {
		res := Tasks{}
		rows.Scan(&res.Name, &res.Title, &res.Task, &res.Created, &res.DueDate)

		b.Tasks = append(b.Tasks, res)
	}
	db.Close()

	checkErr(err)

	err = templates.ExecuteTemplate(w, "index.html", &b)
	checkErr(err)

}

func todoHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	todo := vars["id"]

	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	rows, err := db.Query("select public from tasks where name=?", html.EscapeString(todo))
	checkErr(err)
	var public bool
	for rows.Next() {
		rows.Scan(&public)
	}

	if loggedIn(r) != true && public == false {
		http.Redirect(w, r, "/login", 302)
	}
	p := Tasks{}
	query, err := db.Query("select title, task, duedate, created, completed from tasks where name=?", html.EscapeString(todo))
	checkErr(err)

	for query.Next() {
		query.Scan(&p.Title, &p.Task, &p.DueDate, &p.Created, &p.Completed)
	}

	err = templates.ExecuteTemplate(w, "todo.html", &p)
	checkErr(err)

}

func addHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "add.html", "")
		checkErr(err)

	case "POST":
		title := r.FormValue("title")
		task := r.FormValue("task")
		duedate := r.FormValue("duedate")
		public := r.FormValue("public")
		name := genName()
		email, err := getEmail(r)
		checkErr(err)

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)
		defer db.Close()

		query, err := db.Prepare("insert into tasks(name, title, task, duedate, created, email, completed, public) values(?, ?, ?, ?, ?, ?, ?, ?)")
		_, err = query.Exec(name, html.EscapeString(title), html.EscapeString(task), html.EscapeString(duedate), time.Now().Format("2016-02-01 15:12:52"), email, false, html.EscapeString(public))
		checkErr(err)

	}

}

func editHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	switch r.Method {
	case "GET":
		b := Tasks{}
		email, err := getEmail(r)
		checkErr(err)

		rows, err := db.Query("select name, title, task, duedate, created, email, completed, public from tasks where email=? and name=?", email, html.EscapeString(todo))
		if err == sql.ErrNoRows {
			http.Redirect(w, r, "/", 302)
		}
		for rows.Next() {
			rows.Scan(&b.Name, &b.Title, &b.Task, &b.DueDate, &b.Created, &b.Email, &b.Completed, &b.Public)
		}

		err = templates.ExecuteTemplate(w, "edit.html", &b)
		checkErr(err)

	case "POST", "PUT":
		title := r.FormValue("title")
		task := r.FormValue("task")
		duedate := r.FormValue("duedate")
		public := r.FormValue("public")

		query, err := db.Prepare("update tasks set title=?, task=?, duedate=?, public=?")
		checkErr(err)
		_, err = query.Exec(html.EscapeString(title), html.EscapeString(task), html.EscapeString(duedate), html.EscapeString(public))
		checkErr(err)

	}

}

func delHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

	switch r.Method {
	case "POST", "DEL":
		vars := mux.Vars(r)
		todo := vars["id"]

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)
		defer db.Close()

		email, err := getEmail(r)
		checkErr(err)

		_, err = db.Query("delete from tasks where name=? and email=?", html.EscapeString(todo), email)
	}

}

func finishHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	email, err := getEmail(r)
	checkErr(err)
	_, err = db.Query("update tasks set completed=true where email=? and name=?", email, html.EscapeString(todo))

}

func userHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

}

func userDelHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "deluser.html", "")
		checkErr(err)
	case "POST", "DEL":
		pass := r.FormValue("password")

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		query, err := db.Prepare("delete from users where email=? and password=?")
		checkErr(err)

		email, err := getEmail(r)
		checkErr(err)
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
		checkErr(err)

		_, err = query.Exec(email, hashedPassword)
		checkErr(err)

	}

}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "login.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		password := r.FormValue("password")
		// open db connection
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		// declare variables for database results
		var hashedPassword []byte
		// read hashedPassword, name and level into variables
		err = db.QueryRow("select password from users where email=?", html.EscapeString(email)).Scan(&hashedPassword)
		if err == sql.ErrNoRows {
			http.Redirect(w, r, "/login", 303)
			return
		}
		checkErr(err)

		// compare bcrypt hash to userinput password
		err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
		if err == nil {
			// prepare cookie
			value := map[string]string{
				"email": email,
			}
			// encode variables into cookie
			if encoded, err := cookieHandler.Encode("session", value); err == nil {
				cookie := &http.Cookie{
					Name:  "session",
					Value: encoded,
					Path:  "/",
				}
				// set user cookie
				http.SetCookie(w, cookie)
			}
			// Redirect to home page
			http.Redirect(w, r, "/", 302)
		}
		// Redirect to login page
		http.Redirect(w, r, "/login", 302)

	}

}

func registerHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "register.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		pass := r.FormValue("password")
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()
		//_, err = db.Query("select email from users where email=?", html.EscapeString(email))
		//checkErr(err)
		//if err == sql.ErrNoRows {
		query, err := db.Prepare("INSERT into users(email, password) values(?, ?)")
		checkErr(err)
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
		checkErr(err)

		_, err = query.Exec(html.EscapeString(email), hashedPassword)
		checkErr(err)
		http.Redirect(w, r, "/login", 302)

		//}
		http.Redirect(w, r, "/register", 302)

	}

}

func logoutHandler(w http.ResponseWriter, r *http.Request) {
	cookie := &http.Cookie{
		Name:   "session",
		Value:  "",
		Path:   "/",
		MaxAge: -1,
	}
	http.SetCookie(w, cookie)
	http.Redirect(w, r, "/", 301)

}

func resetHandler(w http.ResponseWriter, r *http.Request) {

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/", rootHandler)

	//	router.HandleFunc("/todo", todoHandler)
	router.HandleFunc("/todo/add", addHandler)
	//	router.HandleFunc("/todo/{id}", todoHandler)
	router.HandleFunc("/todo/edit/{id}", editHandler)
	router.HandleFunc("/todo/del/{id}", delHandler)

	router.HandleFunc("/todo/finish/{id}", finishHandler)

	router.HandleFunc("/user", userHandler)
	router.HandleFunc("/user/del", userDelHandler)

	router.HandleFunc("/register", registerHandler)
	router.HandleFunc("/login", loginHandler)
	router.HandleFunc("/logout", logoutHandler)
	router.HandleFunc("/resetpass", resetHandler)
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<commit_msg>Fix up url handlers<commit_after>package todo

import (
	"database/sql"
	"html"
	"html/template"
	"log"
	"net/http"
	"time"

	"github.com/dchest/uniuri"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"github.com/gorilla/securecookie"
	"golang.org/x/crypto/bcrypt"
)

const (
	PORT     = ":8080"
	LENGTH   = 12
	USERNAME = "root"
	// PASS database password
	PASS = ""
	// NAME database name
	NAME = ""
	// DATABASE connection String
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

var templates = template.Must(template.ParseFiles("static/index.html", "static/login.html", "static/register.html", "static/todo.html", "static/edit.html", "static/add.html"))

// generate new random cookie keys
var cookieHandler = securecookie.New(
	securecookie.GenerateRandomKey(64),
	securecookie.GenerateRandomKey(32),
)

type User struct {
	ID       int
	Email    string
	Password string
}

type Tasks struct {
	Name      string `json:"name"`
	Title     string `json:"title"`
	Task      string `json:"task"`
	Created   string `json:"created"`
	DueDate   string `json:"duedate"`
	Email     string `json:"email"`
	Completed bool   `json:"completed"`
	Public    bool   `json:"public"`
}

type Page struct {
	Tasks []Tasks `json:"tasks"`
}

func checkErr(err error) {
	if err != nil {
		log.Println(err)
	}
}

func genName() string {
	name := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	_, err = db.Query("select name from tasks where name=?", name)
	db.Close()
	if err == sql.ErrNoRows {
		genName()
	}
	checkErr(err)
	return name
}

func loggedIn(r *http.Request) bool {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return false
	}
	err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return false
	}
	return true

}

func getEmail(r *http.Request) (string, error) {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return "", err
	}
	err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return "", err
	}
	return cookieValue["email"], nil

}

func rootHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	email, err := getEmail(r)
	checkErr(err)

	rows, err := db.Query("select name, title, task, created, duedate from tasks where email=? order by duedate asc", email)
	checkErr(err)

	b := Page{Tasks: []Tasks{}}

	for rows.Next() {
		res := Tasks{}
		rows.Scan(&res.Name, &res.Title, &res.Task, &res.Created, &res.DueDate)

		b.Tasks = append(b.Tasks, res)
	}
	db.Close()

	checkErr(err)

	err = templates.ExecuteTemplate(w, "index.html", &b)
	checkErr(err)

}

func todoHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	todo := vars["id"]

	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	rows, err := db.Query("select public from tasks where name=?", html.EscapeString(todo))
	checkErr(err)
	var public bool
	for rows.Next() {
		rows.Scan(&public)
	}

	if loggedIn(r) != true && public == false {
		http.Redirect(w, r, "/login", 302)
	}
	p := Tasks{}
	query, err := db.Query("select title, task, duedate, created, completed from tasks where name=?", html.EscapeString(todo))
	checkErr(err)

	for query.Next() {
		query.Scan(&p.Title, &p.Task, &p.DueDate, &p.Created, &p.Completed)
	}

	err = templates.ExecuteTemplate(w, "todo.html", &p)
	checkErr(err)

}

func addHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "add.html", "")
		checkErr(err)

	case "POST":
		title := r.FormValue("title")
		task := r.FormValue("task")
		duedate := r.FormValue("duedate")
		public := r.FormValue("public")
		name := genName()
		email, err := getEmail(r)
		checkErr(err)

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)
		defer db.Close()

		query, err := db.Prepare("insert into tasks(name, title, task, duedate, created, email, completed, public) values(?, ?, ?, ?, ?, ?, ?, ?)")
		_, err = query.Exec(name, html.EscapeString(title), html.EscapeString(task), html.EscapeString(duedate), time.Now().Format("2016-02-01 15:12:52"), email, false, html.EscapeString(public))
		checkErr(err)

	}

}

func editHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	switch r.Method {
	case "GET":
		b := Tasks{}
		email, err := getEmail(r)
		checkErr(err)

		rows, err := db.Query("select name, title, task, duedate, created, email, completed, public from tasks where email=? and name=?", email, html.EscapeString(todo))
		if err == sql.ErrNoRows {
			http.Redirect(w, r, "/", 302)
		}
		for rows.Next() {
			rows.Scan(&b.Name, &b.Title, &b.Task, &b.DueDate, &b.Created, &b.Email, &b.Completed, &b.Public)
		}

		err = templates.ExecuteTemplate(w, "edit.html", &b)
		checkErr(err)

	case "POST", "PUT":
		title := r.FormValue("title")
		task := r.FormValue("task")
		duedate := r.FormValue("duedate")
		public := r.FormValue("public")

		query, err := db.Prepare("update tasks set title=?, task=?, duedate=?, public=?")
		checkErr(err)
		_, err = query.Exec(html.EscapeString(title), html.EscapeString(task), html.EscapeString(duedate), html.EscapeString(public))
		checkErr(err)

	}

}

func delHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

	switch r.Method {
	case "POST", "DEL":
		vars := mux.Vars(r)
		todo := vars["id"]

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)
		defer db.Close()

		email, err := getEmail(r)
		checkErr(err)

		_, err = db.Query("delete from tasks where name=? and email=?", html.EscapeString(todo), email)
	}

}

func finishHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)
	defer db.Close()

	email, err := getEmail(r)
	checkErr(err)
	_, err = db.Query("update tasks set completed=true where email=? and name=?", email, html.EscapeString(todo))

}

func userHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

}

func userDelHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "deluser.html", "")
		checkErr(err)
	case "POST", "DEL":
		pass := r.FormValue("password")

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		query, err := db.Prepare("delete from users where email=? and password=?")
		checkErr(err)

		email, err := getEmail(r)
		checkErr(err)
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
		checkErr(err)

		_, err = query.Exec(email, hashedPassword)
		checkErr(err)

	}

}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "login.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		password := r.FormValue("password")
		// open db connection
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		// declare variables for database results
		var hashedPassword []byte
		// read hashedPassword, name and level into variables
		err = db.QueryRow("select password from users where email=?", html.EscapeString(email)).Scan(&hashedPassword)
		if err == sql.ErrNoRows {
			http.Redirect(w, r, "/login", 303)
			return
		}
		checkErr(err)

		// compare bcrypt hash to userinput password
		err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
		if err == nil {
			// prepare cookie
			value := map[string]string{
				"email": email,
			}
			// encode variables into cookie
			if encoded, err := cookieHandler.Encode("session", value); err == nil {
				cookie := &http.Cookie{
					Name:  "session",
					Value: encoded,
					Path:  "/",
				}
				// set user cookie
				http.SetCookie(w, cookie)
			}
			// Redirect to home page
			http.Redirect(w, r, "/", 302)
		}
		// Redirect to login page
		http.Redirect(w, r, "/login", 302)

	}

}

func registerHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "register.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		pass := r.FormValue("password")
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()
		//_, err = db.Query("select email from users where email=?", html.EscapeString(email))
		//checkErr(err)
		//if err == sql.ErrNoRows {
		query, err := db.Prepare("INSERT into users(email, password) values(?, ?)")
		checkErr(err)
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
		checkErr(err)

		_, err = query.Exec(html.EscapeString(email), hashedPassword)
		checkErr(err)
		http.Redirect(w, r, "/login", 302)

		//}
		http.Redirect(w, r, "/register", 302)

	}

}

func logoutHandler(w http.ResponseWriter, r *http.Request) {
	cookie := &http.Cookie{
		Name:   "session",
		Value:  "",
		Path:   "/",
		MaxAge: -1,
	}
	http.SetCookie(w, cookie)
	http.Redirect(w, r, "/", 301)

}

func resetHandler(w http.ResponseWriter, r *http.Request) {

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/", rootHandler)

	router.HandleFunc("/todo", todoHandler)
	router.HandleFunc("/add", addHandler)
	router.HandleFunc("/todo/{id}", todoHandler)
	router.HandleFunc("/edit/{id}", editHandler)
	router.HandleFunc("/del/{id}", delHandler)

	router.HandleFunc("/finish/{id}", finishHandler)

	router.HandleFunc("/user", userHandler)
	router.HandleFunc("/user/del", userDelHandler)

	router.HandleFunc("/register", registerHandler)
	router.HandleFunc("/login", loginHandler)
	router.HandleFunc("/logout", logoutHandler)
	router.HandleFunc("/resetpass", resetHandler)
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<|endoftext|>


<commit_before>// Package sbuf implements a byte buffer that can be wiped. The underlying
// byte slice is wiped on read before being declaimed, and when the
// buffer is closed, its storage is zeroised.
package sbuf

import (
	"errors"
	"io"
)

func zero(in []byte, n int) {
	if in == nil {
		return
	}

	stop := n
	if stop > len(in) || stop == 0 {
		stop = len(in)
	}

	for i := 0; i < stop; i++ {
		in[i] ^= in[i]
	}
}

// A Buffer is a variable-sized buffer of bytes with Read and Write
// methods. The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
	buf []byte
}

// NewBuffer creates a new buffer with the specified capacity.
func NewBuffer(n int) *Buffer {
	return &Buffer{
		buf: make([]byte, 0, n),
	}
}

// NewBufferFrom creates a new buffer from the byte slice passed in. The
// original data will be wiped.
func NewBufferFrom(p []byte) *Buffer {
	buf := NewBuffer(len(p))
	buf.Write(p)
	zero(p, len(p))
	return buf
}

// Read reads the next len(p) bytes from the buffer or until the buffer
// is drained. The return value n is the number of bytes read. If the
// buffer has no data to return, err is io.EOF (unless len(p) is zero);
// otherwise it is nil.
func (buf *Buffer) Read(p []byte) (int, error) {
	if len(buf.buf) == 0 {
		if len(p) == 0 {
			return 0, nil
		}
		return 0, io.EOF
	}

	copyLength := len(p)
	if copyLength > len(buf.buf) {
		copyLength = len(buf.buf)
	}

	copy(p, buf.buf)
	zero(buf.buf, len(p))
	buf.buf = buf.buf[copyLength:]
	return copyLength, nil
}

func (buf *Buffer) grow(n int) {
	tmp := make([]byte, len(buf.buf), len(buf.buf)+n)
	copy(tmp, buf.buf)
	zero(buf.buf, len(buf.buf))
	buf.buf = tmp
}

// Write appends the contents of p to the buffer, growing the buffer
// as needed. The return value n is the length of p; err is always nil.
func (buf *Buffer) Write(p []byte) (int, error) {
	r := len(buf.buf) + len(p)
	if cap(buf.buf) < r {
		l := r
		for {
			if l > r {
				break
			}
			l *= 2
		}
		buf.grow(l - cap(buf.buf))
	}
	buf.buf = append(buf.buf, p...)
	return len(p), nil
}

// Close destroys and zeroises the buffer.
func (buf *Buffer) Close() {
	zero(buf.buf, len(buf.buf))
	buf.buf = nil
}

// Len returns the length of the buffer.
func (buf *Buffer) Len() int {
	return len(buf.buf)
}

// Cap returns the capacity of the buffer.
func (buf *Buffer) Cap() int {
	return cap(buf.buf)
}
<commit_msg>Remove unused import 'errors'.<commit_after>// Package sbuf implements a byte buffer that can be wiped. The underlying
// byte slice is wiped on read before being declaimed, and when the
// buffer is closed, its storage is zeroised.
package sbuf

import "io"

func zero(in []byte, n int) {
	if in == nil {
		return
	}

	stop := n
	if stop > len(in) || stop == 0 {
		stop = len(in)
	}

	for i := 0; i < stop; i++ {
		in[i] ^= in[i]
	}
}

// A Buffer is a variable-sized buffer of bytes with Read and Write
// methods. The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
	buf []byte
}

// NewBuffer creates a new buffer with the specified capacity.
func NewBuffer(n int) *Buffer {
	return &Buffer{
		buf: make([]byte, 0, n),
	}
}

// NewBufferFrom creates a new buffer from the byte slice passed in. The
// original data will be wiped.
func NewBufferFrom(p []byte) *Buffer {
	buf := NewBuffer(len(p))
	buf.Write(p)
	zero(p, len(p))
	return buf
}

// Read reads the next len(p) bytes from the buffer or until the buffer
// is drained. The return value n is the number of bytes read. If the
// buffer has no data to return, err is io.EOF (unless len(p) is zero);
// otherwise it is nil.
func (buf *Buffer) Read(p []byte) (int, error) {
	if len(buf.buf) == 0 {
		if len(p) == 0 {
			return 0, nil
		}
		return 0, io.EOF
	}

	copyLength := len(p)
	if copyLength > len(buf.buf) {
		copyLength = len(buf.buf)
	}

	copy(p, buf.buf)
	zero(buf.buf, len(p))
	buf.buf = buf.buf[copyLength:]
	return copyLength, nil
}

func (buf *Buffer) grow(n int) {
	tmp := make([]byte, len(buf.buf), len(buf.buf)+n)
	copy(tmp, buf.buf)
	zero(buf.buf, len(buf.buf))
	buf.buf = tmp
}

// Write appends the contents of p to the buffer, growing the buffer
// as needed. The return value n is the length of p; err is always nil.
func (buf *Buffer) Write(p []byte) (int, error) {
	r := len(buf.buf) + len(p)
	if cap(buf.buf) < r {
		l := r
		for {
			if l > r {
				break
			}
			l *= 2
		}
		buf.grow(l - cap(buf.buf))
	}
	buf.buf = append(buf.buf, p...)
	return len(p), nil
}

// Close destroys and zeroises the buffer.
func (buf *Buffer) Close() {
	zero(buf.buf, len(buf.buf))
	buf.buf = nil
}

// Len returns the length of the buffer.
func (buf *Buffer) Len() int {
	return len(buf.buf)
}

// Cap returns the capacity of the buffer.
func (buf *Buffer) Cap() int {
	return cap(buf.buf)
}
<|endoftext|>