<commit_before>package main

/*
 * Copyright 2014 Albert P. Tobey <atobey@datastax.com> @AlTobey
 *
 * 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.
 *
 * This program is a Docker entrypoint for Apache Cassandra.
 */

import (
	"bytes"
	"flag"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/tobert/sprok"
	"gopkg.in/yaml.v2"
)

const ugid = 1337

type CassandraDockerConfig struct {
	VolDir         string   // read/write data, should be a volume
	SrcConfDir     string   // root path for assets to copy to the volume
	ConfDir        string   // conf directory
	DataDir        string   // data directory
	CommitLogDir   string   // cl directory
	LogDir         string   // log directory
	SavedCachesDir string   // saved_caches directory
	CqlshDotDir    string   // ~/.cassandra
	CassandraYaml  string   // conf/cassandra.yaml
	SprokDir       string   // conf/sproks directory
	ExtraArgs      []string // extra args not caught by flag.Parse
	// Cassandra configuration items
	ClusterName      string // cluster_name in cassandra.yaml
	Seeds            string // seeds value for cassandra.yaml
	CassandraLogfile string // system.log
	DefaultIP        string // IP of the default route interface
	JmxPort          int
}

func main() {
	cdc := CassandraDockerConfig{
		VolDir:           "/data",
		SrcConfDir:       "/src/conf",
		ConfDir:          "/data/conf",
		DataDir:          "/data/data",
		CommitLogDir:     "/data/commitlog",
		LogDir:           "/data/log",
		SavedCachesDir:   "/data/saved_caches",
		CqlshDotDir:      "/data/.cassandra",
		CassandraYaml:    "/data/conf/cassandra.yaml",
		SprokDir:         "/data/conf/sproks",
		ClusterName:      "Docker Cluster",
		Seeds:            "127.0.0.1",
		CassandraLogfile: "/data/log/system.log",
		DefaultIP:        "127.0.0.1",
		JmxPort:          7199,
	}

	var command, sprokFile string
	var args []string

	// handle symlink commands, e.g. ln -s /bin/cassandra-docker /bin/cqlsh
	if path.Base(os.Args[0]) != "cassandra-docker" {
		command = path.Base(os.Args[0])
		args = os.Args[1:]
	} else {
		// extract the subcommand from os.Args
		switch len(os.Args) {
		// no arguments, just start Cassandra
		// TODO: this should probably only happen if it's pid 1
		case 1:
			command = "cassandra"
		// exactly one argument with no args
		case 2:
			command = os.Args[1]
			args = []string{}
		// command + arguments
		default:
			command = os.Args[1]
			args = os.Args[2:]
		}
	}

	// parse the subcommand and arguments to it
	fs := flag.NewFlagSet("cassandra-docker", flag.ExitOnError)
	switch command {
	case "cassandra":
		fs.StringVar(&cdc.Seeds, "seeds", "127.0.0.1", "comma-delimited list of seeds for clustering")
		fs.StringVar(&cdc.ClusterName, "name", "Docker Cluster", "cluster_name")
		sprokFile = path.Join(cdc.SprokDir, "cassandra.yaml")
	case "cqlsh":
		sprokFile = path.Join(cdc.SprokDir, "cqlsh.yaml")
	case "nodetool":
		fs.IntVar(&cdc.JmxPort, "p", 7199, "jmx port")
		sprokFile = path.Join(cdc.SprokDir, "nodetool.yaml")
	case "cassandra-stress":
		sprokFile = path.Join(cdc.SprokDir, "cassandra-stress.yaml")
	default:
		log.Fatalf("invalid command '%s'", command)
	}

	fs.Parse(args)

	// copy the remaining command-line args to cdc
	cdc.ExtraArgs = fs.Args()

	// bootstrap - find the default IP, make directories, copy files
	cdc.setDefaultIP()
	if strings.EqualFold(cdc.Seeds,"127.0.0.1") {
		cdc.Seeds=cdc.DefaultIP;
	}

	cdc.mkdirs()
	// copies files from src to data, running them through as templates
	// in the process. existing files are not overwritten
	cdc.tmplCopy()

	// load the sprok config
	fd, err := os.Open(sprokFile)
	if err != nil {
		log.Fatalf("error opening '%s' for read: %s\n", sprokFile, err)
	}

	// render the config template before unmarshaling
	// this allows sprok files to work across upgrades with smart use
	// of glob() to work around files with version numbers in them
	var data bytes.Buffer
	cdc.render(fd, &data)

	// configure the process from the yaml
	proc := sprok.NewProcess()
	err = yaml.Unmarshal(data.Bytes(), &proc)
	if err != nil {
		log.Fatalf("could not parse YAML in file '%s': %s\n", sprokFile, err)
	}

	// this is an actual execve(3p), this process is replaced with the new one
	proc.Exec()
}

func (cdc *CassandraDockerConfig) mkdirs() {
	mkdirAll(cdc.ConfDir)
	mkdirAll(cdc.DataDir)
	mkdirAll(cdc.CommitLogDir)
	mkdirAll(cdc.LogDir)
	mkdirAll(cdc.SavedCachesDir)
	mkdirAll(cdc.CqlshDotDir)
	mkdirAll(cdc.SprokDir)

	chownAll(cdc.DataDir)
	chownAll(cdc.CommitLogDir)
	chownAll(cdc.LogDir)
	chownAll(cdc.SavedCachesDir)
	chownAll(cdc.CqlshDotDir)
}

// tmplCopy reads all the files in cdc.SrcConfDir, treating them as text
// templates, then writes them to cdc.ConfDir. If a file exists in ConfDir
// it is not overwritten.
func (cdc *CassandraDockerConfig) tmplCopy() {
	walk := func(fromName string, fromFi os.FileInfo, err error) error {
		if err != nil {
			log.Fatalf("failed to find files in '%s': %s\n", cdc.SrcConfDir, err)
		}

		// only safe for same filesystem with no relative paths or symlinks
		toName := strings.Replace(fromName, cdc.SrcConfDir, cdc.ConfDir, 1)

		if exists(toName) {
			return nil // try not to overwrite any existing files
		} else if strings.HasPrefix(path.Base(fromName), ".") {
			return nil
		} else if fromFi.IsDir() {
			if exists(toName) {
				return nil
			} else {
				mkdirAll(toName)
			}
		} else if fromFi.Mode().IsRegular() {
			// don't render sprok files, only copy them
			// they will get rendered at run time
			if strings.HasSuffix(path.Dir(fromName), "sproks") {
				cp(fromName, toName)
			} else {
				cdc.renderFile(fromName, toName)
			}
		} else {
			log.Fatalf("unsupported file mode on file '%s'\n", fromName)
		}

		return nil
	}

	err := filepath.Walk(cdc.SrcConfDir, walk)
	if err != nil {
		log.Fatalf("tmplCopy() failed: %s\n", err)
	}
}

// renderFile renders one file to another using text/template
func (cdc *CassandraDockerConfig) renderFile(src, dest string) {
	in, err := os.Open(src)
	if err != nil {
		log.Fatalf("could not open '%s' for reading: %s\n", src, err)
	}
	defer in.Close()

	out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		log.Fatalf("could not open '%s' for write: %s\n", dest, err)
	}
	defer out.Close()

	cdc.render(in, out)
}

// render renders an io.Reader to an io.Writer using text/template
func (cdc *CassandraDockerConfig) render(in io.Reader, out io.Writer) {
	funcMap := template.FuncMap{
		"glob": Globber,
	}

	tdata, err := ioutil.ReadAll(in)
	if err != nil {
		log.Fatalf("template read failed: %s\n", err)
	}

	tmpl, err := template.New("whatever").Funcs(funcMap).Parse(string(tdata))
	if err != nil {
		log.Fatalf("template parsing failed: %s", err)
	}

	err = tmpl.Execute(out, cdc)
	if err != nil {
		log.Fatalf("template rendering failed: %s\n", err)
	}
}

// setDefaultIP finds the first configured interface that is not a loopback
// and sets the cdc.DefaultIP value
func (cdc *CassandraDockerConfig) setDefaultIP() {
	ifaces, err := net.Interfaces()
	if err != nil {
		log.Fatalf("error while listing network interfaces: %s\n", err)
	}

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 {
			continue // interface is down
		}
		if iface.Flags&net.FlagLoopback != 0 {
			continue // ignore loopback interface
		}

		addrs, err := iface.Addrs()
		if err != nil {
			log.Fatalf("error while examining network interface: %s\n", err)
		}

		// for now, just go with the first interface that is up
		// and is not a loopback, which should cover most Docker setups
		for _, addr := range addrs {
			switch v := addr.(type) {
			case *net.IPNet:
				cdc.DefaultIP = v.IP.String()
				return
			}
		}
	}
}

// Globber takes paths, performs a glob match, then returns
// all the results joined with the specified seperator.
func Globber(sep string, globs ...string) string {
	if len(globs) == 0 {
		log.Fatalf("Globber() requires at least one path.\n")
	}

	out := []string{}

	for _, glob := range globs {
		filenames, err := filepath.Glob(glob)
		if err != nil {
			log.Fatalf("file glob failed: %s\n", err)
		}

		for _, filename := range filenames {
			out = append(out, filename)
		}
	}

	return strings.Join(out, sep)
}

// exists returns boolean whether a path exists or not
func exists(name string) bool {
	_, err := os.Stat(name)
	if os.IsNotExist(err) {
		return false
	} else if err != nil {
		log.Fatalf("could not stat file '%s': %s\n", name, err)
	}

	return true
}

// mkdirAll creates a directory recursively, crashes the program on error.
func mkdirAll(name string) {
	err := os.MkdirAll(name, 0755)
	if err != nil {
		log.Fatalf("os.MkdirAll('%s') failed: %s\n", name)
	}
}

func chownAll(name string) {
	walk := func(fname string, fi os.FileInfo, err error) error {
		if err != nil {
			log.Fatalf("error during fs walk of '%s': %s\n", fname, err)

		}

		return os.Chown(fname, ugid, ugid)
	}

	err := filepath.Walk(name, walk)
	if err != nil {
		log.Fatalf("chownAll('%s') failed: %s\n", name, err)
	}
}

// cp copies a file, crashing the program on any errors
// It does not attempt to use rename.
func cp(from, to string) {
	in, err := os.Open(from)
	if err != nil {
		log.Fatalf("could not open '%s' for reading: %s\n", from, err)
	}
	defer in.Close()

	out, err := os.OpenFile(to, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		log.Fatalf("could not open '%s' for writing: %s\n", to, err)
	}
	defer out.Close()

	_, err = io.Copy(out, in)
	if err != nil {
		log.Fatalf("data copy failed for file '%s': %s\n", to, err)
	}
}
<commit_msg>go fmt<commit_after>package main

/*
 * Copyright 2014 Albert P. Tobey <atobey@datastax.com> @AlTobey
 *
 * 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.
 *
 * This program is a Docker entrypoint for Apache Cassandra.
 */

import (
	"bytes"
	"flag"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/tobert/sprok"
	"gopkg.in/yaml.v2"
)

const ugid = 1337

type CassandraDockerConfig struct {
	VolDir         string   // read/write data, should be a volume
	SrcConfDir     string   // root path for assets to copy to the volume
	ConfDir        string   // conf directory
	DataDir        string   // data directory
	CommitLogDir   string   // cl directory
	LogDir         string   // log directory
	SavedCachesDir string   // saved_caches directory
	CqlshDotDir    string   // ~/.cassandra
	CassandraYaml  string   // conf/cassandra.yaml
	SprokDir       string   // conf/sproks directory
	ExtraArgs      []string // extra args not caught by flag.Parse
	// Cassandra configuration items
	ClusterName      string // cluster_name in cassandra.yaml
	Seeds            string // seeds value for cassandra.yaml
	CassandraLogfile string // system.log
	DefaultIP        string // IP of the default route interface
	JmxPort          int
}

func main() {
	cdc := CassandraDockerConfig{
		VolDir:           "/data",
		SrcConfDir:       "/src/conf",
		ConfDir:          "/data/conf",
		DataDir:          "/data/data",
		CommitLogDir:     "/data/commitlog",
		LogDir:           "/data/log",
		SavedCachesDir:   "/data/saved_caches",
		CqlshDotDir:      "/data/.cassandra",
		CassandraYaml:    "/data/conf/cassandra.yaml",
		SprokDir:         "/data/conf/sproks",
		ClusterName:      "Docker Cluster",
		Seeds:            "127.0.0.1",
		CassandraLogfile: "/data/log/system.log",
		DefaultIP:        "127.0.0.1",
		JmxPort:          7199,
	}

	var command, sprokFile string
	var args []string

	// handle symlink commands, e.g. ln -s /bin/cassandra-docker /bin/cqlsh
	if path.Base(os.Args[0]) != "cassandra-docker" {
		command = path.Base(os.Args[0])
		args = os.Args[1:]
	} else {
		// extract the subcommand from os.Args
		switch len(os.Args) {
		// no arguments, just start Cassandra
		// TODO: this should probably only happen if it's pid 1
		case 1:
			command = "cassandra"
		// exactly one argument with no args
		case 2:
			command = os.Args[1]
			args = []string{}
		// command + arguments
		default:
			command = os.Args[1]
			args = os.Args[2:]
		}
	}

	// parse the subcommand and arguments to it
	fs := flag.NewFlagSet("cassandra-docker", flag.ExitOnError)
	switch command {
	case "cassandra":
		fs.StringVar(&cdc.Seeds, "seeds", "127.0.0.1", "comma-delimited list of seeds for clustering")
		fs.StringVar(&cdc.ClusterName, "name", "Docker Cluster", "cluster_name")
		sprokFile = path.Join(cdc.SprokDir, "cassandra.yaml")
	case "cqlsh":
		sprokFile = path.Join(cdc.SprokDir, "cqlsh.yaml")
	case "nodetool":
		fs.IntVar(&cdc.JmxPort, "p", 7199, "jmx port")
		sprokFile = path.Join(cdc.SprokDir, "nodetool.yaml")
	case "cassandra-stress":
		sprokFile = path.Join(cdc.SprokDir, "cassandra-stress.yaml")
	default:
		log.Fatalf("invalid command '%s'", command)
	}

	fs.Parse(args)

	// copy the remaining command-line args to cdc
	cdc.ExtraArgs = fs.Args()

	// bootstrap - find the default IP, make directories, copy files
	cdc.setDefaultIP()
	if strings.EqualFold(cdc.Seeds, "127.0.0.1") {
		cdc.Seeds = cdc.DefaultIP
	}

	cdc.mkdirs()
	// copies files from src to data, running them through as templates
	// in the process. existing files are not overwritten
	cdc.tmplCopy()

	// load the sprok config
	fd, err := os.Open(sprokFile)
	if err != nil {
		log.Fatalf("error opening '%s' for read: %s\n", sprokFile, err)
	}

	// render the config template before unmarshaling
	// this allows sprok files to work across upgrades with smart use
	// of glob() to work around files with version numbers in them
	var data bytes.Buffer
	cdc.render(fd, &data)

	// configure the process from the yaml
	proc := sprok.NewProcess()
	err = yaml.Unmarshal(data.Bytes(), &proc)
	if err != nil {
		log.Fatalf("could not parse YAML in file '%s': %s\n", sprokFile, err)
	}

	// this is an actual execve(3p), this process is replaced with the new one
	proc.Exec()
}

