<commit_before>package wrap

import (
	"io"
	"net/rpc"
	"sync"
	"time"

	"github.com/go-playground/log"
	"github.com/go-playground/wave/client"
)

// RetryReconnectEndpoint is interface needed to comply with wrapping
// with Rety and Reconnect logic.
type RetryReconnectEndpoint interface {
	client.Endpoint
	SetClient(*rpc.Client)
	NewClient() (*rpc.Client, error)
}

// RetryReconnect wraps the given RetryReconnectEndpoint endpoint and automatically
// handles logic to reconnect and retry
func RetryReconnect(endpoint RetryReconnectEndpoint, retryDuration time.Duration) (e client.Endpoint, err error) {

	rr := &retryReconnect{
		RetryReconnectEndpoint: endpoint,
		clientMutex:            new(sync.RWMutex),
		reconnectMutex:         new(sync.RWMutex),
		retryDuration:          retryDuration,
		isDisconnected:         true,
	}

	e = rr

	_, err = rr.NewClient()
	if err != nil {
		return
	}

	return
}

type retryReconnect struct {
	RetryReconnectEndpoint
	clientMutex    *sync.RWMutex
	reconnectMutex *sync.RWMutex
	retryDuration  time.Duration
	isDisconnected bool // defaults to true
}

var _ client.Endpoint = &retryReconnect{}
var _ RetryReconnectEndpoint = &retryReconnect{}

func (r *retryReconnect) NewClient() (c *rpc.Client, err error) {

	r.reconnectMutex.Lock()
	r.isDisconnected = true
	r.reconnectMutex.Unlock()

	// a bunch of calls could have gotten here if server was busy, lets double check we're still disconnected
	// right after lock is released
	r.clientMutex.Lock()
	defer r.clientMutex.Unlock()

	// check if still disconnected
	r.reconnectMutex.RLock()
	if !r.isDisconnected {
		r.reconnectMutex.RUnlock()
		return nil, err
	}
	r.reconnectMutex.RUnlock()

	for i := 0; i < 3; i++ {
		c, err = r.RetryReconnectEndpoint.NewClient()
		if err != nil {
			time.Sleep(r.retryDuration)
			continue
		}

		r.SetClient(c)

		r.reconnectMutex.Lock()
		r.isDisconnected = false
		r.reconnectMutex.Unlock()

		return
	}

	log.WithFields(log.F("err", err)).Alert("RPC Connection could not be established/reestablished")

	go func() {

		r.clientMutex.Lock()
		defer r.clientMutex.Unlock()

		for {

			var client *rpc.Client
			var err2 error

			time.Sleep(r.retryDuration)
			client, err2 = r.RetryReconnectEndpoint.NewClient()
			if err2 != nil {
				time.Sleep(r.retryDuration)
				continue
			}

			r.SetClient(client)

			r.reconnectMutex.Lock()
			r.isDisconnected = false
			r.reconnectMutex.Unlock()

			break
		}
	}()

	err = rpc.ErrShutdown
	return
}

func (r *retryReconnect) Call(args interface{}, reply interface{}) (err error) {

	// check if disconnected
	r.reconnectMutex.RLock()
	if r.isDisconnected {
		r.reconnectMutex.RUnlock()
		return rpc.ErrShutdown
	}
	r.reconnectMutex.RUnlock()

RETRY:
	// make rpc call
	r.clientMutex.RLock()
	err = r.RetryReconnectEndpoint.Call(args, reply)
	r.clientMutex.RUnlock()

	// if error indicates a disconnect of some sort, try and reconnect
	if err != nil && err == rpc.ErrShutdown || err == io.EOF || err == io.ErrUnexpectedEOF {

		_, err = r.NewClient()
		if err != nil {
			return
		}

		goto RETRY
	}

	return
}

func (r *retryReconnect) Go(args interface{}, reply interface{}, done chan *rpc.Call) (c *rpc.Call) {

	if done == nil {
		done = make(chan *rpc.Call, 10)
	}

	c = &rpc.Call{
		ServiceMethod: r.ServiceMethod(),
		Args:          args,
		Reply:         reply,
		Done:          done,
	}

	// check if disconnected
	r.reconnectMutex.RLock()
	if r.isDisconnected {
		r.reconnectMutex.RUnlock()

		c.Error = rpc.ErrShutdown

		go func() {
			c.Done <- c
		}()
		return
	}

	r.reconnectMutex.RUnlock()

	dc := make(chan *rpc.Call, cap(done))
	// make rpc call
	r.clientMutex.RLock()
	c2 := r.RetryReconnectEndpoint.Go(args, reply, dc)
	r.clientMutex.RUnlock()

	go func() {
		res := <-c2.Done

		if res.Error != nil && res.Error == rpc.ErrShutdown || res.Error == io.EOF || res.Error == io.ErrUnexpectedEOF {
			_, res.Error = r.NewClient()
			if res.Error != nil {
				c.Done <- res
				return
			}

			// RETRY
			r.clientMutex.RLock()
			c2 = r.RetryReconnectEndpoint.Go(args, reply, dc)
			r.clientMutex.RUnlock()

			res = <-c2.Done
		}

		c.Done <- res
	}()

	return
}
<commit_msg>Add some documentation<commit_after>package wrap

import (
	"io"
	"net/rpc"
	"sync"
	"time"

	"github.com/go-playground/log"
	"github.com/go-playground/wave/client"
)

// RetryReconnectEndpoint is interface needed to comply with wrapping
// with Rety and Reconnect logic.
type RetryReconnectEndpoint interface {
	client.Endpoint

	// SetClient is called after a successful connection is established/reestablished
	// NOTE: should close the existing client in this method if not nil prior to
	// updating to the new one passed in.
	SetClient(*rpc.Client)

	// NewClient calls the RetryReconnectEndpoint's NewClient method
	// wrapping it with retry/reconnect logic
	NewClient() (*rpc.Client, error)
}

// RetryReconnect wraps the given RetryReconnectEndpoint endpoint and automatically
// handles logic to reconnect and retry
func RetryReconnect(endpoint RetryReconnectEndpoint, retryDuration time.Duration) (e client.Endpoint, err error) {

	rr := &retryReconnect{
		RetryReconnectEndpoint: endpoint,
		clientMutex:            new(sync.RWMutex),
		reconnectMutex:         new(sync.RWMutex),
		retryDuration:          retryDuration,
		isDisconnected:         true,
	}

	e = rr

	_, err = rr.NewClient()
	if err != nil {
		return
	}

	return
}

type retryReconnect struct {
	RetryReconnectEndpoint
	clientMutex    *sync.RWMutex
	reconnectMutex *sync.RWMutex
	retryDuration  time.Duration
	isDisconnected bool // defaults to true
}

var _ client.Endpoint = &retryReconnect{}
var _ RetryReconnectEndpoint = &retryReconnect{}

// NewClient calls the RetryReconnectEndpoint's NewClient method
// wrapping it with retry/reconnect logic
func (r *retryReconnect) NewClient() (c *rpc.Client, err error) {

	r.reconnectMutex.Lock()
	r.isDisconnected = true
	r.reconnectMutex.Unlock()

	// a bunch of calls could have gotten here if server was busy, lets double check we're still disconnected
	// right after lock is released
	r.clientMutex.Lock()
	defer r.clientMutex.Unlock()

	// check if still disconnected
	r.reconnectMutex.RLock()
	if !r.isDisconnected {
		r.reconnectMutex.RUnlock()
		return nil, err
	}
	r.reconnectMutex.RUnlock()

	for i := 0; i < 3; i++ {
		c, err = r.RetryReconnectEndpoint.NewClient()
		if err != nil {
			time.Sleep(r.retryDuration)
			continue
		}

		r.SetClient(c)

		r.reconnectMutex.Lock()
		r.isDisconnected = false
		r.reconnectMutex.Unlock()

		return
	}

	log.WithFields(log.F("err", err)).Alert("RPC Connection could not be established/reestablished")

	go func() {

		r.clientMutex.Lock()
		defer r.clientMutex.Unlock()

		for {

			var client *rpc.Client
			var err2 error

			time.Sleep(r.retryDuration)
			client, err2 = r.RetryReconnectEndpoint.NewClient()
			if err2 != nil {
				time.Sleep(r.retryDuration)
				continue
			}

			r.SetClient(client)

			r.reconnectMutex.Lock()
			r.isDisconnected = false
			r.reconnectMutex.Unlock()

			break
		}
	}()

	err = rpc.ErrShutdown
	return
}

// Call calls the RetryReconnectEndpoint's Call method
// wrapping it with retry/reconnect logic
func (r *retryReconnect) Call(args interface{}, reply interface{}) (err error) {

	// check if disconnected
	r.reconnectMutex.RLock()
	if r.isDisconnected {
		r.reconnectMutex.RUnlock()
		return rpc.ErrShutdown
	}
	r.reconnectMutex.RUnlock()

RETRY:
	// make rpc call
	r.clientMutex.RLock()
	err = r.RetryReconnectEndpoint.Call(args, reply)
	r.clientMutex.RUnlock()

	// if error indicates a disconnect of some sort, try and reconnect
	if err != nil && err == rpc.ErrShutdown || err == io.EOF || err == io.ErrUnexpectedEOF {

		_, err = r.NewClient()
		if err != nil {
			return
		}

		goto RETRY
	}

	return
}

// Go calls the RetryReconnectEndpoint's Call method
// wrapping it with retry/reconnect logic
func (r *retryReconnect) Go(args interface{}, reply interface{}, done chan *rpc.Call) (c *rpc.Call) {

	if done == nil {
		done = make(chan *rpc.Call, 10)
	}

	c = &rpc.Call{
		ServiceMethod: r.ServiceMethod(),
		Args:          args,
		Reply:         reply,
		Done:          done,
	}

	// check if disconnected
	r.reconnectMutex.RLock()
	if r.isDisconnected {
		r.reconnectMutex.RUnlock()

		c.Error = rpc.ErrShutdown

		go func() {
			c.Done <- c
		}()
		return
	}

	r.reconnectMutex.RUnlock()

	dc := make(chan *rpc.Call, cap(done))
	// make rpc call
	r.clientMutex.RLock()
	c2 := r.RetryReconnectEndpoint.Go(args, reply, dc)
	r.clientMutex.RUnlock()

	go func() {
		res := <-c2.Done

		if res.Error != nil && res.Error == rpc.ErrShutdown || res.Error == io.EOF || res.Error == io.ErrUnexpectedEOF {
			_, res.Error = r.NewClient()
			if res.Error != nil {
				c.Done <- res
				return
			}

			// RETRY
			r.clientMutex.RLock()
			c2 = r.RetryReconnectEndpoint.Go(args, reply, dc)
			r.clientMutex.RUnlock()

			res = <-c2.Done
		}

		c.Done <- res
	}()

	return
}
<|endoftext|>


