<commit_before>package daemon

import (
	"fmt"
	"github.com/ant0ine/go-urlrouter"
	"github.com/nytlabs/streamtools/blocks"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	//"strings"
	"errors"
)

const (
	READ_MAX = 1024768
)

var (
	// channel that returns the next ID
	idChan chan string
)

// Daemon keeps track of all the blocks and connections
type Daemon struct {
	blockMap map[string]*blocks.Block
}

// The rootHandler returns information about the whole system
func (d *Daemon) rootHandler(w http.ResponseWriter, r *http.Request, m map[string]string) {
	fmt.Fprintln(w, "hello! this is streamtools")
	fmt.Fprintln(w, "ID: BlockType")
	for id, block := range d.blockMap {
		fmt.Fprintln(w, id+":", block.BlockType)
	}
}

// The createHandler creates new blocks
func (d *Daemon) createHandler(w http.ResponseWriter, r *http.Request, m map[string]string) {
	err := r.ParseForm()
	if err != nil {
		ApiResponse(w, 500, "BAD_REQUEST")
		return
	}

	var id string
	var blockType string
	fType, typeExists := r.Form["blockType"]
	fID, idExists := r.Form["id"]

	if typeExists == false {
		ApiResponse(w, 500, "MISSING_BLOCKTYPE")
		return
	} else {
		blockType = fType[0]
	}

	_, inLibrary := blocks.Library[blockType]
	if inLibrary == false {
		ApiResponse(w, 500, "INVALID_BLOCKTYPE")
		return
	}

	if idExists == false {
		id = <-idChan
	} else {
		_, notUnique := d.blockMap[fID[0]]
		if notUnique == true {
			ApiResponse(w, 500, "BLOCK_ID_ALREADY_EXISTS")
			return
		} else {
			id = fID[0]
		}
	}

	d.CreateBlock(blockType, id)

	ApiResponse(w, 200, "BLOCK_CREATED")
}

func (d *Daemon) deleteHandler(w http.ResponseWriter, r *http.Request, m map[string]string) {
	err := r.ParseForm()
	if err != nil {
		ApiResponse(w, 500, "BAD_REQUEST")
		return
	}

	_, hasID := r.Form["id"]
	if hasID == false {
		ApiResponse(w, 500, "MISSING_BLOCK_ID")
		return
	}

	id := r.Form["id"][0]

	err = d.DeleteBlock(id)

	if err != nil {
		ApiResponse(w, 500, err.Error())
		return
	}

	ApiResponse(w, 200, "BLOCK_DELETED")
}

func (d *Daemon) DeleteBlock(id string) error {
	block, ok := d.blockMap[id]
	if ok == false {
		return errors.New("BLOCK_NOT_FOUND")
	}

	for k, _ := range block.InBlocks {
		d.blockMap[k].AddChan <- &blocks.OutChanMsg{
			Action: blocks.DELETE_OUT_CHAN,
			ID:     block.ID,
		}

		log.Println("disconnecting \"" + block.ID + "\" from \"" + k + "\"")

		delete(d.blockMap[k].OutBlocks, block.ID)

		if d.blockMap[k].BlockType == "connection" {
			d.DeleteBlock(k)
		}
	}

	for k, _ := range block.OutBlocks {
		delete(d.blockMap[k].InBlocks, block.ID)
		if d.blockMap[k].BlockType == "connection" {
			d.DeleteBlock(k)
		}
	}

	block.QuitChan <- true
	delete(d.blockMap, id)

	return nil
}

// The connectHandler connects together two blocks
func (d *Daemon) connectHandler(w http.ResponseWriter, r *http.Request, m map[string]string) {
	var id string

	err := r.ParseForm()
	if err != nil {
		ApiResponse(w, 500, "BAD_REQUEST")
		return
	}

	_, hasFrom := r.Form["from"]
	if hasFrom == false {
		ApiResponse(w, 500, "MISSING_FROM_BLOCK_ID")
		return
	}

	_, hasTo := r.Form["to"]
	if hasTo == false {
		ApiResponse(w, 500, "MISSING_TO_BLOCK_ID")
		return
	}

	fID, hasID := r.Form["id"]
	if hasID == false {
		id = <-idChan
	} else {
		_, ok := d.blockMap[fID[0]]
		if ok == false {
			id = fID[0]
		} else {
			ApiResponse(w, 500, "BLOCK_ID_ALREADY_EXISTS")
			return
		}
	}

	from := r.Form["from"][0]
	to := r.Form["to"][0]

	if len(from) == 0 {
		ApiResponse(w, 500, "MISSING_FROM_BLOCK_ID")
		return
	}

	if len(to) == 0 {
		ApiResponse(w, 500, "MISSING_TO_BLOCK_ID")
		return
	}

	_, exists := d.blockMap[from]
	if exists == false {
		ApiResponse(w, 500, "FROM_BLOCK_NOT_FOUND")
		return
	}

	_, exists = d.blockMap[to]
	if exists == false {
		ApiResponse(w, 500, "TO_BLOCK_NOT_FOUND")
		return
	}

	d.CreateConnection(from, to, id)

	ApiResponse(w, 200, "CONNECTION_CREATED")
}

// The routeHandler deals with any incoming message sent to an arbitrary block endpoint
func (d *Daemon) routeHandler(w http.ResponseWriter, r *http.Request, m map[string]string) {
	//id := strings.Split(r.URL.Path, "/")[2]
	//route := strings.Split(r.URL.Path, "/")[3]

	id, ok := m["id"]
	if ok == false {
		ApiResponse(w, 500, "MISSING_BLOCK_ID")
		return
	}

	route, ok := m["route"]
	if ok == false {
		ApiResponse(w, 500, "MISSING_ROUTE")
		return
	}

	_, ok = d.blockMap[id]
	if ok == false {
		ApiResponse(w, 500, "BLOCK_ID_NOT_FOUND")
		return
	}

	_, ok = d.blockMap[id].Routes[route]
	if ok == false {
		ApiResponse(w, 500, "ROUTE_NOT_FOUND")
		return
	}

	msg, err := ioutil.ReadAll(io.LimitReader(r.Body, READ_MAX))

	if err != nil {
		ApiResponse(w, 500, "BAD_REQUEST")
		return
	}

	ResponseChan := make(chan []byte)
	blockRouteChan := d.blockMap[id].Routes[route]
	blockRouteChan <- blocks.RouteResponse{
		Msg:          msg,
		ResponseChan: ResponseChan,
	}
	respMsg := <-ResponseChan

	DataResponse(w, respMsg)
}

func (d *Daemon) libraryHandler(w http.ResponseWriter, r *http.Request, m map[string]string) {
	fmt.Fprint(w, "libraryBlob")
}

func (d *Daemon) CreateConnection(from string, to string, ID string) {
	d.CreateBlock("connection", ID)

	d.blockMap[from].AddChan <- &blocks.OutChanMsg{
		Action:  blocks.CREATE_OUT_CHAN,
		OutChan: d.blockMap[ID].InChan,
		ID:      ID,
	}

	d.blockMap[ID].AddChan <- &blocks.OutChanMsg{
		Action:  blocks.CREATE_OUT_CHAN,
		OutChan: d.blockMap[to].InChan,
		ID:      to,
	}

	// add the from block to the list of inblocks for connection.
	d.blockMap[from].OutBlocks[ID] = true
	d.blockMap[ID].InBlocks[from] = true
	d.blockMap[ID].OutBlocks[to] = true
	d.blockMap[to].InBlocks[ID] = true

	log.Println("connected", d.blockMap[from].ID, "to", d.blockMap[to].ID)
}

func (d *Daemon) CreateBlock(name string, ID string) {
	// TODO: Clean this up.
	//
	// In order to avoid data races the blocks held in daemon's blockMap
	// are not the same blocks held in each block routine. When CreateBlock
	// is called, we actually create two blocks: one to store in daemon's
	// blockMap and one to send to the block routine.
	//
	// The block stored in daemon's blockmap doesn't make use of OutChans as
	// a block's OutChans can be dynamically modified when connections are
	// added or deleted. All of the other fields, such as ID, name, and all
	// the channels that go into the block (inChan, Routes) are the SAME
	// in both the daemon blockMap block and the blockroutine block.
	//
	// Becauase of this very minor difference it would be a huge semantic help
	// if the type going to the blockroutines was actually different than the
	// type being kept in daemon's blockmap.
	//
	// Modifications to blocks in daemon's blockMap will obviously not
	// proliferate to blockroutines and all changes (such as adding outchans)
	// can only be done through messages. A future daemon block type might
	// want to restrict how daemon blocks can be used, such as creating
	// getters and no setters. Or perhaps a setter automatically takes care
	// of sending a message to the blockroutine to emulate the manipulation
	// of a single variable.

	// create the block that will be stored in blockMap
	b, _ := blocks.NewBlock(name, ID)
	//d.createRoutes(b)
	d.blockMap[b.ID] = b
	d.blockMap[b.ID].InBlocks = make(map[string]bool)
	d.blockMap[b.ID].OutBlocks = make(map[string]bool)

	// create the block that will be sent to the blockroutine and copy all
	// chan references from the previously created block
	c, _ := blocks.NewBlock(name, ID)
	for k, v := range b.Routes {
		c.Routes[k] = v
	}

	c.InChan = b.InChan
	c.AddChan = b.AddChan
	c.QuitChan = b.QuitChan

	//create outchans for use only by blockroutine block.
	c.OutChans = make(map[string]chan *simplejson.Json)

	go blocks.Library[name].Routine(c)

	log.Println("started block \"" + ID + "\" of type " + name)
}

func (d *Daemon) Run(port string) {

	// start the ID Service
	idChan = make(chan string)
	go IDService(idChan)

	// start the library service
	blocks.BuildLibrary()

	// initialise the block maps
	d.blockMap = make(map[string]*blocks.Block)

	// instantiate the base handlers
	router := urlrouter.Router{
		Routes: []urlrouter.Route{
			urlrouter.Route{
				PathExp: "/",
				Dest:    d.rootHandler,
			},
			urlrouter.Route{
				PathExp: "/create",
				Dest:    d.createHandler,
			},
			urlrouter.Route{
				PathExp: "/delete",
				Dest:    d.deleteHandler,
			},
			urlrouter.Route{
				PathExp: "/connect",
				Dest:    d.connectHandler,
			},
			urlrouter.Route{
				PathExp: "/blocks/:id/:route",
				Dest:    d.routeHandler,
			},
		},
	}

	router.Start()

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		route, params := router.FindRouteFromURL(r.URL)
		handler := route.Dest.(func(http.ResponseWriter, *http.Request, map[string]string))
		handler(w, r, params)
	})

	// start the http server
	log.Println("starting stream tools on port", port)
	err := http.ListenAndServe(":"+port, nil)
	if err != nil {
		log.Fatalf(err.Error())
	}
}
<commit_msg>daemon<commit_after>package daemon

import (
	"fmt"
	"github.com/ant0ine/go-urlrouter"
	"github.com/nytlabs/streamtools/blocks"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	//"strings"
	"errors"
)

const (
	READ_MAX = 1024768
)

var (
	// channel that returns the next ID
	idChan chan string
)

// Daemon keeps track of all the blocks and connections
type Daemon struct {
	blockMap map[string]*blocks.Block
}

// The rootHandler returns information about the whole system
func (d *Daemon) rootHandler(w http.ResponseWriter, r *http.Request, m map[string]string) {
	fmt.Fprintln(w, "hello! this is streamtools")
	fmt.Fprintln(w, "ID: BlockType")
	for id, block := range d.blockMap {
		fmt.Fprintln(w, id+":", block.BlockType)
	}
}

// The createHandler creates new blocks
func (d *Daemon) createHandler(w http.ResponseWriter, r *http.Request, m map[string]string) {
	err := r.ParseForm()
	if err != nil {
		ApiResponse(w, 500, "BAD_REQUEST")
		return
	}

	var id string
	var blockType string
	fType, typeExists := r.Form["blockType"]
	fID, idExists := r.Form["id"]

	if typeExists == false {
		ApiResponse(w, 500, "MISSING_BLOCKTYPE")
		return
	} else {
		blockType = fType[0]
	}

	_, inLibrary := blocks.Library[blockType]
	if inLibrary == false {
		ApiResponse(w, 500, "INVALID_BLOCKTYPE")
		return
	}

	if idExists == false {
		id = <-idChan
	} else {
		_, notUnique := d.blockMap[fID[0]]
		if notUnique == true {
			ApiResponse(w, 500, "BLOCK_ID_ALREADY_EXISTS")
			return
		} else {
			id = fID[0]
		}
	}

	d.CreateBlock(blockType, id)

	ApiResponse(w, 200, "BLOCK_CREATED")
}

func (d *Daemon) deleteHandler(w http.ResponseWriter, r *http.Request, m map[string]string) {
	err := r.ParseForm()
	if err != nil {
		ApiResponse(w, 500, "BAD_REQUEST")
		return
	}

	_, hasID := r.Form["id"]
	if hasID == false {
		ApiResponse(w, 500, "MISSING_BLOCK_ID")
		return
	}

	id := r.Form["id"][0]

	err = d.DeleteBlock(id)

	if err != nil {
		ApiResponse(w, 500, err.Error())
		return
	}

	ApiResponse(w, 200, "BLOCK_DELETED")
}