func (cdc *CassandraDockerConfig) mkdirs() {
	mkdirAll(cdc.ConfDir)
	mkdirAll(cdc.DataDir)
	mkdirAll(cdc.CommitLogDir)
	mkdirAll(cdc.LogDir)
	mkdirAll(cdc.SavedCachesDir)
	mkdirAll(cdc.CqlshDotDir)
	mkdirAll(cdc.SprokDir)

	chownAll(cdc.DataDir)
	chownAll(cdc.CommitLogDir)
	chownAll(cdc.LogDir)
	chownAll(cdc.SavedCachesDir)
	chownAll(cdc.CqlshDotDir)
}

// tmplCopy reads all the files in cdc.SrcConfDir, treating them as text
// templates, then writes them to cdc.ConfDir. If a file exists in ConfDir
// it is not overwritten.
func (cdc *CassandraDockerConfig) tmplCopy() {
	walk := func(fromName string, fromFi os.FileInfo, err error) error {
		if err != nil {
			log.Fatalf("failed to find files in '%s': %s\n", cdc.SrcConfDir, err)
		}

		// only safe for same filesystem with no relative paths or symlinks
		toName := strings.Replace(fromName, cdc.SrcConfDir, cdc.ConfDir, 1)

		if exists(toName) {
			return nil // try not to overwrite any existing files
		} else if strings.HasPrefix(path.Base(fromName), ".") {
			return nil
		} else if fromFi.IsDir() {
			if exists(toName) {
				return nil
			} else {
				mkdirAll(toName)
			}
		} else if fromFi.Mode().IsRegular() {
			// don't render sprok files, only copy them
			// they will get rendered at run time
			if strings.HasSuffix(path.Dir(fromName), "sproks") {
				cp(fromName, toName)
			} else {
				cdc.renderFile(fromName, toName)
			}
		} else {
			log.Fatalf("unsupported file mode on file '%s'\n", fromName)
		}

		return nil
	}

	err := filepath.Walk(cdc.SrcConfDir, walk)
	if err != nil {
		log.Fatalf("tmplCopy() failed: %s\n", err)
	}
}

// renderFile renders one file to another using text/template
func (cdc *CassandraDockerConfig) renderFile(src, dest string) {
	in, err := os.Open(src)
	if err != nil {
		log.Fatalf("could not open '%s' for reading: %s\n", src, err)
	}
	defer in.Close()

	out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		log.Fatalf("could not open '%s' for write: %s\n", dest, err)
	}
	defer out.Close()

	cdc.render(in, out)
}

// render renders an io.Reader to an io.Writer using text/template
func (cdc *CassandraDockerConfig) render(in io.Reader, out io.Writer) {
	funcMap := template.FuncMap{
		"glob": Globber,
	}

	tdata, err := ioutil.ReadAll(in)
	if err != nil {
		log.Fatalf("template read failed: %s\n", err)
	}

	tmpl, err := template.New("whatever").Funcs(funcMap).Parse(string(tdata))
	if err != nil {
		log.Fatalf("template parsing failed: %s", err)
	}

	err = tmpl.Execute(out, cdc)
	if err != nil {
		log.Fatalf("template rendering failed: %s\n", err)
	}
}

// setDefaultIP finds the first configured interface that is not a loopback
// and sets the cdc.DefaultIP value
func (cdc *CassandraDockerConfig) setDefaultIP() {
	ifaces, err := net.Interfaces()
	if err != nil {
		log.Fatalf("error while listing network interfaces: %s\n", err)
	}

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 {
			continue // interface is down
		}
		if iface.Flags&net.FlagLoopback != 0 {
			continue // ignore loopback interface
		}

		addrs, err := iface.Addrs()
		if err != nil {
			log.Fatalf("error while examining network interface: %s\n", err)
		}

		// for now, just go with the first interface that is up
		// and is not a loopback, which should cover most Docker setups
		for _, addr := range addrs {
			switch v := addr.(type) {
			case *net.IPNet:
				cdc.DefaultIP = v.IP.String()
				return
			}
		}
	}
}

// Globber takes paths, performs a glob match, then returns
// all the results joined with the specified seperator.
func Globber(sep string, globs ...string) string {
	if len(globs) == 0 {
		log.Fatalf("Globber() requires at least one path.\n")
	}

	out := []string{}

	for _, glob := range globs {
		filenames, err := filepath.Glob(glob)
		if err != nil {
			log.Fatalf("file glob failed: %s\n", err)
		}

		for _, filename := range filenames {
			out = append(out, filename)
		}
	}

	return strings.Join(out, sep)
}

// exists returns boolean whether a path exists or not
func exists(name string) bool {
	_, err := os.Stat(name)
	if os.IsNotExist(err) {
		return false
	} else if err != nil {
		log.Fatalf("could not stat file '%s': %s\n", name, err)
	}

	return true
}

// mkdirAll creates a directory recursively, crashes the program on error.
func mkdirAll(name string) {
	err := os.MkdirAll(name, 0755)
	if err != nil {
		log.Fatalf("os.MkdirAll('%s') failed: %s\n", name)
	}
}

func chownAll(name string) {
	walk := func(fname string, fi os.FileInfo, err error) error {
		if err != nil {
			log.Fatalf("error during fs walk of '%s': %s\n", fname, err)

		}

		return os.Chown(fname, ugid, ugid)
	}

	err := filepath.Walk(name, walk)
	if err != nil {
		log.Fatalf("chownAll('%s') failed: %s\n", name, err)
	}
}

// cp copies a file, crashing the program on any errors
// It does not attempt to use rename.
func cp(from, to string) {
	in, err := os.Open(from)
	if err != nil {
		log.Fatalf("could not open '%s' for reading: %s\n", from, err)
	}
	defer in.Close()

	out, err := os.OpenFile(to, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		log.Fatalf("could not open '%s' for writing: %s\n", to, err)
	}
	defer out.Close()

	_, err = io.Copy(out, in)
	if err != nil {
		log.Fatalf("data copy failed for file '%s': %s\n", to, err)
	}
}
<|endoftext|>


<commit_before>package multiclusterapp

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/pkg/errors"
	"github.com/rancher/norman/api/access"
	"github.com/rancher/norman/parse"
	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/convert"
	gaccess "github.com/rancher/rancher/pkg/api/customization/globalnamespaceaccess"
	"github.com/rancher/rancher/pkg/namespace"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	managementschema "github.com/rancher/types/apis/management.cattle.io/v3/schema"
	"github.com/rancher/types/client/management/v3"

	"k8s.io/apimachinery/pkg/api/meta"
)

const (
	addProjectsAction    = "addProjects"
	removeProjectsAction = "removeProjects"
)

func (w Wrapper) Formatter(apiContext *types.APIContext, resource *types.RawResource) {
	resource.AddAction(apiContext, "rollback")
	resource.AddAction(apiContext, addProjectsAction)
	resource.AddAction(apiContext, removeProjectsAction)
	resource.Links["revisions"] = apiContext.URLBuilder.Link("revisions", resource)
}

func (w Wrapper) ActionHandler(actionName string, action *types.Action, apiContext *types.APIContext) error {
	var mcApp client.MultiClusterApp
	if err := access.ByID(apiContext, &managementschema.Version, client.MultiClusterAppType, apiContext.ID, &mcApp); err != nil {
		return err
	}
	switch actionName {
	case "rollback":
		data, err := ioutil.ReadAll(apiContext.Request.Body)
		if err != nil {
			return errors.Wrap(err, "reading request body error")
		}
		input := client.MultiClusterAppRollbackInput{}
		if err = json.Unmarshal(data, &input); err != nil {
			return errors.Wrap(err, "unmarshal input error")
		}
		id := input.RevisionID
		splitID := strings.Split(input.RevisionID, ":")
		if len(splitID) == 2 {
			id = splitID[1]
		}
		revision, err := w.MultiClusterAppRevisionLister.Get(namespace.GlobalNamespace, id)
		if err != nil {
			return err
		}
		obj, err := w.MultiClusterAppLister.Get(namespace.GlobalNamespace, mcApp.Name)
		if err != nil {
			return err
		}
		if obj.Status.RevisionName == revision.Name {
			return nil
		}
		toUpdate := obj.DeepCopy()
		toUpdate.Spec.TemplateVersionName = revision.TemplateVersionName
		toUpdate.Spec.Answers = revision.Answers
		_, err = w.MultiClusterApps.Update(toUpdate)
		return err
	case addProjectsAction:
		return w.addProjects(apiContext)
	case removeProjectsAction:
		return w.removeProjects(apiContext)
	default:
		return fmt.Errorf("bad action for multiclusterapp %v", actionName)
	}
}

func (w Wrapper) addProjects(request *types.APIContext) error {
	mcapp, existingProjects, inputProjects, inputAnswers, err := w.modifyProjects(request, addProjectsAction)
	if err != nil {
		return err
	}
	updatedMcApp := mcapp
	if len(inputAnswers) > 0 {
		mcappToUpdate := mcapp.DeepCopy()
		mcappToUpdate.Spec.Answers = append(mcappToUpdate.Spec.Answers, inputAnswers...)
		if updatedMcApp, err = w.MultiClusterApps.Update(mcappToUpdate); err != nil {
			return err
		}
	}
	mcappToUpdate := updatedMcApp.DeepCopy()
	for _, p := range inputProjects {
		if !existingProjects[p] {
			mcappToUpdate.Spec.Targets = append(mcappToUpdate.Spec.Targets, v3.Target{ProjectName: p})
		}
	}
	return w.updateMcApp(mcappToUpdate, request, "addedProjects")
}

func (w Wrapper) removeProjects(request *types.APIContext) error {
	mcapp, _, inputProjects, _, err := w.modifyProjects(request, removeProjectsAction)
	if err != nil {
		return err
	}
	mcappToUpdate := mcapp.DeepCopy()
	toRemoveProjects := make(map[string]bool)
	var finalTargets []v3.Target
	for _, p := range inputProjects {
		toRemoveProjects[p] = true
	}
	for _, t := range mcapp.Spec.Targets {
		if !toRemoveProjects[t.ProjectName] {
			finalTargets = append(finalTargets, t)
		}
	}
	mcappToUpdate.Spec.Targets = finalTargets
	return w.updateMcApp(mcappToUpdate, request, "removedProjects")
}

func (w Wrapper) modifyProjects(request *types.APIContext, actionName string) (*v3.MultiClusterApp, map[string]bool, []string, []v3.Answer, error) {
	split := strings.SplitN(request.ID, ":", 2)
	if len(split) != 2 {
		return nil, map[string]bool{}, []string{}, []v3.Answer{}, fmt.Errorf("incorrect multi cluster app ID %v", request.ID)
	}
	var inputProjects []string
	var inputAnswers []v3.Answer
	existingProjects := make(map[string]bool)
	mcapp, err := w.MultiClusterAppLister.Get(split[0], split[1])
	if err != nil {
		return nil, existingProjects, inputProjects, inputAnswers, err
	}
	// ensure that caller is not a readonly member of multiclusterapp, else abort
	callerID := request.Request.Header.Get(gaccess.ImpersonateUserHeader)
	metaAccessor, err := meta.Accessor(mcapp)
	if err != nil {
		return nil, existingProjects, inputProjects, inputAnswers, err
	}
	creatorID, ok := metaAccessor.GetAnnotations()[creatorIDAnn]
	if !ok {
		return nil, existingProjects, inputProjects, inputAnswers, fmt.Errorf("multiclusterapp %v has no creatorId annotation", metaAccessor.GetName())
	}
	ma := gaccess.MemberAccess{
		Users:              w.Users,
		PrtbLister:         w.PrtbLister,
		CrtbLister:         w.CrtbLister,
		RoleTemplateLister: w.RoleTemplateLister,
	}
	accessType, err := ma.GetAccessTypeOfCaller(callerID, creatorID, mcapp.Name, mcapp.Spec.Members)
	if err != nil {
		return nil, existingProjects, inputProjects, inputAnswers, err
	}
	if accessType != gaccess.OwnerAccess {
		return nil, existingProjects, inputProjects, inputAnswers, fmt.Errorf("only owners can modify projects of multiclusterapp")
	}
	var updateMultiClusterAppTargetsInput v3.UpdateMultiClusterAppTargetsInput
	actionInput, err := parse.ReadBody(request.Request)
	if err != nil {
		return nil, existingProjects, inputProjects, inputAnswers, err
	}
	if err = convert.ToObj(actionInput, &updateMultiClusterAppTargetsInput); err != nil {
		return nil, existingProjects, inputProjects, inputAnswers, err
	}
	inputProjects = updateMultiClusterAppTargetsInput.Projects
	for _, p := range mcapp.Spec.Targets {
		existingProjects[p.ProjectName] = true
	}
	if actionName == addProjectsAction {
		if err = ma.EnsureRoleInTargets(inputProjects, mcapp.Spec.Roles, callerID); err != nil {
			return nil, existingProjects, inputProjects, inputAnswers, err
		}
	}
	inputAnswers = updateMultiClusterAppTargetsInput.Answers
	// check if the input includes answers, and if they are only for the input projects
	if len(inputAnswers) > 0 {
		inputProjectsMap := make(map[string]bool)
		for _, p := range inputProjects {
			if !inputProjectsMap[p] {
				inputProjectsMap[p] = true
			}
		}
		for _, a := range inputAnswers {
			if a.ProjectName == "" {
				return nil, existingProjects, inputProjects, inputAnswers, fmt.Errorf("can only provide project-scoped answers for new projects through add/remove projects action")
			}
			if !inputProjectsMap[a.ProjectName] {
				return nil, existingProjects, inputProjects, inputAnswers, fmt.Errorf("the project %v is not among the ones provided in input", a.ProjectName)
			}
		}
	}
	return mcapp, existingProjects, inputProjects, inputAnswers, nil
}