<commit_before>package commands

import (
	"context"
	"time"

	"github.com/spf13/cobra"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"

	"github.com/argoproj/argo-cd/common"
	"github.com/argoproj/argo-cd/errors"
	appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
	"github.com/argoproj/argo-cd/reposerver"
	"github.com/argoproj/argo-cd/server"
	"github.com/argoproj/argo-cd/util/cache"
	"github.com/argoproj/argo-cd/util/cli"
	"github.com/argoproj/argo-cd/util/stats"
	"github.com/argoproj/argo-cd/util/tls"
)

// NewCommand returns a new instance of an argocd command
func NewCommand() *cobra.Command {
	var (
		insecure               bool
		listenPort             int
		metricsPort            int
		logLevel               string
		glogLevel              int
		clientConfig           clientcmd.ClientConfig
		staticAssetsDir        string
		baseHRef               string
		repoServerAddress      string
		dexServerAddress       string
		disableAuth            bool
		tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
		cacheSrc               func() (*cache.Cache, error)
	)
	var command = &cobra.Command{
		Use:   cliName,
		Short: "Run the argocd API server",
		Long:  "Run the argocd API server",
		Run: func(c *cobra.Command, args []string) {
			cli.SetLogLevel(logLevel)
			cli.SetGLogLevel(glogLevel)

			config, err := clientConfig.ClientConfig()
			errors.CheckError(err)
			config.QPS = common.K8sClientConfigQPS
			config.Burst = common.K8sClientConfigBurst

			namespace, _, err := clientConfig.Namespace()
			errors.CheckError(err)

			tlsConfigCustomizer, err := tlsConfigCustomizerSrc()
			errors.CheckError(err)
			cache, err := cacheSrc()
			errors.CheckError(err)

			kubeclientset := kubernetes.NewForConfigOrDie(config)
			appclientset := appclientset.NewForConfigOrDie(config)
			repoclientset := reposerver.NewRepoServerClientset(repoServerAddress, 0)

			argoCDOpts := server.ArgoCDServerOpts{
				Insecure:            insecure,
				ListenPort:          listenPort,
				MetricsPort:         metricsPort,
				Namespace:           namespace,
				StaticAssetsDir:     staticAssetsDir,
				BaseHRef:            baseHRef,
				KubeClientset:       kubeclientset,
				AppClientset:        appclientset,
				RepoClientset:       repoclientset,
				DexServerAddr:       dexServerAddress,
				DisableAuth:         disableAuth,
				TLSConfigCustomizer: tlsConfigCustomizer,
				Cache:               cache,
			}

			stats.RegisterStackDumper()
			stats.StartStatsTicker(10 * time.Minute)
			stats.RegisterHeapDumper("memprofile")

			for {
				ctx := context.Background()
				ctx, cancel := context.WithCancel(ctx)
				argocd := server.NewServer(ctx, argoCDOpts)
				argocd.Run(ctx, listenPort, metricsPort)
				cancel()
			}
		},
	}

	clientConfig = cli.AddKubectlFlagsToCmd(command)
	command.Flags().BoolVar(&insecure, "insecure", false, "Run server without TLS")
	command.Flags().StringVar(&staticAssetsDir, "staticassets", "", "Static assets directory path")
	command.Flags().StringVar(&baseHRef, "basehref", "/", "Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from /")
	command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
	command.Flags().IntVar(&glogLevel, "gloglevel", 0, "Set the glog logging level")
	command.Flags().StringVar(&repoServerAddress, "repo-server", common.DefaultRepoServerAddr, "Repo server address")
	command.Flags().StringVar(&dexServerAddress, "dex-server", common.DefaultDexServerAddr, "Dex server address")
	command.Flags().BoolVar(&disableAuth, "disable-auth", false, "Disable client authentication")
	command.AddCommand(cli.NewVersionCmd(cliName))
	command.Flags().IntVar(&listenPort, "port", common.DefaultPortAPIServer, "Listen on given port")
	command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDAPIServerMetrics, "Start metrics on given port")
	tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(command)
	cacheSrc = cache.AddCacheFlagsToCmd(command)
	return command
}
<commit_msg>Issue #1820 - Make sure api server to repo server grpc calls have timeout (#1832)<commit_after>package commands

import (
	"context"
	"time"

	"github.com/spf13/cobra"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"

	"github.com/argoproj/argo-cd/common"
	"github.com/argoproj/argo-cd/errors"
	appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
	"github.com/argoproj/argo-cd/reposerver"
	"github.com/argoproj/argo-cd/server"
	"github.com/argoproj/argo-cd/util/cache"
	"github.com/argoproj/argo-cd/util/cli"
	"github.com/argoproj/argo-cd/util/stats"
	"github.com/argoproj/argo-cd/util/tls"
)

// NewCommand returns a new instance of an argocd command
func NewCommand() *cobra.Command {
	var (
		insecure                 bool
		listenPort               int
		metricsPort              int
		logLevel                 string
		glogLevel                int
		clientConfig             clientcmd.ClientConfig
		repoServerTimeoutSeconds int
		staticAssetsDir          string
		baseHRef                 string
		repoServerAddress        string
		dexServerAddress         string
		disableAuth              bool
		tlsConfigCustomizerSrc   func() (tls.ConfigCustomizer, error)
		cacheSrc                 func() (*cache.Cache, error)
	)
	var command = &cobra.Command{
		Use:   cliName,
		Short: "Run the argocd API server",
		Long:  "Run the argocd API server",
		Run: func(c *cobra.Command, args []string) {
			cli.SetLogLevel(logLevel)
			cli.SetGLogLevel(glogLevel)

			config, err := clientConfig.ClientConfig()
			errors.CheckError(err)
			config.QPS = common.K8sClientConfigQPS
			config.Burst = common.K8sClientConfigBurst

			namespace, _, err := clientConfig.Namespace()
			errors.CheckError(err)

			tlsConfigCustomizer, err := tlsConfigCustomizerSrc()
			errors.CheckError(err)
			cache, err := cacheSrc()
			errors.CheckError(err)

			kubeclientset := kubernetes.NewForConfigOrDie(config)
			appclientset := appclientset.NewForConfigOrDie(config)
			repoclientset := reposerver.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds)

			argoCDOpts := server.ArgoCDServerOpts{
				Insecure:            insecure,
				ListenPort:          listenPort,
				MetricsPort:         metricsPort,
				Namespace:           namespace,
				StaticAssetsDir:     staticAssetsDir,
				BaseHRef:            baseHRef,
				KubeClientset:       kubeclientset,
				AppClientset:        appclientset,
				RepoClientset:       repoclientset,
				DexServerAddr:       dexServerAddress,
				DisableAuth:         disableAuth,
				TLSConfigCustomizer: tlsConfigCustomizer,
				Cache:               cache,
			}

			stats.RegisterStackDumper()
			stats.StartStatsTicker(10 * time.Minute)
			stats.RegisterHeapDumper("memprofile")

			for {
				ctx := context.Background()
				ctx, cancel := context.WithCancel(ctx)
				argocd := server.NewServer(ctx, argoCDOpts)
				argocd.Run(ctx, listenPort, metricsPort)
				cancel()
			}
		},
	}

	clientConfig = cli.AddKubectlFlagsToCmd(command)
	command.Flags().BoolVar(&insecure, "insecure", false, "Run server without TLS")
	command.Flags().StringVar(&staticAssetsDir, "staticassets", "", "Static assets directory path")
	command.Flags().StringVar(&baseHRef, "basehref", "/", "Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from /")
	command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
	command.Flags().IntVar(&glogLevel, "gloglevel", 0, "Set the glog logging level")
	command.Flags().StringVar(&repoServerAddress, "repo-server", common.DefaultRepoServerAddr, "Repo server address")
	command.Flags().StringVar(&dexServerAddress, "dex-server", common.DefaultDexServerAddr, "Dex server address")
	command.Flags().BoolVar(&disableAuth, "disable-auth", false, "Disable client authentication")
	command.AddCommand(cli.NewVersionCmd(cliName))
	command.Flags().IntVar(&listenPort, "port", common.DefaultPortAPIServer, "Listen on given port")
	command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDAPIServerMetrics, "Start metrics on given port")
	command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", 60, "Repo server RPC call timeout seconds.")
	tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(command)
	cacheSrc = cache.AddCacheFlagsToCmd(command)
	return command
}
<|endoftext|>


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

package http

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"
	"time"
)

// This implementation is done according to RFC 6265:
//
//    http://tools.ietf.org/html/rfc6265

// A Cookie represents an HTTP cookie as sent in the Set-Cookie header of an
// HTTP response or the Cookie header of an HTTP request.
type Cookie struct {
	Name       string
	Value      string
	Path       string
	Domain     string
	Expires    time.Time
	RawExpires string

	// MaxAge=0 means no 'Max-Age' attribute specified.
	// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
	// MaxAge>0 means Max-Age attribute present and given in seconds
	MaxAge   int
	Secure   bool
	HttpOnly bool
	Raw      string
	Unparsed []string // Raw text of unparsed attribute-value pairs
}