func (d *Daemon) DeleteBlock(id string) error {
	block, ok := d.blockMap[id]
	if ok == false {
		return errors.New("BLOCK_NOT_FOUND")
	}

	for k, _ := range block.InBlocks {
		d.blockMap[k].AddChan <- &blocks.OutChanMsg{
			Action: blocks.DELETE_OUT_CHAN,
			ID:     block.ID,
		}

		log.Println("disconnecting \"" + block.ID + "\" from \"" + k + "\"")

		delete(d.blockMap[k].OutBlocks, block.ID)

		if d.blockMap[k].BlockType == "connection" {
			d.DeleteBlock(k)
		}
	}

	for k, _ := range block.OutBlocks {
		delete(d.blockMap[k].InBlocks, block.ID)
		if d.blockMap[k].BlockType == "connection" {
			d.DeleteBlock(k)
		}
	}

	block.QuitChan <- true
	delete(d.blockMap, id)

	return nil
}

// The connectHandler connects together two blocks
func (d *Daemon) connectHandler(w http.ResponseWriter, r *http.Request, m map[string]string) {
	var id string

	err := r.ParseForm()
	if err != nil {
		ApiResponse(w, 500, "BAD_REQUEST")
		return
	}

	_, hasFrom := r.Form["from"]
	if hasFrom == false {
		ApiResponse(w, 500, "MISSING_FROM_BLOCK_ID")
		return
	}

	_, hasTo := r.Form["to"]
	if hasTo == false {
		ApiResponse(w, 500, "MISSING_TO_BLOCK_ID")
		return
	}

	fID, hasID := r.Form["id"]
	if hasID == false {
		id = <-idChan
	} else {
		_, ok := d.blockMap[fID[0]]
		if ok == false {
			id = fID[0]
		} else {
			ApiResponse(w, 500, "BLOCK_ID_ALREADY_EXISTS")
			return
		}
	}

	from := r.Form["from"][0]
	to := r.Form["to"][0]

	if len(from) == 0 {
		ApiResponse(w, 500, "MISSING_FROM_BLOCK_ID")
		return
	}

	if len(to) == 0 {
		ApiResponse(w, 500, "MISSING_TO_BLOCK_ID")
		return
	}

	_, exists := d.blockMap[from]
	if exists == false {
		ApiResponse(w, 500, "FROM_BLOCK_NOT_FOUND")
		return
	}

	_, exists = d.blockMap[to]
	if exists == false {
		ApiResponse(w, 500, "TO_BLOCK_NOT_FOUND")
		return
	}

	d.CreateConnection(from, to, id)

	ApiResponse(w, 200, "CONNECTION_CREATED")
}

// The routeHandler deals with any incoming message sent to an arbitrary block endpoint
func (d *Daemon) routeHandler(w http.ResponseWriter, r *http.Request, m map[string]string) {
	//id := strings.Split(r.URL.Path, "/")[2]
	//route := strings.Split(r.URL.Path, "/")[3]

	id, ok := m["id"]
	if ok == false {
		ApiResponse(w, 500, "MISSING_BLOCK_ID")
		return
	}

	route, ok := m["route"]
	if ok == false {
		ApiResponse(w, 500, "MISSING_ROUTE")
		return
	}

	_, ok = d.blockMap[id]
	if ok == false {
		ApiResponse(w, 500, "BLOCK_ID_NOT_FOUND")
		return
	}

	_, ok = d.blockMap[id].Routes[route]
	if ok == false {
		ApiResponse(w, 500, "ROUTE_NOT_FOUND")
		return
	}

	msg, err := ioutil.ReadAll(io.LimitReader(r.Body, READ_MAX))

	if err != nil {
		ApiResponse(w, 500, "BAD_REQUEST")
		return
	}

	ResponseChan := make(chan []byte)
	blockRouteChan := d.blockMap[id].Routes[route]
	blockRouteChan <- blocks.RouteResponse{
		Msg:          msg,
		ResponseChan: ResponseChan,
	}
	respMsg := <-ResponseChan

	DataResponse(w, respMsg)
}

func (d *Daemon) libraryHandler(w http.ResponseWriter, r *http.Request, m map[string]string) {
	fmt.Fprint(w, "libraryBlob")
}

func (d *Daemon) CreateConnection(from string, to string, ID string) {
	d.CreateBlock("connection", ID)

	d.blockMap[from].AddChan <- &blocks.OutChanMsg{
		Action:  blocks.CREATE_OUT_CHAN,
		OutChan: d.blockMap[ID].InChan,
		ID:      ID,
	}

	d.blockMap[ID].AddChan <- &blocks.OutChanMsg{
		Action:  blocks.CREATE_OUT_CHAN,
		OutChan: d.blockMap[to].InChan,
		ID:      to,
	}

	// add the from block to the list of inblocks for connection.
	d.blockMap[from].OutBlocks[ID] = true
	d.blockMap[ID].InBlocks[from] = true
	d.blockMap[ID].OutBlocks[to] = true
	d.blockMap[to].InBlocks[ID] = true

	log.Println("connected", d.blockMap[from].ID, "to", d.blockMap[to].ID)
}

func (d *Daemon) CreateBlock(name string, ID string) {
	// TODO: Clean this up.
	//
	// In order to avoid data races the blocks held in daemon's blockMap
	// are not the same blocks held in each block routine. When CreateBlock
	// is called, we actually create two blocks: one to store in daemon's
	// blockMap and one to send to the block routine.
	//
	// The block stored in daemon's blockmap doesn't make use of OutChans as
	// a block's OutChans can be dynamically modified when connections are
	// added or deleted. All of the other fields, such as ID, name, and all
	// the channels that go into the block (inChan, Routes) are the SAME
	// in both the daemon blockMap block and the blockroutine block.
	//
	// Becauase of this very minor difference it would be a huge semantic help
	// if the type going to the blockroutines was actually different than the
	// type being kept in daemon's blockmap.
	//
	// Modifications to blocks in daemon's blockMap will obviously not
	// proliferate to blockroutines and all changes (such as adding outchans)
	// can only be done through messages. A future daemon block type might
	// want to restrict how daemon blocks can be used, such as creating
	// getters and no setters. Or perhaps a setter automatically takes care
	// of sending a message to the blockroutine to emulate the manipulation
	// of a single variable.

	// create the block that will be stored in blockMap
	b, _ := blocks.NewBlock(name, ID)
	//d.createRoutes(b)
	d.blockMap[b.ID] = b
	d.blockMap[b.ID].InBlocks = make(map[string]bool)
	d.blockMap[b.ID].OutBlocks = make(map[string]bool)

	// create the block that will be sent to the blockroutine and copy all
	// chan references from the previously created block
	c, _ := blocks.NewBlock(name, ID)
	for k, v := range b.Routes {
		c.Routes[k] = v
	}

	c.InChan = b.InChan
	c.AddChan = b.AddChan
	c.QuitChan = b.QuitChan

	//create outchans for use only by blockroutine block.
	c.OutChans = make(map[string]chan blocks.BMsg)

	go blocks.Library[name].Routine(c)

	log.Println("started block \"" + ID + "\" of type " + name)
}

func (d *Daemon) Run(port string) {

	// start the ID Service
	idChan = make(chan string)
	go IDService(idChan)

	// start the library service
	blocks.BuildLibrary()

	// initialise the block maps
	d.blockMap = make(map[string]*blocks.Block)

	// instantiate the base handlers
	router := urlrouter.Router{
		Routes: []urlrouter.Route{
			urlrouter.Route{
				PathExp: "/",
				Dest:    d.rootHandler,
			},
			urlrouter.Route{
				PathExp: "/create",
				Dest:    d.createHandler,
			},
			urlrouter.Route{
				PathExp: "/delete",
				Dest:    d.deleteHandler,
			},
			urlrouter.Route{
				PathExp: "/connect",
				Dest:    d.connectHandler,
			},
			urlrouter.Route{
				PathExp: "/blocks/:id/:route",
				Dest:    d.routeHandler,
			},
		},
	}

	router.Start()

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		route, params := router.FindRouteFromURL(r.URL)
		handler := route.Dest.(func(http.ResponseWriter, *http.Request, map[string]string))
		handler(w, r, params)
	})

	// start the http server
	log.Println("starting stream tools on port", port)
	err := http.ListenAndServe(":"+port, nil)
	if err != nil {
		log.Fatalf(err.Error())
	}
}
<|endoftext|>


<commit_before>package daemon // import "github.com/docker/docker/daemon"