func (w Wrapper) updateMcApp(mcappToUpdate *v3.MultiClusterApp, request *types.APIContext, message string) error {
	if _, err := w.MultiClusterApps.Update(mcappToUpdate); err != nil {
		return err
	}

	op := map[string]interface{}{
		"message": message,
	}
	request.WriteResponse(http.StatusOK, op)
	return nil
}
<commit_msg>Update mcapp addProjectInput to client.UpdateMultiClusterAppTargetsInput<commit_after>package multiclusterapp

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/pkg/errors"
	"github.com/rancher/norman/api/access"
	"github.com/rancher/norman/parse"
	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/convert"
	gaccess "github.com/rancher/rancher/pkg/api/customization/globalnamespaceaccess"
	"github.com/rancher/rancher/pkg/namespace"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	managementschema "github.com/rancher/types/apis/management.cattle.io/v3/schema"
	"github.com/rancher/types/client/management/v3"

	"k8s.io/apimachinery/pkg/api/meta"
)

const (
	addProjectsAction    = "addProjects"
	removeProjectsAction = "removeProjects"
)

func (w Wrapper) Formatter(apiContext *types.APIContext, resource *types.RawResource) {
	resource.AddAction(apiContext, "rollback")
	resource.AddAction(apiContext, addProjectsAction)
	resource.AddAction(apiContext, removeProjectsAction)
	resource.Links["revisions"] = apiContext.URLBuilder.Link("revisions", resource)
}

func (w Wrapper) ActionHandler(actionName string, action *types.Action, apiContext *types.APIContext) error {
	var mcApp client.MultiClusterApp
	if err := access.ByID(apiContext, &managementschema.Version, client.MultiClusterAppType, apiContext.ID, &mcApp); err != nil {
		return err
	}
	switch actionName {
	case "rollback":
		data, err := ioutil.ReadAll(apiContext.Request.Body)
		if err != nil {
			return errors.Wrap(err, "reading request body error")
		}
		input := client.MultiClusterAppRollbackInput{}
		if err = json.Unmarshal(data, &input); err != nil {
			return errors.Wrap(err, "unmarshal input error")
		}
		id := input.RevisionID
		splitID := strings.Split(input.RevisionID, ":")
		if len(splitID) == 2 {
			id = splitID[1]
		}
		revision, err := w.MultiClusterAppRevisionLister.Get(namespace.GlobalNamespace, id)
		if err != nil {
			return err
		}
		obj, err := w.MultiClusterAppLister.Get(namespace.GlobalNamespace, mcApp.Name)
		if err != nil {
			return err
		}
		if obj.Status.RevisionName == revision.Name {
			return nil
		}
		toUpdate := obj.DeepCopy()
		toUpdate.Spec.TemplateVersionName = revision.TemplateVersionName
		toUpdate.Spec.Answers = revision.Answers
		_, err = w.MultiClusterApps.Update(toUpdate)
		return err
	case addProjectsAction:
		return w.addProjects(apiContext)
	case removeProjectsAction:
		return w.removeProjects(apiContext)
	default:
		return fmt.Errorf("bad action for multiclusterapp %v", actionName)
	}
}

func (w Wrapper) addProjects(request *types.APIContext) error {
	mcapp, existingProjects, inputProjects, inputAnswers, err := w.modifyProjects(request, addProjectsAction)
	if err != nil {
		return err
	}
	updatedMcApp := mcapp
	if len(inputAnswers) > 0 {
		mcappToUpdate := mcapp.DeepCopy()
		mcappToUpdate.Spec.Answers = append(mcappToUpdate.Spec.Answers, inputAnswers...)
		if updatedMcApp, err = w.MultiClusterApps.Update(mcappToUpdate); err != nil {
			return err
		}
	}
	mcappToUpdate := updatedMcApp.DeepCopy()
	for _, p := range inputProjects {
		if !existingProjects[p] {
			mcappToUpdate.Spec.Targets = append(mcappToUpdate.Spec.Targets, v3.Target{ProjectName: p})
		}
	}
	return w.updateMcApp(mcappToUpdate, request, "addedProjects")
}

func (w Wrapper) removeProjects(request *types.APIContext) error {
	mcapp, _, inputProjects, _, err := w.modifyProjects(request, removeProjectsAction)
	if err != nil {
		return err
	}
	mcappToUpdate := mcapp.DeepCopy()
	toRemoveProjects := make(map[string]bool)
	var finalTargets []v3.Target
	for _, p := range inputProjects {
		toRemoveProjects[p] = true
	}
	for _, t := range mcapp.Spec.Targets {
		if !toRemoveProjects[t.ProjectName] {
			finalTargets = append(finalTargets, t)
		}
	}
	mcappToUpdate.Spec.Targets = finalTargets
	return w.updateMcApp(mcappToUpdate, request, "removedProjects")
}

func (w Wrapper) modifyProjects(request *types.APIContext, actionName string) (*v3.MultiClusterApp, map[string]bool, []string, []v3.Answer, error) {
	split := strings.SplitN(request.ID, ":", 2)
	if len(split) != 2 {
		return nil, map[string]bool{}, []string{}, []v3.Answer{}, fmt.Errorf("incorrect multi cluster app ID %v", request.ID)
	}
	var inputProjects []string
	var inputAnswers []v3.Answer
	existingProjects := make(map[string]bool)
	mcapp, err := w.MultiClusterAppLister.Get(split[0], split[1])
	if err != nil {
		return nil, existingProjects, inputProjects, inputAnswers, err
	}
	// ensure that caller is not a readonly member of multiclusterapp, else abort
	callerID := request.Request.Header.Get(gaccess.ImpersonateUserHeader)
	metaAccessor, err := meta.Accessor(mcapp)
	if err != nil {
		return nil, existingProjects, inputProjects, inputAnswers, err
	}
	creatorID, ok := metaAccessor.GetAnnotations()[creatorIDAnn]
	if !ok {
		return nil, existingProjects, inputProjects, inputAnswers, fmt.Errorf("multiclusterapp %v has no creatorId annotation", metaAccessor.GetName())
	}
	ma := gaccess.MemberAccess{
		Users:              w.Users,
		PrtbLister:         w.PrtbLister,
		CrtbLister:         w.CrtbLister,
		RoleTemplateLister: w.RoleTemplateLister,
	}
	accessType, err := ma.GetAccessTypeOfCaller(callerID, creatorID, mcapp.Name, mcapp.Spec.Members)
	if err != nil {
		return nil, existingProjects, inputProjects, inputAnswers, err
	}
	if accessType != gaccess.OwnerAccess {
		return nil, existingProjects, inputProjects, inputAnswers, fmt.Errorf("only owners can modify projects of multiclusterapp")
	}
	var updateMultiClusterAppTargetsInput client.UpdateMultiClusterAppTargetsInput
	actionInput, err := parse.ReadBody(request.Request)
	if err != nil {
		return nil, existingProjects, inputProjects, inputAnswers, err
	}
	if err = convert.ToObj(actionInput, &updateMultiClusterAppTargetsInput); err != nil {
		return nil, existingProjects, inputProjects, inputAnswers, err
	}
	inputProjects = updateMultiClusterAppTargetsInput.Projects
	for _, p := range mcapp.Spec.Targets {
		existingProjects[p.ProjectName] = true
	}
	if actionName == addProjectsAction {
		if err = ma.EnsureRoleInTargets(inputProjects, mcapp.Spec.Roles, callerID); err != nil {
			return nil, existingProjects, inputProjects, inputAnswers, err
		}
	}
	for _, a := range updateMultiClusterAppTargetsInput.Answers {
		inputAnswers = append(inputAnswers, v3.Answer{
			ProjectName: a.ProjectID,
			ClusterName: a.ClusterID,
			Values:      a.Values,
		})
	}
	// check if the input includes answers, and if they are only for the input projects
	if len(inputAnswers) > 0 {
		inputProjectsMap := make(map[string]bool)
		for _, p := range inputProjects {
			if !inputProjectsMap[p] {
				inputProjectsMap[p] = true
			}
		}
		for _, a := range inputAnswers {
			if a.ProjectName == "" {
				return nil, existingProjects, inputProjects, inputAnswers, fmt.Errorf("can only provide project-scoped answers for new projects through add/remove projects action")
			}
			if !inputProjectsMap[a.ProjectName] {
				return nil, existingProjects, inputProjects, inputAnswers, fmt.Errorf("the project %v is not among the ones provided in input", a.ProjectName)
			}
		}
	}
	return mcapp, existingProjects, inputProjects, inputAnswers, nil
}

func (w Wrapper) updateMcApp(mcappToUpdate *v3.MultiClusterApp, request *types.APIContext, message string) error {
	if _, err := w.MultiClusterApps.Update(mcappToUpdate); err != nil {
		return err
	}

	op := map[string]interface{}{
		"message": message,
	}
	request.WriteResponse(http.StatusOK, op)
	return nil
}
<|endoftext|>


<commit_before>package version

import (
	"fmt"
	"runtime"
	"strings"

	"github.com/lxc/lxd/shared/osarch"
)

// UserAgent contains a string suitable as a user-agent
var UserAgent = getUserAgent()
var userAgentStorageBackends []string
var userAgentFeatures []string

func getUserAgent() string {
	archID, err := osarch.ArchitectureId(runtime.GOARCH)
	if err != nil {
		panic(err)
	}

	arch, err := osarch.ArchitectureName(archID)
	if err != nil {
		panic(err)
	}

	osTokens := []string{strings.Title(runtime.GOOS), arch}
	osTokens = append(osTokens, getPlatformVersionStrings()...)

	// Initial version string
	agent := fmt.Sprintf("LXD %s", Version)

	// OS information
	agent = fmt.Sprintf("%s (%s)", agent, strings.Join(osTokens, "; "))

	// Storage information
	if len(userAgentStorageBackends) > 0 {
		agent = fmt.Sprintf("%s (%s)", agent, strings.Join(userAgentStorageBackends, "; "))
	}

	// Feature information
	if len(userAgentFeatures) > 0 {
		agent = fmt.Sprintf("%s (%s)", agent, strings.Join(userAgentFeatures, "; "))
	}

	return agent
}

// UserAgentStorageBackends updates the list of storage backends to include in the user-agent
func UserAgentStorageBackends(backends []string) {
	userAgentStorageBackends = backends
	UserAgent = getUserAgent()
}

// UserAgentFeatures updates the list of advertised features
func UserAgentFeatures(features []string) {
	userAgentFeatures = features
	UserAgent = getUserAgent()
}
<commit_msg>shared/version: Replace deprecated strings.Title.<commit_after>package version

import (
	"fmt"
	"runtime"
	"strings"

	"golang.org/x/text/cases"
	"golang.org/x/text/language"

	"github.com/lxc/lxd/shared/osarch"
)

// UserAgent contains a string suitable as a user-agent
var UserAgent = getUserAgent()
var userAgentStorageBackends []string
var userAgentFeatures []string

func getUserAgent() string {
	archID, err := osarch.ArchitectureId(runtime.GOARCH)
	if err != nil {
		panic(err)
	}

	arch, err := osarch.ArchitectureName(archID)
	if err != nil {
		panic(err)
	}
	osTokens := []string{cases.Title(language.English).String(arch)}
	osTokens = append(osTokens, getPlatformVersionStrings()...)

	// Initial version string
	agent := fmt.Sprintf("LXD %s", Version)

	// OS information
	agent = fmt.Sprintf("%s (%s)", agent, strings.Join(osTokens, "; "))

	// Storage information
	if len(userAgentStorageBackends) > 0 {
		agent = fmt.Sprintf("%s (%s)", agent, strings.Join(userAgentStorageBackends, "; "))
	}

	// Feature information
	if len(userAgentFeatures) > 0 {
		agent = fmt.Sprintf("%s (%s)", agent, strings.Join(userAgentFeatures, "; "))
	}

	return agent
}

// UserAgentStorageBackends updates the list of storage backends to include in the user-agent
func UserAgentStorageBackends(backends []string) {
	userAgentStorageBackends = backends
	UserAgent = getUserAgent()
}