// readSetCookies parses all "Set-Cookie" values from
// the header h and returns the successfully parsed Cookies.
func readSetCookies(h Header) []*Cookie {
	cookies := []*Cookie{}
	for _, line := range h["Set-Cookie"] {
		parts := strings.Split(strings.TrimSpace(line), ";")
		if len(parts) == 1 && parts[0] == "" {
			continue
		}
		parts[0] = strings.TrimSpace(parts[0])
		j := strings.Index(parts[0], "=")
		if j < 0 {
			continue
		}
		name, value := parts[0][:j], parts[0][j+1:]
		if !isCookieNameValid(name) {
			continue
		}
		value, success := parseCookieValue(value)
		if !success {
			continue
		}
		c := &Cookie{
			Name:  name,
			Value: value,
			Raw:   line,
		}
		for i := 1; i < len(parts); i++ {
			parts[i] = strings.TrimSpace(parts[i])
			if len(parts[i]) == 0 {
				continue
			}

			attr, val := parts[i], ""
			if j := strings.Index(attr, "="); j >= 0 {
				attr, val = attr[:j], attr[j+1:]
			}
			lowerAttr := strings.ToLower(attr)
			parseCookieValueFn := parseCookieValue
			if lowerAttr == "expires" {
				parseCookieValueFn = parseCookieExpiresValue
			}
			val, success = parseCookieValueFn(val)
			if !success {
				c.Unparsed = append(c.Unparsed, parts[i])
				continue
			}
			switch lowerAttr {
			case "secure":
				c.Secure = true
				continue
			case "httponly":
				c.HttpOnly = true
				continue
			case "domain":
				c.Domain = val
				// TODO: Add domain parsing
				continue
			case "max-age":
				secs, err := strconv.Atoi(val)
				if err != nil || secs < 0 || secs != 0 && val[0] == '0' {
					break
				}
				if secs <= 0 {
					c.MaxAge = -1
				} else {
					c.MaxAge = secs
				}
				continue
			case "expires":
				c.RawExpires = val
				exptime, err := time.Parse(time.RFC1123, val)
				if err != nil {
					exptime, err = time.Parse("Mon, 02-Jan-2006 15:04:05 MST", val)
					if err != nil {
						c.Expires = time.Time{}
						break
					}
				}
				c.Expires = exptime.UTC()
				continue
			case "path":
				c.Path = val
				// TODO: Add path parsing
				continue
			}
			c.Unparsed = append(c.Unparsed, parts[i])
		}
		cookies = append(cookies, c)
	}
	return cookies
}

// SetCookie adds a Set-Cookie header to the provided ResponseWriter's headers.
func SetCookie(w ResponseWriter, cookie *Cookie) {
	w.Header().Add("Set-Cookie", cookie.String())
}

// String returns the serialization of the cookie for use in a Cookie
// header (if only Name and Value are set) or a Set-Cookie response
// header (if other fields are set).
func (c *Cookie) String() string {
	var b bytes.Buffer
	fmt.Fprintf(&b, "%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value))
	if len(c.Path) > 0 {
		fmt.Fprintf(&b, "; Path=%s", sanitizeValue(c.Path))
	}
	if len(c.Domain) > 0 {
		fmt.Fprintf(&b, "; Domain=%s", sanitizeValue(c.Domain))
	}
	if c.Expires.Unix() > 0 {
		fmt.Fprintf(&b, "; Expires=%s", c.Expires.UTC().Format(time.RFC1123))
	}
	if c.MaxAge > 0 {
		fmt.Fprintf(&b, "; Max-Age=%d", c.MaxAge)
	} else if c.MaxAge < 0 {
		fmt.Fprintf(&b, "; Max-Age=0")
	}
	if c.HttpOnly {
		fmt.Fprintf(&b, "; HttpOnly")
	}
	if c.Secure {
		fmt.Fprintf(&b, "; Secure")
	}
	return b.String()
}

// readCookies parses all "Cookie" values from the header h and
// returns the successfully parsed Cookies.
//
// if filter isn't empty, only cookies of that name are returned
func readCookies(h Header, filter string) []*Cookie {
	cookies := []*Cookie{}
	lines, ok := h["Cookie"]
	if !ok {
		return cookies
	}

	for _, line := range lines {
		parts := strings.Split(strings.TrimSpace(line), ";")
		if len(parts) == 1 && parts[0] == "" {
			continue
		}
		// Per-line attributes
		parsedPairs := 0
		for i := 0; i < len(parts); i++ {
			parts[i] = strings.TrimSpace(parts[i])
			if len(parts[i]) == 0 {
				continue
			}
			name, val := parts[i], ""
			if j := strings.Index(name, "="); j >= 0 {
				name, val = name[:j], name[j+1:]
			}
			if !isCookieNameValid(name) {
				continue
			}
			if filter != "" && filter != name {
				continue
			}
			val, success := parseCookieValue(val)
			if !success {
				continue
			}
			cookies = append(cookies, &Cookie{Name: name, Value: val})
			parsedPairs++
		}
	}
	return cookies
}

var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-")

func sanitizeName(n string) string {
	return cookieNameSanitizer.Replace(n)
}

var cookieValueSanitizer = strings.NewReplacer("\n", " ", "\r", " ", ";", " ")

func sanitizeValue(v string) string {
	return cookieValueSanitizer.Replace(v)
}

func unquoteCookieValue(v string) string {
	if len(v) > 1 && v[0] == '"' && v[len(v)-1] == '"' {
		return v[1 : len(v)-1]
	}
	return v
}

func isCookieByte(c byte) bool {
	switch {
	case c == 0x21, 0x23 <= c && c <= 0x2b, 0x2d <= c && c <= 0x3a,
		0x3c <= c && c <= 0x5b, 0x5d <= c && c <= 0x7e:
		return true
	}
	return false
}

func isCookieExpiresByte(c byte) (ok bool) {
	return isCookieByte(c) || c == ',' || c == ' '
}

func parseCookieValue(raw string) (string, bool) {
	return parseCookieValueUsing(raw, isCookieByte)
}

func parseCookieExpiresValue(raw string) (string, bool) {
	return parseCookieValueUsing(raw, isCookieExpiresByte)
}

func parseCookieValueUsing(raw string, validByte func(byte) bool) (string, bool) {
	raw = unquoteCookieValue(raw)
	for i := 0; i < len(raw); i++ {
		if !validByte(raw[i]) {
			return "", false
		}
	}
	return raw, true
}

func isCookieNameValid(raw string) bool {
	for _, c := range raw {
		if !isToken(byte(c)) {
			return false
		}
	}
	return true
}
<commit_msg>http: Allow cookies with negative Max-Age attribute as these are allowed by RFC 6265 sec 5.2.2.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package http

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"
	"time"
)

// This implementation is done according to RFC 6265:
//
//    http://tools.ietf.org/html/rfc6265

// A Cookie represents an HTTP cookie as sent in the Set-Cookie header of an
// HTTP response or the Cookie header of an HTTP request.
type Cookie struct {
	Name       string
	Value      string
	Path       string
	Domain     string
	Expires    time.Time
	RawExpires string

	// MaxAge=0 means no 'Max-Age' attribute specified.
	// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
	// MaxAge>0 means Max-Age attribute present and given in seconds
	MaxAge   int
	Secure   bool
	HttpOnly bool
	Raw      string
	Unparsed []string // Raw text of unparsed attribute-value pairs
}

// readSetCookies parses all "Set-Cookie" values from
// the header h and returns the successfully parsed Cookies.
func readSetCookies(h Header) []*Cookie {
	cookies := []*Cookie{}
	for _, line := range h["Set-Cookie"] {
		parts := strings.Split(strings.TrimSpace(line), ";")
		if len(parts) == 1 && parts[0] == "" {
			continue
		}
		parts[0] = strings.TrimSpace(parts[0])
		j := strings.Index(parts[0], "=")
		if j < 0 {
			continue
		}
		name, value := parts[0][:j], parts[0][j+1:]
		if !isCookieNameValid(name) {
			continue
		}
		value, success := parseCookieValue(value)
		if !success {
			continue
		}
		c := &Cookie{
			Name:  name,
			Value: value,
			Raw:   line,
		}
		for i := 1; i < len(parts); i++ {
			parts[i] = strings.TrimSpace(parts[i])
			if len(parts[i]) == 0 {
				continue
			}

			attr, val := parts[i], ""
			if j := strings.Index(attr, "="); j >= 0 {
				attr, val = attr[:j], attr[j+1:]
			}
			lowerAttr := strings.ToLower(attr)
			parseCookieValueFn := parseCookieValue
			if lowerAttr == "expires" {
				parseCookieValueFn = parseCookieExpiresValue
			}
			val, success = parseCookieValueFn(val)
			if !success {
				c.Unparsed = append(c.Unparsed, parts[i])
				continue
			}
			switch lowerAttr {
			case "secure":
				c.Secure = true
				continue
			case "httponly":
				c.HttpOnly = true
				continue
			case "domain":
				c.Domain = val
				// TODO: Add domain parsing
				continue
			case "max-age":
				secs, err := strconv.Atoi(val)
				if err != nil || secs != 0 && val[0] == '0' {
					break
				}
				if secs <= 0 {
					c.MaxAge = -1
				} else {
					c.MaxAge = secs
				}
				continue
			case "expires":
				c.RawExpires = val
				exptime, err := time.Parse(time.RFC1123, val)
				if err != nil {
					exptime, err = time.Parse("Mon, 02-Jan-2006 15:04:05 MST", val)
					if err != nil {
						c.Expires = time.Time{}
						break
					}
				}
				c.Expires = exptime.UTC()
				continue
			case "path":
				c.Path = val
				// TODO: Add path parsing
				continue
			}
			c.Unparsed = append(c.Unparsed, parts[i])
		}
		cookies = append(cookies, c)
	}
	return cookies
}

// SetCookie adds a Set-Cookie header to the provided ResponseWriter's headers.
func SetCookie(w ResponseWriter, cookie *Cookie) {
	w.Header().Add("Set-Cookie", cookie.String())
}