import (
	"context"
	"fmt"
	"os"
	"path"
	"strings"
	"time"

	"github.com/docker/docker/api/types"
	containertypes "github.com/docker/docker/api/types/container"
	"github.com/docker/docker/container"
	"github.com/docker/docker/errdefs"
	"github.com/docker/docker/pkg/containerfs"
	"github.com/opencontainers/selinux/go-selinux"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// ContainerRm removes the container id from the filesystem. An error
// is returned if the container is not found, or if the remove
// fails. If the remove succeeds, the container name is released, and
// network links are removed.
func (daemon *Daemon) ContainerRm(name string, config *types.ContainerRmConfig) error {
	start := time.Now()
	ctr, err := daemon.GetContainer(name)
	if err != nil {
		return err
	}

	// Container state RemovalInProgress should be used to avoid races.
	if inProgress := ctr.SetRemovalInProgress(); inProgress {
		err := fmt.Errorf("removal of container %s is already in progress", name)
		return errdefs.Conflict(err)
	}
	defer ctr.ResetRemovalInProgress()

	// check if container wasn't deregistered by previous rm since Get
	if c := daemon.containers.Get(ctr.ID); c == nil {
		return nil
	}

	if config.RemoveLink {
		return daemon.rmLink(ctr, name)
	}

	err = daemon.cleanupContainer(ctr, *config)
	containerActions.WithValues("delete").UpdateSince(start)

	return err
}

func (daemon *Daemon) rmLink(container *container.Container, name string) error {
	if name[0] != '/' {
		name = "/" + name
	}
	parent, n := path.Split(name)
	if parent == "/" {
		return fmt.Errorf("Conflict, cannot remove the default link name of the container")
	}

	parent = strings.TrimSuffix(parent, "/")
	pe, err := daemon.containersReplica.Snapshot().GetID(parent)
	if err != nil {
		return fmt.Errorf("Cannot get parent %s for link name %s", parent, name)
	}

	daemon.releaseName(name)
	parentContainer, _ := daemon.GetContainer(pe)
	if parentContainer != nil {
		daemon.linkIndex.unlink(name, container, parentContainer)
		if err := daemon.updateNetwork(parentContainer); err != nil {
			logrus.Debugf("Could not update network to remove link %s: %v", n, err)
		}
	}
	return nil
}

// cleanupContainer unregisters a container from the daemon, stops stats
// collection and cleanly removes contents and metadata from the filesystem.
func (daemon *Daemon) cleanupContainer(container *container.Container, config types.ContainerRmConfig) error {
	if container.IsRunning() {
		if !config.ForceRemove {
			state := container.StateString()
			procedure := "Stop the container before attempting removal or force remove"
			if state == "paused" {
				procedure = "Unpause and then " + strings.ToLower(procedure)
			}
			err := fmt.Errorf("You cannot remove a %s container %s. %s", state, container.ID, procedure)
			return errdefs.Conflict(err)
		}
		if err := daemon.Kill(container); err != nil {
			return fmt.Errorf("Could not kill running container %s, cannot remove - %v", container.ID, err)
		}
	}

	// stop collection of stats for the container regardless
	// if stats are currently getting collected.
	daemon.statsCollector.StopCollection(container)

	// stopTimeout is the number of seconds to wait for the container to stop
	// gracefully before forcibly killing it.
	//
	// Why 3 seconds? The timeout specified here was originally added in commit
	// 1615bb08c7c3fc6c4b22db0a633edda516f97cf0, which added a custom timeout to
	// some commands, but lacking an option for a timeout on "docker rm", was
	// hardcoded to 10 seconds. Commit 28fd289b448164b77affd8103c0d96fd8110daf9
	// later on updated this to 3 seconds (but no background on that change).
	//
	// If you arrived here and know the answer, you earned yourself a picture
	// of a cute animal of your own choosing.
	var stopTimeout = 3
	if err := daemon.containerStop(context.TODO(), container, containertypes.StopOptions{Timeout: &stopTimeout}); err != nil {
		return err
	}

	// Mark container dead. We don't want anybody to be restarting it.
	container.Lock()
	container.Dead = true

	// Save container state to disk. So that if error happens before
	// container meta file got removed from disk, then a restart of
	// docker should not make a dead container alive.
	if err := container.CheckpointTo(daemon.containersReplica); err != nil && !os.IsNotExist(err) {
		logrus.Errorf("Error saving dying container to disk: %v", err)
	}
	container.Unlock()

	// When container creation fails and `RWLayer` has not been created yet, we
	// do not call `ReleaseRWLayer`
	if container.RWLayer != nil {
		if err := daemon.imageService.ReleaseLayer(container.RWLayer); err != nil {
			err = errors.Wrapf(err, "container %s", container.ID)
			container.SetRemovalError(err)
			return err
		}
		container.RWLayer = nil
	}

	if err := containerfs.EnsureRemoveAll(container.Root); err != nil {
		err = errors.Wrapf(err, "unable to remove filesystem for %s", container.ID)
		container.SetRemovalError(err)
		return err
	}

	linkNames := daemon.linkIndex.delete(container)
	selinux.ReleaseLabel(container.ProcessLabel)
	daemon.containers.Delete(container.ID)
	daemon.containersReplica.Delete(container)
	if err := daemon.removeMountPoints(container, config.RemoveVolume); err != nil {
		logrus.Error(err)
	}
	for _, name := range linkNames {
		daemon.releaseName(name)
	}
	container.SetRemoved()
	stateCtr.del(container.ID)

	daemon.LogContainerEvent(container, "destroy")
	return nil
}
<commit_msg>Lock container when deleting its root directory<commit_after>package daemon // import "github.com/docker/docker/daemon"

import (
	"context"
	"fmt"
	"os"
	"path"
	"strings"
	"time"

	"github.com/docker/docker/api/types"
	containertypes "github.com/docker/docker/api/types/container"
	"github.com/docker/docker/container"
	"github.com/docker/docker/errdefs"
	"github.com/docker/docker/pkg/containerfs"
	"github.com/opencontainers/selinux/go-selinux"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// ContainerRm removes the container id from the filesystem. An error
// is returned if the container is not found, or if the remove
// fails. If the remove succeeds, the container name is released, and
// network links are removed.
func (daemon *Daemon) ContainerRm(name string, config *types.ContainerRmConfig) error {
	start := time.Now()
	ctr, err := daemon.GetContainer(name)
	if err != nil {
		return err
	}

	// Container state RemovalInProgress should be used to avoid races.
	if inProgress := ctr.SetRemovalInProgress(); inProgress {
		err := fmt.Errorf("removal of container %s is already in progress", name)
		return errdefs.Conflict(err)
	}
	defer ctr.ResetRemovalInProgress()

	// check if container wasn't deregistered by previous rm since Get
	if c := daemon.containers.Get(ctr.ID); c == nil {
		return nil
	}

	if config.RemoveLink {
		return daemon.rmLink(ctr, name)
	}

	err = daemon.cleanupContainer(ctr, *config)
	containerActions.WithValues("delete").UpdateSince(start)

	return err
}

func (daemon *Daemon) rmLink(container *container.Container, name string) error {
	if name[0] != '/' {
		name = "/" + name
	}
	parent, n := path.Split(name)
	if parent == "/" {
		return fmt.Errorf("Conflict, cannot remove the default link name of the container")
	}

	parent = strings.TrimSuffix(parent, "/")
	pe, err := daemon.containersReplica.Snapshot().GetID(parent)
	if err != nil {
		return fmt.Errorf("Cannot get parent %s for link name %s", parent, name)
	}

	daemon.releaseName(name)
	parentContainer, _ := daemon.GetContainer(pe)
	if parentContainer != nil {
		daemon.linkIndex.unlink(name, container, parentContainer)
		if err := daemon.updateNetwork(parentContainer); err != nil {
			logrus.Debugf("Could not update network to remove link %s: %v", n, err)
		}
	}
	return nil
}

// cleanupContainer unregisters a container from the daemon, stops stats
// collection and cleanly removes contents and metadata from the filesystem.
func (daemon *Daemon) cleanupContainer(container *container.Container, config types.ContainerRmConfig) error {
	if container.IsRunning() {
		if !config.ForceRemove {
			state := container.StateString()
			procedure := "Stop the container before attempting removal or force remove"
			if state == "paused" {
				procedure = "Unpause and then " + strings.ToLower(procedure)
			}
			err := fmt.Errorf("You cannot remove a %s container %s. %s", state, container.ID, procedure)
			return errdefs.Conflict(err)
		}
		if err := daemon.Kill(container); err != nil {
			return fmt.Errorf("Could not kill running container %s, cannot remove - %v", container.ID, err)
		}
	}

	// stop collection of stats for the container regardless
	// if stats are currently getting collected.
	daemon.statsCollector.StopCollection(container)

	// stopTimeout is the number of seconds to wait for the container to stop
	// gracefully before forcibly killing it.
	//
	// Why 3 seconds? The timeout specified here was originally added in commit
	// 1615bb08c7c3fc6c4b22db0a633edda516f97cf0, which added a custom timeout to
	// some commands, but lacking an option for a timeout on "docker rm", was
	// hardcoded to 10 seconds. Commit 28fd289b448164b77affd8103c0d96fd8110daf9
	// later on updated this to 3 seconds (but no background on that change).
	//
	// If you arrived here and know the answer, you earned yourself a picture
	// of a cute animal of your own choosing.
	var stopTimeout = 3
	if err := daemon.containerStop(context.TODO(), container, containertypes.StopOptions{Timeout: &stopTimeout}); err != nil {
		return err
	}

	// Mark container dead. We don't want anybody to be restarting it.
	container.Lock()
	container.Dead = true

	// Save container state to disk. So that if error happens before
	// container meta file got removed from disk, then a restart of
	// docker should not make a dead container alive.
	if err := container.CheckpointTo(daemon.containersReplica); err != nil && !os.IsNotExist(err) {
		logrus.Errorf("Error saving dying container to disk: %v", err)
	}
	container.Unlock()

	// When container creation fails and `RWLayer` has not been created yet, we
	// do not call `ReleaseRWLayer`
	if container.RWLayer != nil {
		if err := daemon.imageService.ReleaseLayer(container.RWLayer); err != nil {
			err = errors.Wrapf(err, "container %s", container.ID)
			container.SetRemovalError(err)
			return err
		}
		container.RWLayer = nil
	}

	// Hold the container lock while deleting the container root directory
	// so that other goroutines don't attempt to concurrently open files
	// within it. Having any file open on Windows (without the
	// FILE_SHARE_DELETE flag) will block it from being deleted.
	container.Lock()
	err := containerfs.EnsureRemoveAll(container.Root)
	container.Unlock()
	if err != nil {
		err = errors.Wrapf(err, "unable to remove filesystem for %s", container.ID)
		container.SetRemovalError(err)
		return err
	}

	linkNames := daemon.linkIndex.delete(container)
	selinux.ReleaseLabel(container.ProcessLabel)
	daemon.containers.Delete(container.ID)
	daemon.containersReplica.Delete(container)
	if err := daemon.removeMountPoints(container, config.RemoveVolume); err != nil {
		logrus.Error(err)
	}
	for _, name := range linkNames {
		daemon.releaseName(name)
	}
	container.SetRemoved()
	stateCtr.del(container.ID)

	daemon.LogContainerEvent(container, "destroy")
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1

const (
	LabelHostname           = "kubernetes.io/hostname"
	LabelZoneFailureDomain  = "failure-domain.beta.kubernetes.io/zone"
	LabelMultiZoneDelimiter = "__"
	LabelZoneRegion         = "failure-domain.beta.kubernetes.io/region"

	LabelInstanceType = "beta.kubernetes.io/instance-type"

	LabelOS   = "kubernetes.io/os"
	LabelArch = "kubernetes.io/arch"
	// The OS/Arch labels are promoted to GA in 1.14. kubelet applies both beta
	// and GA labels to ensure backward compatibility.
	// TODO: stop applying the beta OS/Arch labels in Kubernetes 1.17.
	LegacyLabelOS   = "beta.kubernetes.io/os"
	LegacyLabelArch = "beta.kubernetes.io/arch"

	// LabelNamespaceSuffixKubelet is an allowed label namespace suffix kubelets can self-set ([*.]kubelet.kubernetes.io/*)
	LabelNamespaceSuffixKubelet = "kubelet.kubernetes.io"
	// LabelNamespaceSuffixNode is an allowed label namespace suffix kubelets can self-set ([*.]node.kubernetes.io/*)
	LabelNamespaceSuffixNode = "node.kubernetes.io"

	// LabelNamespaceNodeRestriction is a forbidden label namespace that kubelets may not self-set when the NodeRestriction admission plugin is enabled
	LabelNamespaceNodeRestriction = "node-restriction.kubernetes.io"
)
<commit_msg>Move LabelMultiZoneDelimiter to pkg/volume/util<commit_after>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1

const (
	LabelHostname          = "kubernetes.io/hostname"
	LabelZoneFailureDomain = "failure-domain.beta.kubernetes.io/zone"
	LabelZoneRegion        = "failure-domain.beta.kubernetes.io/region"

	LabelInstanceType = "beta.kubernetes.io/instance-type"

	LabelOS   = "kubernetes.io/os"
	LabelArch = "kubernetes.io/arch"
	// The OS/Arch labels are promoted to GA in 1.14. kubelet applies both beta
	// and GA labels to ensure backward compatibility.
	// TODO: stop applying the beta OS/Arch labels in Kubernetes 1.17.
	LegacyLabelOS   = "beta.kubernetes.io/os"
	LegacyLabelArch = "beta.kubernetes.io/arch"

	// LabelNamespaceSuffixKubelet is an allowed label namespace suffix kubelets can self-set ([*.]kubelet.kubernetes.io/*)
	LabelNamespaceSuffixKubelet = "kubelet.kubernetes.io"
	// LabelNamespaceSuffixNode is an allowed label namespace suffix kubelets can self-set ([*.]node.kubernetes.io/*)
	LabelNamespaceSuffixNode = "node.kubernetes.io"

	// LabelNamespaceNodeRestriction is a forbidden label namespace that kubelets may not self-set when the NodeRestriction admission plugin is enabled
	LabelNamespaceNodeRestriction = "node-restriction.kubernetes.io"
)
<|endoftext|>


<commit_before>package daemon

import (
	"fmt"
	"github.com/Clever/sphinx/config"
	"github.com/Clever/sphinx/handlers"
	"github.com/Clever/sphinx/ratelimiter"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
)

type Daemon interface {
	Start()
	LoadConfig(config config.Config) error
}

type daemon struct {
	handler     http.Handler
	proxy       config.Proxy
	healthCheck config.HealthCheck
}

// setUpHealthCheckService sets up a health check service at the given port
// that can be pinged at the given endpoint to determine if Sphinx is still
// running.
func setUpHealthCheckService(port string, endpoint string) {
	mux := http.NewServeMux()
	mux.HandleFunc(endpoint, func(rw http.ResponseWriter, req *http.Request) {
		rw.WriteHeader(http.StatusOK)
		log.Printf("Health-check endpoint pinged.")
	})
	go http.ListenAndServe(":"+port, mux)
	log.Printf("Health-check listening on:%s%s", port, endpoint)
}

func (d *daemon) Start() {
	log.Printf("Limiter listening on %s", d.proxy.Listen)
	// Only set up the health check service if it is enabled.
	if d.healthCheck.Enabled {
		setUpHealthCheckService(d.healthCheck.Port, d.healthCheck.Endpoint)
	}
	log.Fatal(http.ListenAndServe(d.proxy.Listen, d))
	return
}

func (d *daemon) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	d.handler.ServeHTTP(rw, req)
}

func (d *daemon) LoadConfig(newConfig config.Config) error {
	// We don't support changing the Proxy listen port because it would mean closing and
	// restarting the server. If users want to do this they should restart the process.
	if d.proxy.Listen != "" && d.proxy.Listen != newConfig.Proxy.Listen {
		return fmt.Errorf("SPHINX_LOAD_CONFIG_FAILED. Can't change listen port")
	}

	d.proxy = newConfig.Proxy
	d.healthCheck = newConfig.HealthCheck
	target, _ := url.Parse(d.proxy.Host) // already tested for invalid Host
	proxy := httputil.NewSingleHostReverseProxy(target)
	rateLimiter, err := ratelimiter.New(newConfig)
	if err != nil {
		return fmt.Errorf("SPHINX_LOAD_CONFIG_FAILED: %s", err.Error())
	}

	// Set the proxy and handler daemon fields
	switch d.proxy.Handler {
	case "http":
		d.handler = handlers.NewHTTPLimiter(rateLimiter, proxy, d.proxy.AllowOnError)
		return nil
	case "httplogger":
		d.handler = handlers.NewHTTPLogger(rateLimiter, proxy)
		return nil
	default:
		return fmt.Errorf("unrecognized handler %s", d.proxy.Handler)
	}

}

// NewDaemon takes in config.Configuration and creates a sphinx listener
func New(config config.Config) (Daemon, error) {

	out := &daemon{}
	err := out.LoadConfig(config)
	if err != nil {
		return nil, err
	}

	return out, nil
}
<commit_msg>Update health check logging to use kayvee<commit_after>package daemon

import (
	"fmt"
	"github.com/Clever/sphinx/config"
	"github.com/Clever/sphinx/handlers"
	"github.com/Clever/sphinx/ratelimiter"
	"gopkg.in/Clever/kayvee-go.v2"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
)

type Daemon interface {
	Start()
	LoadConfig(config config.Config) error
}

type daemon struct {
	handler     http.Handler
	proxy       config.Proxy
	healthCheck config.HealthCheck
}

// setUpHealthCheckService sets up a health check service at the given port
// that can be pinged at the given endpoint to determine if Sphinx is still
// running.
func setUpHealthCheckService(port string, endpoint string) {
	mux := http.NewServeMux()
	mux.HandleFunc(endpoint, func(rw http.ResponseWriter, req *http.Request) {
		rw.WriteHeader(http.StatusOK)
		log.Printf(kayvee.Format(map[string]interface{}{
			"source": "sphinx",
			"title":  "Health-Check",
			"type":   "counter",
			"value":  1}))
	})
	go http.ListenAndServe(":"+port, mux)
	log.Printf("Health-check listening on:%s%s", port, endpoint)
}

func (d *daemon) Start() {
	log.Printf("Limiter listening on %s", d.proxy.Listen)
	// Only set up the health check service if it is enabled.
	if d.healthCheck.Enabled {
		setUpHealthCheckService(d.healthCheck.Port, d.healthCheck.Endpoint)
	}
	log.Fatal(http.ListenAndServe(d.proxy.Listen, d))
	return
}

func (d *daemon) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	d.handler.ServeHTTP(rw, req)
}

func (d *daemon) LoadConfig(newConfig config.Config) error {
	// We don't support changing the Proxy listen port because it would mean closing and
	// restarting the server. If users want to do this they should restart the process.
	if d.proxy.Listen != "" && d.proxy.Listen != newConfig.Proxy.Listen {
		return fmt.Errorf("SPHINX_LOAD_CONFIG_FAILED. Can't change listen port")
	}

	d.proxy = newConfig.Proxy
	d.healthCheck = newConfig.HealthCheck
	target, _ := url.Parse(d.proxy.Host) // already tested for invalid Host
	proxy := httputil.NewSingleHostReverseProxy(target)
	rateLimiter, err := ratelimiter.New(newConfig)
	if err != nil {
		return fmt.Errorf("SPHINX_LOAD_CONFIG_FAILED: %s", err.Error())
	}

	// Set the proxy and handler daemon fields
	switch d.proxy.Handler {
	case "http":
		d.handler = handlers.NewHTTPLimiter(rateLimiter, proxy, d.proxy.AllowOnError)
		return nil
	case "httplogger":
		d.handler = handlers.NewHTTPLogger(rateLimiter, proxy)
		return nil
	default:
		return fmt.Errorf("unrecognized handler %s", d.proxy.Handler)
	}

}

// NewDaemon takes in config.Configuration and creates a sphinx listener
func New(config config.Config) (Daemon, error) {

	out := &daemon{}
	err := out.LoadConfig(config)
	if err != nil {
		return nil, err
	}

	return out, nil
}
<|endoftext|>


<commit_before>package daemon

import (
	derr "github.com/docker/docker/errors"
)

// ContainerRename changes the name of a container, using the oldName
// to find the container. An error is returned if newName is already
// reserved.
func (daemon *Daemon) ContainerRename(oldName, newName string) error {
	if oldName == "" || newName == "" {
		return derr.ErrorCodeEmptyRename
	}

	container, err := daemon.Get(oldName)
	if err != nil {
		return err
	}

	oldName = container.Name

	container.Lock()
	defer container.Unlock()
	if newName, err = daemon.reserveName(container.ID, newName); err != nil {
		return derr.ErrorCodeRenameTaken.WithArgs(err)
	}

	container.Name = newName

	undo := func() {
		container.Name = oldName
		daemon.reserveName(container.ID, oldName)
		daemon.containerGraphDB.Delete(newName)
	}

	if err := daemon.containerGraphDB.Delete(oldName); err != nil {
		undo()
		return derr.ErrorCodeRenameDelete.WithArgs(oldName, err)
	}

	if err := container.toDisk(); err != nil {
		undo()
		return err
	}

	container.logEvent("rename")
	return nil
}
<commit_msg>Add libnetwork call on daemon rename<commit_after>package daemon

import (
	"github.com/Sirupsen/logrus"
	derr "github.com/docker/docker/errors"
	"github.com/docker/libnetwork"
	"strings"
)

// ContainerRename changes the name of a container, using the oldName
// to find the container. An error is returned if newName is already
// reserved.
func (daemon *Daemon) ContainerRename(oldName, newName string) error {
	var (
		err       error
		sid       string
		sb        libnetwork.Sandbox
		container *Container
	)

	if oldName == "" || newName == "" {
		return derr.ErrorCodeEmptyRename
	}

	container, err = daemon.Get(oldName)
	if err != nil {
		return err
	}

	oldName = container.Name

	container.Lock()
	defer container.Unlock()
	if newName, err = daemon.reserveName(container.ID, newName); err != nil {
		return derr.ErrorCodeRenameTaken.WithArgs(err)
	}

	container.Name = newName

	defer func() {
		if err != nil {
			container.Name = oldName
			daemon.reserveName(container.ID, oldName)
			daemon.containerGraphDB.Delete(newName)
		}
	}()

	if err = daemon.containerGraphDB.Delete(oldName); err != nil {
		return derr.ErrorCodeRenameDelete.WithArgs(oldName, err)
	}

	if err = container.toDisk(); err != nil {
		return err
	}

	if !container.Running {
		container.logEvent("rename")
		return nil
	}

	defer func() {
		if err != nil {
			container.Name = oldName
			if e := container.toDisk(); e != nil {
				logrus.Errorf("%s: Failed in writing to Disk on rename failure: %v", container.ID, e)
			}
		}
	}()

	sid = container.NetworkSettings.SandboxID
	sb, err = daemon.netController.SandboxByID(sid)
	if err != nil {
		return err
	}

	err = sb.Rename(strings.TrimPrefix(container.Name, "/"))
	if err != nil {
		return err
	}

	container.logEvent("rename")
	return nil
}
<|endoftext|>


<commit_before>package member

import (
	"doozer/paxos"
	"doozer/store"
	"doozer/util"
	"strings"
)

func Clean(st *store.Store, p paxos.Proposer) {
	logger := util.NewLogger("member")
	for ev := range st.Watch("/session/*") {
		if !ev.IsDel() {
			continue
		}

		parts := strings.Split(ev.Path, "/", 3)
		name := parts[2]
		logger.Printf("lost session %s", name)

		go clearSlot(p, ev, name)
		go removeMember(p, ev, name)
		go removeInfo(p, ev, name)
	}
}

func clearSlot(p paxos.Proposer, g store.Getter, name string) {
	ch, err := store.Walk(g, "/doozer/slot/*")
	if err != nil {
		panic(err)
	}

	for ev := range ch {
		if ev.Body == name {
			paxos.Set(p, ev.Path, "", ev.Cas)
		}
	}
}

func removeMember(p paxos.Proposer, g store.Getter, name string) {
	k := "/doozer/members/" + name
	_, cas := g.Get(k)
	if cas != store.Missing {
		paxos.Del(p, k, cas)
	}
}

func removeInfo(p paxos.Proposer, g store.Getter, name string) {
	ch, err := store.Walk(g, "/doozer/info/"+name+"/**")
	if err != nil {
		panic(err)
	}

	for ev := range ch {
		paxos.Del(p, ev.Path, ev.Cas)
	}
}
<commit_msg>member: run clean-up async but guarantee order<commit_after>package member

import (
	"doozer/paxos"
	"doozer/store"
	"doozer/util"
	"strings"
)

func Clean(st *store.Store, p paxos.Proposer) {
	logger := util.NewLogger("member")
	for ev := range st.Watch("/session/*") {
		if !ev.IsDel() {
			continue
		}

		parts := strings.Split(ev.Path, "/", 3)
		name := parts[2]
		logger.Printf("lost session %s", name)

		go func() {
			clearSlot(p, ev, name)
			removeMember(p, ev, name)
			removeInfo(p, ev, name)
		}()
	}
}

func clearSlot(p paxos.Proposer, g store.Getter, name string) {
	ch, err := store.Walk(g, "/doozer/slot/*")
	if err != nil {
		panic(err)
	}

	for ev := range ch {
		if ev.Body == name {
			paxos.Set(p, ev.Path, "", ev.Cas)
		}
	}
}

func removeMember(p paxos.Proposer, g store.Getter, name string) {
	k := "/doozer/members/" + name
	_, cas := g.Get(k)
	if cas != store.Missing {
		paxos.Del(p, k, cas)
	}
}

func removeInfo(p paxos.Proposer, g store.Getter, name string) {
	ch, err := store.Walk(g, "/doozer/info/"+name+"/**")
	if err != nil {
		panic(err)
	}

	for ev := range ch {
		paxos.Del(p, ev.Path, ev.Cas)
	}
}
<|endoftext|>


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

package os

import (
	"errors"
	"runtime"
	"syscall"
	"time"
)

// The only signal values guaranteed to be present on all systems
// are Interrupt (send the process an interrupt) and Kill (force
// the process to exit).
var (
	Interrupt Signal = syscall.Note("interrupt")
	Kill      Signal = syscall.Note("kill")
)

func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
	sysattr := &syscall.ProcAttr{
		Dir: attr.Dir,
		Env: attr.Env,
		Sys: attr.Sys,
	}

	for _, f := range attr.Files {
		sysattr.Files = append(sysattr.Files, f.Fd())
	}

	pid, h, e := syscall.StartProcess(name, argv, sysattr)
	if e != nil {
		return nil, &PathError{"fork/exec", name, e}
	}

	return newProcess(pid, h), nil
}