// UserAgentFeatures updates the list of advertised features
func UserAgentFeatures(features []string) {
	userAgentFeatures = features
	UserAgent = getUserAgent()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/sha256"
	"database/sql"
	"encoding/json"
	"github.com/bmizerany/pat"
	"github.com/bmizerany/pq"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

const (
	pgUniqueViolation = "23505"
)

var db *sql.DB

// Examples:
//
//   PUT /hk-1-linux-386.json
//   PUT /hk-linux-386.json
//
//   GET /hk-current-linux-386.json
//   GET /hk-1-linux-386.json
//   GET /hk.gz
func web() {
	initwebdb()
	m := pat.New()
	m.Get("/:cmd.gz", http.HandlerFunc(initial))
	m.Get("/:cmd-current-:plat.json", http.HandlerFunc(curInfo))
	m.Get("/:cmd-:ver-:plat.json", http.HandlerFunc(getHash))
	m.Get("/release.json", http.HandlerFunc(listReleases))
	m.Put("/:cmd-:ver-:os-:arch.json", authenticate{herokaiOnly{http.HandlerFunc(putVer)}})
	m.Put("/:cmd-:os-:arch.json", authenticate{herokaiOnly{http.HandlerFunc(setCur)}})
	m.Get("/", http.FileServer(http.Dir("hkdist/public")))
	if os.Getenv("ALLOWHTTP") != "" {
		http.Handle("/", m)
	} else {
		http.Handle("/", httpsOnly{m})
	}
	err := http.ListenAndServe(":"+os.Getenv("PORT"), nil)
	if err != nil {
		log.Fatalf(`{"func":"ListenAndServe", "error":%q}`, err)
	}
}

func setCur(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	q := r.URL.Query()
	plat := q.Get(":os") + "-" + q.Get(":arch")
	cmd := q.Get(":cmd")
	if strings.IndexFunc(plat, badIdentRune) >= 0 ||
		strings.IndexFunc(cmd, badIdentRune) >= 0 {
		http.Error(w, "bad character in path", 400)
		return
	}

	var info struct{ Version string }
	if !readReqJSON(w, r, 1000, &info) {
		return
	}
	_, err := db.Exec(`
		update cur set curver=$1 where plat=$2 and cmd=$3
	`, info.Version, plat, cmd)
	if err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	_, err = db.Exec(`
		insert into cur (plat, cmd, curver)
		select $1, $2, $3
		where not exists (select 1 from cur where plat=$1 and cmd=$2)
	`, plat, cmd, info.Version)
	if err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	if _, err = db.Exec(`update mod set t=now()`); err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	io.WriteString(w, "ok\n")
}

func scan(w http.ResponseWriter, r *http.Request, q *sql.Row, v ...interface{}) bool {
	switch err := q.Scan(v...); err {
	case nil:
	case sql.ErrNoRows:
		http.NotFound(w, r)
		return false
	default:
		log.Println(err)
		w.WriteHeader(500)
		return false
	}
	return true
}

func lookupCurInfo(w http.ResponseWriter, r *http.Request, plat, cmd string) (v struct {
	Version string
	Sha256  []byte
}, ok bool) {
	const s = `select c.curver, r.sha256 from cur c, release r
				where c.plat=$1 and c.cmd=$2
				and c.plat = r.plat and c.cmd = r.cmd and c.curver = r.ver`
	ok = scan(w, r, db.QueryRow(s, plat, cmd), &v.Version, &v.Sha256)
	return
}

func initial(w http.ResponseWriter, r *http.Request) {
	cmd := r.URL.Query().Get(":cmd")
	plat := guessPlat(r.UserAgent())
	if info, ok := lookupCurInfo(w, r, plat, cmd); ok {
		url := s3DistURL + cmd + "-" + info.Version + "-" + plat + ".gz"
		http.Redirect(w, r, url, http.StatusTemporaryRedirect)
	}
}

func curInfo(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	if info, ok := lookupCurInfo(w, r, q.Get(":plat"), q.Get(":cmd")); ok {
		logErr(json.NewEncoder(w).Encode(info))
	}
}

func getHash(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	var info struct{ Sha256 []byte }
	const s = `select sha256 from release where plat=$1 and cmd=$2 and ver=$3`
	if scan(w, r, db.QueryRow(s, q.Get(":plat"), q.Get(":cmd"), q.Get(":ver")), &info.Sha256) {
		logErr(json.NewEncoder(w).Encode(info))
	}
}

func listReleases(w http.ResponseWriter, r *http.Request) {
	rels := make([]release, 0)
	rows, err := db.Query(`select plat, cmd, ver, sha256 from release`)
	if err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	for rows.Next() {
		var rel release
		err := rows.Scan(&rel.Plat, &rel.Cmd, &rel.Ver, &rel.Sha256)
		if err != nil {
			log.Println(err)
		} else {
			rels = append(rels, rel)
		}
	}
	if err := rows.Err(); err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	b := new(bytes.Buffer)
	if err = json.NewEncoder(b).Encode(rels); err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	var mod time.Time
	db.QueryRow(`select t from mod`).Scan(&mod)
	http.ServeContent(w, r, "", mod, bytes.NewReader(b.Bytes()))
}

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

func guessArch(ua string) string {
	if strings.Contains(ua, "amd64") || strings.Contains(ua, "x86_64") {
		return "amd64"
	}
	return "386"
}

func guessOS(ua string) string {
	ua = strings.ToLower(ua)
	if strings.Contains(ua, "windows") {
		return "windows"
	}
	if strings.Contains(ua, "mac os x") || strings.Contains(ua, "darwin") {
		return "darwin"
	}
	return "linux"
}

func guessPlat(ua string) string {
	return guessOS(ua) + "-" + guessArch(ua)
}

func putVer(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	q := r.URL.Query()
	plat := q.Get(":os") + "-" + q.Get(":arch")
	cmd := q.Get(":cmd")
	ver := q.Get(":ver")
	if strings.IndexFunc(plat, badIdentRune) >= 0 ||
		strings.IndexFunc(cmd, badIdentRune) >= 0 ||
		strings.IndexFunc(ver, badVersionRune) >= 0 {
		http.Error(w, "bad character in path", 400)
		return
	}

	var info struct{ Sha256 []byte }
	if !readReqJSON(w, r, 1000, &info) {
		return
	}
	if len(info.Sha256) != sha256.Size {
		log.Printf("bad hash length %d != %d", len(info.Sha256), sha256.Size)
		http.Error(w, "unprocessable entity", 422)
		return
	}

	_, err := db.Exec(`
		insert into release (plat, cmd, ver, sha256)
		values ($1, $2, $3, $4)
	`, plat, cmd, ver, info.Sha256)
	if pe, ok := err.(pq.PGError); ok && pe.Get('C') == pgUniqueViolation {
		http.Error(w, "conflict", http.StatusConflict)
		return
	} else if err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	if _, err = db.Exec(`update mod set t=now()`); err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	w.WriteHeader(http.StatusCreated)
	io.WriteString(w, "created\n")
}

func readReqJSON(w http.ResponseWriter, r *http.Request, n int64, v interface{}) bool {
	err := json.NewDecoder(http.MaxBytesReader(w, r.Body, n)).Decode(v)
	if err != nil {
		http.Error(w, "unprocessable entity", 422)
	}
	return err == nil
}

type authenticate struct {
	http.Handler
}

func (x authenticate) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	hr, _ := http.NewRequest("GET", "https://api.heroku.com/user", nil)
	hr.Header.Set("Accept", "application/json")
	hr.Header.Set("Authorization", r.Header.Get("Authorization"))
	res, err := http.DefaultClient.Do(hr)
	if err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	if res.StatusCode == 401 {
		http.Error(w, "unauthorized", 401)
		return
	}
	if res.StatusCode != 200 {
		log.Println("unexpected status from heroku api:", res.StatusCode)
		http.Error(w, "internal error", 500)
		return
	}

	var info struct {
		Email string
	}
	err = json.NewDecoder(res.Body).Decode(&info)
	res.Body.Close()
	if err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}

	r.Header.Set(":email", info.Email)
	x.Handler.ServeHTTP(w, r)
}

type herokaiOnly struct {
	http.Handler
}

func (x herokaiOnly) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !strings.HasSuffix(r.Header.Get(":email"), "@heroku.com") {
		http.Error(w, "unauthorized", 401)
		return
	}
	x.Handler.ServeHTTP(w, r)
}

type httpsOnly struct {
	http.Handler
}

func (x httpsOnly) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Header.Get("X-Forwarded-Proto") != "https" {
		url := *r.URL
		url.Scheme = "https"
		http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
		return
	}
	w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
	x.Handler.ServeHTTP(w, r)
}

func mustExec(q string) {
	if _, err := db.Exec(q); err != nil {
		log.Fatal(err)
	}
}

func initwebdb() {
	connstr, err := pq.ParseURL(os.Getenv("DATABASE_URL"))
	if err != nil {
		log.Fatal("pq.ParseURL", err)
	}
	db, err = sql.Open("postgres", connstr+" sslmode=disable")
	if err != nil {
		log.Fatal("sql.Open", err)
	}
	mustExec(`SET bytea_output = 'hex'`) // work around https://github.com/bmizerany/pq/issues/76
	mustExec(`create table if not exists release (
		plat text not null,
		cmd text not null,
		ver text not null,
		sha256 bytea not null,
		primary key (plat, cmd, ver)
	)`)
	mustExec(`create table if not exists cur (
		plat text not null,
		cmd text not null,
		curver text not null,
		foreign key (plat, cmd, curver) references release (plat, cmd, ver),
		primary key (plat, cmd)
	)`)
	mustExec(`create table if not exists mod (
		t timestamptz not null
	)`)
	mustExec(`insert into mod (t)
		select now()
		where not exists (select 1 from mod)
	`)
}

func badIdentRune(r rune) bool {
	return !(r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' || r == '-')
}

func badVersionRune(r rune) bool {
	return !(r >= '0' && r <= '9' || r == '.')
}
<commit_msg>use package secureheader<commit_after>package main

import (
	"bytes"
	"crypto/sha256"
	"database/sql"
	"encoding/json"
	"github.com/bmizerany/pat"
	"github.com/bmizerany/pq"
	"github.com/kr/secureheader"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

const (
	pgUniqueViolation = "23505"
)

var db *sql.DB

// Examples:
//
//   PUT /hk-1-linux-386.json
//   PUT /hk-linux-386.json
//
//   GET /hk-current-linux-386.json
//   GET /hk-1-linux-386.json
//   GET /hk.gz
func web() {
	initwebdb()
	m := pat.New()
	m.Get("/:cmd.gz", http.HandlerFunc(initial))
	m.Get("/:cmd-current-:plat.json", http.HandlerFunc(curInfo))
	m.Get("/:cmd-:ver-:plat.json", http.HandlerFunc(getHash))
	m.Get("/release.json", http.HandlerFunc(listReleases))
	m.Put("/:cmd-:ver-:os-:arch.json", authenticate{herokaiOnly{http.HandlerFunc(putVer)}})
	m.Put("/:cmd-:os-:arch.json", authenticate{herokaiOnly{http.HandlerFunc(setCur)}})
	m.Get("/", http.FileServer(http.Dir("hkdist/public")))
	http.Handle("/", m)
	secureheader.DefaultConfig.HTTPSRedirect = false // use httpsOnly (defined below) instead
	var h http.Handler = secureheader.DefaultConfig
	if os.Getenv("ALLOWHTTP") == "" {
		h = httpsOnly{h}
	}
	err := http.ListenAndServe(":"+os.Getenv("PORT"), h)
	if err != nil {
		log.Fatalf(`{"func":"ListenAndServe", "error":%q}`, err)
	}
}

func setCur(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	q := r.URL.Query()
	plat := q.Get(":os") + "-" + q.Get(":arch")
	cmd := q.Get(":cmd")
	if strings.IndexFunc(plat, badIdentRune) >= 0 ||
		strings.IndexFunc(cmd, badIdentRune) >= 0 {
		http.Error(w, "bad character in path", 400)
		return
	}

	var info struct{ Version string }
	if !readReqJSON(w, r, 1000, &info) {
		return
	}
	_, err := db.Exec(`
		update cur set curver=$1 where plat=$2 and cmd=$3
	`, info.Version, plat, cmd)
	if err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	_, err = db.Exec(`
		insert into cur (plat, cmd, curver)
		select $1, $2, $3
		where not exists (select 1 from cur where plat=$1 and cmd=$2)
	`, plat, cmd, info.Version)
	if err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	if _, err = db.Exec(`update mod set t=now()`); err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	io.WriteString(w, "ok\n")
}

func scan(w http.ResponseWriter, r *http.Request, q *sql.Row, v ...interface{}) bool {
	switch err := q.Scan(v...); err {
	case nil:
	case sql.ErrNoRows:
		http.NotFound(w, r)
		return false
	default:
		log.Println(err)
		w.WriteHeader(500)
		return false
	}
	return true
}

func lookupCurInfo(w http.ResponseWriter, r *http.Request, plat, cmd string) (v struct {
	Version string
	Sha256  []byte
}, ok bool) {
	const s = `select c.curver, r.sha256 from cur c, release r
				where c.plat=$1 and c.cmd=$2
				and c.plat = r.plat and c.cmd = r.cmd and c.curver = r.ver`
	ok = scan(w, r, db.QueryRow(s, plat, cmd), &v.Version, &v.Sha256)
	return
}

func initial(w http.ResponseWriter, r *http.Request) {
	cmd := r.URL.Query().Get(":cmd")
	plat := guessPlat(r.UserAgent())
	if info, ok := lookupCurInfo(w, r, plat, cmd); ok {
		url := s3DistURL + cmd + "-" + info.Version + "-" + plat + ".gz"
		http.Redirect(w, r, url, http.StatusTemporaryRedirect)
	}
}

func curInfo(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	if info, ok := lookupCurInfo(w, r, q.Get(":plat"), q.Get(":cmd")); ok {
		logErr(json.NewEncoder(w).Encode(info))
	}
}

func getHash(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	var info struct{ Sha256 []byte }
	const s = `select sha256 from release where plat=$1 and cmd=$2 and ver=$3`
	if scan(w, r, db.QueryRow(s, q.Get(":plat"), q.Get(":cmd"), q.Get(":ver")), &info.Sha256) {
		logErr(json.NewEncoder(w).Encode(info))
	}
}

func listReleases(w http.ResponseWriter, r *http.Request) {
	rels := make([]release, 0)
	rows, err := db.Query(`select plat, cmd, ver, sha256 from release`)
	if err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	for rows.Next() {
		var rel release
		err := rows.Scan(&rel.Plat, &rel.Cmd, &rel.Ver, &rel.Sha256)
		if err != nil {
			log.Println(err)
		} else {
			rels = append(rels, rel)
		}
	}
	if err := rows.Err(); err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	b := new(bytes.Buffer)
	if err = json.NewEncoder(b).Encode(rels); err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	var mod time.Time
	db.QueryRow(`select t from mod`).Scan(&mod)
	http.ServeContent(w, r, "", mod, bytes.NewReader(b.Bytes()))
}

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

func guessArch(ua string) string {
	if strings.Contains(ua, "amd64") || strings.Contains(ua, "x86_64") {
		return "amd64"
	}
	return "386"
}

func guessOS(ua string) string {
	ua = strings.ToLower(ua)
	if strings.Contains(ua, "windows") {
		return "windows"
	}
	if strings.Contains(ua, "mac os x") || strings.Contains(ua, "darwin") {
		return "darwin"
	}
	return "linux"
}

func guessPlat(ua string) string {
	return guessOS(ua) + "-" + guessArch(ua)
}

func putVer(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	q := r.URL.Query()
	plat := q.Get(":os") + "-" + q.Get(":arch")
	cmd := q.Get(":cmd")
	ver := q.Get(":ver")
	if strings.IndexFunc(plat, badIdentRune) >= 0 ||
		strings.IndexFunc(cmd, badIdentRune) >= 0 ||
		strings.IndexFunc(ver, badVersionRune) >= 0 {
		http.Error(w, "bad character in path", 400)
		return
	}

	var info struct{ Sha256 []byte }
	if !readReqJSON(w, r, 1000, &info) {
		return
	}
	if len(info.Sha256) != sha256.Size {
		log.Printf("bad hash length %d != %d", len(info.Sha256), sha256.Size)
		http.Error(w, "unprocessable entity", 422)
		return
	}

	_, err := db.Exec(`
		insert into release (plat, cmd, ver, sha256)
		values ($1, $2, $3, $4)
	`, plat, cmd, ver, info.Sha256)
	if pe, ok := err.(pq.PGError); ok && pe.Get('C') == pgUniqueViolation {
		http.Error(w, "conflict", http.StatusConflict)
		return
	} else if err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	if _, err = db.Exec(`update mod set t=now()`); err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	w.WriteHeader(http.StatusCreated)
	io.WriteString(w, "created\n")
}

func readReqJSON(w http.ResponseWriter, r *http.Request, n int64, v interface{}) bool {
	err := json.NewDecoder(http.MaxBytesReader(w, r.Body, n)).Decode(v)
	if err != nil {
		http.Error(w, "unprocessable entity", 422)
	}
	return err == nil
}

type authenticate struct {
	http.Handler
}

func (x authenticate) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	hr, _ := http.NewRequest("GET", "https://api.heroku.com/user", nil)
	hr.Header.Set("Accept", "application/json")
	hr.Header.Set("Authorization", r.Header.Get("Authorization"))
	res, err := http.DefaultClient.Do(hr)
	if err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}
	if res.StatusCode == 401 {
		http.Error(w, "unauthorized", 401)
		return
	}
	if res.StatusCode != 200 {
		log.Println("unexpected status from heroku api:", res.StatusCode)
		http.Error(w, "internal error", 500)
		return
	}

	var info struct {
		Email string
	}
	err = json.NewDecoder(res.Body).Decode(&info)
	res.Body.Close()
	if err != nil {
		log.Println(err)
		http.Error(w, "internal error", 500)
		return
	}

	r.Header.Set(":email", info.Email)
	x.Handler.ServeHTTP(w, r)
}