// String returns the serialization of the cookie for use in a Cookie
// header (if only Name and Value are set) or a Set-Cookie response
// header (if other fields are set).
func (c *Cookie) String() string {
	var b bytes.Buffer
	fmt.Fprintf(&b, "%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value))
	if len(c.Path) > 0 {
		fmt.Fprintf(&b, "; Path=%s", sanitizeValue(c.Path))
	}
	if len(c.Domain) > 0 {
		fmt.Fprintf(&b, "; Domain=%s", sanitizeValue(c.Domain))
	}
	if c.Expires.Unix() > 0 {
		fmt.Fprintf(&b, "; Expires=%s", c.Expires.UTC().Format(time.RFC1123))
	}
	if c.MaxAge > 0 {
		fmt.Fprintf(&b, "; Max-Age=%d", c.MaxAge)
	} else if c.MaxAge < 0 {
		fmt.Fprintf(&b, "; Max-Age=0")
	}
	if c.HttpOnly {
		fmt.Fprintf(&b, "; HttpOnly")
	}
	if c.Secure {
		fmt.Fprintf(&b, "; Secure")
	}
	return b.String()
}

// readCookies parses all "Cookie" values from the header h and
// returns the successfully parsed Cookies.
//
// if filter isn't empty, only cookies of that name are returned
func readCookies(h Header, filter string) []*Cookie {
	cookies := []*Cookie{}
	lines, ok := h["Cookie"]
	if !ok {
		return cookies
	}

	for _, line := range lines {
		parts := strings.Split(strings.TrimSpace(line), ";")
		if len(parts) == 1 && parts[0] == "" {
			continue
		}
		// Per-line attributes
		parsedPairs := 0
		for i := 0; i < len(parts); i++ {
			parts[i] = strings.TrimSpace(parts[i])
			if len(parts[i]) == 0 {
				continue
			}
			name, val := parts[i], ""
			if j := strings.Index(name, "="); j >= 0 {
				name, val = name[:j], name[j+1:]
			}
			if !isCookieNameValid(name) {
				continue
			}
			if filter != "" && filter != name {
				continue
			}
			val, success := parseCookieValue(val)
			if !success {
				continue
			}
			cookies = append(cookies, &Cookie{Name: name, Value: val})
			parsedPairs++
		}
	}
	return cookies
}

var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-")

func sanitizeName(n string) string {
	return cookieNameSanitizer.Replace(n)
}

var cookieValueSanitizer = strings.NewReplacer("\n", " ", "\r", " ", ";", " ")

func sanitizeValue(v string) string {
	return cookieValueSanitizer.Replace(v)
}

func unquoteCookieValue(v string) string {
	if len(v) > 1 && v[0] == '"' && v[len(v)-1] == '"' {
		return v[1 : len(v)-1]
	}
	return v
}

func isCookieByte(c byte) bool {
	switch {
	case c == 0x21, 0x23 <= c && c <= 0x2b, 0x2d <= c && c <= 0x3a,
		0x3c <= c && c <= 0x5b, 0x5d <= c && c <= 0x7e:
		return true
	}
	return false
}

func isCookieExpiresByte(c byte) (ok bool) {
	return isCookieByte(c) || c == ',' || c == ' '
}

func parseCookieValue(raw string) (string, bool) {
	return parseCookieValueUsing(raw, isCookieByte)
}

func parseCookieExpiresValue(raw string) (string, bool) {
	return parseCookieValueUsing(raw, isCookieExpiresByte)
}

func parseCookieValueUsing(raw string, validByte func(byte) bool) (string, bool) {
	raw = unquoteCookieValue(raw)
	for i := 0; i < len(raw); i++ {
		if !validByte(raw[i]) {
			return "", false
		}
	}
	return raw, true
}

func isCookieNameValid(raw string) bool {
	for _, c := range raw {
		if !isToken(byte(c)) {
			return false
		}
	}
	return true
}
<|endoftext|>


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

// Package atomic provides low-level atomic memory primitives
// useful for implementing synchronization algorithms.
//
// These functions require great care to be used correctly.
// Except for special, low-level applications, synchronization is better
// done with channels or the facilities of the sync package.
// Share memory by communicating;
// don't communicate by sharing memory.
//
// The compare-and-swap operation, implemented by the CompareAndSwapT
// functions, is the atomic equivalent of:
//
//	if *val == old {
//		*val = new
//		return true
//	}
//	return false
//
package atomic

// BUG(rsc):
// On ARM, the 64-bit functions use instructions unavailable before ARM 11.
//
// On x86-32, the 64-bit functions use instructions unavailable before the Pentium.

// CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value.
func CompareAndSwapInt32(val *int32, old, new int32) (swapped bool)

// CompareAndSwapInt64 executes the compare-and-swap operation for an int64 value.
func CompareAndSwapInt64(val *int64, old, new int64) (swapped bool)

// CompareAndSwapUint32 executes the compare-and-swap operation for a uint32 value.
func CompareAndSwapUint32(val *uint32, old, new uint32) (swapped bool)

// CompareAndSwapUint64 executes the compare-and-swap operation for a uint64 value.
func CompareAndSwapUint64(val *uint64, old, new uint64) (swapped bool)

// CompareAndSwapUintptr executes the compare-and-swap operation for a uintptr value.
func CompareAndSwapUintptr(val *uintptr, old, new uintptr) (swapped bool)

// AddInt32 atomically adds delta to *val and returns the new value.
func AddInt32(val *int32, delta int32) (new int32)

// AddUint32 atomically adds delta to *val and returns the new value.
func AddUint32(val *uint32, delta uint32) (new uint32)

// AddInt64 atomically adds delta to *val and returns the new value.
func AddInt64(val *int64, delta int64) (new int64)

// AddUint64 atomically adds delta to *val and returns the new value.
func AddUint64(val *uint64, delta uint64) (new uint64)

// AddUintptr atomically adds delta to *val and returns the new value.
func AddUintptr(val *uintptr, delta uintptr) (new uintptr)
<commit_msg>sync/atomic: make BUG comment godoc-compatible<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package atomic provides low-level atomic memory primitives
// useful for implementing synchronization algorithms.
//
// These functions require great care to be used correctly.
// Except for special, low-level applications, synchronization is better
// done with channels or the facilities of the sync package.
// Share memory by communicating;
// don't communicate by sharing memory.
//
// The compare-and-swap operation, implemented by the CompareAndSwapT
// functions, is the atomic equivalent of:
//
//	if *val == old {
//		*val = new
//		return true
//	}
//	return false
//
package atomic

// BUG(rsc): On ARM, the 64-bit functions use instructions unavailable before ARM 11.
//
// On x86-32, the 64-bit functions use instructions unavailable before the Pentium.

// CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value.
func CompareAndSwapInt32(val *int32, old, new int32) (swapped bool)

// CompareAndSwapInt64 executes the compare-and-swap operation for an int64 value.
func CompareAndSwapInt64(val *int64, old, new int64) (swapped bool)

// CompareAndSwapUint32 executes the compare-and-swap operation for a uint32 value.
func CompareAndSwapUint32(val *uint32, old, new uint32) (swapped bool)

// CompareAndSwapUint64 executes the compare-and-swap operation for a uint64 value.
func CompareAndSwapUint64(val *uint64, old, new uint64) (swapped bool)

// CompareAndSwapUintptr executes the compare-and-swap operation for a uintptr value.
func CompareAndSwapUintptr(val *uintptr, old, new uintptr) (swapped bool)

// AddInt32 atomically adds delta to *val and returns the new value.
func AddInt32(val *int32, delta int32) (new int32)

// AddUint32 atomically adds delta to *val and returns the new value.
func AddUint32(val *uint32, delta uint32) (new uint32)

// AddInt64 atomically adds delta to *val and returns the new value.
func AddInt64(val *int64, delta int64) (new int64)

// AddUint64 atomically adds delta to *val and returns the new value.
func AddUint64(val *uint64, delta uint64) (new uint64)

// AddUintptr atomically adds delta to *val and returns the new value.
func AddUintptr(val *uintptr, delta uintptr) (new uintptr)
<|endoftext|>


<commit_before>package todotxt

import (
        "testing"
        "github.com/stretchr/testify/assert"
)

func TestLoadTaskList (t *testing.T) {
        tasklist := LoadTaskList("todo.txt")
        assert.Equal(t, tasklist.Len(), 8, "Something went wrong with LoadTaskList")
}

func TestLoadTaskListNonExistent (t *testing.T) {
        defer func(){
                if r:=recover(); r!=nil {
                        // recovered
                } else {
                        t.Errorf("Something went seriously wrong")
                }
        }()
        tasklist := LoadTaskList("nonexistent-file.txt")

        t.Errorf("Something is still wrong %v", tasklist)
}

func TestCreateTask (t *testing.T) {
        task := CreateTask(1, "(A) +funny task with prioity and project")

        assert.Equal(t, task.id, 1, "id should be 1")
        assert.Equal(t, rune(task.priority), rune('A'), "priority should be A")

        projects := make([]string, 1)
        projects[0] = "+funny"

        assert.Equal(t, task.projects, projects, "there should be a project for sure")
        assert.Equal(t, task.todo, "+funny task with prioity and project", "todo should equal")

}
<commit_msg>added finished task test<commit_after>package todotxt

import (
        "testing"
        "github.com/stretchr/testify/assert"
)

func TestLoadTaskList (t *testing.T) {
        tasklist := LoadTaskList("todo.txt")
        assert.Equal(t, tasklist.Len(), 8, "Something went wrong with LoadTaskList")
}

func TestLoadTaskListNonExistent (t *testing.T) {
        defer func(){
                if r:=recover(); r!=nil {
                        // recovered
                } else {
                        t.Errorf("Something went seriously wrong")
                }
        }()
        tasklist := LoadTaskList("nonexistent-file.txt")

        t.Errorf("Something is still wrong %v", tasklist)
}

func TestCreateTask (t *testing.T) {
        task := CreateTask(1, "(A) +funny task with prioity and project")

        assert.Equal(t, task.id, 1, "id should be 1")
        assert.Equal(t, rune(task.priority), rune('A'), "priority should be A")

        projects := make([]string, 1)
        projects[0] = "+funny"

        assert.Equal(t, task.projects, projects, "there should be a project for sure")
        assert.Equal(t, task.todo, "+funny task with prioity and project", "todo should equal")

        finished_task := CreateTask(1, "x This is a finished task")

        assert.Equal(t, finished_task.id, 1)
        assert.Equal(t, finished_task.todo, "This is a finished task")
}
<|endoftext|>


<commit_before>package main

import "strings"

func initRString() *RObject {
	obj := &RObject{}
	obj.name = "RString"
	obj.ivars = make(map[string]Object)
	obj.class = nil
	obj.methods = make(map[string]*RMethod)

	// RString method initialization
	obj.methods["new"] = &RMethod{gofunc: RString_new}
	obj.methods["to_s"] = &RMethod{gofunc: RString_to_s}
	obj.methods["inspect"] = &RMethod{gofunc: RString_inspect}
	obj.methods["size"] = &RMethod{gofunc: RString_length}
	obj.methods["len"] = &RMethod{gofunc: RString_length}
	obj.methods["split"] = &RMethod{gofunc: RString_split}

	return obj
}

// String.new(str='')
// v = [string]
func RString_new(vm *GobiesVM, receiver Object, v []Object) Object {
	str := ""
	if len(v) == 1 {
		str = v[0].(string)
	}

	obj := &RObject{}
	obj.class = vm.consts["RString"]
	obj.val.str = str

	return obj
}

func RString_to_s(vm *GobiesVM, receiver Object, v []Object) Object {
	obj := receiver.(*RObject)
	return obj.val.str
}

func RString_inspect(vm *GobiesVM, receiver Object, v []Object) Object {
	obj := receiver.(*RObject)
	str := obj.val.str
	array := []string{"'", str, "'"}
	return strings.Join(array, "")
}

func RString_length(vm *GobiesVM, receiver Object, v []Object) Object {
	obj := receiver.(*RObject)
	arg := make([]Object, 1, 1)
	arg[0] = int64(len(obj.val.str))
	return RFixnum_new(vm, receiver, arg)
}

func RString_split(vm *GobiesVM, receiver Object, v []Object) Object {
	obj := receiver.(*RObject)
	sep := v[0].(*RObject).val.str

	// Manually escape linebreak if any
	sep = strings.Replace(sep, "\\n", "\n", -1)

	strList := strings.Split(obj.val.str, sep)
	arg := make([]Object, len(strList), len(strList))
	for i, v := range strList {
		dummy_arg := make([]Object, 1, 1)
		dummy_arg[0] = v
		arg[i] = RString_new(vm, nil, dummy_arg)
	}
	return RArray_new(vm, nil, arg)
}
<commit_msg>[RString] modify split() behavior to fit MRI stdlib<commit_after>package main

import "strings"

func initRString() *RObject {
	obj := &RObject{}
	obj.name = "RString"
	obj.ivars = make(map[string]Object)
	obj.class = nil
	obj.methods = make(map[string]*RMethod)

	// RString method initialization
	obj.methods["new"] = &RMethod{gofunc: RString_new}
	obj.methods["to_s"] = &RMethod{gofunc: RString_to_s}
	obj.methods["inspect"] = &RMethod{gofunc: RString_inspect}
	obj.methods["size"] = &RMethod{gofunc: RString_length}
	obj.methods["len"] = &RMethod{gofunc: RString_length}
	obj.methods["split"] = &RMethod{gofunc: RString_split}

	return obj
}

// String.new(str='')
// v = [string]
func RString_new(vm *GobiesVM, receiver Object, v []Object) Object {
	str := ""
	if len(v) == 1 {
		str = v[0].(string)
	}

	obj := &RObject{}
	obj.class = vm.consts["RString"]
	obj.val.str = str

	return obj
}

func RString_to_s(vm *GobiesVM, receiver Object, v []Object) Object {
	obj := receiver.(*RObject)
	return obj.val.str
}

func RString_inspect(vm *GobiesVM, receiver Object, v []Object) Object {
	obj := receiver.(*RObject)
	str := obj.val.str
	array := []string{"'", str, "'"}
	return strings.Join(array, "")
}

func RString_length(vm *GobiesVM, receiver Object, v []Object) Object {
	obj := receiver.(*RObject)
	arg := make([]Object, 1, 1)
	arg[0] = int64(len(obj.val.str))
	return RFixnum_new(vm, receiver, arg)
}

func RString_split(vm *GobiesVM, receiver Object, v []Object) Object {
	obj := receiver.(*RObject)
	sep := v[0].(*RObject).val.str
	str := obj.val.str

	// Manually unescape linebreak if any
	sep = strings.Replace(sep, "\\n", "\n", -1)

	strList := []string{str}

	// Split string individually for each separator
	for _, pass := range sep {
		oldStrList := []string{}

		for _, item := range strList {
			oldStrList = append(oldStrList, item)
		}
		strList = []string{}
		for _, item := range oldStrList {
			tempStrList := strings.Split(item, string(pass))
			for _, splitted := range tempStrList {
				if len(splitted) == 0 {
					continue
				}
				strList = append(strList, splitted)
			}
		}
	}

	arg := make([]Object, len(strList), len(strList))
	for i, v := range strList {
		dummy_arg := make([]Object, 1, 1)
		dummy_arg[0] = v
		arg[i] = RString_new(vm, nil, dummy_arg)
	}
	return RArray_new(vm, nil, arg)
}
<|endoftext|>


<commit_before>package main

func initRThread() *RObject {
	obj := &RObject{}
	obj.name = "RThread"
	obj.ivars = make(map[string]Object)
	obj.class = nil
	obj.methods = make(map[string]*RMethod)

	// RThread method initialization
	obj.methods["new"] = &RMethod{gofunc: RThread_new}

	return obj
}

func RThread_new(vm *GobiesVM, env *ThreadEnv, receiver Object, v []Object) Object {
	vm.transactionEnd(env)

	wg.Add(1)
	go vm.executeThread(v[0].(*RObject).methods["def"].def)

	vm.transactionBegin(env, []Instruction{})

	return nil
}
<commit_msg>Thread.new doesn't need to care about transaction<commit_after>package main

func initRThread() *RObject {
	obj := &RObject{}
	obj.name = "RThread"
	obj.ivars = make(map[string]Object)
	obj.class = nil
	obj.methods = make(map[string]*RMethod)

	// RThread method initialization
	obj.methods["new"] = &RMethod{gofunc: RThread_new}

	return obj
}

func RThread_new(vm *GobiesVM, env *ThreadEnv, receiver Object, v []Object) Object {
	wg.Add(1)
	go vm.executeThread(v[0].(*RObject).methods["def"].def, env)

	return nil
}
<|endoftext|>


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

package main

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

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

// Advanced tokens, that are generated in the 2nd step from identifiers
const TOKEN_FOR = 27;
const TOKEN_IF = 28;

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

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

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

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

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

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

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

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

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

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

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

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

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

    //
    // Actual scanning part starts here
    //

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return newToken;
}