func (p *Process) writeProcFile(file string, data string) error {
	f, e := OpenFile("/proc/"+itoa(p.Pid)+"/"+file, O_WRONLY, 0)
	if e != nil {
		return e
	}
	defer f.Close()
	_, e = f.Write([]byte(data))
	return e
}

func (p *Process) signal(sig Signal) error {
	if p.done() {
		return errors.New("os: process already finished")
	}
	if sig == Kill {
		// Special-case the kill signal since it doesn't use /proc/$pid/note.
		return p.Kill()
	}
	if e := p.writeProcFile("note", sig.String()); e != nil {
		return NewSyscallError("signal", e)
	}
	return nil
}

func (p *Process) kill() error {
	if e := p.writeProcFile("ctl", "kill"); e != nil {
		return NewSyscallError("kill", e)
	}
	return nil
}

func (p *Process) wait() (ps *ProcessState, err error) {
	var waitmsg syscall.Waitmsg

	if p.Pid == -1 {
		return nil, ErrInvalid
	}
	err = syscall.WaitProcess(p.Pid, &waitmsg)
	if err != nil {
		return nil, NewSyscallError("wait", err)
	}

	p.setDone()
	ps = &ProcessState{
		pid:    waitmsg.Pid,
		status: &waitmsg,
	}
	return ps, nil
}

func (p *Process) release() error {
	// NOOP for Plan 9.
	p.Pid = -1
	// no need for a finalizer anymore
	runtime.SetFinalizer(p, nil)
	return nil
}

func findProcess(pid int) (p *Process, err error) {
	// NOOP for Plan 9.
	return newProcess(pid, 0), nil
}

// ProcessState stores information about a process, as reported by Wait.
type ProcessState struct {
	pid    int              // The process's id.
	status *syscall.Waitmsg // System-dependent status info.
}

// Pid returns the process id of the exited process.
func (p *ProcessState) Pid() int {
	return p.pid
}

func (p *ProcessState) exited() bool {
	return p.status.Exited()
}

func (p *ProcessState) success() bool {
	return p.status.ExitStatus() == 0
}

func (p *ProcessState) sys() interface{} {
	return p.status
}

func (p *ProcessState) sysUsage() interface{} {
	return p.status
}

func (p *ProcessState) userTime() time.Duration {
	return time.Duration(p.status.Time[0]) * time.Millisecond
}

func (p *ProcessState) systemTime() time.Duration {
	return time.Duration(p.status.Time[1]) * time.Millisecond
}

func (p *ProcessState) String() string {
	if p == nil {
		return "<nil>"
	}
	return "exit status: " + p.status.Msg
}
<commit_msg>os: relax the way we kill processes on Plan 9<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package os

import (
	"errors"
	"runtime"
	"syscall"
	"time"
)

// The only signal values guaranteed to be present on all systems
// are Interrupt (send the process an interrupt) and Kill (force
// the process to exit).
var (
	Interrupt Signal = syscall.Note("interrupt")
	Kill      Signal = syscall.Note("kill")
)

func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
	sysattr := &syscall.ProcAttr{
		Dir: attr.Dir,
		Env: attr.Env,
		Sys: attr.Sys,
	}

	for _, f := range attr.Files {
		sysattr.Files = append(sysattr.Files, f.Fd())
	}

	pid, h, e := syscall.StartProcess(name, argv, sysattr)
	if e != nil {
		return nil, &PathError{"fork/exec", name, e}
	}

	return newProcess(pid, h), nil
}

func (p *Process) writeProcFile(file string, data string) error {
	f, e := OpenFile("/proc/"+itoa(p.Pid)+"/"+file, O_WRONLY, 0)
	if e != nil {
		return e
	}
	defer f.Close()
	_, e = f.Write([]byte(data))
	return e
}