type herokaiOnly struct {
	http.Handler
}

func (x herokaiOnly) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !strings.HasSuffix(r.Header.Get(":email"), "@heroku.com") {
		http.Error(w, "unauthorized", 401)
		return
	}
	x.Handler.ServeHTTP(w, r)
}

type httpsOnly struct {
	http.Handler
}

func (x httpsOnly) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Header.Get("X-Forwarded-Proto") != "https" {
		url := *r.URL
		url.Scheme = "https"
		http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
		return
	}
	x.Handler.ServeHTTP(w, r)
}

func mustExec(q string) {
	if _, err := db.Exec(q); err != nil {
		log.Fatal(err)
	}
}

func initwebdb() {
	connstr, err := pq.ParseURL(os.Getenv("DATABASE_URL"))
	if err != nil {
		log.Fatal("pq.ParseURL", err)
	}
	db, err = sql.Open("postgres", connstr+" sslmode=disable")
	if err != nil {
		log.Fatal("sql.Open", err)
	}
	mustExec(`SET bytea_output = 'hex'`) // work around https://github.com/bmizerany/pq/issues/76
	mustExec(`create table if not exists release (
		plat text not null,
		cmd text not null,
		ver text not null,
		sha256 bytea not null,
		primary key (plat, cmd, ver)
	)`)
	mustExec(`create table if not exists cur (
		plat text not null,
		cmd text not null,
		curver text not null,
		foreign key (plat, cmd, curver) references release (plat, cmd, ver),
		primary key (plat, cmd)
	)`)
	mustExec(`create table if not exists mod (
		t timestamptz not null
	)`)
	mustExec(`insert into mod (t)
		select now()
		where not exists (select 1 from mod)
	`)
}

func badIdentRune(r rune) bool {
	return !(r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' || r == '-')
}

func badVersionRune(r rune) bool {
	return !(r >= '0' && r <= '9' || r == '.')
}
<|endoftext|>


<commit_before>package hcsshim

import (
	"github.com/sirupsen/logrus"
)

type HNSSupportedFeatures struct {
	Acl HNSAclFeatures `json:"ACL"`
}

type HNSAclFeatures struct {
	AclAddressLists       bool `json:"AclAddressLists"`
	AclNoHostRulePriority bool `json:"AclHostRulePriority"`
	AclPortRanges         bool `json:"AclPortRanges"`
	AclRuleId             bool `json:"AclRuleId"`
}

func GetHNSSupportedFeatures() HNSSupportedFeatures {
	var hnsFeatures HNSSupportedFeatures

	globals, err := GetHNSGlobals()
	if err != nil {
		// Expected on pre-17060 builds, all features will be false/unsupported
		logrus.Debugf("Unable to obtain HNS globals: %s", err)
		return hnsFeatures
	}

	hnsFeatures.Acl = HNSAclFeatures{
		AclAddressLists:       isHNSFeatureSupported(globals.Version, HNSVersion1804),
		AclNoHostRulePriority: isHNSFeatureSupported(globals.Version, HNSVersion1804),
		AclPortRanges:         isHNSFeatureSupported(globals.Version, HNSVersion1804),
		AclRuleId:             isHNSFeatureSupported(globals.Version, HNSVersion1804),
	}

	return hnsFeatures
}

func isHNSFeatureSupported(currentVersion HNSVersion, minVersionSupported HNSVersion) bool {
	if currentVersion.Major < minVersionSupported.Major {
		return false
	}
	if currentVersion.Major > minVersionSupported.Major {
		return true
	}
	if currentVersion.Minor < minVersionSupported.Minor {
		return false
	}
	return true
}
<commit_msg>Fixing comment<commit_after>package hcsshim

import (
	"github.com/sirupsen/logrus"
)

type HNSSupportedFeatures struct {
	Acl HNSAclFeatures `json:"ACL"`
}

type HNSAclFeatures struct {
	AclAddressLists       bool `json:"AclAddressLists"`
	AclNoHostRulePriority bool `json:"AclHostRulePriority"`
	AclPortRanges         bool `json:"AclPortRanges"`
	AclRuleId             bool `json:"AclRuleId"`
}

func GetHNSSupportedFeatures() HNSSupportedFeatures {
	var hnsFeatures HNSSupportedFeatures

	globals, err := GetHNSGlobals()
	if err != nil {
		// Expected on pre-1804 builds, all features will be false/unsupported
		logrus.Debugf("Unable to obtain HNS globals: %s", err)
		return hnsFeatures
	}

	hnsFeatures.Acl = HNSAclFeatures{
		AclAddressLists:       isHNSFeatureSupported(globals.Version, HNSVersion1804),
		AclNoHostRulePriority: isHNSFeatureSupported(globals.Version, HNSVersion1804),
		AclPortRanges:         isHNSFeatureSupported(globals.Version, HNSVersion1804),
		AclRuleId:             isHNSFeatureSupported(globals.Version, HNSVersion1804),
	}

	return hnsFeatures
}

func isHNSFeatureSupported(currentVersion HNSVersion, minVersionSupported HNSVersion) bool {
	if currentVersion.Major < minVersionSupported.Major {
		return false
	}
	if currentVersion.Major > minVersionSupported.Major {
		return true
	}
	if currentVersion.Minor < minVersionSupported.Minor {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package generate

import (
	"os"
	"testing"

	"github.com/aaasen/kapok/parse"
)

func BenchmarkGenerate(b *testing.B) {
	nodes, err := os.Create(".kapok-benchmark-nodes.csv")

	if err != nil {
		b.Error(err)
	}

	rels, err := os.Create(".kapok-benchmark-rels.csv")

	if err != nil {
		b.Error(err)
	}

	page := &parse.Page{
		Title:      "origin",
		Links:      []string{"a", "b", "c"},
		Categories: []string{"cat0", "cat1", "cat2"},
	}

	gen := generate.NewCSVGenerator()

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		gen.GeneratePage(page, nodes, rels)
	}
}
<commit_msg>removes generate tests<commit_after><|endoftext|>


<commit_before>// Copyright 2013 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 main

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

	"code.google.com/p/go.tools/go/vcs"
)

// These variables are copied from the gobuilder's environment
// to the envv of its subprocesses.
var extraEnv = []string{
	"GOARM",

	// For Unix derivatives.
	"CC",
	"PATH",
	"TMPDIR",
	"USER",

	// For Plan 9.
	"objtype",
	"cputype",
	"path",
}

// builderEnv represents the environment that a Builder will run tests in.
type builderEnv interface {
	// setup sets up the builder environment and returns the directory to run the buildCmd in.
	setup(repo *Repo, workpath, hash string, envv []string) (string, error)
}

// goEnv represents the builderEnv for the main Go repo.
type goEnv struct {
	goos, goarch string
}

func (b *Builder) envv() []string {
	if runtime.GOOS == "windows" {
		return b.envvWindows()
	}

	var e []string
	if *buildTool == "go" {
		e = []string{
			"GOOS=" + b.goos,
			"GOHOSTOS=" + b.goos,
			"GOARCH=" + b.goarch,
			"GOHOSTARCH=" + b.goarch,
			"GOROOT_FINAL=/usr/local/go",
		}
	}

	for _, k := range extraEnv {
		if s, ok := getenvOk(k); ok {
			e = append(e, k+"="+s)
		}
	}
	return e
}

func (b *Builder) envvWindows() []string {
	var start map[string]string
	if *buildTool == "go" {
		start = map[string]string{
			"GOOS":         b.goos,
			"GOHOSTOS":     b.goos,
			"GOARCH":       b.goarch,
			"GOHOSTARCH":   b.goarch,
			"GOROOT_FINAL": `c:\go`,
			"GOBUILDEXIT":  "1", // exit all.bat with completion status.
		}
	}

	for _, name := range extraEnv {
		if s, ok := getenvOk(name); ok {
			start[name] = s
		}
	}
	skip := map[string]bool{
		"GOBIN":   true,
		"GOROOT":  true,
		"INCLUDE": true,
		"LIB":     true,
	}
	var e []string
	for name, v := range start {
		e = append(e, name+"="+v)
		skip[name] = true
	}
	for _, kv := range os.Environ() {
		s := strings.SplitN(kv, "=", 2)
		name := strings.ToUpper(s[0])
		switch {
		case name == "":
			// variables, like "=C:=C:\", just copy them
			e = append(e, kv)
		case !skip[name]:
			e = append(e, kv)
			skip[name] = true
		}
	}
	return e
}

// setup for a goEnv clones the main go repo to workpath/go at the provided hash
// and returns the path workpath/go/src, the location of all go build scripts.
func (env *goEnv) setup(repo *Repo, workpath, hash string, envv []string) (string, error) {
	goworkpath := filepath.Join(workpath, "go")
	if _, err := repo.Clone(goworkpath, hash); err != nil {
		return "", fmt.Errorf("error cloning repository: %s", err)
	}
	return filepath.Join(goworkpath, "src"), nil
}

// gccgoEnv represents the builderEnv for the gccgo compiler.
type gccgoEnv struct{}

// setup for a gccgoEnv clones the gofrontend repo to workpath/go at the hash
// and clones the latest GCC branch to repo.Path/gcc. The gccgo sources are
// replaced with the updated sources in the gofrontend repo and gcc gets
// gets configured and built in workpath/gcc-objdir. The path to
// workpath/gcc-objdir is returned.
func (env *gccgoEnv) setup(repo *Repo, workpath, hash string, envv []string) (string, error) {
	gofrontendpath := filepath.Join(workpath, "gofrontend")
	gccpath := filepath.Join(repo.Path, "gcc")

	// get a handle to SVN vcs.Cmd for pulling down GCC.
	svn := vcs.ByCmd("svn")

	// only pull down gcc if we don't have a local copy.
	if _, err := os.Stat(gccpath); err != nil {
		if err := timeout(*cmdTimeout, func() error {
			// pull down a working copy of GCC.
			return svn.Create(gccpath, *gccPath)
		}); err != nil {
			return "", err
		}
	}
	if err := svn.Download(gccpath); err != nil {
		return "", err
	}

	// clone gofrontend repo at specified revision
	if _, err := repo.Clone(gofrontendpath, hash); err != nil {
		return "", err
	}

	// remove gccpath/gcc/go/gofrontend and gcc/libgo
	gccgopath := filepath.Join(gccpath, "gcc", "go", "gofrontend")
	gcclibgopath := filepath.Join(gccpath, "libgo")
	if err := os.RemoveAll(gccgopath); err != nil {
		return "", err
	}
	if err := os.RemoveAll(gcclibgopath); err != nil {
		return "", err
	}

	// copy gofrontend and libgo to appropriate locations
	if err := copyDir(filepath.Join(gofrontendpath, "go"), gccgopath); err != nil {
		return "", fmt.Errorf("Failed to copy gofrontend/go to gcc/go/gofrontend: %s\n", err)
	}
	if err := copyDir(filepath.Join(gofrontendpath, "libgo"), gcclibgopath); err != nil {
		return "", fmt.Errorf("Failed to copy gofrontend/libgo to gcc/libgo: %s\n", err)
	}

	// make objdir to work in
	gccobjdir := filepath.Join(workpath, "gcc-objdir")
	if err := os.Mkdir(gccobjdir, mkdirPerm); err != nil {
		return "", err
	}

	// configure GCC with substituted gofrontend and libgo
	gccConfigCmd := []string{filepath.Join(gccpath, "configure"), "--enable-languages=c,c++,go", "--disable-bootstrap"}
	if _, err := runOutput(*cmdTimeout, envv, ioutil.Discard, gccobjdir, gccConfigCmd...); err != nil {
		return "", fmt.Errorf("Failed to configure GCC: %s", err)
	}

	// build gcc
	if _, err := runOutput(*buildTimeout, envv, ioutil.Discard, gccobjdir, "make"); err != nil {
		return "", fmt.Errorf("Failed to build GCC: %s", err)
	}

	return gccobjdir, nil

}

// copyDir copies the src directory into the dst
func copyDir(src, dst string) error {
	return filepath.Walk(src, func(path string, f os.FileInfo, err error) error {
		dstPath := strings.Replace(path, src, dst, 1)
		if f.IsDir() {
			return os.Mkdir(dstPath, mkdirPerm)
		}

		srcFile, err := os.Open(path)
		if err != nil {
			return err
		}
		defer srcFile.Close()

		dstFile, err := os.Create(dstPath)
		if err != nil {
			return err
		}

		if _, err := io.Copy(dstFile, srcFile); err != nil {
			return err
		}
		return dstFile.Close()
	})
}

func getenvOk(k string) (v string, ok bool) {
	v = os.Getenv(k)
	if v != "" {
		return v, true
	}
	keq := k + "="
	for _, kv := range os.Environ() {
		if kv == keq {
			return "", true
		}
	}
	return "", false
}
<commit_msg>go.tools/dashboard: Don't keep unversioned directories in gcc copy.<commit_after>// Copyright 2013 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 main

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

	"code.google.com/p/go.tools/go/vcs"
)