func GetNextToken(fd uint64, oldToken Token) Token {
    var newToken Token;
    newToken = GetNextTokenRaw(fd,oldToken)

    // Convert integer from byte array to integer value
    if newToken.id == TOKEN_INTEGER {
        newToken.intValue = ByteBufToInt(newToken.value,newToken.value_len);
    }

    // Convert single quoted characters to integer
    if newToken.id == TOKEN_CHAR {
        if newToken.value_len != 1 {
            tmp_error (">> Scanner: Only single characters are supported!");
        } else {
            newToken.id = TOKEN_INTEGER;
            newToken.intValue = ToIntFromByte(newToken.value[0]);
        }
    }

    // Convert identifier to keyworded tokens (if possible)
    // <TODO>

    return newToken;
}

// Move to libgogo?
func ToIntFromByte(b byte) uint64 {
    return uint64(b);
}

// Move to libgogo?
func ByteBufToInt(byteBuf [255]byte, bufLen uint64) uint64 {
    var m1 uint64;
    var i uint64;
    var val uint64;

    val = 0;

    for i = 0; bufLen > 0 ; bufLen = bufLen -1 {
        m1 = pow(10,bufLen);
        val = val + mul( m1, uint64(byteBuf[i]) - 48 );
        i = i +1;
    }

    return val;
}

// Move to libgogo?
func mul(m1 uint64, m2 uint64) uint64 {
    var val uint64;
    for val = 0 ; m2 > 0 ; m2 = m2 -1  {
        val = val + m1;
    }
    return val;
}

// Move to libgogo?
func pow(base uint64, exponent uint64) uint64 {
    var val uint64;
    for val = 1; exponent > 1 ; exponent = exponent -1 {
        val = mul(val,base);
    }
    return val;
}

// Move to libgogo?
func charToIntToken(oldToken Token) Token {
    return oldToken;
}

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

// Move something like this to libgogo?
func tmp_error ( s string) {
    fmt.Printf("%s\n",s);
    libgogo.Exit(1);
}

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

    for tok = GetNextToken(fd,tok); tok.id != TOKEN_EOS; tok = GetNextToken(fd,tok) {
        tmp_print(tok);
    }
}
<commit_msg>scanner.go: Output of integer token values and more efficient conversion of strings to the latter (no more need for additional mul and pow routines)<commit_after>// Copyright 2010 The GoGo Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package main

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

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

// Advanced tokens, that are generated in the 2nd step from identifiers
const TOKEN_FOR = 27;
const TOKEN_IF = 28;

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

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

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

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

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

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

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

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

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

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

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

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

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

    //
    // Actual scanning part starts here
    //

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return newToken;
}

func GetNextToken(fd uint64, oldToken Token) Token {
    var newToken Token;
    newToken = GetNextTokenRaw(fd,oldToken)

    // Convert integer from byte array to integer value
    if newToken.id == TOKEN_INTEGER {
        newToken.intValue = ByteBufToInt(newToken.value,newToken.value_len);
    }

    // Convert single quoted characters to integer
    if newToken.id == TOKEN_CHAR {
        if newToken.value_len != 1 {
            tmp_error (">> Scanner: Only single characters are supported!");
        } else {
            newToken.id = TOKEN_INTEGER;
            newToken.intValue = ToIntFromByte(newToken.value[0]);
        }
    }

    // Convert identifier to keyworded tokens (if possible)
    // <TODO>

    return newToken;
}

// Move to libgogo?
func ToIntFromByte(b byte) uint64 {
    return uint64(b);
}

// Move to libgogo?
func ByteBufToInt(byteBuf [255]byte, bufLen uint64) uint64 {
    var i uint64;
    var val uint64;

    val = 0;

    for i = 0; i < bufLen ; i = i +1 {
        val = val * 10;
        val = val + uint64(byteBuf[i]) - 48;
    }

    return val;
}

// Move to libgogo?
func charToIntToken(oldToken Token) Token {
    return oldToken;
}