func (p *Process) signal(sig Signal) error {
	if p.done() {
		return errors.New("os: process already finished")
	}
	if e := p.writeProcFile("note", sig.String()); e != nil {
		return NewSyscallError("signal", e)
	}
	return nil
}

func (p *Process) kill() error {
	return p.signal(Kill)
}

func (p *Process) wait() (ps *ProcessState, err error) {
	var waitmsg syscall.Waitmsg

	if p.Pid == -1 {
		return nil, ErrInvalid
	}
	err = syscall.WaitProcess(p.Pid, &waitmsg)
	if err != nil {
		return nil, NewSyscallError("wait", err)
	}

	p.setDone()
	ps = &ProcessState{
		pid:    waitmsg.Pid,
		status: &waitmsg,
	}
	return ps, nil
}

func (p *Process) release() error {
	// NOOP for Plan 9.
	p.Pid = -1
	// no need for a finalizer anymore
	runtime.SetFinalizer(p, nil)
	return nil
}

func findProcess(pid int) (p *Process, err error) {
	// NOOP for Plan 9.
	return newProcess(pid, 0), nil
}

// ProcessState stores information about a process, as reported by Wait.
type ProcessState struct {
	pid    int              // The process's id.
	status *syscall.Waitmsg // System-dependent status info.
}

// Pid returns the process id of the exited process.
func (p *ProcessState) Pid() int {
	return p.pid
}

func (p *ProcessState) exited() bool {
	return p.status.Exited()
}

func (p *ProcessState) success() bool {
	return p.status.ExitStatus() == 0
}

func (p *ProcessState) sys() interface{} {
	return p.status
}

func (p *ProcessState) sysUsage() interface{} {
	return p.status
}

func (p *ProcessState) userTime() time.Duration {
	return time.Duration(p.status.Time[0]) * time.Millisecond
}

func (p *ProcessState) systemTime() time.Duration {
	return time.Duration(p.status.Time[1]) * time.Millisecond
}

func (p *ProcessState) String() string {
	if p == nil {
		return "<nil>"
	}
	return "exit status: " + p.status.Msg
}
<|endoftext|>


<commit_before>package binny

import (
	"encoding"
	"encoding/gob"
	"fmt"
	"reflect"
	"sync"
)

type DecoderTypeError struct {
	Expected string
	Actual   Type
}

func (dte DecoderTypeError) Error() string {
	return "expected " + dte.Expected + ", got " + dte.Actual.String()
}

type decoderFunc func(dec *Decoder, v reflect.Value) error

var (
	decCache = struct {
		sync.RWMutex
		m map[reflect.Type]decoderFunc
	}{m: map[reflect.Type]decoderFunc{}}
)

func typeDecoder(t reflect.Type) (fn decoderFunc) {
	decCache.RLock()
	fn = decCache.m[t]
	decCache.RUnlock()
	if fn != nil {
		return
	}

	decCache.Lock()
	decCache.m[t] = nil
	decCache.Unlock()

	fn = newTypeDecoder(t)
	decCache.Lock()
	decCache.m[t] = fn
	if k := t.Kind(); k == reflect.Struct || k == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
		updateCachedFields(false)
	}
	decCache.Unlock()
	return
}

func nopeDec(*Decoder, reflect.Value) error { panic("Fly, you fools!") }

func newTypeDecoder(t reflect.Type) decoderFunc {
	k := t.Kind()
	if t.Implements(unmarshalerType) {
		if k == reflect.Ptr {
			return newPtrDecoder(unmarshalerDecoder, false)
		}
		return unmarshalerDecoder
	}

	if t.Implements(binaryUnmarshalerType) {
		if k == reflect.Ptr {
			return newPtrDecoder(binaryUnmarshalerDecoder, false)
		}
		return binaryUnmarshalerDecoder
	}
	if t.Implements(gobDecoderType) {
		if k == reflect.Ptr {
			return newPtrDecoder(gobDecoder, false)
		}
		return gobDecoder
	}

	if k != reflect.Ptr {
		t := reflect.PtrTo(t)
		if t.Implements(unmarshalerType) {
			return addrDecoder(unmarshalerDecoder)
		}
		if t.Implements(binaryUnmarshalerType) {
			return addrDecoder(binaryUnmarshalerDecoder)
		}
		if t.Implements(gobDecoderType) {
			return addrDecoder(gobDecoder)
		}
	}
	switch k {
	case reflect.Bool:
		return boolDecoder
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return intDecoder
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return uintDecoder
	case reflect.Float32:
		return float32Decoder
	case reflect.Float64:
		return float64Decoder
	case reflect.Complex64:
		return complex64Decoder
	case reflect.Complex128:
		return complex128Decoder
	case reflect.String:
		return stringDecoder
	case reflect.Map:
		if !isNative(t.Key().Kind(), true) { // eventually will support pointer keys and structs, but not gonna happen until it's needed
			return invalidDecoder
		}
		return newMapDecoder(t)
	case reflect.Slice, reflect.Array:
		return newSliceDecoder(t.Elem())
	case reflect.Struct:
		return newStructDecoder(t)
	case reflect.Ptr:
		return newPtrDecoder(typeDecoder(t.Elem()), true)
		// case reflect.Interface:
		// 	return ifaceEncoder
	}
	return invalidDecoder
}

func unmarshalerDecoder(d *Decoder, v reflect.Value) error {
	return v.Interface().(Unmarshaler).UnmarshalBinny(d)
}

func binaryUnmarshalerDecoder(d *Decoder, v reflect.Value) error {
	return d.ReadBinary(v.Interface().(encoding.BinaryUnmarshaler))
}

func gobDecoder(d *Decoder, v reflect.Value) error {
	return d.ReadGob(v.Interface().(gob.GobDecoder))
}

func boolDecoder(d *Decoder, v reflect.Value) error {
	b, err := d.ReadBool()
	v.SetBool(b)
	return err
}

func intDecoder(d *Decoder, v reflect.Value) error {
	i, _, err := d.ReadInt()
	v.SetInt(i)
	return err
}

func uintDecoder(d *Decoder, v reflect.Value) error {
	i, _, err := d.ReadUint()
	v.SetUint(i)
	return err
}

func float32Decoder(d *Decoder, v reflect.Value) error {
	f, err := d.ReadFloat32()
	v.SetFloat(float64(f))
	return err
}

func float64Decoder(d *Decoder, v reflect.Value) error {
	f, err := d.ReadFloat64()
	v.SetFloat(f)
	return err
}

func complex64Decoder(d *Decoder, v reflect.Value) error {
	c, err := d.ReadComplex64()
	v.SetComplex(complex128(c))
	return err
}

func complex128Decoder(d *Decoder, v reflect.Value) error {
	c, err := d.ReadComplex128()
	v.SetComplex(c)
	return err
}

func stringDecoder(d *Decoder, v reflect.Value) error {
	s, err := d.ReadString()
	v.SetString(s)
	return err
}

func bytesDecoder(d *Decoder, v reflect.Value) error {
	b, err := d.ReadBytes()
	v.SetBytes(b)
	return err
}

func invalidDecoder(d *Decoder, v reflect.Value) error {
	return fmt.Errorf("%v is not supported", v.Type().String())
}

type sliceDecoder struct {
	t reflect.Type
}

func (sd sliceDecoder) decode(d *Decoder, v reflect.Value) error {
	if err := d.expectType(Slice); err != nil {
		return err
	}

	ln, _, err := d.ReadUint()

	if err != nil {
		return err
	}

	if v.Kind() == reflect.Slice {
		if ln := int(ln); v.Cap() < ln {
			v.Set(reflect.MakeSlice(v.Type(), ln, ln))
		} else {
			v.SetLen(ln)
			v.SetCap(ln)
		}
	}
	dec := typeDecoder(sd.t)
	for i := 0; i < int(ln); i++ {
		// this is a bug
		if d.peekType() == Nil {
			d.readType()
			continue
		}
		if err = dec(d, v.Index(i)); err != nil {
			return err
		}
	}

	return d.expectType(EOV)
}

func newSliceDecoder(t reflect.Type) decoderFunc {
	if t.Kind() == reflect.Uint8 {
		return bytesDecoder
	}
	typeDecoder(t)
	d := sliceDecoder{t: t}
	return d.decode
}

type structDecoder struct {
	t reflect.Type
}

func (sd structDecoder) decode(d *Decoder, v reflect.Value) error {
	var (
		flds   = cachedTypeFields(sd.t)
		fields = make(map[string]*field, len(flds))
	)
	for i := range flds {
		f := &flds[i] // need a pointer so when we later update fields .dec it'd get picked up.
		fields[f.name] = f
	}
	if err := d.expectType(Struct); err != nil {
		if err, ok := err.(DecoderTypeError); ok && err.Actual == Nil || err.Actual == EmptyStruct {
			return nil
		}
		return err
	}
	for {
		n, err := d.ReadString()
		if err != nil {
			if err, ok := err.(DecoderTypeError); ok && err.Actual == EOV {
				return nil
			}
			return err
		}
		if f, ok := fields[n]; ok {
			fld := fieldByIndex(v, f.index, true)
			if err := f.dec(d, fld); err != nil {
				return err
			}
		}
	}
}

func newStructDecoder(t reflect.Type) decoderFunc {
	sd := structDecoder{t}
	return sd.decode
}

type mapDecoder struct {
	kt, vt reflect.Type
}

func (md mapDecoder) decode(d *Decoder, v reflect.Value) error {
	if err := d.expectType(Map); err != nil {
		return err
	}
	ln, _, err := d.ReadUint()

	if err != nil {
		return err
	}

	t := v.Type()
	if v.IsNil() {
		v.Set(reflect.MakeMap(t))
	}
	kdec, vdec := typeDecoder(md.kt), typeDecoder(md.vt)
	for i, kt, vt := 0, t.Key(), t.Elem(); i < int(ln); i++ {
		key := reflect.New(kt).Elem()
		if err = kdec(d, key); err != nil {
			return err
		}
		if d.peekType() == Nil {
			v.SetMapIndex(key, reflect.Zero(vt))
			d.readType()
			continue
		}
		val := reflect.New(vt).Elem()
		if err = vdec(d, val); err != nil {
			return err
		}
		v.SetMapIndex(key, val)
	}
	return d.expectType(EOV)
}

func newMapDecoder(t reflect.Type) decoderFunc {
	md := mapDecoder{t.Key(), t.Elem()}
	return md.decode
}

type ptrDecoder struct {
	dec decoderFunc
}

func (pd ptrDecoder) decodeElem(d *Decoder, v reflect.Value) error {
	if v.IsNil() {
		v.Set(reflect.New(v.Type().Elem()))
	}
	return pd.dec(d, v.Elem())
}

func (pd ptrDecoder) decode(d *Decoder, v reflect.Value) error {
	if v.IsNil() {
		v.Set(reflect.New(v.Type().Elem()))
	}
	return pd.dec(d, v)
}

func newPtrDecoder(fn decoderFunc, elem bool) decoderFunc {
	pd := ptrDecoder{fn}
	if elem {
		return pd.decodeElem
	}
	return pd.decode
}

func addrDecoder(fn decoderFunc) decoderFunc {
	return func(d *Decoder, v reflect.Value) error {
		if v = v.Addr(); v.IsNil() {
			v.Set(reflect.New(v.Type().Elem().Elem()))
		}
		return d.Decode(v.Interface())
	}
}
<commit_msg>preallocate maps with size<commit_after>package binny

import (
	"encoding"
	"encoding/gob"
	"fmt"
	"reflect"
	"sync"
)

type DecoderTypeError struct {
	Expected string
	Actual   Type
}

func (dte DecoderTypeError) Error() string {
	return "expected " + dte.Expected + ", got " + dte.Actual.String()
}

type decoderFunc func(dec *Decoder, v reflect.Value) error

var (
	decCache = struct {
		sync.RWMutex
		m map[reflect.Type]decoderFunc
	}{m: map[reflect.Type]decoderFunc{}}
)

func typeDecoder(t reflect.Type) (fn decoderFunc) {
	decCache.RLock()
	fn = decCache.m[t]
	decCache.RUnlock()
	if fn != nil {
		return
	}

	decCache.Lock()
	decCache.m[t] = nil
	decCache.Unlock()

	fn = newTypeDecoder(t)
	decCache.Lock()
	decCache.m[t] = fn
	if k := t.Kind(); k == reflect.Struct || k == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
		updateCachedFields(false)
	}
	decCache.Unlock()
	return
}

func nopeDec(*Decoder, reflect.Value) error { panic("Fly, you fools!") }