// These variables are copied from the gobuilder's environment
// to the envv of its subprocesses.
var extraEnv = []string{
	"GOARM",

	// For Unix derivatives.
	"CC",
	"PATH",
	"TMPDIR",
	"USER",

	// For Plan 9.
	"objtype",
	"cputype",
	"path",
}

// builderEnv represents the environment that a Builder will run tests in.
type builderEnv interface {
	// setup sets up the builder environment and returns the directory to run the buildCmd in.
	setup(repo *Repo, workpath, hash string, envv []string) (string, error)
}

// goEnv represents the builderEnv for the main Go repo.
type goEnv struct {
	goos, goarch string
}

func (b *Builder) envv() []string {
	if runtime.GOOS == "windows" {
		return b.envvWindows()
	}

	var e []string
	if *buildTool == "go" {
		e = []string{
			"GOOS=" + b.goos,
			"GOHOSTOS=" + b.goos,
			"GOARCH=" + b.goarch,
			"GOHOSTARCH=" + b.goarch,
			"GOROOT_FINAL=/usr/local/go",
		}
	}

	for _, k := range extraEnv {
		if s, ok := getenvOk(k); ok {
			e = append(e, k+"="+s)
		}
	}
	return e
}

func (b *Builder) envvWindows() []string {
	var start map[string]string
	if *buildTool == "go" {
		start = map[string]string{
			"GOOS":         b.goos,
			"GOHOSTOS":     b.goos,
			"GOARCH":       b.goarch,
			"GOHOSTARCH":   b.goarch,
			"GOROOT_FINAL": `c:\go`,
			"GOBUILDEXIT":  "1", // exit all.bat with completion status.
		}
	}

	for _, name := range extraEnv {
		if s, ok := getenvOk(name); ok {
			start[name] = s
		}
	}
	skip := map[string]bool{
		"GOBIN":   true,
		"GOROOT":  true,
		"INCLUDE": true,
		"LIB":     true,
	}
	var e []string
	for name, v := range start {
		e = append(e, name+"="+v)
		skip[name] = true
	}
	for _, kv := range os.Environ() {
		s := strings.SplitN(kv, "=", 2)
		name := strings.ToUpper(s[0])
		switch {
		case name == "":
			// variables, like "=C:=C:\", just copy them
			e = append(e, kv)
		case !skip[name]:
			e = append(e, kv)
			skip[name] = true
		}
	}
	return e
}

// setup for a goEnv clones the main go repo to workpath/go at the provided hash
// and returns the path workpath/go/src, the location of all go build scripts.
func (env *goEnv) setup(repo *Repo, workpath, hash string, envv []string) (string, error) {
	goworkpath := filepath.Join(workpath, "go")
	if _, err := repo.Clone(goworkpath, hash); err != nil {
		return "", fmt.Errorf("error cloning repository: %s", err)
	}
	return filepath.Join(goworkpath, "src"), nil
}

// gccgoEnv represents the builderEnv for the gccgo compiler.
type gccgoEnv struct{}

// setup for a gccgoEnv clones the gofrontend repo to workpath/go at the hash
// and clones the latest GCC branch to repo.Path/gcc. The gccgo sources are
// replaced with the updated sources in the gofrontend repo and gcc gets
// gets configured and built in workpath/gcc-objdir. The path to
// workpath/gcc-objdir is returned.
func (env *gccgoEnv) setup(repo *Repo, workpath, hash string, envv []string) (string, error) {
	gofrontendpath := filepath.Join(workpath, "gofrontend")
	gccpath := filepath.Join(repo.Path, "gcc")
	gccgopath := filepath.Join(gccpath, "gcc", "go", "gofrontend")
	gcclibgopath := filepath.Join(gccpath, "libgo")

	// get a handle to SVN vcs.Cmd for pulling down GCC.
	svn := vcs.ByCmd("svn")

	// only pull down gcc if we don't have a local copy.
	if _, err := os.Stat(gccpath); err != nil {
		if err := timeout(*cmdTimeout, func() error {
			// pull down a working copy of GCC.
			return svn.Create(gccpath, *gccPath)
		}); err != nil {
			return "", err
		}
	} else {
		// make sure to remove gccgopath and gcclibgopath before
		// updating the repo to avoid file clobbering.
		if err := os.RemoveAll(gccgopath); err != nil {
			return "", err
		}
		if err := os.RemoveAll(gcclibgopath); err != nil {
			return "", err
		}
	}
	if err := svn.Download(gccpath); err != nil {
		return "", err
	}

	// clone gofrontend repo at specified revision
	if _, err := repo.Clone(gofrontendpath, hash); err != nil {
		return "", err
	}

	// remove gccgopath and gcclibgopath before copying over gofrontend.
	if err := os.RemoveAll(gccgopath); err != nil {
		return "", err
	}
	if err := os.RemoveAll(gcclibgopath); err != nil {
		return "", err
	}

	// copy gofrontend and libgo to appropriate locations
	if err := copyDir(filepath.Join(gofrontendpath, "go"), gccgopath); err != nil {
		return "", fmt.Errorf("Failed to copy gofrontend/go to gcc/go/gofrontend: %s\n", err)
	}
	if err := copyDir(filepath.Join(gofrontendpath, "libgo"), gcclibgopath); err != nil {
		return "", fmt.Errorf("Failed to copy gofrontend/libgo to gcc/libgo: %s\n", err)
	}

	// make objdir to work in
	gccobjdir := filepath.Join(workpath, "gcc-objdir")
	if err := os.Mkdir(gccobjdir, mkdirPerm); err != nil {
		return "", err
	}

	// configure GCC with substituted gofrontend and libgo
	gccConfigCmd := []string{filepath.Join(gccpath, "configure"), "--enable-languages=c,c++,go", "--disable-bootstrap"}
	if _, err := runOutput(*cmdTimeout, envv, ioutil.Discard, gccobjdir, gccConfigCmd...); err != nil {
		return "", fmt.Errorf("Failed to configure GCC: %s", err)
	}

	// build gcc
	if _, err := runOutput(*buildTimeout, envv, ioutil.Discard, gccobjdir, "make"); err != nil {
		return "", fmt.Errorf("Failed to build GCC: %s", err)
	}

	return gccobjdir, nil
}

// copyDir copies the src directory into the dst
func copyDir(src, dst string) error {
	return filepath.Walk(src, func(path string, f os.FileInfo, err error) error {
		dstPath := strings.Replace(path, src, dst, 1)
		if f.IsDir() {
			return os.Mkdir(dstPath, mkdirPerm)
		}

		srcFile, err := os.Open(path)
		if err != nil {
			return err
		}
		defer srcFile.Close()

		dstFile, err := os.Create(dstPath)
		if err != nil {
			return err
		}

		if _, err := io.Copy(dstFile, srcFile); err != nil {
			return err
		}
		return dstFile.Close()
	})
}

func getenvOk(k string) (v string, ok bool) {
	v = os.Getenv(k)
	if v != "" {
		return v, true
	}
	keq := k + "="
	for _, kv := range os.Environ() {
		if kv == keq {
			return "", true
		}
	}
	return "", false
}
<|endoftext|>


<commit_before>package posix

import (
	"fmt"
	"os"
	"syscall"

	co "github.com/lunixbochs/usercorn/go/kernel/common"
	"github.com/lunixbochs/usercorn/go/native"
)

func (k *PosixKernel) Socket(domain, typ, protocol int) uint64 {
	fd, err := syscall.Socket(domain, typ, protocol)
	if err != nil {
		return Errno(err)
	}
	return uint64(fd)
}

func (k *PosixKernel) Connect(fd co.Fd, sa syscall.Sockaddr, size co.Len) uint64 {
	return Errno(syscall.Connect(int(fd), sa))
}

func (k *PosixKernel) Bind(fd co.Fd, sa syscall.Sockaddr, size co.Len) uint64 {
	return Errno(syscall.Bind(int(fd), sa))
}

func (k *PosixKernel) Sendto(fd co.Fd, buf co.Buf, size co.Len, flags int, sa syscall.Sockaddr, socklen co.Len) uint64 {
	msg := make([]byte, size)
	if err := buf.Unpack(msg); err != nil {
		return UINT64_MAX // FIXME
	}
	return Errno(syscall.Sendto(int(fd), msg, flags, sa))
}

func (k *PosixKernel) Recvfrom(fd co.Fd, buf co.Buf, size co.Len, flags int, from co.Buf, fromlen co.Len) uint64 {
	p := make([]byte, size)
	if n, _, err := syscall.Recvfrom(int(fd), p, flags); err != nil {
		// TODO: need kernel.Pack() so we can pack a sockaddr into from
		buf.Pack(p)
		return uint64(n)
	} else {
		return UINT64_MAX // FIXME
	}
}

func (k *PosixKernel) Getsockopt(fd co.Fd, level, opt int, valueOut, valueSizeOut co.Buf) uint64 {
	// TODO: dispatch/support both addr and int types
	value, err := syscall.GetsockoptInt(int(fd), level, opt)
	if err != nil {
		return Errno(err)
	}
	value32 := int32(value)
	size := int32(4)
	if err := valueOut.Pack(value32); err != nil {
		return UINT64_MAX // FIXME
	}
	if err := valueSizeOut.Pack(size); err != nil {
		return UINT64_MAX // FIXME
	}
	return 0
}

func (k *PosixKernel) Setsockopt(fd co.Fd, level, opt int, valueIn co.Buf, size int) uint64 {
	// TODO: dispatch/support all setsockopt types
	if size != 4 {
		fmt.Fprintf(os.Stderr, "WARNING: unsupported Setsockopt type %d\n", size)
		return UINT64_MAX // FIXME
	}
	var value int32
	if err := valueIn.Unpack(&value); err != nil {
		return UINT64_MAX // FIXME
	}
	if err := syscall.SetsockoptInt(int(fd), level, opt, opt); err != nil {
		return Errno(err)
	}
	return 0
}

func getfdset(f *native.Fdset32) *syscall.FdSet {
	if f == nil {
		return nil
	}
	return f.Native()
}

func putfdset(b co.Obuf, fdset *syscall.FdSet) error {
	if fdset != nil && b.Addr != 0 {
		return b.Pack(fdset)
	}
	return nil
}

func (k *PosixKernel) Select(args []co.Obuf, nfds int, readfds, writefds, errorfds *native.Fdset32, timeout *syscall.Timeval) uint64 {
	// TODO: might need to tweak 64-bit little-endian fdset parsing
	r, w, e := getfdset(readfds), getfdset(writefds), getfdset(errorfds)
	if err := nativeSelect(nfds, r, w, e, timeout); err != nil {
		return Errno(err)
	}
	// write out fdsets
	putfdset(args[1], r)
	putfdset(args[2], w)
	putfdset(args[3], e)
	return 0
}
<commit_msg>add 'shutdown' syscall<commit_after>package posix

import (
	"fmt"
	"os"
	"syscall"

	co "github.com/lunixbochs/usercorn/go/kernel/common"
	"github.com/lunixbochs/usercorn/go/native"
)

func (k *PosixKernel) Socket(domain, typ, protocol int) uint64 {
	fd, err := syscall.Socket(domain, typ, protocol)
	if err != nil {
		return Errno(err)
	}
	return uint64(fd)
}

func (k *PosixKernel) Connect(fd co.Fd, sa syscall.Sockaddr, size co.Len) uint64 {
	return Errno(syscall.Connect(int(fd), sa))
}