// Move something like this to libgogo?
func tmp_print(tok Token) {
    var i int;
    fmt.Printf("Token Id: %d\n",tok.id);
    if tok.id == TOKEN_IDENTIFIER || tok.id == TOKEN_STRING {
        fmt.Printf("Identifier/String value: ");
        for i=0;tok.value[i] != 0;i=i+1 {
            fmt.Printf("%c",tok.value[i]);
        }
        fmt.Printf("\n");
    } else {
        if tok.id == TOKEN_INTEGER {
            fmt.Printf("Integer value: ");
            fmt.Printf("%d", tok.intValue);
            fmt.Printf("\n");
        }
    }
}

// Move something like this to libgogo?
func tmp_error ( s string) {
    fmt.Printf("%s\n",s);
    libgogo.Exit(1);
}

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

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


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

package templates

import (
	"bytes"
	"fmt"
	"io"
	"net"
	"net/http"
	"path"
	"strconv"
	"strings"
	"sync"
	"text/template"

	"github.com/Masterminds/sprig/v3"
	"github.com/alecthomas/chroma/formatters/html"
	"github.com/caddyserver/caddy/v2/modules/caddyhttp"
	"github.com/yuin/goldmark"
	highlighting "github.com/yuin/goldmark-highlighting"
	"github.com/yuin/goldmark/extension"
	"github.com/yuin/goldmark/parser"
	gmhtml "github.com/yuin/goldmark/renderer/html"
)

// templateContext is the templateContext with which HTTP templates are executed.
type templateContext struct {
	Root       http.FileSystem
	Req        *http.Request
	Args       []interface{} // defined by arguments to .Include
	RespHeader tplWrappedHeader

	config *Templates
}

// OriginalReq returns the original, unmodified, un-rewritten request as
// it originally came in over the wire.
func (c templateContext) OriginalReq() http.Request {
	or, _ := c.Req.Context().Value(caddyhttp.OriginalRequestCtxKey).(http.Request)
	return or
}

// funcInclude returns the contents of filename relative to the site root.
// Note that included files are NOT escaped, so you should only include
// trusted files. If it is not trusted, be sure to use escaping functions
// in your template.
func (c templateContext) funcInclude(filename string, args ...interface{}) (string, error) {
	if c.Root == nil {
		return "", fmt.Errorf("root file system not specified")
	}

	file, err := c.Root.Open(filename)
	if err != nil {
		return "", err
	}
	defer file.Close()

	bodyBuf := bufPool.Get().(*bytes.Buffer)
	bodyBuf.Reset()
	defer bufPool.Put(bodyBuf)

	_, err = io.Copy(bodyBuf, file)
	if err != nil {
		return "", err
	}

	c.Args = args

	err = c.executeTemplateInBuffer(filename, bodyBuf)
	if err != nil {
		return "", err
	}

	return bodyBuf.String(), nil
}

// funcHTTPInclude returns the body of a virtual (lightweight) request
// to the given URI on the same server. Note that included bodies
// are NOT escaped, so you should only include trusted resources.
// If it is not trusted, be sure to use escaping functions yourself.
func (c templateContext) funcHTTPInclude(uri string) (string, error) {
	// prevent virtual request loops by counting how many levels
	// deep we are; and if we get too deep, return an error
	recursionCount := 1
	if numStr := c.Req.Header.Get(recursionPreventionHeader); numStr != "" {
		num, err := strconv.Atoi(numStr)
		if err != nil {
			return "", fmt.Errorf("parsing %s: %v", recursionPreventionHeader, err)
		}
		if num >= 3 {
			return "", fmt.Errorf("virtual request cycle")
		}
		recursionCount = num + 1
	}

	buf := bufPool.Get().(*bytes.Buffer)
	buf.Reset()
	defer bufPool.Put(buf)

	virtReq, err := http.NewRequest("GET", uri, nil)
	if err != nil {
		return "", err
	}
	virtReq.Host = c.Req.Host
	virtReq.Header = c.Req.Header.Clone()
	virtReq.Trailer = c.Req.Trailer.Clone()
	virtReq.Header.Set(recursionPreventionHeader, strconv.Itoa(recursionCount))

	vrw := &virtualResponseWriter{body: buf, header: make(http.Header)}
	server := c.Req.Context().Value(caddyhttp.ServerCtxKey).(http.Handler)

	server.ServeHTTP(vrw, virtReq)
	if vrw.status >= 400 {
		return "", fmt.Errorf("http %d", vrw.status)
	}

	err = c.executeTemplateInBuffer(uri, buf)
	if err != nil {
		return "", err
	}

	return buf.String(), nil
}

func (c templateContext) executeTemplateInBuffer(tplName string, buf *bytes.Buffer) error {
	tpl := template.New(tplName)
	if len(c.config.Delimiters) == 2 {
		tpl.Delims(c.config.Delimiters[0], c.config.Delimiters[1])
	}

	tpl.Funcs(sprigFuncMap)

	tpl.Funcs(template.FuncMap{
		"include":          c.funcInclude,
		"httpInclude":      c.funcHTTPInclude,
		"stripHTML":        c.funcStripHTML,
		"markdown":         c.funcMarkdown,
		"splitFrontMatter": c.funcSplitFrontMatter,
		"listFiles":        c.funcListFiles,
	})

	parsedTpl, err := tpl.Parse(buf.String())
	if err != nil {
		return err
	}

	buf.Reset() // reuse buffer for output

	return parsedTpl.Execute(buf, c)
}

// Cookie gets the value of a cookie with name name.
func (c templateContext) Cookie(name string) string {
	cookies := c.Req.Cookies()
	for _, cookie := range cookies {
		if cookie.Name == name {
			return cookie.Value
		}
	}
	return ""
}

// RemoteIP gets the IP address of the client making the request.
func (c templateContext) RemoteIP() string {
	ip, _, err := net.SplitHostPort(c.Req.RemoteAddr)
	if err != nil {
		return c.Req.RemoteAddr
	}
	return ip
}

// Host returns the hostname portion of the Host header
// from the HTTP request.
func (c templateContext) Host() (string, error) {
	host, _, err := net.SplitHostPort(c.Req.Host)
	if err != nil {
		if !strings.Contains(c.Req.Host, ":") {
			// common with sites served on the default port 80
			return c.Req.Host, nil
		}
		return "", err
	}
	return host, nil
}

// funcStripHTML returns s without HTML tags. It is fairly naive
// but works with most valid HTML inputs.
func (c templateContext) funcStripHTML(s string) string {
	var buf bytes.Buffer
	var inTag, inQuotes bool
	var tagStart int
	for i, ch := range s {
		if inTag {
			if ch == '>' && !inQuotes {
				inTag = false
			} else if ch == '<' && !inQuotes {
				// false start
				buf.WriteString(s[tagStart:i])
				tagStart = i
			} else if ch == '"' {
				inQuotes = !inQuotes
			}
			continue
		}
		if ch == '<' {
			inTag = true
			tagStart = i
			continue
		}
		buf.WriteRune(ch)
	}
	if inTag {
		// false start
		buf.WriteString(s[tagStart:])
	}
	return buf.String()
}

// funcMarkdown renders the markdown body as HTML. The resulting
// HTML is NOT escaped so that it can be rendered as HTML.
func (c templateContext) funcMarkdown(input interface{}) (string, error) {
	inputStr := toString(input)

	md := goldmark.New(
		goldmark.WithExtensions(
			extension.GFM,
			extension.Footnote,
			highlighting.NewHighlighting(
				highlighting.WithFormatOptions(
					html.WithClasses(true),
				),
			),
		),
		goldmark.WithParserOptions(
			parser.WithAutoHeadingID(),
		),
		goldmark.WithRendererOptions(
			gmhtml.WithHardWraps(),
			gmhtml.WithUnsafe(), // TODO: this is not awesome, maybe should be configurable?
		),
	)

	buf := bufPool.Get().(*bytes.Buffer)
	buf.Reset()
	defer bufPool.Put(buf)

	md.Convert([]byte(inputStr), buf)

	return buf.String(), nil
}

// splitFrontMatter parses front matter out from the beginning of input,
// and returns the separated key-value pairs and the body/content. input
// must be a "stringy" value.
func (c templateContext) funcSplitFrontMatter(input interface{}) (parsedMarkdownDoc, error) {
	meta, body, err := extractFrontMatter(toString(input))
	if err != nil {
		return parsedMarkdownDoc{}, err
	}
	return parsedMarkdownDoc{Meta: meta, Body: body}, nil
}

// funcListFiles reads and returns a slice of names from the given
// directory relative to the root of c.
func (c templateContext) funcListFiles(name string) ([]string, error) {
	if c.Root == nil {
		return nil, fmt.Errorf("root file system not specified")
	}

	dir, err := c.Root.Open(path.Clean(name))
	if err != nil {
		return nil, err
	}
	defer dir.Close()

	stat, err := dir.Stat()
	if err != nil {
		return nil, err
	}
	if !stat.IsDir() {
		return nil, fmt.Errorf("%v is not a directory", name)
	}

	dirInfo, err := dir.Readdir(0)
	if err != nil {
		return nil, err
	}

	names := make([]string, len(dirInfo))
	for i, fileInfo := range dirInfo {
		names[i] = fileInfo.Name()
	}

	return names, nil
}

// tplWrappedHeader wraps niladic functions so that they
// can be used in templates. (Template functions must
// return a value.)
type tplWrappedHeader struct{ http.Header }

// Add adds a header field value, appending val to
// existing values for that field. It returns an
// empty string.
func (h tplWrappedHeader) Add(field, val string) string {
	h.Header.Add(field, val)
	return ""
}

// Set sets a header field value, overwriting any
// other values for that field. It returns an
// empty string.
func (h tplWrappedHeader) Set(field, val string) string {
	h.Header.Set(field, val)
	return ""
}

// Del deletes a header field. It returns an empty string.
func (h tplWrappedHeader) Del(field string) string {
	h.Header.Del(field)
	return ""
}

func toString(input interface{}) string {
	switch v := input.(type) {
	case string:
		return v
	case fmt.Stringer:
		return v.String()
	case error:
		return v.Error()
	default:
		return fmt.Sprintf("%v", input)
	}
}