func newTypeDecoder(t reflect.Type) decoderFunc {
	k := t.Kind()
	if t.Implements(unmarshalerType) {
		if k == reflect.Ptr {
			return newPtrDecoder(unmarshalerDecoder, false)
		}
		return unmarshalerDecoder
	}

	if t.Implements(binaryUnmarshalerType) {
		if k == reflect.Ptr {
			return newPtrDecoder(binaryUnmarshalerDecoder, false)
		}
		return binaryUnmarshalerDecoder
	}
	if t.Implements(gobDecoderType) {
		if k == reflect.Ptr {
			return newPtrDecoder(gobDecoder, false)
		}
		return gobDecoder
	}

	if k != reflect.Ptr {
		t := reflect.PtrTo(t)
		if t.Implements(unmarshalerType) {
			return addrDecoder(unmarshalerDecoder)
		}
		if t.Implements(binaryUnmarshalerType) {
			return addrDecoder(binaryUnmarshalerDecoder)
		}
		if t.Implements(gobDecoderType) {
			return addrDecoder(gobDecoder)
		}
	}
	switch k {
	case reflect.Bool:
		return boolDecoder
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return intDecoder
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return uintDecoder
	case reflect.Float32:
		return float32Decoder
	case reflect.Float64:
		return float64Decoder
	case reflect.Complex64:
		return complex64Decoder
	case reflect.Complex128:
		return complex128Decoder
	case reflect.String:
		return stringDecoder
	case reflect.Map:
		if !isNative(t.Key().Kind(), true) { // eventually will support pointer keys and structs, but not gonna happen until it's needed
			return invalidDecoder
		}
		return newMapDecoder(t)
	case reflect.Slice, reflect.Array:
		return newSliceDecoder(t.Elem())
	case reflect.Struct:
		return newStructDecoder(t)
	case reflect.Ptr:
		return newPtrDecoder(typeDecoder(t.Elem()), true)
		// case reflect.Interface:
		// 	return ifaceEncoder
	}
	return invalidDecoder
}

func unmarshalerDecoder(d *Decoder, v reflect.Value) error {
	return v.Interface().(Unmarshaler).UnmarshalBinny(d)
}

func binaryUnmarshalerDecoder(d *Decoder, v reflect.Value) error {
	return d.ReadBinary(v.Interface().(encoding.BinaryUnmarshaler))
}

func gobDecoder(d *Decoder, v reflect.Value) error {
	return d.ReadGob(v.Interface().(gob.GobDecoder))
}

func boolDecoder(d *Decoder, v reflect.Value) error {
	b, err := d.ReadBool()
	v.SetBool(b)
	return err
}

func intDecoder(d *Decoder, v reflect.Value) error {
	i, _, err := d.ReadInt()
	v.SetInt(i)
	return err
}

func uintDecoder(d *Decoder, v reflect.Value) error {
	i, _, err := d.ReadUint()
	v.SetUint(i)
	return err
}

func float32Decoder(d *Decoder, v reflect.Value) error {
	f, err := d.ReadFloat32()
	v.SetFloat(float64(f))
	return err
}

func float64Decoder(d *Decoder, v reflect.Value) error {
	f, err := d.ReadFloat64()
	v.SetFloat(f)
	return err
}

func complex64Decoder(d *Decoder, v reflect.Value) error {
	c, err := d.ReadComplex64()
	v.SetComplex(complex128(c))
	return err
}

func complex128Decoder(d *Decoder, v reflect.Value) error {
	c, err := d.ReadComplex128()
	v.SetComplex(c)
	return err
}

func stringDecoder(d *Decoder, v reflect.Value) error {
	s, err := d.ReadString()
	v.SetString(s)
	return err
}

func bytesDecoder(d *Decoder, v reflect.Value) error {
	b, err := d.ReadBytes()
	v.SetBytes(b)
	return err
}

func invalidDecoder(d *Decoder, v reflect.Value) error {
	return fmt.Errorf("%v is not supported", v.Type().String())
}

type sliceDecoder struct {
	t reflect.Type
}

func (sd sliceDecoder) decode(d *Decoder, v reflect.Value) error {
	if err := d.expectType(Slice); err != nil {
		return err
	}

	ln, _, err := d.ReadUint()

	if err != nil {
		return err
	}

	if v.Kind() == reflect.Slice {
		if ln := int(ln); v.Cap() < ln {
			v.Set(reflect.MakeSlice(v.Type(), ln, ln))
		} else {
			v.SetLen(ln)
			v.SetCap(ln)
		}
	}

	dec := typeDecoder(sd.t)
	for i := 0; i < int(ln); i++ {
		// this is a bug
		if d.peekType() == Nil {
			d.readType()
			continue
		}

		if err = dec(d, v.Index(i)); err != nil {
			return err
		}
	}

	return d.expectType(EOV)
}

func newSliceDecoder(t reflect.Type) decoderFunc {
	if t.Kind() == reflect.Uint8 {
		return bytesDecoder
	}
	d := sliceDecoder{t: t}
	return d.decode
}

type structDecoder struct {
	t reflect.Type
}

func (sd structDecoder) decode(d *Decoder, v reflect.Value) error {
	var (
		flds   = cachedTypeFields(sd.t)
		fields = make(map[string]*field, len(flds))
	)
	for i := range flds {
		f := &flds[i] // need a pointer so when we later update fields .dec it'd get picked up.
		fields[f.name] = f
	}
	if err := d.expectType(Struct); err != nil {
		if err, ok := err.(DecoderTypeError); ok && err.Actual == Nil || err.Actual == EmptyStruct {
			return nil
		}
		return err
	}
	for {
		n, err := d.ReadString()
		if err != nil {
			if err, ok := err.(DecoderTypeError); ok && err.Actual == EOV {
				return nil
			}
			return err
		}
		if f, ok := fields[n]; ok {
			fld := fieldByIndex(v, f.index, true)
			if err := f.dec(d, fld); err != nil {
				return err
			}
		}
	}
}

func newStructDecoder(t reflect.Type) decoderFunc {
	sd := structDecoder{t}
	return sd.decode
}

type mapDecoder struct {
	kt, vt reflect.Type
}

func (md mapDecoder) decode(d *Decoder, v reflect.Value) error {
	if err := d.expectType(Map); err != nil {
		return err
	}
	ln, _, err := d.ReadUint()

	if err != nil {
		return err
	}

	t := v.Type()
	if v.IsNil() {
		v.Set(reflect.MakeMapWithSize(t, int(ln)))
	}

	kdec, vdec := typeDecoder(md.kt), typeDecoder(md.vt)
	for i, kt, vt := 0, t.Key(), t.Elem(); i < int(ln); i++ {
		key := reflect.New(kt).Elem()
		if err = kdec(d, key); err != nil {
			return err
		}
		if d.peekType() == Nil {
			v.SetMapIndex(key, reflect.Zero(vt))
			d.readType()
			continue
		}
		val := reflect.New(vt).Elem()
		if err = vdec(d, val); err != nil {
			return err
		}
		v.SetMapIndex(key, val)
	}

	return d.expectType(EOV)
}

func newMapDecoder(t reflect.Type) decoderFunc {
	md := mapDecoder{t.Key(), t.Elem()}
	return md.decode
}

type ptrDecoder struct {
	dec decoderFunc
}

func (pd ptrDecoder) decodeElem(d *Decoder, v reflect.Value) error {
	if v.IsNil() {
		v.Set(reflect.New(v.Type().Elem()))
	}
	return pd.dec(d, v.Elem())
}

func (pd ptrDecoder) decode(d *Decoder, v reflect.Value) error {
	if v.IsNil() {
		v.Set(reflect.New(v.Type().Elem()))
	}
	return pd.dec(d, v)
}

func newPtrDecoder(fn decoderFunc, elem bool) decoderFunc {
	pd := ptrDecoder{fn}
	if elem {
		return pd.decodeElem
	}
	return pd.decode
}

func addrDecoder(fn decoderFunc) decoderFunc {
	return func(d *Decoder, v reflect.Value) error {
		if v = v.Addr(); v.IsNil() {
			v.Set(reflect.New(v.Type().Elem().Elem()))
		}
		return d.Decode(v.Interface())
	}
}
<|endoftext|>


<commit_before>package xlogtools

import (
	"errors"
	"regexp"
	"strconv"
	"strings"

	"github.com/greensnark/go-sequell/crawl/data"
	"github.com/greensnark/go-sequell/crawl/god"
	"github.com/greensnark/go-sequell/crawl/killer"
	"github.com/greensnark/go-sequell/crawl/place"
	"github.com/greensnark/go-sequell/crawl/player"
	"github.com/greensnark/go-sequell/crawl/version"
	"github.com/greensnark/go-sequell/text"
	"github.com/greensnark/go-sequell/xlog"
)

type XlogType int

const (
	Unknown XlogType = iota
	Log
	Milestone
)

var ErrNoSrc = errors.New("`src` field is not set")

func (x XlogType) String() string {
	switch x {
	case Log:
		return "logfile"
	case Milestone:
		return "milestones"
	}
	return "unk"
}

func (x XlogType) BaseTable() string {
	switch x {
	case Log:
		return "logrecord"
	case Milestone:
		return "milestone"
	}
	return ""
}

func Type(line xlog.Xlog) XlogType {
	if _, ok := line["type"]; ok {
		return Milestone
	}
	return Log
}

func NormalizeBool(b string) string {
	if b != "" && b != "0" {
		return "t"
	}
	return "f"
}

func ValidXlog(log xlog.Xlog) bool {
	return log["start"] != "" && log["name"] != "" &&
		(log["end"] != "" || log["time"] != "")
}

func NormalizeLog(log xlog.Xlog) (xlog.Xlog, error) {
	if _, exists := log["src"]; !exists {
		return log, ErrNoSrc
	}

	log["v"] = version.FullVersion(log["v"])
	log["cv"] = version.MajorVersion(log["v"])
	if version.IsAlpha(log["v"]) {
		log["alpha"] = "t"
	}
	if log["alpha"] != "" {
		log["cv"] += "-a"
	}
	log["vnum"] = strconv.FormatUint(version.VersionNumericId(log["v"]), 10)
	log["cvnum"] = strconv.FormatUint(version.VersionNumericId(log["cv"]), 10)
	log["vlongnum"] =
		strconv.FormatUint(version.VersionNumericId(log["vlong"]), 10)
	log["tiles"] = NormalizeBool(log["tiles"])
	log["wiz"] = NormalizeBool(log["wiz"])
	if log["ntv"] == "" {
		log["ntv"] = "0"
	}
	log["place"] = place.CanonicalPlace(log["place"])
	log["oplace"] = place.CanonicalPlace(log["oplace"])
	log["br"] = place.CanonicalPlace(log["br"])
	log["god"] = god.CanonicalGod(log["god"])
	log["crace"] = player.NormalizeRace(log["race"])
	log["rstart"] = log["start"]
	log["game_key"] = log["name"] + ":" + log["src"] + ":" + log["rstart"]

	milestone := Type(log) == Milestone
	if milestone {
		log["verb"] = log["type"]
		log["noun"] = text.FirstNotEmpty(log["milestone"], "?")
		log["rtime"] = log["time"]
		log["oplace"] = text.FirstNotEmpty(log["oplace"], log["place"])
		NormalizeMilestoneFields(log)
	} else {
		log["vmsg"] = text.FirstNotEmpty(log["vmsg"], log["tmsg"])
		log["map"] = NormalizeMapName(log["map"])
		log["killermap"] = NormalizeMapName(log["killermap"])
		log["ikiller"] = text.FirstNotEmpty(log["ikiller"], log["killer"])
		log["ckiller"] =
			killer.NormalizeKiller(
				text.FirstNotEmpty(log["killer"], log["ktyp"]),
				log["killer"], log["killer_flags"])
		log["cikiller"] =
			killer.NormalizeKiller(log["ikiller"], log["ikiller"], "")
		log["kmod"] = killer.NormalizeKmod(log["killer"])
		log["ckaux"] = killer.NormalizeKaux(log["kaux"])
		log["rend"] = log["end"]
	}

	CanonicalizeFields(log)
	sanitizeGold(log)

	return log, nil
}

func NormalizeMapName(mapname string) string {
	return strings.Replace(mapname, ",", ";", -1)
}

var milestoneVerbMap = data.Crawl.StringMap("milestone-verb-mappings")
var rActionWord = regexp.MustCompile(`(\w+) (.*?)\.?$`)
var rGhostWord = regexp.MustCompile(`(\w+) the ghost of (\S+)`)
var rAbyssCause = regexp.MustCompile(`\((.*?)\)$`)

func NormalizeMilestoneFields(log xlog.Xlog) {
	verb := log["verb"]
	if mappedVerb, ok := milestoneVerbMap[verb]; ok {
		log["verb"] = mappedVerb
		verb = mappedVerb
	}

	noun := log["noun"]
	switch verb {
	case "uniq":
		actionMatch := rActionWord.FindStringSubmatch(noun)
		if actionMatch != nil {
			actionWord, actedUpon := actionMatch[1], actionMatch[2]
			noun = actedUpon
			verb = qualifyVerbAction(verb, actionWord)
		}
	case "ghost":
		ghostMatch := rGhostWord.FindStringSubmatch(noun)
		if ghostMatch != nil {
			verb = qualifyVerbAction(verb, ghostMatch[1])
			noun = ghostMatch[2]
		}
	case "abyss.enter":
		abyssCauseMatch := rAbyssCause.FindStringSubmatch(noun)
		if abyssCauseMatch != nil {
			noun = text.FirstNotEmpty(abyssCauseMatch[1], "?")
		}
	case "br.enter", "br.end", "br.mid":
		noun = place.StripPlaceDepth(log["place"])
	case "br.exit":
		noun = place.StripPlaceDepth(log["oplace"])
	case "rune":
		noun = FoundRuneName(noun)
	case "orb":
		noun = "orb"
	case "god.mollify":
		noun = MollifiedGodName(noun)
	case "god.renounce":
		noun = RenouncedGodName(noun)
	case "god.worship":
		noun = WorshippedGodName(noun)
	case "god.maxpiety":
		noun = MaxedPietyGodName(noun)
	case "monstrous":
		noun = "demonspawn"
	case "shaft":
		noun = ShaftedPlace(noun)
	}
	log["verb"] = verb
	if noun != "" {
		log["noun"] = noun
	}
}