func (k *PosixKernel) Bind(fd co.Fd, sa syscall.Sockaddr, size co.Len) uint64 {
	return Errno(syscall.Bind(int(fd), sa))
}

func (k *PosixKernel) Shutdown(fd co.Fd, how int) uint64 {
	return Errno(syscall.Shutdown(int(fd), how))
}

func (k *PosixKernel) Sendto(fd co.Fd, buf co.Buf, size co.Len, flags int, sa syscall.Sockaddr, socklen co.Len) uint64 {
	msg := make([]byte, size)
	if err := buf.Unpack(msg); err != nil {
		return UINT64_MAX // FIXME
	}
	return Errno(syscall.Sendto(int(fd), msg, flags, sa))
}

func (k *PosixKernel) Recvfrom(fd co.Fd, buf co.Buf, size co.Len, flags int, from co.Buf, fromlen co.Len) uint64 {
	p := make([]byte, size)
	if n, _, err := syscall.Recvfrom(int(fd), p, flags); err != nil {
		// TODO: need kernel.Pack() so we can pack a sockaddr into from
		buf.Pack(p)
		return uint64(n)
	} else {
		return UINT64_MAX // FIXME
	}
}

func (k *PosixKernel) Getsockopt(fd co.Fd, level, opt int, valueOut, valueSizeOut co.Buf) uint64 {
	// TODO: dispatch/support both addr and int types
	value, err := syscall.GetsockoptInt(int(fd), level, opt)
	if err != nil {
		return Errno(err)
	}
	value32 := int32(value)
	size := int32(4)
	if err := valueOut.Pack(value32); err != nil {
		return UINT64_MAX // FIXME
	}
	if err := valueSizeOut.Pack(size); err != nil {
		return UINT64_MAX // FIXME
	}
	return 0
}

func (k *PosixKernel) Setsockopt(fd co.Fd, level, opt int, valueIn co.Buf, size int) uint64 {
	// TODO: dispatch/support all setsockopt types
	if size != 4 {
		fmt.Fprintf(os.Stderr, "WARNING: unsupported Setsockopt type %d\n", size)
		return UINT64_MAX // FIXME
	}
	var value int32
	if err := valueIn.Unpack(&value); err != nil {
		return UINT64_MAX // FIXME
	}
	if err := syscall.SetsockoptInt(int(fd), level, opt, opt); err != nil {
		return Errno(err)
	}
	return 0
}

func getfdset(f *native.Fdset32) *syscall.FdSet {
	if f == nil {
		return nil
	}
	return f.Native()
}

func putfdset(b co.Obuf, fdset *syscall.FdSet) error {
	if fdset != nil && b.Addr != 0 {
		return b.Pack(fdset)
	}
	return nil
}

func (k *PosixKernel) Select(args []co.Obuf, nfds int, readfds, writefds, errorfds *native.Fdset32, timeout *syscall.Timeval) uint64 {
	// TODO: might need to tweak 64-bit little-endian fdset parsing
	r, w, e := getfdset(readfds), getfdset(writefds), getfdset(errorfds)
	if err := nativeSelect(nfds, r, w, e, timeout); err != nil {
		return Errno(err)
	}
	// write out fdsets
	putfdset(args[1], r)
	putfdset(args[2], w)
	putfdset(args[3], e)
	return 0
}
<|endoftext|>


<commit_before>package native

import (
	"syscall"
)

func StatfsToLinux(s *syscall.Statfs_t) *LinuxStatfs_t {
	return &LinuxStatfs_t{
		Type:    s.Type,
		Bsize:   s.Bsize,
		Blocks:  s.Blocks,
		Bfree:   s.Bfree,
		Bavail:  s.Bavail,
		Files:   s.Files,
		Ffree:   s.Ffree,
		Fsid:    s.Fsid,
		Namelen: s.Namelen,
		Frsize:  s.Frsize,
		Flags:   s.Flags,
	}
}

func StatfsToDarwin32(s *syscall.Statfs_t) *DarwinStatfs32_t {
	// TODO: fs type, owner, mnt names?
	return &DarwinStatfs32_t{
		Bsize:  int(s.Bsize),
		Blocks: int(s.Blocks),
		Bfree:  int(s.Bfree),
		Bavail: int(s.Bavail),
		Files:  int(s.Files),
		Ffree:  int(s.Ffree),
		Fsid:   s.Fsid,
	}
}

func StatfsToDarwin64(s *syscall.Statfs_t) *DarwinStatfs64_t {
	// TODO: fs type, owner, mnt names?
	return &DarwinStatfs64_t{
		Bsize:  uint32(s.Bsize),
		Blocks: uint64(s.Blocks),
		Bfree:  uint64(s.Bfree),
		Bavail: uint64(s.Bavail),
		Files:  uint64(s.Files),
		Ffree:  uint64(s.Ffree),
		Fsid:   s.Fsid,
	}
}
<commit_msg>cast statfs members for @petevine<commit_after>package native

import (
	"syscall"
)

func StatfsToLinux(s *syscall.Statfs_t) *LinuxStatfs_t {
	return &LinuxStatfs_t{
		Type:    int64(s.Type),
		Bsize:   int64(s.Bsize),
		Blocks:  s.Blocks,
		Bfree:   s.Bfree,
		Bavail:  s.Bavail,
		Files:   s.Files,
		Ffree:   s.Ffree,
		Fsid:    s.Fsid,
		Namelen: int64(s.Namelen),
		Frsize:  int64(s.Frsize),
		Flags:   int64(s.Flags),
	}
}

func StatfsToDarwin32(s *syscall.Statfs_t) *DarwinStatfs32_t {
	// TODO: fs type, owner, mnt names?
	return &DarwinStatfs32_t{
		Bsize:  int(s.Bsize),
		Blocks: int(s.Blocks),
		Bfree:  int(s.Bfree),
		Bavail: int(s.Bavail),
		Files:  int(s.Files),
		Ffree:  int(s.Ffree),
		Fsid:   s.Fsid,
	}
}

func StatfsToDarwin64(s *syscall.Statfs_t) *DarwinStatfs64_t {
	// TODO: fs type, owner, mnt names?
	return &DarwinStatfs64_t{
		Bsize:  uint32(s.Bsize),
		Blocks: uint64(s.Blocks),
		Bfree:  uint64(s.Bfree),
		Bavail: uint64(s.Bavail),
		Files:  uint64(s.Files),
		Ffree:  uint64(s.Ffree),
		Fsid:   s.Fsid,
	}
}
<|endoftext|>


<commit_before>package clitable

var mapFields = []string{"Key", "Value"}

// PrintHorizontal - Prints horizontal table from a map.
func PrintHorizontal(m map[string]interface{}) {
	// Create new table with
	table := New(mapFields)
	// Convert map to rows list
	rows := mapToRows(m)
	// Add rows to table
	for _, row := range rows {
		table.AddRow(row)
	}
	// Do not print head
	table.HideHead = true
	// Print table
	table.Print()
}

func mapToRows(m map[string]interface{}) (rows []map[string]interface{}) {
	rows = []map[string]interface{}{}
	for key, value := range m {
		row := map[string]interface{}{}
		row["Key"] = key
		row["Value"] = value
		rows = append(rows, row)
	}
	return
}
<commit_msg>Title case horizontal table key (feature)<commit_after>package clitable

import "strings"

var mapFields = []string{"Key", "Value"}

// PrintHorizontal - Prints horizontal table from a map.
func PrintHorizontal(m map[string]interface{}) {
	// Create new table with
	table := New(mapFields)
	// Convert map to rows list
	rows := mapToRows(m)
	// Add rows to table
	for _, row := range rows {
		table.AddRow(row)
	}
	// Do not print head
	table.HideHead = true
	// Print table
	table.Print()
}

func mapToRows(m map[string]interface{}) (rows []map[string]interface{}) {
	rows = []map[string]interface{}{}
	for key, value := range m {
		row := map[string]interface{}{}
		row["Key"] = strings.Title(key)
		row["Value"] = value
		rows = append(rows, row)
	}
	return
}
<|endoftext|>


<commit_before>package html // import "github.com/tdewolff/parse/html"

import (
	"io"
	"strconv"

	"github.com/tdewolff/parse"
)

////////////////////////////////////////////////////////////////

// TokenType determines the type of token, eg. a number or a semicolon.
type TokenType uint32

// TokenType values.
const (
	ErrorToken TokenType = iota // extra token when errors occur
	CommentToken
	DoctypeToken
	StartTagToken
	StartTagCloseToken
	StartTagVoidToken
	EndTagToken
	AttributeToken
	TextToken
)

// String returns the string representation of a TokenType.
func (tt TokenType) String() string {
	switch tt {
	case ErrorToken:
		return "Error"
	case CommentToken:
		return "Comment"
	case DoctypeToken:
		return "Doctype"
	case StartTagToken:
		return "StartTag"
	case StartTagCloseToken:
		return "StartTagClose"
	case StartTagVoidToken:
		return "StartTagVoid"
	case EndTagToken:
		return "EndTag"
	case AttributeToken:
		return "Attribute"
	case TextToken:
		return "Text"
	}
	return "Invalid(" + strconv.Itoa(int(tt)) + ")"
}

////////////////////////////////////////////////////////////////

// Tokenizer is the state for the tokenizer.
type Tokenizer struct {
	r   *parse.ShiftBuffer

	rawTag  Hash
	inTag   bool
	attrVal []byte
}

// NewTokenizer returns a new Tokenizer for a given io.Reader.
func NewTokenizer(r io.Reader) *Tokenizer {
	return &Tokenizer{
		r:    parse.NewShiftBuffer(r),
	}
}

// Err returns the error encountered during tokenization, this is often io.EOF but also other errors can be returned.
func (z Tokenizer) Err() error {
	return z.r.Err()
}

// IsEOF returns true when it has encountered EOF and thus loaded the last buffer in memory.
func (z Tokenizer) IsEOF() bool {
	return z.r.IsEOF()
}

// Next returns the next Token. It returns ErrorToken when an error was encountered. Using Err() one can retrieve the error message.
func (z *Tokenizer) Next() (TokenType, []byte) {
	var c byte
	if z.inTag {
		z.attrVal = []byte{}
		c = z.r.Peek(0)
		if c != '>' && c != '/' {
			return AttributeToken, z.shiftAttribute()
		}
		z.inTag = false
		if c == '/' {
			z.r.Move(2)
			return StartTagVoidToken, z.r.Shift()
		} else {
			z.r.Move(1)
			return StartTagCloseToken, z.r.Shift()
		}
	}

	if z.rawTag != 0 {
		if rawText := z.shiftRawText(); len(rawText) > 0 {
			z.rawTag = 0
			return TextToken, rawText
		}
		z.rawTag = 0
	}

	for {
		c = z.r.Peek(0)
		if c == 0 {
			if z.r.Pos() > 0 {
				return TextToken, z.r.Shift()
			} else {
				return ErrorToken, []byte{}
			}
		} else if c == '<' {
			c = z.r.Peek(1)
			if z.r.Pos() > 0 {
				if c == '/' && z.r.Peek(2) != 0 || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '!' || c == '?' {
					return TextToken, z.r.Shift()
				}
			} else if c == '/' && z.r.Peek(2) != 0 {
				z.r.Move(2)
				z.r.Skip()
				if c = z.r.Peek(0); c != '>' && !('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
					return CommentToken, z.shiftBogusComment()
				}
				return EndTagToken, z.shiftEndTag()
			} else if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' {
				z.r.Move(1)
				z.r.Skip()
				z.inTag = true
				return StartTagToken, z.shiftStartTag()
			} else if c == '!' {
				z.r.Move(2)
				z.r.Skip()
				return z.readMarkup()
			} else if c == '?' {
				z.r.Move(1)
				z.r.Skip()
				return CommentToken, z.shiftBogusComment()
			}
		}
		z.r.Move(1)
	}
	return ErrorToken, []byte{}
}

func (z *Tokenizer) AttrVal() []byte {
	return z.attrVal
}

func (z *Tokenizer) RawTag() Hash {
	return z.rawTag
}

////////////////////////////////////////////////////////////////

/*
The following functions follow the specifications at http://www.w3.org/html/wg/drafts/html/master/syntax.html
*/

func (z *Tokenizer) shiftRawText() []byte {
	if z.rawTag == Plaintext {
		for {
			if z.r.Peek(0) == 0 {
				return z.r.Shift()
			}
			z.r.Move(1)
		}
	} else { // RCDATA, RAWTEXT and SCRIPT
		for {
			c := z.r.Peek(0)
			if c == 0 {
				return z.r.Shift()
			} else if c == '<' && z.r.Peek(1) == '/' {
				nPos := z.r.Pos()
				z.r.Move(2)
				for {
					if c = z.r.Peek(0); !('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
						break
					}
					z.r.Move(1)
				}
				if h := ToHash(parse.CopyToLower(z.r.Bytes()[nPos+2:])); h == z.rawTag {
					z.r.MoveTo(nPos)
					return z.r.Shift()
				}
			} else if z.rawTag == Script && z.at([]byte("<!--")) {
				z.r.Move(4)
				inScript := false
				for {
					if z.at([]byte("-->")) {
						z.r.Move(3)
						break
					} else if z.at([]byte("<script")) {
						z.r.Move(7)
						inScript = true
					} else if z.at([]byte("</script")) {
						if inScript {
							z.r.Move(8)
							inScript = false
						} else {
							return z.r.Shift()
						}
					}
					z.r.Move(1)
				}
			} else {
				z.r.Move(1)
			}
		}
	}
}