var bufPool = sync.Pool{
	New: func() interface{} {
		return new(bytes.Buffer)
	},
}

// at time of writing, sprig.FuncMap() makes a copy, thus
// involves iterating the whole map, so do it just once
var sprigFuncMap = sprig.TxtFuncMap()

const recursionPreventionHeader = "Caddy-Templates-Include"
<commit_msg>templates: Add env function (closes #3237)<commit_after>// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package templates

import (
	"bytes"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"sync"
	"text/template"

	"github.com/Masterminds/sprig/v3"
	"github.com/alecthomas/chroma/formatters/html"
	"github.com/caddyserver/caddy/v2/modules/caddyhttp"
	"github.com/yuin/goldmark"
	highlighting "github.com/yuin/goldmark-highlighting"
	"github.com/yuin/goldmark/extension"
	"github.com/yuin/goldmark/parser"
	gmhtml "github.com/yuin/goldmark/renderer/html"
)

// templateContext is the templateContext with which HTTP templates are executed.
type templateContext struct {
	Root       http.FileSystem
	Req        *http.Request
	Args       []interface{} // defined by arguments to .Include
	RespHeader tplWrappedHeader

	config *Templates
}

// OriginalReq returns the original, unmodified, un-rewritten request as
// it originally came in over the wire.
func (c templateContext) OriginalReq() http.Request {
	or, _ := c.Req.Context().Value(caddyhttp.OriginalRequestCtxKey).(http.Request)
	return or
}

// funcInclude returns the contents of filename relative to the site root.
// Note that included files are NOT escaped, so you should only include
// trusted files. If it is not trusted, be sure to use escaping functions
// in your template.
func (c templateContext) funcInclude(filename string, args ...interface{}) (string, error) {
	if c.Root == nil {
		return "", fmt.Errorf("root file system not specified")
	}

	file, err := c.Root.Open(filename)
	if err != nil {
		return "", err
	}
	defer file.Close()

	bodyBuf := bufPool.Get().(*bytes.Buffer)
	bodyBuf.Reset()
	defer bufPool.Put(bodyBuf)

	_, err = io.Copy(bodyBuf, file)
	if err != nil {
		return "", err
	}

	c.Args = args

	err = c.executeTemplateInBuffer(filename, bodyBuf)
	if err != nil {
		return "", err
	}

	return bodyBuf.String(), nil
}

// funcHTTPInclude returns the body of a virtual (lightweight) request
// to the given URI on the same server. Note that included bodies
// are NOT escaped, so you should only include trusted resources.
// If it is not trusted, be sure to use escaping functions yourself.
func (c templateContext) funcHTTPInclude(uri string) (string, error) {
	// prevent virtual request loops by counting how many levels
	// deep we are; and if we get too deep, return an error
	recursionCount := 1
	if numStr := c.Req.Header.Get(recursionPreventionHeader); numStr != "" {
		num, err := strconv.Atoi(numStr)
		if err != nil {
			return "", fmt.Errorf("parsing %s: %v", recursionPreventionHeader, err)
		}
		if num >= 3 {
			return "", fmt.Errorf("virtual request cycle")
		}
		recursionCount = num + 1
	}

	buf := bufPool.Get().(*bytes.Buffer)
	buf.Reset()
	defer bufPool.Put(buf)

	virtReq, err := http.NewRequest("GET", uri, nil)
	if err != nil {
		return "", err
	}
	virtReq.Host = c.Req.Host
	virtReq.Header = c.Req.Header.Clone()
	virtReq.Trailer = c.Req.Trailer.Clone()
	virtReq.Header.Set(recursionPreventionHeader, strconv.Itoa(recursionCount))

	vrw := &virtualResponseWriter{body: buf, header: make(http.Header)}
	server := c.Req.Context().Value(caddyhttp.ServerCtxKey).(http.Handler)

	server.ServeHTTP(vrw, virtReq)
	if vrw.status >= 400 {
		return "", fmt.Errorf("http %d", vrw.status)
	}

	err = c.executeTemplateInBuffer(uri, buf)
	if err != nil {
		return "", err
	}

	return buf.String(), nil
}

func (c templateContext) executeTemplateInBuffer(tplName string, buf *bytes.Buffer) error {
	tpl := template.New(tplName)
	if len(c.config.Delimiters) == 2 {
		tpl.Delims(c.config.Delimiters[0], c.config.Delimiters[1])
	}

	tpl.Funcs(sprigFuncMap)

	tpl.Funcs(template.FuncMap{
		"include":          c.funcInclude,
		"httpInclude":      c.funcHTTPInclude,
		"stripHTML":        c.funcStripHTML,
		"markdown":         c.funcMarkdown,
		"splitFrontMatter": c.funcSplitFrontMatter,
		"listFiles":        c.funcListFiles,
		"env":              c.funcEnv,
	})

	parsedTpl, err := tpl.Parse(buf.String())
	if err != nil {
		return err
	}

	buf.Reset() // reuse buffer for output

	return parsedTpl.Execute(buf, c)
}

func (templateContext) funcEnv(varName string) string {
	return os.Getenv(varName)
}

// Cookie gets the value of a cookie with name name.
func (c templateContext) Cookie(name string) string {
	cookies := c.Req.Cookies()
	for _, cookie := range cookies {
		if cookie.Name == name {
			return cookie.Value
		}
	}
	return ""
}

// RemoteIP gets the IP address of the client making the request.
func (c templateContext) RemoteIP() string {
	ip, _, err := net.SplitHostPort(c.Req.RemoteAddr)
	if err != nil {
		return c.Req.RemoteAddr
	}
	return ip
}

// Host returns the hostname portion of the Host header
// from the HTTP request.
func (c templateContext) Host() (string, error) {
	host, _, err := net.SplitHostPort(c.Req.Host)
	if err != nil {
		if !strings.Contains(c.Req.Host, ":") {
			// common with sites served on the default port 80
			return c.Req.Host, nil
		}
		return "", err
	}
	return host, nil
}

// funcStripHTML returns s without HTML tags. It is fairly naive
// but works with most valid HTML inputs.
func (templateContext) funcStripHTML(s string) string {
	var buf bytes.Buffer
	var inTag, inQuotes bool
	var tagStart int
	for i, ch := range s {
		if inTag {
			if ch == '>' && !inQuotes {
				inTag = false
			} else if ch == '<' && !inQuotes {
				// false start
				buf.WriteString(s[tagStart:i])
				tagStart = i
			} else if ch == '"' {
				inQuotes = !inQuotes
			}
			continue
		}
		if ch == '<' {
			inTag = true
			tagStart = i
			continue
		}
		buf.WriteRune(ch)
	}
	if inTag {
		// false start
		buf.WriteString(s[tagStart:])
	}
	return buf.String()
}

// funcMarkdown renders the markdown body as HTML. The resulting
// HTML is NOT escaped so that it can be rendered as HTML.
func (templateContext) funcMarkdown(input interface{}) (string, error) {
	inputStr := toString(input)

	md := goldmark.New(
		goldmark.WithExtensions(
			extension.GFM,
			extension.Footnote,
			highlighting.NewHighlighting(
				highlighting.WithFormatOptions(
					html.WithClasses(true),
				),
			),
		),
		goldmark.WithParserOptions(
			parser.WithAutoHeadingID(),
		),
		goldmark.WithRendererOptions(
			gmhtml.WithHardWraps(),
			gmhtml.WithUnsafe(), // TODO: this is not awesome, maybe should be configurable?
		),
	)

	buf := bufPool.Get().(*bytes.Buffer)
	buf.Reset()
	defer bufPool.Put(buf)

	md.Convert([]byte(inputStr), buf)

	return buf.String(), nil
}

// splitFrontMatter parses front matter out from the beginning of input,
// and returns the separated key-value pairs and the body/content. input
// must be a "stringy" value.
func (templateContext) funcSplitFrontMatter(input interface{}) (parsedMarkdownDoc, error) {
	meta, body, err := extractFrontMatter(toString(input))
	if err != nil {
		return parsedMarkdownDoc{}, err
	}
	return parsedMarkdownDoc{Meta: meta, Body: body}, nil
}

// funcListFiles reads and returns a slice of names from the given
// directory relative to the root of c.
func (c templateContext) funcListFiles(name string) ([]string, error) {
	if c.Root == nil {
		return nil, fmt.Errorf("root file system not specified")
	}

	dir, err := c.Root.Open(path.Clean(name))
	if err != nil {
		return nil, err
	}
	defer dir.Close()

	stat, err := dir.Stat()
	if err != nil {
		return nil, err
	}
	if !stat.IsDir() {
		return nil, fmt.Errorf("%v is not a directory", name)
	}

	dirInfo, err := dir.Readdir(0)
	if err != nil {
		return nil, err
	}

	names := make([]string, len(dirInfo))
	for i, fileInfo := range dirInfo {
		names[i] = fileInfo.Name()
	}

	return names, nil
}

// tplWrappedHeader wraps niladic functions so that they
// can be used in templates. (Template functions must
// return a value.)
type tplWrappedHeader struct{ http.Header }

// Add adds a header field value, appending val to
// existing values for that field. It returns an
// empty string.
func (h tplWrappedHeader) Add(field, val string) string {
	h.Header.Add(field, val)
	return ""
}

// Set sets a header field value, overwriting any
// other values for that field. It returns an
// empty string.
func (h tplWrappedHeader) Set(field, val string) string {
	h.Header.Set(field, val)
	return ""
}

// Del deletes a header field. It returns an empty string.
func (h tplWrappedHeader) Del(field string) string {
	h.Header.Del(field)
	return ""
}

func toString(input interface{}) string {
	switch v := input.(type) {
	case string:
		return v
	case fmt.Stringer:
		return v.String()
	case error:
		return v.Error()
	default:
		return fmt.Sprintf("%v", input)
	}
}

var bufPool = sync.Pool{
	New: func() interface{} {
		return new(bytes.Buffer)
	},
}

// at time of writing, sprig.FuncMap() makes a copy, thus
// involves iterating the whole map, so do it just once
var sprigFuncMap = sprig.TxtFuncMap()

const recursionPreventionHeader = "Caddy-Templates-Include"
<|endoftext|>


<commit_before><commit_msg>added support for removal of projects<commit_after><|endoftext|>


<commit_before>package cloudup

import (
	"encoding/base64"
	"encoding/binary"
	"fmt"
	"k8s.io/kops/upup/pkg/api"
	"math/big"
	"net"
	"sort"
	"strings"
	"text/template"
)

type TemplateFunctions struct {
	cluster *api.Cluster
	tags    map[string]struct{}
	region  string
}

func (tf *TemplateFunctions) WellKnownServiceIP(id int) (net.IP, error) {
	_, cidr, err := net.ParseCIDR(tf.cluster.Spec.ServiceClusterIPRange)
	if err != nil {
		return nil, fmt.Errorf("error parsing ServiceClusterIPRange %q: %v", tf.cluster.Spec.ServiceClusterIPRange, err)
	}

	ip4 := cidr.IP.To4()
	if ip4 != nil {
		n := binary.BigEndian.Uint32(ip4)
		n += uint32(id)
		serviceIP := make(net.IP, len(ip4))
		binary.BigEndian.PutUint32(serviceIP, n)
		return serviceIP, nil
	}

	ip6 := cidr.IP.To16()
	if ip6 != nil {
		baseIPInt := big.NewInt(0)
		baseIPInt.SetBytes(ip6)
		serviceIPInt := big.NewInt(0)
		serviceIPInt.Add(big.NewInt(int64(id)), baseIPInt)
		serviceIP := make(net.IP, len(ip6))
		serviceIPBytes := serviceIPInt.Bytes()
		for i := range serviceIPBytes {
			serviceIP[len(serviceIP)-len(serviceIPBytes)+i] = serviceIPBytes[i]
		}
		return serviceIP, nil
	}

	return nil, fmt.Errorf("Unexpected IP address type for ServiceClusterIPRange: %s", tf.cluster.Spec.ServiceClusterIPRange)
}

func (tf *TemplateFunctions) AddTo(dest template.FuncMap) {
	dest["EtcdClusterMemberTags"] = tf.EtcdClusterMemberTags
	dest["SharedVPC"] = tf.SharedVPC
	dest["WellKnownServiceIP"] = tf.WellKnownServiceIP
	dest["AdminCIDR"] = tf.AdminCIDR

	dest["Base64Encode"] = func(s string) string {
		return base64.StdEncoding.EncodeToString([]byte(s))
	}
	dest["replace"] = func(s, find, replace string) string {
		return strings.Replace(s, find, replace, -1)
	}
	dest["join"] = func(a []string, sep string) string {
		return strings.Join(a, sep)
	}

	dest["ClusterName"] = func() string {
		return tf.cluster.Name
	}

	dest["HasTag"] = func(tag string) bool {
		_, found := tf.tags[tag]
		return found
	}

	dest["IAMPrefix"] = tf.IAMPrefix
	dest["IAMServiceEC2"] = tf.IAMServiceEC2

	dest["AssociatePublicIP"] = func() string {
		return tf.cluster.Spec.AssociatePublicIP
	}
}

func (tf *TemplateFunctions) EtcdClusterMemberTags(etcd *api.EtcdClusterSpec, m *api.EtcdMemberSpec) map[string]string {
	tags := make(map[string]string)

	var allMembers []string

	for _, m := range etcd.Members {
		allMembers = append(allMembers, m.Name)
	}

	sort.Strings(allMembers)

	// This is the configuration of the etcd cluster
	tags["k8s.io/etcd/"+etcd.Name] = m.Name + "/" + strings.Join(allMembers, ",")

	// This says "only mount on a master"
	tags["k8s.io/role/master"] = "1"

	return tags
}

// SharedVPC is a simple helper function which makes the templates for a shared VPC clearer
func (tf *TemplateFunctions) SharedVPC() bool {
	return tf.cluster.Spec.NetworkID != ""
}

// AdminCIDR returns the single CIDR that is allowed access to the admin ports of the cluster (22, 443 on master)
func (tf *TemplateFunctions) AdminCIDR() (string, error) {
	if len(tf.cluster.Spec.AdminAccess) == 0 {
		return "0.0.0.0/0", nil
	}
	if len(tf.cluster.Spec.AdminAccess) == 1 {
		return tf.cluster.Spec.AdminAccess[0], nil
	}
	return "", fmt.Errorf("Multiple AdminAccess rules are not (currently) supported")
}

// IAMServiceEC2 returns the name of the IAM service for EC2 in the current region
// it is ec2.amazonaws.com everywhere but in cn-north, where it is ec2.amazonaws.com.cn
func (tf *TemplateFunctions) IAMServiceEC2() string {
	switch tf.region {
	case "cn-north-1":
		return "ec2.amazonaws.com.cn"
	default:
		return "ec2.amazonaws.com"
	}
}

// IAMPrefix returns the prefix for AWS ARNs in the current region, for use with IAM
// it is arn:aws everywhere but in cn-north, where it is arn:aws-cn
func (tf *TemplateFunctions) IAMPrefix() string {
	switch tf.region {
	case "cn-north-1":
		return "arn:aws-cn"
	default:
		return "arn:aws"
	}
}
<commit_msg>add parameter --disable-associate-public-ip<commit_after>package cloudup

import (
	"encoding/base64"
	"encoding/binary"
	"fmt"
	"k8s.io/kops/upup/pkg/api"
	"math/big"
	"net"
	"sort"
	"strings"
	"text/template"
)

type TemplateFunctions struct {
	cluster *api.Cluster
	tags    map[string]struct{}
	region  string
}

func (tf *TemplateFunctions) WellKnownServiceIP(id int) (net.IP, error) {
	_, cidr, err := net.ParseCIDR(tf.cluster.Spec.ServiceClusterIPRange)
	if err != nil {
		return nil, fmt.Errorf("error parsing ServiceClusterIPRange %q: %v", tf.cluster.Spec.ServiceClusterIPRange, err)
	}

	ip4 := cidr.IP.To4()
	if ip4 != nil {
		n := binary.BigEndian.Uint32(ip4)
		n += uint32(id)
		serviceIP := make(net.IP, len(ip4))
		binary.BigEndian.PutUint32(serviceIP, n)
		return serviceIP, nil
	}

	ip6 := cidr.IP.To16()
	if ip6 != nil {
		baseIPInt := big.NewInt(0)
		baseIPInt.SetBytes(ip6)
		serviceIPInt := big.NewInt(0)
		serviceIPInt.Add(big.NewInt(int64(id)), baseIPInt)
		serviceIP := make(net.IP, len(ip6))
		serviceIPBytes := serviceIPInt.Bytes()
		for i := range serviceIPBytes {
			serviceIP[len(serviceIP)-len(serviceIPBytes)+i] = serviceIPBytes[i]
		}
		return serviceIP, nil
	}

	return nil, fmt.Errorf("Unexpected IP address type for ServiceClusterIPRange: %s", tf.cluster.Spec.ServiceClusterIPRange)
}

func (tf *TemplateFunctions) AddTo(dest template.FuncMap) {
	dest["EtcdClusterMemberTags"] = tf.EtcdClusterMemberTags
	dest["SharedVPC"] = tf.SharedVPC
	dest["WellKnownServiceIP"] = tf.WellKnownServiceIP
	dest["AdminCIDR"] = tf.AdminCIDR

	dest["Base64Encode"] = func(s string) string {
		return base64.StdEncoding.EncodeToString([]byte(s))
	}
	dest["replace"] = func(s, find, replace string) string {
		return strings.Replace(s, find, replace, -1)
	}
	dest["join"] = func(a []string, sep string) string {
		return strings.Join(a, sep)
	}

	dest["ClusterName"] = func() string {
		return tf.cluster.Name
	}

	dest["HasTag"] = func(tag string) bool {
		_, found := tf.tags[tag]
		return found
	}

	dest["IAMPrefix"] = tf.IAMPrefix
	dest["IAMServiceEC2"] = tf.IAMServiceEC2

	dest["AssociatePublicIP"] = func() bool {
		return tf.cluster.Spec.AssociatePublicIP
	}
}

func (tf *TemplateFunctions) EtcdClusterMemberTags(etcd *api.EtcdClusterSpec, m *api.EtcdMemberSpec) map[string]string {
	tags := make(map[string]string)

	var allMembers []string

	for _, m := range etcd.Members {
		allMembers = append(allMembers, m.Name)
	}

	sort.Strings(allMembers)

	// This is the configuration of the etcd cluster
	tags["k8s.io/etcd/"+etcd.Name] = m.Name + "/" + strings.Join(allMembers, ",")

	// This says "only mount on a master"
	tags["k8s.io/role/master"] = "1"

	return tags
}

// SharedVPC is a simple helper function which makes the templates for a shared VPC clearer
func (tf *TemplateFunctions) SharedVPC() bool {
	return tf.cluster.Spec.NetworkID != ""
}

// AdminCIDR returns the single CIDR that is allowed access to the admin ports of the cluster (22, 443 on master)
func (tf *TemplateFunctions) AdminCIDR() (string, error) {
	if len(tf.cluster.Spec.AdminAccess) == 0 {
		return "0.0.0.0/0", nil
	}
	if len(tf.cluster.Spec.AdminAccess) == 1 {
		return tf.cluster.Spec.AdminAccess[0], nil
	}
	return "", fmt.Errorf("Multiple AdminAccess rules are not (currently) supported")
}

// IAMServiceEC2 returns the name of the IAM service for EC2 in the current region
// it is ec2.amazonaws.com everywhere but in cn-north, where it is ec2.amazonaws.com.cn
func (tf *TemplateFunctions) IAMServiceEC2() string {
	switch tf.region {
	case "cn-north-1":
		return "ec2.amazonaws.com.cn"
	default:
		return "ec2.amazonaws.com"
	}
}

// IAMPrefix returns the prefix for AWS ARNs in the current region, for use with IAM
// it is arn:aws everywhere but in cn-north, where it is arn:aws-cn
func (tf *TemplateFunctions) IAMPrefix() string {
	switch tf.region {
	case "cn-north-1":
		return "arn:aws-cn"
	default:
		return "arn:aws"
	}
}
<|endoftext|>