func qualifyVerbAction(verb string, actionWord string) string {
	if actionWord == "banished" {
		return verb + ".ban"
	}
	if actionWord == "pacified" {
		return verb + ".pac"
	}
	if actionWord == "enslaved" {
		return verb + ".ens"
	}
	return verb
}

var rFoundRune = regexp.MustCompile(`found an? (\S+) rune`)

func textReSubmatch(text string, reg *regexp.Regexp, submatch int) string {
	m := reg.FindStringSubmatch(text)
	if m != nil {
		return m[submatch]
	}
	return text
}

func FoundRuneName(found string) string {
	return textReSubmatch(found, rFoundRune, 1)
}

var rMollifiedGod = regexp.MustCompile(`^(?:partially )?mollified (.*)[.]$`)

func MollifiedGodName(mollifiedMsg string) string {
	return textReSubmatch(mollifiedMsg, rMollifiedGod, 1)
}

var rRenouncedGod = regexp.MustCompile(`^abandoned (.*)[.]$`)

func RenouncedGodName(renounceMsg string) string {
	return textReSubmatch(renounceMsg, rRenouncedGod, 1)
}

var rMaxedPietyGod = regexp.MustCompile(`^became the Champion of (.*)[.]$`)

func MaxedPietyGodName(maxpietyMsg string) string {
	return textReSubmatch(maxpietyMsg, rMaxedPietyGod, 1)
}

var rWorshippedGod = regexp.MustCompile(`^became a worshipper of (.*)[.]$`)

func WorshippedGodName(worshipMsg string) string {
	return textReSubmatch(worshipMsg, rWorshippedGod, 1)
}

var rShaftedPlace = regexp.MustCompile(`fell down a shaft to (.*)[.]$`)

func ShaftedPlace(shaftMsg string) string {
	return textReSubmatch(shaftMsg, rShaftedPlace, 1)
}

var fieldInputTransforms = data.Crawl.Map("field-input-transforms")

func CanonicalizeFields(log xlog.Xlog) {
	for field, transforms := range fieldInputTransforms {
		fieldname := field.(string)
		if value, ok := log[fieldname]; ok {
			transformMap := transforms.(map[interface{}]interface{})
			for isearch, ireplace := range transformMap {
				search := isearch.(string)
				replace := ireplace.(string)
				if value == search {
					value = replace
				}
			}
			log[fieldname] = value
		}
	}
}

func sanitizeGold(log xlog.Xlog) {
	if text.ParseInt(log["gold"], 0) < 0 ||
		text.ParseInt(log["goldfound"], 0) < 0 ||
		text.ParseInt(log["goldspent"], 0) < 0 {
		log["gold"] = "0"
		log["goldfound"] = "0"
		log["goldspent"] = "0"
	}
}
<commit_msg>Parse sacrifice milestone (elliptic).<commit_after>package xlogtools

import (
	"errors"
	"regexp"
	"strconv"
	"strings"

	"github.com/greensnark/go-sequell/crawl/data"
	"github.com/greensnark/go-sequell/crawl/god"
	"github.com/greensnark/go-sequell/crawl/killer"
	"github.com/greensnark/go-sequell/crawl/place"
	"github.com/greensnark/go-sequell/crawl/player"
	"github.com/greensnark/go-sequell/crawl/version"
	"github.com/greensnark/go-sequell/text"
	"github.com/greensnark/go-sequell/xlog"
)

type XlogType int

const (
	Unknown XlogType = iota
	Log
	Milestone
)

var ErrNoSrc = errors.New("`src` field is not set")

func (x XlogType) String() string {
	switch x {
	case Log:
		return "logfile"
	case Milestone:
		return "milestones"
	}
	return "unk"
}

func (x XlogType) BaseTable() string {
	switch x {
	case Log:
		return "logrecord"
	case Milestone:
		return "milestone"
	}
	return ""
}

func Type(line xlog.Xlog) XlogType {
	if _, ok := line["type"]; ok {
		return Milestone
	}
	return Log
}

func NormalizeBool(b string) string {
	if b != "" && b != "0" {
		return "t"
	}
	return "f"
}

func ValidXlog(log xlog.Xlog) bool {
	return log["start"] != "" && log["name"] != "" &&
		(log["end"] != "" || log["time"] != "")
}

func NormalizeLog(log xlog.Xlog) (xlog.Xlog, error) {
	if _, exists := log["src"]; !exists {
		return log, ErrNoSrc
	}

	log["v"] = version.FullVersion(log["v"])
	log["cv"] = version.MajorVersion(log["v"])
	if version.IsAlpha(log["v"]) {
		log["alpha"] = "t"
	}
	if log["alpha"] != "" {
		log["cv"] += "-a"
	}
	log["vnum"] = strconv.FormatUint(version.VersionNumericId(log["v"]), 10)
	log["cvnum"] = strconv.FormatUint(version.VersionNumericId(log["cv"]), 10)
	log["vlongnum"] =
		strconv.FormatUint(version.VersionNumericId(log["vlong"]), 10)
	log["tiles"] = NormalizeBool(log["tiles"])
	log["wiz"] = NormalizeBool(log["wiz"])
	if log["ntv"] == "" {
		log["ntv"] = "0"
	}
	log["place"] = place.CanonicalPlace(log["place"])
	log["oplace"] = place.CanonicalPlace(log["oplace"])
	log["br"] = place.CanonicalPlace(log["br"])
	log["god"] = god.CanonicalGod(log["god"])
	log["crace"] = player.NormalizeRace(log["race"])
	log["rstart"] = log["start"]
	log["game_key"] = log["name"] + ":" + log["src"] + ":" + log["rstart"]

	milestone := Type(log) == Milestone
	if milestone {
		log["verb"] = log["type"]
		log["noun"] = text.FirstNotEmpty(log["milestone"], "?")
		log["rtime"] = log["time"]
		log["oplace"] = text.FirstNotEmpty(log["oplace"], log["place"])
		NormalizeMilestoneFields(log)
	} else {
		log["vmsg"] = text.FirstNotEmpty(log["vmsg"], log["tmsg"])
		log["map"] = NormalizeMapName(log["map"])
		log["killermap"] = NormalizeMapName(log["killermap"])
		log["ikiller"] = text.FirstNotEmpty(log["ikiller"], log["killer"])
		log["ckiller"] =
			killer.NormalizeKiller(
				text.FirstNotEmpty(log["killer"], log["ktyp"]),
				log["killer"], log["killer_flags"])
		log["cikiller"] =
			killer.NormalizeKiller(log["ikiller"], log["ikiller"], "")
		log["kmod"] = killer.NormalizeKmod(log["killer"])
		log["ckaux"] = killer.NormalizeKaux(log["kaux"])
		log["rend"] = log["end"]
	}

	CanonicalizeFields(log)
	sanitizeGold(log)

	return log, nil
}

func NormalizeMapName(mapname string) string {
	return strings.Replace(mapname, ",", ";", -1)
}

var milestoneVerbMap = data.Crawl.StringMap("milestone-verb-mappings")
var rActionWord = regexp.MustCompile(`(\w+) (.*?)\.?$`)
var rGhostWord = regexp.MustCompile(`(\w+) the ghost of (\S+)`)
var rAbyssCause = regexp.MustCompile(`\((.*?)\)$`)
var rSacrificedThing = regexp.MustCompile(`sacrificed (?:an? )?(.*)[!.]$`)

func NormalizeMilestoneFields(log xlog.Xlog) {
	verb := log["verb"]
	if mappedVerb, ok := milestoneVerbMap[verb]; ok {
		log["verb"] = mappedVerb
		verb = mappedVerb
	}

	noun := log["noun"]
	switch verb {
	case "sacrifice":
		sacMatch := rSacrificedThing.FindStringSubmatch(noun)
		if sacMatch != nil {
			noun = strings.TrimSpace(sacMatch[1])
		}
	case "uniq":
		actionMatch := rActionWord.FindStringSubmatch(noun)
		if actionMatch != nil {
			actionWord, actedUpon := actionMatch[1], actionMatch[2]
			noun = actedUpon
			verb = qualifyVerbAction(verb, actionWord)
		}
	case "ghost":
		ghostMatch := rGhostWord.FindStringSubmatch(noun)
		if ghostMatch != nil {
			verb = qualifyVerbAction(verb, ghostMatch[1])
			noun = ghostMatch[2]
		}
	case "abyss.enter":
		abyssCauseMatch := rAbyssCause.FindStringSubmatch(noun)
		if abyssCauseMatch != nil {
			noun = text.FirstNotEmpty(abyssCauseMatch[1], "?")
		}
	case "br.enter", "br.end", "br.mid":
		noun = place.StripPlaceDepth(log["place"])
	case "br.exit":
		noun = place.StripPlaceDepth(log["oplace"])
	case "rune":
		noun = FoundRuneName(noun)
	case "orb":
		noun = "orb"
	case "god.mollify":
		noun = MollifiedGodName(noun)
	case "god.renounce":
		noun = RenouncedGodName(noun)
	case "god.worship":
		noun = WorshippedGodName(noun)
	case "god.maxpiety":
		noun = MaxedPietyGodName(noun)
	case "monstrous":
		noun = "demonspawn"
	case "shaft":
		noun = ShaftedPlace(noun)
	}
	log["verb"] = verb
	if noun != "" {
		log["noun"] = noun
	}
}

func qualifyVerbAction(verb string, actionWord string) string {
	if actionWord == "banished" {
		return verb + ".ban"
	}
	if actionWord == "pacified" {
		return verb + ".pac"
	}
	if actionWord == "enslaved" {
		return verb + ".ens"
	}
	return verb
}

var rFoundRune = regexp.MustCompile(`found an? (\S+) rune`)

func textReSubmatch(text string, reg *regexp.Regexp, submatch int) string {
	m := reg.FindStringSubmatch(text)
	if m != nil {
		return m[submatch]
	}
	return text
}

func FoundRuneName(found string) string {
	return textReSubmatch(found, rFoundRune, 1)
}

var rMollifiedGod = regexp.MustCompile(`^(?:partially )?mollified (.*)[.]$`)

func MollifiedGodName(mollifiedMsg string) string {
	return textReSubmatch(mollifiedMsg, rMollifiedGod, 1)
}

var rRenouncedGod = regexp.MustCompile(`^abandoned (.*)[.]$`)

func RenouncedGodName(renounceMsg string) string {
	return textReSubmatch(renounceMsg, rRenouncedGod, 1)
}

var rMaxedPietyGod = regexp.MustCompile(`^became the Champion of (.*)[.]$`)

func MaxedPietyGodName(maxpietyMsg string) string {
	return textReSubmatch(maxpietyMsg, rMaxedPietyGod, 1)
}

var rWorshippedGod = regexp.MustCompile(`^became a worshipper of (.*)[.]$`)

func WorshippedGodName(worshipMsg string) string {
	return textReSubmatch(worshipMsg, rWorshippedGod, 1)
}

var rShaftedPlace = regexp.MustCompile(`fell down a shaft to (.*)[.]$`)

func ShaftedPlace(shaftMsg string) string {
	return textReSubmatch(shaftMsg, rShaftedPlace, 1)
}

var fieldInputTransforms = data.Crawl.Map("field-input-transforms")

func CanonicalizeFields(log xlog.Xlog) {
	for field, transforms := range fieldInputTransforms {
		fieldname := field.(string)
		if value, ok := log[fieldname]; ok {
			transformMap := transforms.(map[interface{}]interface{})
			for isearch, ireplace := range transformMap {
				search := isearch.(string)
				replace := ireplace.(string)
				if value == search {
					value = replace
				}
			}
			log[fieldname] = value
		}
	}
}

func sanitizeGold(log xlog.Xlog) {
	if text.ParseInt(log["gold"], 0) < 0 ||
		text.ParseInt(log["goldfound"], 0) < 0 ||
		text.ParseInt(log["goldspent"], 0) < 0 {
		log["gold"] = "0"
		log["goldfound"] = "0"
		log["goldspent"] = "0"
	}
}
<|endoftext|>


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

package strconv

import (
	"bytes"
	"os"
	"strings"
	"unicode"
	"utf8"
)

const lowerhex = "0123456789abcdef"