func (z *Tokenizer) readMarkup() (TokenType, []byte) {
	if z.at([]byte("--")) {
		z.r.Move(2)
		z.r.Skip()
		for {
			if z.r.Peek(0) == 0 {
				return CommentToken, z.r.Shift()
			} else if z.at([]byte("-->")) {
				comment := z.r.Shift()
				z.r.Move(3)
				z.r.Skip()
				return CommentToken, comment
			} else if z.at([]byte("--!>")) {
				comment := z.r.Shift()
				z.r.Move(4)
				z.r.Skip()
				return CommentToken, comment
			}
			z.r.Move(1)
		}
	} else if z.at([]byte("[CDATA[")) {
		z.r.Move(7)
		z.r.Skip()
		for {
			if z.r.Peek(0) == 0 {
				return TextToken, z.r.Shift()
			} else if z.at([]byte("]]>")) {
				text := z.r.Shift()
				z.r.Move(3)
				z.r.Skip()
				return TextToken, text
			}
			z.r.Move(1)
		}
	} else {
		z.r.Skip()
		if z.atCaseInsensitive([]byte("doctype")) {
			z.r.Move(7)
			for {
				if c := z.r.Peek(0); c == '>' || c == 0 {
					doctype := z.r.Shift()
					z.r.Move(1)
					z.r.Skip()
					return DoctypeToken, doctype
				}
				z.r.Move(1)
			}
		}
	}
	bogus := z.shiftBogusComment()
	return CommentToken, bogus
}

func (z *Tokenizer) shiftBogusComment() []byte {
	for {
		if c := z.r.Peek(0); c == '>' || c == 0 {
			bogus := z.r.Shift()
			z.r.Move(1)
			z.r.Skip()
			return bogus
		}
		z.r.Move(1)
	}
}

func (z *Tokenizer) shiftStartTag() []byte {
	for {
		if c := z.r.Peek(0); c == ' ' || c == '>' || c == '/' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == 0 {
			break
		}
		z.r.Move(1)
	}
	name := parse.ToLower(z.r.Shift())
	if h := ToHash(name); h == Textarea || h == Title || h == Style || h == Xmp || h == Iframe || h == Noembed || h == Noframes || h == Noscript || h == Script || h == Plaintext {
		z.rawTag = h
	}
	z.skipWhitespace() // before attribute name state
	return name
}

func (z *Tokenizer) shiftAttribute() []byte {
	for { // attribute name state
		if c := z.r.Peek(0); c == ' ' || c == '=' || c == '>' || c == '/' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == 0 {
			break
		}
		z.r.Move(1)
	}
	name := parse.ToLower(z.r.Shift())
	z.skipWhitespace() // after attribute name state
	if z.r.Peek(0) == '=' {
		z.r.Move(1)
		z.skipWhitespace() // before attribute value state
		delim := z.r.Peek(0)
		if delim == '"' || delim == '\'' { // attribute value single- and double-quoted state
			z.r.Move(1)
			for {
				if z.r.Peek(0) == delim {
					break
				}
				z.r.Move(1)
			}
			z.r.Move(1)
		} else { // attribute value unquoted state
			for {
				if c := z.r.Peek(0); c == ' ' || c == '>' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == 0 {
					break
				}
				z.r.Move(1)
			}
		}
		z.attrVal = z.r.Shift()
		z.skipWhitespace() // before attribute name state or after attribute quoted value state
	} else {
		z.attrVal = nil
	}
	return name
}

func (z *Tokenizer) shiftEndTag() []byte {
	for {
		c := z.r.Peek(0)
		if c == 0 {
			return z.r.Shift()
		} else if c == '>' {
			name := parse.ToLower(z.r.Shift())
			z.r.Move(1)
			z.r.Skip()
			return name
		}
		z.r.Move(1)
	}
}

func (z *Tokenizer) skipWhitespace() {
	for {
		c := z.r.Peek(0)
		if c != ' ' && c != '\t' && c != '\n' && c != '\r' && c != '\f' || c == 0 {
			break
		}
		z.r.Move(1)
	}
	z.r.Skip()
}

func (z *Tokenizer) at(b []byte) bool {
	for i, c := range b {
		if z.r.Peek(i) != c {
			return false
		}
	}
	return true
}

func (z *Tokenizer) atCaseInsensitive(b []byte) bool {
	for i, c := range b {
		if z.r.Peek(i) != c && (z.r.Peek(i) | ('a' - 'A')) != c {
			return false
		}
	}
	return true
}
<commit_msg>Small optimization<commit_after>package html // import "github.com/tdewolff/parse/html"

import (
	"io"
	"strconv"

	"github.com/tdewolff/parse"
)

////////////////////////////////////////////////////////////////

// TokenType determines the type of token, eg. a number or a semicolon.
type TokenType uint32

// TokenType values.
const (
	ErrorToken TokenType = iota // extra token when errors occur
	CommentToken
	DoctypeToken
	StartTagToken
	StartTagCloseToken
	StartTagVoidToken
	EndTagToken
	AttributeToken
	TextToken
)

// String returns the string representation of a TokenType.
func (tt TokenType) String() string {
	switch tt {
	case ErrorToken:
		return "Error"
	case CommentToken:
		return "Comment"
	case DoctypeToken:
		return "Doctype"
	case StartTagToken:
		return "StartTag"
	case StartTagCloseToken:
		return "StartTagClose"
	case StartTagVoidToken:
		return "StartTagVoid"
	case EndTagToken:
		return "EndTag"
	case AttributeToken:
		return "Attribute"
	case TextToken:
		return "Text"
	}
	return "Invalid(" + strconv.Itoa(int(tt)) + ")"
}

////////////////////////////////////////////////////////////////

// Tokenizer is the state for the tokenizer.
type Tokenizer struct {
	r   *parse.ShiftBuffer

	rawTag  Hash
	inTag   bool
	attrVal []byte
}

// NewTokenizer returns a new Tokenizer for a given io.Reader.
func NewTokenizer(r io.Reader) *Tokenizer {
	return &Tokenizer{
		r:    parse.NewShiftBuffer(r),
	}
}

// Err returns the error encountered during tokenization, this is often io.EOF but also other errors can be returned.
func (z Tokenizer) Err() error {
	return z.r.Err()
}

// IsEOF returns true when it has encountered EOF and thus loaded the last buffer in memory.
func (z Tokenizer) IsEOF() bool {
	return z.r.IsEOF()
}

// Next returns the next Token. It returns ErrorToken when an error was encountered. Using Err() one can retrieve the error message.
func (z *Tokenizer) Next() (TokenType, []byte) {
	var c byte
	if z.inTag {
		z.attrVal = nil
		c = z.r.Peek(0)
		if c != '>' && c != '/' {
			return AttributeToken, z.shiftAttribute()
		}
		z.inTag = false
		if c == '/' {
			z.r.Move(2)
			return StartTagVoidToken, z.r.Shift()
		} else {
			z.r.Move(1)
			return StartTagCloseToken, z.r.Shift()
		}
	}

	if z.rawTag != 0 {
		if rawText := z.shiftRawText(); len(rawText) > 0 {
			z.rawTag = 0
			return TextToken, rawText
		}
		z.rawTag = 0
	}

	for {
		c = z.r.Peek(0)
		if c == 0 {
			if z.r.Pos() > 0 {
				return TextToken, z.r.Shift()
			} else {
				return ErrorToken, []byte{}
			}
		} else if c == '<' {
			c = z.r.Peek(1)
			if z.r.Pos() > 0 {
				if c == '/' && z.r.Peek(2) != 0 || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '!' || c == '?' {
					return TextToken, z.r.Shift()
				}
			} else if c == '/' && z.r.Peek(2) != 0 {
				z.r.Move(2)
				z.r.Skip()
				if c = z.r.Peek(0); c != '>' && !('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
					return CommentToken, z.shiftBogusComment()
				}
				return EndTagToken, z.shiftEndTag()
			} else if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' {
				z.r.Move(1)
				z.r.Skip()
				z.inTag = true
				return StartTagToken, z.shiftStartTag()
			} else if c == '!' {
				z.r.Move(2)
				z.r.Skip()
				return z.readMarkup()
			} else if c == '?' {
				z.r.Move(1)
				z.r.Skip()
				return CommentToken, z.shiftBogusComment()
			}
		}
		z.r.Move(1)
	}
	return ErrorToken, []byte{}
}

func (z *Tokenizer) AttrVal() []byte {
	return z.attrVal
}

func (z *Tokenizer) RawTag() Hash {
	return z.rawTag
}

////////////////////////////////////////////////////////////////

/*
The following functions follow the specifications at http://www.w3.org/html/wg/drafts/html/master/syntax.html
*/

func (z *Tokenizer) shiftRawText() []byte {
	if z.rawTag == Plaintext {
		for {
			if z.r.Peek(0) == 0 {
				return z.r.Shift()
			}
			z.r.Move(1)
		}
	} else { // RCDATA, RAWTEXT and SCRIPT
		for {
			c := z.r.Peek(0)
			if c == 0 {
				return z.r.Shift()
			} else if c == '<' && z.r.Peek(1) == '/' {
				nPos := z.r.Pos()
				z.r.Move(2)
				for {
					if c = z.r.Peek(0); !('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
						break
					}
					z.r.Move(1)
				}
				if h := ToHash(parse.CopyToLower(z.r.Bytes()[nPos+2:])); h == z.rawTag {
					z.r.MoveTo(nPos)
					return z.r.Shift()
				}
			} else if z.rawTag == Script && z.at([]byte("<!--")) {
				z.r.Move(4)
				inScript := false
				for {
					if z.at([]byte("-->")) {
						z.r.Move(3)
						break
					} else if z.at([]byte("<script")) {
						z.r.Move(7)
						inScript = true
					} else if z.at([]byte("</script")) {
						if inScript {
							z.r.Move(8)
							inScript = false
						} else {
							return z.r.Shift()
						}
					}
					z.r.Move(1)
				}
			} else {
				z.r.Move(1)
			}
		}
	}
}

func (z *Tokenizer) readMarkup() (TokenType, []byte) {
	if z.at([]byte("--")) {
		z.r.Move(2)
		z.r.Skip()
		for {
			if z.r.Peek(0) == 0 {
				return CommentToken, z.r.Shift()
			} else if z.at([]byte("-->")) {
				comment := z.r.Shift()
				z.r.Move(3)
				z.r.Skip()
				return CommentToken, comment
			} else if z.at([]byte("--!>")) {
				comment := z.r.Shift()
				z.r.Move(4)
				z.r.Skip()
				return CommentToken, comment
			}
			z.r.Move(1)
		}
	} else if z.at([]byte("[CDATA[")) {
		z.r.Move(7)
		z.r.Skip()
		for {
			if z.r.Peek(0) == 0 {
				return TextToken, z.r.Shift()
			} else if z.at([]byte("]]>")) {
				text := z.r.Shift()
				z.r.Move(3)
				z.r.Skip()
				return TextToken, text
			}
			z.r.Move(1)
		}
	} else {
		z.r.Skip()
		if z.atCaseInsensitive([]byte("doctype")) {
			z.r.Move(7)
			for {
				if c := z.r.Peek(0); c == '>' || c == 0 {
					doctype := z.r.Shift()
					z.r.Move(1)
					z.r.Skip()
					return DoctypeToken, doctype
				}
				z.r.Move(1)
			}
		}
	}
	bogus := z.shiftBogusComment()
	return CommentToken, bogus
}

func (z *Tokenizer) shiftBogusComment() []byte {
	for {
		if c := z.r.Peek(0); c == '>' || c == 0 {
			bogus := z.r.Shift()
			z.r.Move(1)
			z.r.Skip()
			return bogus
		}
		z.r.Move(1)
	}
}

func (z *Tokenizer) shiftStartTag() []byte {
	for {
		if c := z.r.Peek(0); c == ' ' || c == '>' || c == '/' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == 0 {
			break
		}
		z.r.Move(1)
	}
	name := parse.ToLower(z.r.Shift())
	if h := ToHash(name); h == Textarea || h == Title || h == Style || h == Xmp || h == Iframe || h == Noembed || h == Noframes || h == Noscript || h == Script || h == Plaintext {
		z.rawTag = h
	}
	z.skipWhitespace() // before attribute name state
	return name
}

func (z *Tokenizer) shiftAttribute() []byte {
	for { // attribute name state
		if c := z.r.Peek(0); c == ' ' || c == '=' || c == '>' || c == '/' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == 0 {
			break
		}
		z.r.Move(1)
	}
	name := parse.ToLower(z.r.Shift())
	z.skipWhitespace() // after attribute name state
	if z.r.Peek(0) == '=' {
		z.r.Move(1)
		z.skipWhitespace() // before attribute value state
		delim := z.r.Peek(0)
		if delim == '"' || delim == '\'' { // attribute value single- and double-quoted state
			z.r.Move(1)
			for {
				if z.r.Peek(0) == delim {
					break
				}
				z.r.Move(1)
			}
			z.r.Move(1)
		} else { // attribute value unquoted state
			for {
				if c := z.r.Peek(0); c == ' ' || c == '>' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == 0 {
					break
				}
				z.r.Move(1)
			}
		}
		z.attrVal = z.r.Shift()
		z.skipWhitespace() // before attribute name state or after attribute quoted value state
	} else {
		z.attrVal = nil
	}
	return name
}

func (z *Tokenizer) shiftEndTag() []byte {
	for {
		c := z.r.Peek(0)
		if c == 0 {
			return z.r.Shift()
		} else if c == '>' {
			name := parse.ToLower(z.r.Shift())
			z.r.Move(1)
			z.r.Skip()
			return name
		}
		z.r.Move(1)
	}
}

func (z *Tokenizer) skipWhitespace() {
	for {
		c := z.r.Peek(0)
		if c != ' ' && c != '\t' && c != '\n' && c != '\r' && c != '\f' || c == 0 {
			break
		}
		z.r.Move(1)
	}
	z.r.Skip()
}

func (z *Tokenizer) at(b []byte) bool {
	for i, c := range b {
		if z.r.Peek(i) != c {
			return false
		}
	}
	return true
}

func (z *Tokenizer) atCaseInsensitive(b []byte) bool {
	for i, c := range b {
		if z.r.Peek(i) != c && (z.r.Peek(i) | ('a' - 'A')) != c {
			return false
		}
	}
	return true
}
<|endoftext|>