// Quote returns a double-quoted Go string literal
// representing s.  The returned string s uses Go escape
// sequences (\t, \n, \xFF, \u0100) for control characters
// and non-ASCII characters.
func Quote(s string) string {
	var buf bytes.Buffer
	buf.WriteByte('"')
	for ; len(s) > 0; s = s[1:] {
		switch c := s[0]; {
		case c == '"':
			buf.WriteString(`\"`)
		case c == '\\':
			buf.WriteString(`\\`)
		case ' ' <= c && c <= '~':
			buf.WriteString(string(c))
		case c == '\a':
			buf.WriteString(`\a`)
		case c == '\b':
			buf.WriteString(`\b`)
		case c == '\f':
			buf.WriteString(`\f`)
		case c == '\n':
			buf.WriteString(`\n`)
		case c == '\r':
			buf.WriteString(`\r`)
		case c == '\t':
			buf.WriteString(`\t`)
		case c == '\v':
			buf.WriteString(`\v`)

		case c >= utf8.RuneSelf && utf8.FullRuneInString(s):
			r, size := utf8.DecodeRuneInString(s)
			if r == utf8.RuneError && size == 1 {
				goto EscX
			}
			s = s[size-1:] // next iteration will slice off 1 more
			if r < 0x10000 {
				buf.WriteString(`\u`)
				for j := uint(0); j < 4; j++ {
					buf.WriteByte(lowerhex[(r>>(12-4*j))&0xF])
				}
			} else {
				buf.WriteString(`\U`)
				for j := uint(0); j < 8; j++ {
					buf.WriteByte(lowerhex[(r>>(28-4*j))&0xF])
				}
			}

		default:
		EscX:
			buf.WriteString(`\x`)
			buf.WriteByte(lowerhex[c>>4])
			buf.WriteByte(lowerhex[c&0xF])
		}
	}
	buf.WriteByte('"')
	return buf.String()
}

// CanBackquote returns whether the string s would be
// a valid Go string literal if enclosed in backquotes.
func CanBackquote(s string) bool {
	for i := 0; i < len(s); i++ {
		if (s[i] < ' ' && s[i] != '\t') || s[i] == '`' {
			return false
		}
	}
	return true
}

func unhex(b byte) (v int, ok bool) {
	c := int(b)
	switch {
	case '0' <= c && c <= '9':
		return c - '0', true
	case 'a' <= c && c <= 'f':
		return c - 'a' + 10, true
	case 'A' <= c && c <= 'F':
		return c - 'A' + 10, true
	}
	return
}

// UnquoteChar decodes the first character or byte in the escaped string
// or character literal represented by the string s.
// It returns four values:
// 1) value, the decoded Unicode code point or byte value;
// 2) multibyte, a boolean indicating whether the decoded character
//    requires a multibyte UTF-8 representation;
// 3) tail, the remainder of the string after the character; and
// 4) an error that will be nil if the character is syntactically valid.
// The second argument, quote, specifies the type of literal being parsed
// and therefore which escaped quote character is permitted.
// If set to a single quote, it permits the sequence \' and disallows unescaped '.
// If set to a double quote, it permits \" and disallows unescaped ".
// If set to zero, it does not permit either escape and allows both quote characters to appear unescaped.
func UnquoteChar(s string, quote byte) (value int, multibyte bool, tail string, err os.Error) {
	// easy cases
	switch c := s[0]; {
	case c == quote && (quote == '\'' || quote == '"'):
		err = os.EINVAL
		return
	case c >= utf8.RuneSelf:
		r, size := utf8.DecodeRuneInString(s)
		return r, true, s[size:], nil
	case c != '\\':
		return int(s[0]), false, s[1:], nil
	}

	// hard case: c is backslash
	if len(s) <= 1 {
		err = os.EINVAL
		return
	}
	c := s[1]
	s = s[2:]

	switch c {
	case 'a':
		value = '\a'
	case 'b':
		value = '\b'
	case 'f':
		value = '\f'
	case 'n':
		value = '\n'
	case 'r':
		value = '\r'
	case 't':
		value = '\t'
	case 'v':
		value = '\v'
	case 'x', 'u', 'U':
		n := 0
		switch c {
		case 'x':
			n = 2
		case 'u':
			n = 4
		case 'U':
			n = 8
		}
		v := 0
		if len(s) < n {
			err = os.EINVAL
			return
		}
		for j := 0; j < n; j++ {
			x, ok := unhex(s[j])
			if !ok {
				err = os.EINVAL
				return
			}
			v = v<<4 | x
		}
		s = s[n:]
		if c == 'x' {
			// single-byte string, possibly not UTF-8
			value = v
			break
		}
		if v > unicode.MaxRune {
			err = os.EINVAL
			return
		}
		value = v
		multibyte = true
	case '0', '1', '2', '3', '4', '5', '6', '7':
		v := int(c) - '0'
		if len(s) < 2 {
			err = os.EINVAL
			return
		}
		for j := 0; j < 2; j++ { // one digit already; two more
			x := int(s[j]) - '0'
			if x < 0 || x > 7 {
				return
			}
			v = (v << 3) | x
		}
		s = s[2:]
		if v > 255 {
			err = os.EINVAL
			return
		}
		value = v
	case '\\':
		value = '\\'
	case '\'', '"':
		if c != quote {
			err = os.EINVAL
			return
		}
		value = int(c)
	default:
		err = os.EINVAL
		return
	}
	tail = s
	return
}

// Unquote interprets s as a single-quoted, double-quoted,
// or backquoted Go string literal, returning the string value
// that s quotes.  (If s is single-quoted, it would be a Go
// character literal; Unquote returns the corresponding
// one-character string.)
func Unquote(s string) (t string, err os.Error) {
	n := len(s)
	if n < 2 {
		return "", os.EINVAL
	}
	quote := s[0]
	if quote != s[n-1] {
		return "", os.EINVAL
	}
	s = s[1 : n-1]

	if quote == '`' {
		if strings.Index(s, "`") >= 0 {
			return "", os.EINVAL
		}
		return s, nil
	}
	if quote != '"' && quote != '\'' {
		return "", os.EINVAL
	}

	var buf bytes.Buffer
	for len(s) > 0 {
		c, multibyte, ss, err := UnquoteChar(s, quote)
		if err != nil {
			return "", err
		}
		s = ss
		if c < utf8.RuneSelf || !multibyte {
			buf.WriteByte(byte(c))
		} else {
			buf.WriteString(string(c))
		}
		if quote == '\'' && len(s) != 0 {
			// single-quoted must be single character
			return "", os.EINVAL
		}
	}
	return buf.String(), nil
}
<commit_msg>strconv: fix comment for godoc<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package strconv

import (
	"bytes"
	"os"
	"strings"
	"unicode"
	"utf8"
)

const lowerhex = "0123456789abcdef"

// Quote returns a double-quoted Go string literal
// representing s.  The returned string s uses Go escape
// sequences (\t, \n, \xFF, \u0100) for control characters
// and non-ASCII characters.
func Quote(s string) string {
	var buf bytes.Buffer
	buf.WriteByte('"')
	for ; len(s) > 0; s = s[1:] {
		switch c := s[0]; {
		case c == '"':
			buf.WriteString(`\"`)
		case c == '\\':
			buf.WriteString(`\\`)
		case ' ' <= c && c <= '~':
			buf.WriteString(string(c))
		case c == '\a':
			buf.WriteString(`\a`)
		case c == '\b':
			buf.WriteString(`\b`)
		case c == '\f':
			buf.WriteString(`\f`)
		case c == '\n':
			buf.WriteString(`\n`)
		case c == '\r':
			buf.WriteString(`\r`)
		case c == '\t':
			buf.WriteString(`\t`)
		case c == '\v':
			buf.WriteString(`\v`)

		case c >= utf8.RuneSelf && utf8.FullRuneInString(s):
			r, size := utf8.DecodeRuneInString(s)
			if r == utf8.RuneError && size == 1 {
				goto EscX
			}
			s = s[size-1:] // next iteration will slice off 1 more
			if r < 0x10000 {
				buf.WriteString(`\u`)
				for j := uint(0); j < 4; j++ {
					buf.WriteByte(lowerhex[(r>>(12-4*j))&0xF])
				}
			} else {
				buf.WriteString(`\U`)
				for j := uint(0); j < 8; j++ {
					buf.WriteByte(lowerhex[(r>>(28-4*j))&0xF])
				}
			}

		default:
		EscX:
			buf.WriteString(`\x`)
			buf.WriteByte(lowerhex[c>>4])
			buf.WriteByte(lowerhex[c&0xF])
		}
	}
	buf.WriteByte('"')
	return buf.String()
}

// CanBackquote returns whether the string s would be
// a valid Go string literal if enclosed in backquotes.
func CanBackquote(s string) bool {
	for i := 0; i < len(s); i++ {
		if (s[i] < ' ' && s[i] != '\t') || s[i] == '`' {
			return false
		}
	}
	return true
}

func unhex(b byte) (v int, ok bool) {
	c := int(b)
	switch {
	case '0' <= c && c <= '9':
		return c - '0', true
	case 'a' <= c && c <= 'f':
		return c - 'a' + 10, true
	case 'A' <= c && c <= 'F':
		return c - 'A' + 10, true
	}
	return
}

// UnquoteChar decodes the first character or byte in the escaped string
// or character literal represented by the string s.
// It returns four values:
//
//	1) value, the decoded Unicode code point or byte value;
//	2) multibyte, a boolean indicating whether the decoded character requires a multibyte UTF-8 representation;
//	3) tail, the remainder of the string after the character; and
//	4) an error that will be nil if the character is syntactically valid.
//
// The second argument, quote, specifies the type of literal being parsed
// and therefore which escaped quote character is permitted.
// If set to a single quote, it permits the sequence \' and disallows unescaped '.
// If set to a double quote, it permits \" and disallows unescaped ".
// If set to zero, it does not permit either escape and allows both quote characters to appear unescaped.
func UnquoteChar(s string, quote byte) (value int, multibyte bool, tail string, err os.Error) {
	// easy cases
	switch c := s[0]; {
	case c == quote && (quote == '\'' || quote == '"'):
		err = os.EINVAL
		return
	case c >= utf8.RuneSelf:
		r, size := utf8.DecodeRuneInString(s)
		return r, true, s[size:], nil
	case c != '\\':
		return int(s[0]), false, s[1:], nil
	}

	// hard case: c is backslash
	if len(s) <= 1 {
		err = os.EINVAL
		return
	}
	c := s[1]
	s = s[2:]

	switch c {
	case 'a':
		value = '\a'
	case 'b':
		value = '\b'
	case 'f':
		value = '\f'
	case 'n':
		value = '\n'
	case 'r':
		value = '\r'
	case 't':
		value = '\t'
	case 'v':
		value = '\v'
	case 'x', 'u', 'U':
		n := 0
		switch c {
		case 'x':
			n = 2
		case 'u':
			n = 4
		case 'U':
			n = 8
		}
		v := 0
		if len(s) < n {
			err = os.EINVAL
			return
		}
		for j := 0; j < n; j++ {
			x, ok := unhex(s[j])
			if !ok {
				err = os.EINVAL
				return
			}
			v = v<<4 | x
		}
		s = s[n:]
		if c == 'x' {
			// single-byte string, possibly not UTF-8
			value = v
			break
		}
		if v > unicode.MaxRune {
			err = os.EINVAL
			return
		}
		value = v
		multibyte = true
	case '0', '1', '2', '3', '4', '5', '6', '7':
		v := int(c) - '0'
		if len(s) < 2 {
			err = os.EINVAL
			return
		}
		for j := 0; j < 2; j++ { // one digit already; two more
			x := int(s[j]) - '0'
			if x < 0 || x > 7 {
				return
			}
			v = (v << 3) | x
		}
		s = s[2:]
		if v > 255 {
			err = os.EINVAL
			return
		}
		value = v
	case '\\':
		value = '\\'
	case '\'', '"':
		if c != quote {
			err = os.EINVAL
			return
		}
		value = int(c)
	default:
		err = os.EINVAL
		return
	}
	tail = s
	return
}

// Unquote interprets s as a single-quoted, double-quoted,
// or backquoted Go string literal, returning the string value
// that s quotes.  (If s is single-quoted, it would be a Go
// character literal; Unquote returns the corresponding
// one-character string.)
func Unquote(s string) (t string, err os.Error) {
	n := len(s)
	if n < 2 {
		return "", os.EINVAL
	}
	quote := s[0]
	if quote != s[n-1] {
		return "", os.EINVAL
	}
	s = s[1 : n-1]

	if quote == '`' {
		if strings.Index(s, "`") >= 0 {
			return "", os.EINVAL
		}
		return s, nil
	}
	if quote != '"' && quote != '\'' {
		return "", os.EINVAL
	}

	var buf bytes.Buffer
	for len(s) > 0 {
		c, multibyte, ss, err := UnquoteChar(s, quote)
		if err != nil {
			return "", err
		}
		s = ss
		if c < utf8.RuneSelf || !multibyte {
			buf.WriteByte(byte(c))
		} else {
			buf.WriteString(string(c))
		}
		if quote == '\'' && len(s) != 0 {
			// single-quoted must be single character
			return "", os.EINVAL
		}
	}
	return buf.String(), nil
}
<|endoftext|>