<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.

// This package partially implements the TLS 1.1 protocol, as specified in RFC 4346.
package tls

import (
	"io"
	"os"
	"net"
	"time"
)

// A Conn represents a secure connection.
type Conn struct {
	net.Conn
	writeChan                 chan<- []byte
	readChan                  <-chan []byte
	requestChan               chan<- interface{}
	readBuf                   []byte
	eof                       bool
	readTimeout, writeTimeout int64
}

func timeout(c chan<- bool, nsecs int64) {
	time.Sleep(nsecs)
	c <- true
}

func (tls *Conn) Read(p []byte) (int, os.Error) {
	if len(tls.readBuf) == 0 {
		if tls.eof {
			return 0, os.EOF
		}

		var timeoutChan chan bool
		if tls.readTimeout > 0 {
			timeoutChan = make(chan bool)
			go timeout(timeoutChan, tls.readTimeout)
		}

		select {
		case b := <-tls.readChan:
			tls.readBuf = b
		case <-timeoutChan:
			return 0, os.EAGAIN
		}

		// TLS distinguishes between orderly closes and truncations. An
		// orderly close is represented by a zero length slice.
		if closed(tls.readChan) {
			return 0, io.ErrUnexpectedEOF
		}
		if len(tls.readBuf) == 0 {
			tls.eof = true
			return 0, os.EOF
		}
	}

	n := copy(p, tls.readBuf)
	tls.readBuf = tls.readBuf[n:]
	return n, nil
}

func (tls *Conn) Write(p []byte) (int, os.Error) {
	if tls.eof || closed(tls.readChan) {
		return 0, os.EOF
	}

	var timeoutChan chan bool
	if tls.writeTimeout > 0 {
		timeoutChan = make(chan bool)
		go timeout(timeoutChan, tls.writeTimeout)
	}

	select {
	case tls.writeChan <- p:
	case <-timeoutChan:
		return 0, os.EAGAIN
	}

	return len(p), nil
}

func (tls *Conn) Close() os.Error {
	close(tls.writeChan)
	close(tls.requestChan)
	tls.eof = true
	return nil
}

func (tls *Conn) SetTimeout(nsec int64) os.Error {
	tls.readTimeout = nsec
	tls.writeTimeout = nsec
	return nil
}

func (tls *Conn) SetReadTimeout(nsec int64) os.Error {
	tls.readTimeout = nsec
	return nil
}

func (tls *Conn) SetWriteTimeout(nsec int64) os.Error {
	tls.writeTimeout = nsec
	return nil
}

func (tls *Conn) GetConnectionState() ConnectionState {
	replyChan := make(chan ConnectionState)
	tls.requestChan <- getConnectionState{replyChan}
	return <-replyChan
}

func (tls *Conn) WaitConnectionState() ConnectionState {
	replyChan := make(chan ConnectionState)
	tls.requestChan <- waitConnectionState{replyChan}
	return <-replyChan
}

type handshaker interface {
	loop(writeChan chan<- interface{}, controlChan chan<- interface{}, msgChan <-chan interface{}, config *Config)
}

// Server establishes a secure connection over the given connection and acts
// as a TLS server.
func startTLSGoroutines(conn net.Conn, h handshaker, config *Config) *Conn {
	tls := new(Conn)
	tls.Conn = conn

	writeChan := make(chan []byte)
	readChan := make(chan []byte)
	requestChan := make(chan interface{})

	tls.writeChan = writeChan
	tls.readChan = readChan
	tls.requestChan = requestChan

	handshakeWriterChan := make(chan interface{})
	processorHandshakeChan := make(chan interface{})
	handshakeProcessorChan := make(chan interface{})
	readerProcessorChan := make(chan *record)

	go new(recordWriter).loop(conn, writeChan, handshakeWriterChan)
	go recordReader(readerProcessorChan, conn)
	go new(recordProcessor).loop(readChan, requestChan, handshakeProcessorChan, readerProcessorChan, processorHandshakeChan)
	go h.loop(handshakeWriterChan, handshakeProcessorChan, processorHandshakeChan, config)

	return tls
}

func Server(conn net.Conn, config *Config) *Conn {
	return startTLSGoroutines(conn, new(serverHandshake), config)
}

func Client(conn net.Conn, config *Config) *Conn {
	return startTLSGoroutines(conn, new(clientHandshake), config)
}

type Listener struct {
	listener net.Listener
	config   *Config
}

func (l Listener) Accept() (c net.Conn, err os.Error) {
	c, err = l.listener.Accept()
	if err != nil {
		return
	}

	c = Server(c, l.config)
	return
}

func (l Listener) Close() os.Error { return l.listener.Close() }

func (l Listener) Addr() net.Addr { return l.listener.Addr() }

// NewListener creates a Listener which accepts connections from an inner
// Listener and wraps each connection with Server.
func NewListener(listener net.Listener, config *Config) (l Listener) {
	l.listener = listener
	l.config = config
	return
}
<commit_msg>crypto/tls: make Listener a pointer.<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.

// This package partially implements the TLS 1.1 protocol, as specified in RFC 4346.
package tls

import (
	"io"
	"os"
	"net"
	"time"
)

// A Conn represents a secure connection.
type Conn struct {
	net.Conn
	writeChan                 chan<- []byte
	readChan                  <-chan []byte
	requestChan               chan<- interface{}
	readBuf                   []byte
	eof                       bool
	readTimeout, writeTimeout int64
}

func timeout(c chan<- bool, nsecs int64) {
	time.Sleep(nsecs)
	c <- true
}

func (tls *Conn) Read(p []byte) (int, os.Error) {
	if len(tls.readBuf) == 0 {
		if tls.eof {
			return 0, os.EOF
		}

		var timeoutChan chan bool
		if tls.readTimeout > 0 {
			timeoutChan = make(chan bool)
			go timeout(timeoutChan, tls.readTimeout)
		}

		select {
		case b := <-tls.readChan:
			tls.readBuf = b
		case <-timeoutChan:
			return 0, os.EAGAIN
		}

		// TLS distinguishes between orderly closes and truncations. An
		// orderly close is represented by a zero length slice.
		if closed(tls.readChan) {
			return 0, io.ErrUnexpectedEOF
		}
		if len(tls.readBuf) == 0 {
			tls.eof = true
			return 0, os.EOF
		}
	}

	n := copy(p, tls.readBuf)
	tls.readBuf = tls.readBuf[n:]
	return n, nil
}

func (tls *Conn) Write(p []byte) (int, os.Error) {
	if tls.eof || closed(tls.readChan) {
		return 0, os.EOF
	}

	var timeoutChan chan bool
	if tls.writeTimeout > 0 {
		timeoutChan = make(chan bool)
		go timeout(timeoutChan, tls.writeTimeout)
	}

	select {
	case tls.writeChan <- p:
	case <-timeoutChan:
		return 0, os.EAGAIN
	}

	return len(p), nil
}

func (tls *Conn) Close() os.Error {
	close(tls.writeChan)
	close(tls.requestChan)
	tls.eof = true
	return nil
}

func (tls *Conn) SetTimeout(nsec int64) os.Error {
	tls.readTimeout = nsec
	tls.writeTimeout = nsec
	return nil
}

func (tls *Conn) SetReadTimeout(nsec int64) os.Error {
	tls.readTimeout = nsec
	return nil
}

func (tls *Conn) SetWriteTimeout(nsec int64) os.Error {
	tls.writeTimeout = nsec
	return nil
}

func (tls *Conn) GetConnectionState() ConnectionState {
	replyChan := make(chan ConnectionState)
	tls.requestChan <- getConnectionState{replyChan}
	return <-replyChan
}

func (tls *Conn) WaitConnectionState() ConnectionState {
	replyChan := make(chan ConnectionState)
	tls.requestChan <- waitConnectionState{replyChan}
	return <-replyChan
}

type handshaker interface {
	loop(writeChan chan<- interface{}, controlChan chan<- interface{}, msgChan <-chan interface{}, config *Config)
}

// Server establishes a secure connection over the given connection and acts
// as a TLS server.
func startTLSGoroutines(conn net.Conn, h handshaker, config *Config) *Conn {
	tls := new(Conn)
	tls.Conn = conn

	writeChan := make(chan []byte)
	readChan := make(chan []byte)
	requestChan := make(chan interface{})

	tls.writeChan = writeChan
	tls.readChan = readChan
	tls.requestChan = requestChan

	handshakeWriterChan := make(chan interface{})
	processorHandshakeChan := make(chan interface{})
	handshakeProcessorChan := make(chan interface{})
	readerProcessorChan := make(chan *record)

	go new(recordWriter).loop(conn, writeChan, handshakeWriterChan)
	go recordReader(readerProcessorChan, conn)
	go new(recordProcessor).loop(readChan, requestChan, handshakeProcessorChan, readerProcessorChan, processorHandshakeChan)
	go h.loop(handshakeWriterChan, handshakeProcessorChan, processorHandshakeChan, config)

	return tls
}

func Server(conn net.Conn, config *Config) *Conn {
	return startTLSGoroutines(conn, new(serverHandshake), config)
}

func Client(conn net.Conn, config *Config) *Conn {
	return startTLSGoroutines(conn, new(clientHandshake), config)
}

type Listener struct {
	listener net.Listener
	config   *Config
}

func (l *Listener) Accept() (c net.Conn, err os.Error) {
	c, err = l.listener.Accept()
	if err != nil {
		return
	}

	c = Server(c, l.config)
	return
}

func (l *Listener) Close() os.Error { return l.listener.Close() }

func (l *Listener) Addr() net.Addr { return l.listener.Addr() }

// NewListener creates a Listener which accepts connections from an inner
// Listener and wraps each connection with Server.
func NewListener(listener net.Listener, config *Config) (l *Listener) {
	l = new(Listener)
	l.listener = listener
	l.config = config
	return
}
<|endoftext|>


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

// Package build provides tools for building Go packages.
package build

import (
	"bytes"
	"exec"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
)

// Build produces a build Script for the given package.
func Build(tree *Tree, pkg string, info *DirInfo) (*Script, os.Error) {
	s := &Script{}
	b := &build{
		script: s,
		path:   filepath.Join(tree.SrcDir(), pkg),
	}
	b.obj = b.abs("_obj") + string(filepath.Separator)

	b.goarch = runtime.GOARCH
	if g := os.Getenv("GOARCH"); g != "" {
		b.goarch = g
	}
	var err os.Error
	b.arch, err = ArchChar(b.goarch)
	if err != nil {
		return nil, err
	}

	// .go files to be built with gc
	gofiles := b.abss(info.GoFiles...)
	s.addInput(gofiles...)

	var ofiles []string // object files to be linked or packed

	// make build directory
	b.mkdir(b.obj)
	s.addIntermediate(b.obj)

	// cgo
	if len(info.CgoFiles) > 0 {
		cgoFiles := b.abss(info.CgoFiles...)
		s.addInput(cgoFiles...)
		outGo, outObj := b.cgo(cgoFiles)
		gofiles = append(gofiles, outGo...)
		ofiles = append(ofiles, outObj...)
		s.addIntermediate(outGo...)
		s.addIntermediate(outObj...)
	}

	// compile
	if len(gofiles) > 0 {
		ofile := b.obj + "_go_." + b.arch
		b.gc(ofile, gofiles...)
		ofiles = append(ofiles, ofile)
		s.addIntermediate(ofile)
	}

	// assemble
	for _, sfile := range info.SFiles {
		ofile := b.obj + sfile[:len(sfile)-1] + b.arch
		sfile = b.abs(sfile)
		s.addInput(sfile)
		b.asm(ofile, sfile)
		ofiles = append(ofiles, ofile)
		s.addIntermediate(ofile)
	}

	if len(ofiles) == 0 {
		return nil, os.NewError("make: no object files to build")
	}

	// choose target file
	var targ string
	if info.IsCommand() {
		// use the last part of the import path as binary name
		_, bin := filepath.Split(pkg)
		if runtime.GOOS == "windows" {
			bin += ".exe"
		}
		targ = filepath.Join(tree.BinDir(), bin)
	} else {
		targ = filepath.Join(tree.PkgDir(), pkg+".a")
	}

	// make target directory
	targDir, _ := filepath.Split(targ)
	b.mkdir(targDir)

	// link binary or pack object
	if info.IsCommand() {
		b.ld(targ, ofiles...)
	} else {
		b.gopack(targ, ofiles...)
	}
	s.Output = append(s.Output, targ)

	return b.script, nil
}

// A Script describes the build process for a Go package.
// The Input, Intermediate, and Output fields are lists of absolute paths.
type Script struct {
	Cmd          []*Cmd
	Input        []string
	Intermediate []string
	Output       []string
}

func (s *Script) addInput(file ...string) {
	s.Input = append(s.Input, file...)
}

func (s *Script) addIntermediate(file ...string) {
	s.Intermediate = append(s.Intermediate, file...)
}

// Run runs the Script's Cmds in order.
func (s *Script) Run() os.Error {
	for _, c := range s.Cmd {
		if err := c.Run(); err != nil {
			return err
		}
	}
	return nil
}

// Stale returns true if the build's inputs are newer than its outputs.
func (s *Script) Stale() bool {
	var latest int64
	// get latest mtime of outputs
	for _, file := range s.Output {
		fi, err := os.Stat(file)
		if err != nil {
			// any error reading output files means stale
			return true
		}
		if m := fi.Mtime_ns; m > latest {
			latest = m
		}
	}
	for _, file := range s.Input {
		fi, err := os.Stat(file)
		if err != nil || fi.Mtime_ns > latest {
			// any error reading input files means stale
			// (attempt to rebuild to figure out why)
			return true
		}
	}
	return false
}

// Clean removes the Script's Intermediate files.
// It tries to remove every file and returns the first error it encounters.
func (s *Script) Clean() (err os.Error) {
	// Reverse order so that directories get removed after the files they contain.
	for i := len(s.Intermediate) - 1; i >= 0; i-- {
		if e := os.Remove(s.Intermediate[i]); err == nil {
			err = e
		}
	}
	return
}

// Clean removes the Script's Intermediate and Output files.
// It tries to remove every file and returns the first error it encounters.
func (s *Script) Nuke() (err os.Error) {
	// Reverse order so that directories get removed after the files they contain.
	for i := len(s.Output) - 1; i >= 0; i-- {
		if e := os.Remove(s.Output[i]); err == nil {
			err = e
		}
	}
	if e := s.Clean(); err == nil {
		err = e
	}
	return
}

// A Cmd describes an individual build command.
type Cmd struct {
	Args   []string // command-line
	Stdout string   // write standard output to this file, "" is passthrough
	Dir    string   // working directory
	Env    []string // environment
	Input  []string // file paths (dependencies)
	Output []string // file paths
}

func (c *Cmd) String() string {
	return strings.Join(c.Args, " ")
}

// Run executes the Cmd.
func (c *Cmd) Run() os.Error {
	out := new(bytes.Buffer)
	cmd := exec.Command(c.Args[0], c.Args[1:]...)
	cmd.Dir = c.Dir
	cmd.Env = c.Env
	cmd.Stdout = out
	cmd.Stderr = out
	if c.Stdout != "" {
		f, err := os.Create(c.Stdout)
		if err != nil {
			return err
		}
		defer f.Close()
		cmd.Stdout = f
	}
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("command %q: %v\n%v", c, err, out)
	}
	return nil
}

// ArchChar returns the architecture character for the given goarch.
// For example, ArchChar("amd64") returns "6".
func ArchChar(goarch string) (string, os.Error) {
	switch goarch {
	case "386":
		return "8", nil
	case "amd64":
		return "6", nil
	case "arm":
		return "5", nil
	}
	return "", os.NewError("unsupported GOARCH " + goarch)
}

type build struct {
	script *Script
	path   string
	obj    string
	goarch string
	arch   string
}

func (b *build) abs(file string) string {
	if filepath.IsAbs(file) {
		return file
	}
	return filepath.Join(b.path, file)
}

func (b *build) abss(file ...string) []string {
	s := make([]string, len(file))
	for i, f := range file {
		s[i] = b.abs(f)
	}
	return s
}

func (b *build) add(c Cmd) {
	b.script.Cmd = append(b.script.Cmd, &c)
}

func (b *build) mkdir(name string) {
	b.add(Cmd{
		Args:   []string{"mkdir", "-p", name},
		Output: []string{name},
	})
}

func (b *build) gc(ofile string, gofiles ...string) {
	gc := b.arch + "g"
	args := append([]string{gc, "-o", ofile}, gcImportArgs...)
	args = append(args, gofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  gofiles,
		Output: []string{ofile},
	})
}

func (b *build) asm(ofile string, sfile string) {
	asm := b.arch + "a"
	b.add(Cmd{
		Args:   []string{asm, "-o", ofile, sfile},
		Input:  []string{sfile},
		Output: []string{ofile},
	})
}

func (b *build) ld(targ string, ofiles ...string) {
	ld := b.arch + "l"
	args := append([]string{ld, "-o", targ}, ldImportArgs...)
	args = append(args, ofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) gopack(targ string, ofiles ...string) {
	b.add(Cmd{
		Args:   append([]string{"gopack", "grc", targ}, ofiles...),
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) cc(ofile string, cfiles ...string) {
	cc := b.arch + "c"
	dir := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
	inc := filepath.Join(runtime.GOROOT(), "pkg", dir)
	args := []string{cc, "-FVw", "-I", inc, "-o", ofile}
	b.add(Cmd{
		Args:   append(args, cfiles...),
		Input:  cfiles,
		Output: []string{ofile},
	})
}

func (b *build) gccCompile(ofile, cfile string) {
	b.add(Cmd{
		Args:   b.gccArgs("-o", ofile, "-c", cfile),
		Input:  []string{cfile},
		Output: []string{ofile},
	})
}

func (b *build) gccLink(ofile string, ofiles ...string) {
	b.add(Cmd{
		Args:   append(b.gccArgs("-o", ofile), ofiles...),
		Input:  ofiles,
		Output: []string{ofile},
	})
}

func (b *build) gccArgs(args ...string) []string {
	// TODO(adg): HOST_CC
	a := []string{"gcc", "-I", b.path, "-g", "-fPIC", "-O2"}
	switch b.arch {
	case "8":
		a = append(a, "-m32")
	case "6":
		a = append(a, "-m64")
	}
	return append(a, args...)
}

var cgoRe = regexp.MustCompile(`[/\\:]`)

func (b *build) cgo(cgofiles []string) (outGo, outObj []string) {
	// cgo
	// TODO(adg): CGOPKGPATH
	// TODO(adg): CGO_FLAGS
	gofiles := []string{b.obj + "_cgo_gotypes.go"}
	cfiles := []string{b.obj + "_cgo_main.c", b.obj + "_cgo_export.c"}
	for _, fn := range cgofiles {
		f := b.obj + cgoRe.ReplaceAllString(fn[:len(fn)-2], "_")
		gofiles = append(gofiles, f+"cgo1.go")
		cfiles = append(cfiles, f+"cgo2.c")
	}
	defunC := b.obj + "_cgo_defun.c"
	output := append([]string{defunC}, cfiles...)
	output = append(output, gofiles...)
	b.add(Cmd{
		Args:   append([]string{"cgo", "--"}, cgofiles...),
		Dir:    b.path,
		Env:    append(os.Environ(), "GOARCH="+b.goarch),
		Input:  cgofiles,
		Output: output,
	})
	outGo = append(outGo, gofiles...)
	exportH := filepath.Join(b.path, "_cgo_export.h")
	b.script.addIntermediate(defunC, exportH, b.obj+"_cgo_flags")
	b.script.addIntermediate(cfiles...)

	// cc _cgo_defun.c
	defunObj := b.obj + "_cgo_defun." + b.arch
	b.cc(defunObj, defunC)
	outObj = append(outObj, defunObj)

	// gcc
	linkobj := make([]string, 0, len(cfiles))
	for _, cfile := range cfiles {
		ofile := cfile[:len(cfile)-1] + "o"
		b.gccCompile(ofile, cfile)
		linkobj = append(linkobj, ofile)
		if !strings.HasSuffix(ofile, "_cgo_main.o") {
			outObj = append(outObj, ofile)
		} else {
			b.script.addIntermediate(ofile)
		}
	}
	dynObj := b.obj + "_cgo_.o"
	b.gccLink(dynObj, linkobj...)
	b.script.addIntermediate(dynObj)

	// cgo -dynimport
	importC := b.obj + "_cgo_import.c"
	b.add(Cmd{
		Args:   []string{"cgo", "-dynimport", dynObj},
		Stdout: importC,
		Input:  []string{dynObj},
		Output: []string{importC},
	})
	b.script.addIntermediate(importC)

	// cc _cgo_import.ARCH
	importObj := b.obj + "_cgo_import." + b.arch
	b.cc(importObj, importC)
	outObj = append(outObj, importObj)

	return
}
<commit_msg>go/build: include Import objects in Script Inputs<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package build provides tools for building Go packages.
package build

import (
	"bytes"
	"exec"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
)

// Build produces a build Script for the given package.
func Build(tree *Tree, pkg string, info *DirInfo) (*Script, os.Error) {
	s := &Script{}
	b := &build{
		script: s,
		path:   filepath.Join(tree.SrcDir(), pkg),
	}
	b.obj = b.abs("_obj") + string(filepath.Separator)

	b.goarch = runtime.GOARCH
	if g := os.Getenv("GOARCH"); g != "" {
		b.goarch = g
	}
	var err os.Error
	b.arch, err = ArchChar(b.goarch)
	if err != nil {
		return nil, err
	}

	// add import object files to list of Inputs
	for _, pkg := range info.Imports {
		t, p, err := FindTree(pkg)
		if err != nil && err != ErrNotFound {
			// FindTree should always be able to suggest an import
			// path and tree. The path must be malformed
			// (for example, an absolute or relative path).
			return nil, os.NewError("build: invalid import: " + pkg)
		}
		s.addInput(filepath.Join(t.PkgDir(), p+".a"))
	}

	// .go files to be built with gc
	gofiles := b.abss(info.GoFiles...)
	s.addInput(gofiles...)

	var ofiles []string // object files to be linked or packed

	// make build directory
	b.mkdir(b.obj)
	s.addIntermediate(b.obj)

	// cgo
	if len(info.CgoFiles) > 0 {
		cgoFiles := b.abss(info.CgoFiles...)
		s.addInput(cgoFiles...)
		outGo, outObj := b.cgo(cgoFiles)
		gofiles = append(gofiles, outGo...)
		ofiles = append(ofiles, outObj...)
		s.addIntermediate(outGo...)
		s.addIntermediate(outObj...)
	}

	// compile
	if len(gofiles) > 0 {
		ofile := b.obj + "_go_." + b.arch
		b.gc(ofile, gofiles...)
		ofiles = append(ofiles, ofile)
		s.addIntermediate(ofile)
	}

	// assemble
	for _, sfile := range info.SFiles {
		ofile := b.obj + sfile[:len(sfile)-1] + b.arch
		sfile = b.abs(sfile)
		s.addInput(sfile)
		b.asm(ofile, sfile)
		ofiles = append(ofiles, ofile)
		s.addIntermediate(ofile)
	}

	if len(ofiles) == 0 {
		return nil, os.NewError("make: no object files to build")
	}

	// choose target file
	var targ string
	if info.IsCommand() {
		// use the last part of the import path as binary name
		_, bin := filepath.Split(pkg)
		if runtime.GOOS == "windows" {
			bin += ".exe"
		}
		targ = filepath.Join(tree.BinDir(), bin)
	} else {
		targ = filepath.Join(tree.PkgDir(), pkg+".a")
	}

	// make target directory
	targDir, _ := filepath.Split(targ)
	b.mkdir(targDir)

	// link binary or pack object
	if info.IsCommand() {
		b.ld(targ, ofiles...)
	} else {
		b.gopack(targ, ofiles...)
	}
	s.Output = append(s.Output, targ)

	return b.script, nil
}

// A Script describes the build process for a Go package.
// The Input, Intermediate, and Output fields are lists of absolute paths.
type Script struct {
	Cmd          []*Cmd
	Input        []string
	Intermediate []string
	Output       []string
}

func (s *Script) addInput(file ...string) {
	s.Input = append(s.Input, file...)
}

func (s *Script) addIntermediate(file ...string) {
	s.Intermediate = append(s.Intermediate, file...)
}

// Run runs the Script's Cmds in order.
func (s *Script) Run() os.Error {
	for _, c := range s.Cmd {
		if err := c.Run(); err != nil {
			return err
		}
	}
	return nil
}

// Stale returns true if the build's inputs are newer than its outputs.
func (s *Script) Stale() bool {
	var latest int64
	// get latest mtime of outputs
	for _, file := range s.Output {
		fi, err := os.Stat(file)
		if err != nil {
			// any error reading output files means stale
			return true
		}
		if m := fi.Mtime_ns; m > latest {
			latest = m
		}
	}
	for _, file := range s.Input {
		fi, err := os.Stat(file)
		if err != nil || fi.Mtime_ns > latest {
			// any error reading input files means stale
			// (attempt to rebuild to figure out why)
			return true
		}
	}
	return false
}

// Clean removes the Script's Intermediate files.
// It tries to remove every file and returns the first error it encounters.
func (s *Script) Clean() (err os.Error) {
	// Reverse order so that directories get removed after the files they contain.
	for i := len(s.Intermediate) - 1; i >= 0; i-- {
		if e := os.Remove(s.Intermediate[i]); err == nil {
			err = e
		}
	}
	return
}

// Clean removes the Script's Intermediate and Output files.
// It tries to remove every file and returns the first error it encounters.
func (s *Script) Nuke() (err os.Error) {
	// Reverse order so that directories get removed after the files they contain.
	for i := len(s.Output) - 1; i >= 0; i-- {
		if e := os.Remove(s.Output[i]); err == nil {
			err = e
		}
	}
	if e := s.Clean(); err == nil {
		err = e
	}
	return
}

// A Cmd describes an individual build command.
type Cmd struct {
	Args   []string // command-line
	Stdout string   // write standard output to this file, "" is passthrough
	Dir    string   // working directory
	Env    []string // environment
	Input  []string // file paths (dependencies)
	Output []string // file paths
}

func (c *Cmd) String() string {
	return strings.Join(c.Args, " ")
}

// Run executes the Cmd.
func (c *Cmd) Run() os.Error {
	out := new(bytes.Buffer)
	cmd := exec.Command(c.Args[0], c.Args[1:]...)
	cmd.Dir = c.Dir
	cmd.Env = c.Env
	cmd.Stdout = out
	cmd.Stderr = out
	if c.Stdout != "" {
		f, err := os.Create(c.Stdout)
		if err != nil {
			return err
		}
		defer f.Close()
		cmd.Stdout = f
	}
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("command %q: %v\n%v", c, err, out)
	}
	return nil
}

// ArchChar returns the architecture character for the given goarch.
// For example, ArchChar("amd64") returns "6".
func ArchChar(goarch string) (string, os.Error) {
	switch goarch {
	case "386":
		return "8", nil
	case "amd64":
		return "6", nil
	case "arm":
		return "5", nil
	}
	return "", os.NewError("unsupported GOARCH " + goarch)
}

type build struct {
	script *Script
	path   string
	obj    string
	goarch string
	arch   string
}

func (b *build) abs(file string) string {
	if filepath.IsAbs(file) {
		return file
	}
	return filepath.Join(b.path, file)
}

func (b *build) abss(file ...string) []string {
	s := make([]string, len(file))
	for i, f := range file {
		s[i] = b.abs(f)
	}
	return s
}

func (b *build) add(c Cmd) {
	b.script.Cmd = append(b.script.Cmd, &c)
}

func (b *build) mkdir(name string) {
	b.add(Cmd{
		Args:   []string{"mkdir", "-p", name},
		Output: []string{name},
	})
}

func (b *build) gc(ofile string, gofiles ...string) {
	gc := b.arch + "g"
	args := append([]string{gc, "-o", ofile}, gcImportArgs...)
	args = append(args, gofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  gofiles,
		Output: []string{ofile},
	})
}

func (b *build) asm(ofile string, sfile string) {
	asm := b.arch + "a"
	b.add(Cmd{
		Args:   []string{asm, "-o", ofile, sfile},
		Input:  []string{sfile},
		Output: []string{ofile},
	})
}

func (b *build) ld(targ string, ofiles ...string) {
	ld := b.arch + "l"
	args := append([]string{ld, "-o", targ}, ldImportArgs...)
	args = append(args, ofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) gopack(targ string, ofiles ...string) {
	b.add(Cmd{
		Args:   append([]string{"gopack", "grc", targ}, ofiles...),
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) cc(ofile string, cfiles ...string) {
	cc := b.arch + "c"
	dir := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
	inc := filepath.Join(runtime.GOROOT(), "pkg", dir)
	args := []string{cc, "-FVw", "-I", inc, "-o", ofile}
	b.add(Cmd{
		Args:   append(args, cfiles...),
		Input:  cfiles,
		Output: []string{ofile},
	})
}

func (b *build) gccCompile(ofile, cfile string) {
	b.add(Cmd{
		Args:   b.gccArgs("-o", ofile, "-c", cfile),
		Input:  []string{cfile},
		Output: []string{ofile},
	})
}

func (b *build) gccLink(ofile string, ofiles ...string) {
	b.add(Cmd{
		Args:   append(b.gccArgs("-o", ofile), ofiles...),
		Input:  ofiles,
		Output: []string{ofile},
	})
}

func (b *build) gccArgs(args ...string) []string {
	// TODO(adg): HOST_CC
	a := []string{"gcc", "-I", b.path, "-g", "-fPIC", "-O2"}
	switch b.arch {
	case "8":
		a = append(a, "-m32")
	case "6":
		a = append(a, "-m64")
	}
	return append(a, args...)
}

var cgoRe = regexp.MustCompile(`[/\\:]`)

func (b *build) cgo(cgofiles []string) (outGo, outObj []string) {
	// cgo
	// TODO(adg): CGOPKGPATH
	// TODO(adg): CGO_FLAGS
	gofiles := []string{b.obj + "_cgo_gotypes.go"}
	cfiles := []string{b.obj + "_cgo_main.c", b.obj + "_cgo_export.c"}
	for _, fn := range cgofiles {
		f := b.obj + cgoRe.ReplaceAllString(fn[:len(fn)-2], "_")
		gofiles = append(gofiles, f+"cgo1.go")
		cfiles = append(cfiles, f+"cgo2.c")
	}
	defunC := b.obj + "_cgo_defun.c"
	output := append([]string{defunC}, cfiles...)
	output = append(output, gofiles...)
	b.add(Cmd{
		Args:   append([]string{"cgo", "--"}, cgofiles...),
		Dir:    b.path,
		Env:    append(os.Environ(), "GOARCH="+b.goarch),
		Input:  cgofiles,
		Output: output,
	})
	outGo = append(outGo, gofiles...)
	exportH := filepath.Join(b.path, "_cgo_export.h")
	b.script.addIntermediate(defunC, exportH, b.obj+"_cgo_flags")
	b.script.addIntermediate(cfiles...)

	// cc _cgo_defun.c
	defunObj := b.obj + "_cgo_defun." + b.arch
	b.cc(defunObj, defunC)
	outObj = append(outObj, defunObj)

	// gcc
	linkobj := make([]string, 0, len(cfiles))
	for _, cfile := range cfiles {
		ofile := cfile[:len(cfile)-1] + "o"
		b.gccCompile(ofile, cfile)
		linkobj = append(linkobj, ofile)
		if !strings.HasSuffix(ofile, "_cgo_main.o") {
			outObj = append(outObj, ofile)
		} else {
			b.script.addIntermediate(ofile)
		}
	}
	dynObj := b.obj + "_cgo_.o"
	b.gccLink(dynObj, linkobj...)
	b.script.addIntermediate(dynObj)

	// cgo -dynimport
	importC := b.obj + "_cgo_import.c"
	b.add(Cmd{
		Args:   []string{"cgo", "-dynimport", dynObj},
		Stdout: importC,
		Input:  []string{dynObj},
		Output: []string{importC},
	})
	b.script.addIntermediate(importC)

	// cc _cgo_import.ARCH
	importObj := b.obj + "_cgo_import." + b.arch
	b.cc(importObj, importC)
	outObj = append(outObj, importObj)

	return
}
<|endoftext|>


<commit_before>package conn

import (
	"fmt"
	"strings"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"

	peer "github.com/jbenet/go-ipfs/p2p/peer"
	debugerror "github.com/jbenet/go-ipfs/util/debugerror"
)

// String returns the string rep of d.
func (d *Dialer) String() string {
	return fmt.Sprintf("<Dialer %s %s ...>", d.LocalPeer, d.LocalAddrs[0])
}

// Dial connects to a peer over a particular address
// Ensures raddr is part of peer.Addresses()
// Example: d.DialAddr(ctx, peer.Addresses()[0], peer)
func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (Conn, error) {

	network, _, err := manet.DialArgs(raddr)
	if err != nil {
		return nil, err
	}

	if strings.HasPrefix(raddr.String(), "/ip4/0.0.0.0") {
		return nil, debugerror.Errorf("Attempted to connect to zero address: %s", raddr)
	}

	var laddr ma.Multiaddr
	if len(d.LocalAddrs) > 0 {
		// laddr := MultiaddrNetMatch(raddr, d.LocalAddrs)
		laddr = NetAddress(network, d.LocalAddrs)
		if laddr == nil {
			return nil, debugerror.Errorf("No local address for network %s", network)
		}
	}

	// TODO: try to get reusing addr/ports to work.
	// madialer := manet.Dialer{LocalAddr: laddr}
	madialer := manet.Dialer{}

	log.Debugf("%s dialing %s %s", d.LocalPeer, remote, raddr)
	maconn, err := madialer.Dial(raddr)
	if err != nil {
		return nil, err
	}

	var connOut Conn
	var errOut error
	done := make(chan struct{})

	// do it async to ensure we respect don contexteone
	go func() {
		defer func() { done <- struct{}{} }()

		c, err := newSingleConn(ctx, d.LocalPeer, remote, maconn)
		if err != nil {
			errOut = err
			return
		}

		if d.PrivateKey == nil {
			log.Warning("dialer %s dialing INSECURELY %s at %s!", d, remote, raddr)
			connOut = c
			return
		}
		c2, err := newSecureConn(ctx, d.PrivateKey, c)
		if err != nil {
			errOut = err
			c.Close()
			return
		}

		connOut = c2
	}()

	select {
	case <-ctx.Done():
		maconn.Close()
		return nil, ctx.Err()
	case <-done:
		// whew, finished.
	}

	return connOut, errOut
}

// MultiaddrProtocolsMatch returns whether two multiaddrs match in protocol stacks.
func MultiaddrProtocolsMatch(a, b ma.Multiaddr) bool {
	ap := a.Protocols()
	bp := b.Protocols()

	if len(ap) != len(bp) {
		return false
	}

	for i, api := range ap {
		if api != bp[i] {
			return false
		}
	}

	return true
}

// MultiaddrNetMatch returns the first Multiaddr found to match  network.
func MultiaddrNetMatch(tgt ma.Multiaddr, srcs []ma.Multiaddr) ma.Multiaddr {
	for _, a := range srcs {
		if MultiaddrProtocolsMatch(tgt, a) {
			return a
		}
	}
	return nil
}

// NetAddress returns the first Multiaddr found for a given network.
func NetAddress(n string, addrs []ma.Multiaddr) ma.Multiaddr {
	for _, a := range addrs {
		for _, p := range a.Protocols() {
			if p.Name == n {
				return a
			}
		}
	}
	return nil
}
<commit_msg>updated multiaddr<commit_after>package conn

import (
	"fmt"
	"strings"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"

	peer "github.com/jbenet/go-ipfs/p2p/peer"
	debugerror "github.com/jbenet/go-ipfs/util/debugerror"
)

// String returns the string rep of d.
func (d *Dialer) String() string {
	return fmt.Sprintf("<Dialer %s %s ...>", d.LocalPeer, d.LocalAddrs[0])
}

// Dial connects to a peer over a particular address
// Ensures raddr is part of peer.Addresses()
// Example: d.DialAddr(ctx, peer.Addresses()[0], peer)
func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (Conn, error) {

	network, _, err := manet.DialArgs(raddr)
	if err != nil {
		return nil, err
	}

	if strings.HasPrefix(raddr.String(), "/ip4/0.0.0.0") {
		return nil, debugerror.Errorf("Attempted to connect to zero address: %s", raddr)
	}

	var laddr ma.Multiaddr
	if len(d.LocalAddrs) > 0 {
		// laddr := MultiaddrNetMatch(raddr, d.LocalAddrs)
		laddr = NetAddress(network, d.LocalAddrs)
		if laddr == nil {
			return nil, debugerror.Errorf("No local address for network %s", network)
		}
	}

	// TODO: try to get reusing addr/ports to work.
	// madialer := manet.Dialer{LocalAddr: laddr}
	madialer := manet.Dialer{}

	log.Debugf("%s dialing %s %s", d.LocalPeer, remote, raddr)
	maconn, err := madialer.Dial(raddr)
	if err != nil {
		return nil, err
	}

	var connOut Conn
	var errOut error
	done := make(chan struct{})

	// do it async to ensure we respect don contexteone
	go func() {
		defer func() { done <- struct{}{} }()

		c, err := newSingleConn(ctx, d.LocalPeer, remote, maconn)
		if err != nil {
			errOut = err
			return
		}

		if d.PrivateKey == nil {
			log.Warning("dialer %s dialing INSECURELY %s at %s!", d, remote, raddr)
			connOut = c
			return
		}
		c2, err := newSecureConn(ctx, d.PrivateKey, c)
		if err != nil {
			errOut = err
			c.Close()
			return
		}

		connOut = c2
	}()

	select {
	case <-ctx.Done():
		maconn.Close()
		return nil, ctx.Err()
	case <-done:
		// whew, finished.
	}

	return connOut, errOut
}

// MultiaddrProtocolsMatch returns whether two multiaddrs match in protocol stacks.
func MultiaddrProtocolsMatch(a, b ma.Multiaddr) bool {
	ap := a.Protocols()
	bp := b.Protocols()

	if len(ap) != len(bp) {
		return false
	}

	for i, api := range ap {
		if api.Code != bp[i].Code {
			return false
		}
	}

	return true
}

// MultiaddrNetMatch returns the first Multiaddr found to match  network.
func MultiaddrNetMatch(tgt ma.Multiaddr, srcs []ma.Multiaddr) ma.Multiaddr {
	for _, a := range srcs {
		if MultiaddrProtocolsMatch(tgt, a) {
			return a
		}
	}
	return nil
}

// NetAddress returns the first Multiaddr found for a given network.
func NetAddress(n string, addrs []ma.Multiaddr) ma.Multiaddr {
	for _, a := range addrs {
		for _, p := range a.Protocols() {
			if p.Name == n {
				return a
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2010 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 cmplx

import "math"

// The original C code, the long comment, and the constants
// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c.
// The go code is a simplified version of the original C.
//
// Cephes Math Library Release 2.8:  June, 2000
// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
//
// The readme file at http://netlib.sandia.gov/cephes/ says:
//    Some software in this archive may be from the book _Methods and
// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
// International, 1989) or from the Cephes Mathematical Library, a
// commercial product. In either event, it is copyrighted by the author.
// What you see here may be used freely but it comes with no support or
// guarantee.
//
//   The two known misprints in the book are repaired here in the
// source listings for the gamma function and the incomplete beta
// integral.
//
//   Stephen L. Moshier
//   moshier@na-net.ornl.gov

// Complex power function
//
// DESCRIPTION:
//
// Raises complex A to the complex Zth power.
// Definition is per AMS55 # 4.2.8,
// analytically equivalent to cpow(a,z) = cexp(z clog(a)).
//
// ACCURACY:
//
//                      Relative error:
// arithmetic   domain     # trials      peak         rms
//    IEEE      -10,+10     30000       9.4e-15     1.5e-15

// Pow returns x**y, the base-x exponential of y.
// For generalized compatiblity with math.Pow:
// Pow(0, ±0) returns 1+0i
// Pow(0, c) for real(c)<0 returns Inf+0i if imag(c) is zero, otherwise Inf+Inf i.
func Pow(x, y complex128) complex128 {
	if x == 0 { // Guaranteed also true for x == -0.
		r, i := real(y), imag(y)
		switch {
		case r == 0:
			return 1
		case r < 0:
			if i == 0 {
				return complex(math.Inf(1), 0)
			}
			return Inf()
		case r > 0:
			return 0
		}
		panic("not reached")
	}
	modulus := Abs(x)
	if modulus == 0 {
		return complex(0, 0)
	}
	r := math.Pow(modulus, real(y))
	arg := Phase(x)
	theta := real(y) * arg
	if imag(y) != 0 {
		r *= math.Exp(-imag(y) * arg)
		theta += imag(y) * math.Log(modulus)
	}
	s, c := math.Sincos(theta)
	return complex(r*c, r*s)
}
<commit_msg>math/cmpx: change space to tab in the Pow docs for better formatting Godoc makes it look better this way; before, it all ran together into nonsense.<commit_after>// Copyright 2010 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 cmplx

import "math"

// The original C code, the long comment, and the constants
// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c.
// The go code is a simplified version of the original C.
//
// Cephes Math Library Release 2.8:  June, 2000
// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
//
// The readme file at http://netlib.sandia.gov/cephes/ says:
//    Some software in this archive may be from the book _Methods and
// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
// International, 1989) or from the Cephes Mathematical Library, a
// commercial product. In either event, it is copyrighted by the author.
// What you see here may be used freely but it comes with no support or
// guarantee.
//
//   The two known misprints in the book are repaired here in the
// source listings for the gamma function and the incomplete beta
// integral.
//
//   Stephen L. Moshier
//   moshier@na-net.ornl.gov

// Complex power function
//
// DESCRIPTION:
//
// Raises complex A to the complex Zth power.
// Definition is per AMS55 # 4.2.8,
// analytically equivalent to cpow(a,z) = cexp(z clog(a)).
//
// ACCURACY:
//
//                      Relative error:
// arithmetic   domain     # trials      peak         rms
//    IEEE      -10,+10     30000       9.4e-15     1.5e-15

// Pow returns x**y, the base-x exponential of y.
// For generalized compatiblity with math.Pow:
//	Pow(0, ±0) returns 1+0i
//	Pow(0, c) for real(c)<0 returns Inf+0i if imag(c) is zero, otherwise Inf+Inf i.
func Pow(x, y complex128) complex128 {
	if x == 0 { // Guaranteed also true for x == -0.
		r, i := real(y), imag(y)
		switch {
		case r == 0:
			return 1
		case r < 0:
			if i == 0 {
				return complex(math.Inf(1), 0)
			}
			return Inf()
		case r > 0:
			return 0
		}
		panic("not reached")
	}
	modulus := Abs(x)
	if modulus == 0 {
		return complex(0, 0)
	}
	r := math.Pow(modulus, real(y))
	arg := Phase(x)
	theta := real(y) * arg
	if imag(y) != 0 {
		r *= math.Exp(-imag(y) * arg)
		theta += imag(y) * math.Log(modulus)
	}
	s, c := math.Sincos(theta)
	return complex(r*c, r*s)
}
<|endoftext|>


<commit_before>// Copyright 2016 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author:polaris	polaris@studygolang.com

package logic

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/polaris1119/config"
	"github.com/polaris1119/goutils"
	"github.com/polaris1119/logger"
	"github.com/polaris1119/times"
)

const (
	WsMsgNotify = iota // 通知消息
	WsMsgOnline        // 发送在线用户数（和需要时也发历史最高）
)

const MessageQueueLen = 2

type Message struct {
	Type int         `json:"type"`
	Body interface{} `json:"body"`
}

func NewMessage(msgType int, msgBody interface{}) *Message {
	return &Message{
		Type: msgType,
		Body: msgBody,
	}
}

type UserData struct {
	// 该用户收到的消息（key为serverId）
	serverMsgQueue map[int]chan *Message
	lastAccessTime time.Time
	onlineDuartion time.Duration

	rwMutex sync.RWMutex
}

func (this *UserData) Len() int {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	return len(this.serverMsgQueue)
}

func (this *UserData) MessageQueue(serverId int) <-chan *Message {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	return this.serverMsgQueue[serverId]
}

func (this *UserData) Remove(serverId int) {
	this.rwMutex.Lock()
	defer this.rwMutex.Unlock()
	delete(this.serverMsgQueue, serverId)
}

func (this *UserData) InitMessageQueue(serverId int) {
	this.rwMutex.Lock()
	defer this.rwMutex.Unlock()
	this.serverMsgQueue[serverId] = make(chan *Message, MessageQueueLen)
}

func (this *UserData) SendMessage(message *Message) {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()

	for serverId, messageQueue := range this.serverMsgQueue {
		// 有可能用户已经退出，导致 messageQueue 满，阻塞
		if len(messageQueue) < MessageQueueLen {
			messageQueue <- message
		} else {
			logger.Infoln("server_id:", serverId, "had close")

			delete(this.serverMsgQueue, serverId)
		}
	}
}

// 用于 expvar 统计信息
type LoginUser struct {
	Uid            int    `json:"uid"`
	LastAccessTime string `json:"last_access_time"`
	OnlineDuartion string `json:"online_duration"`
}
type LoginUserSlice []*LoginUser

func (self LoginUserSlice) String() string {
	b, err := json.Marshal(self)
	if err != nil {
		return "[]"
	}

	return string(b)
}

var Book = &book{users: make(map[int]*UserData), uids: make(map[int]struct{})}

type book struct {
	users map[int]*UserData
	// 登录用户
	uids    map[int]struct{}
	rwMutex sync.RWMutex
}

// 增加一个用户到book中（有可能是用户的另一个请求）
// user为UID或IP地址的int表示
func (this *book) AddUser(user, serverId int, isUid bool) *UserData {
	var userData *UserData
	var ok bool
	this.rwMutex.Lock()
	if userData, ok = this.users[user]; ok {
		this.rwMutex.Unlock()

		userData.InitMessageQueue(serverId)
		userData.onlineDuartion += time.Now().Sub(userData.lastAccessTime)
		userData.lastAccessTime = time.Now()
	} else {
		userData = &UserData{
			serverMsgQueue: map[int]chan *Message{serverId: make(chan *Message, MessageQueueLen)},
			lastAccessTime: time.Now(),
		}
		this.users[user] = userData
		if isUid {
			this.uids[user] = struct{}{}
		}
		length := len(this.users)

		this.rwMutex.Unlock()

		logger.Infoln("user:", user, "had enter")

		onlineInfo := map[string]int{"online": length}
		// 在线人数超过历史最高
		if length > MaxOnlineNum() {
			maxRwMu.Lock()
			maxOnlineNum = length
			onlineInfo["maxonline"] = maxOnlineNum
			maxRwMu.Unlock()
			saveMaxOnlineNum()
		}
		// 广播给其他人：有新用户进来，包括可能的新历史最高
		message := NewMessage(WsMsgOnline, onlineInfo)
		go this.BroadcastToOthersMessage(message, user)
	}

	return userData
}

// 删除用户
func (this *book) DelUser(user, serverId int, isUid bool) {
	this.rwMutex.Lock()
	defer this.rwMutex.Unlock()

	// 已经不存在了
	if _, ok := this.users[user]; !ok {
		if isUid {
			delete(this.uids, user)
		}
		return
	}

	// 自己只有一个页面建立websocket连接
	if this.users[user].Len() == 1 {
		delete(this.users, user)
		if isUid {
			delete(this.uids, user)
		}
	} else {
		this.users[user].Remove(serverId)
	}
}

// 判断用户是否还在线（user 有可能是IP）
func (this *book) UserIsOnline(user int) bool {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	if _, ok := this.users[user]; ok {
		return true
	}
	return false
}

// 判断注册用户是否还在线（user 有可能是IP）
func (this *book) RegUserIsOnline(uid int) bool {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	if _, ok := this.uids[uid]; ok {
		return true
	}
	return false
}

// 在线用户数
func (this *book) Len() int {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	return len(this.users)
}

// 在线注册会员数
func (this *book) LoginLen() int {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	return len(this.uids)
}

func (this *book) LoginUserData() LoginUserSlice {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()

	loginUserData := LoginUserSlice(make([]*LoginUser, 0, len(this.uids)))
	for uid := range this.uids {
		user := this.users[uid]
		loginUserData = append(loginUserData, &LoginUser{
			Uid:            uid,
			LastAccessTime: times.Format("Y-m-d H:i:s", user.lastAccessTime),
			OnlineDuartion: user.onlineDuartion.String(),
		})
	}

	return loginUserData
}

// 给某个用户发送一条消息
func (this *book) PostMessage(uid int, message *Message) {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	if userData, ok := this.users[uid]; ok {
		logger.Infoln("post message to", uid, message)
		go userData.SendMessage(message)
	}
}

// 给所有用户广播消息
func (this *book) BroadcastAllUsersMessage(message *Message) {
	logger.Infoln("BroadcastAllUsersMessage message", message)

	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	for uid, userData := range this.users {
		if userData.Len() == 0 {
			delete(this.users, uid)
			delete(this.uids, uid)
		}
		userData.SendMessage(message)
	}
}

// 给除了自己的其他用户广播消息
func (this *book) BroadcastToOthersMessage(message *Message, myself int) {
	logger.Infoln("BroadcastToOthersMessage message", message)

	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	for uid, userData := range this.users {
		if uid == myself {
			continue
		}

		if userData.Len() == 0 {
			delete(this.users, uid)
			delete(this.uids, uid)
		}
		userData.SendMessage(message)
	}
}

var (
	// 保存历史最大在线用户数
	maxOnlineNum int
	maxRwMu      sync.RWMutex
)

func initMaxOnlineNum() {
	maxRwMu.Lock()
	defer maxRwMu.Unlock()
	if maxOnlineNum == 0 {
		data, err := ioutil.ReadFile(getDataFile())
		if err != nil {
			logger.Errorln("read data file error:", err)
			return
		}
		maxOnlineNum = goutils.MustInt(strings.TrimSpace(string(data)))
	}
}

// 获得历史最高在线人数
func MaxOnlineNum() int {
	initMaxOnlineNum()
	maxRwMu.RLock()
	defer maxRwMu.RUnlock()
	return maxOnlineNum
}

func saveMaxOnlineNum() {
	data := []byte(strconv.Itoa(MaxOnlineNum()))
	err := ioutil.WriteFile(getDataFile(), data, 0777)
	if err != nil {
		logger.Errorln("write data file error:", err)
		return
	}
}

var dataFile string

func getDataFile() string {
	if dataFile != "" {
		return dataFile
	}
	dataFile = config.ConfigFile.MustValue("global", "data_path")
	if !filepath.IsAbs(dataFile) {
		dataFile = config.ROOT + "/" + dataFile
	}
	// 文件夹不存在，则创建
	dataPath := filepath.Dir(dataFile)
	if err := os.MkdirAll(dataPath, 0777); err != nil {
		logger.Errorln("MkdirAll error:", err)
	}
	return dataFile
}
<commit_msg>channel = 3<commit_after>// Copyright 2016 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author:polaris	polaris@studygolang.com

package logic

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/polaris1119/config"
	"github.com/polaris1119/goutils"
	"github.com/polaris1119/logger"
	"github.com/polaris1119/times"
)

const (
	WsMsgNotify = iota // 通知消息
	WsMsgOnline        // 发送在线用户数（和需要时也发历史最高）
)

const MessageQueueLen = 3

type Message struct {
	Type int         `json:"type"`
	Body interface{} `json:"body"`
}

func NewMessage(msgType int, msgBody interface{}) *Message {
	return &Message{
		Type: msgType,
		Body: msgBody,
	}
}

type UserData struct {
	// 该用户收到的消息（key为serverId）
	serverMsgQueue map[int]chan *Message
	lastAccessTime time.Time
	onlineDuartion time.Duration

	rwMutex sync.RWMutex
}

func (this *UserData) Len() int {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	return len(this.serverMsgQueue)
}

func (this *UserData) MessageQueue(serverId int) <-chan *Message {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	return this.serverMsgQueue[serverId]
}

func (this *UserData) Remove(serverId int) {
	this.rwMutex.Lock()
	defer this.rwMutex.Unlock()
	delete(this.serverMsgQueue, serverId)
}

func (this *UserData) InitMessageQueue(serverId int) {
	this.rwMutex.Lock()
	defer this.rwMutex.Unlock()
	this.serverMsgQueue[serverId] = make(chan *Message, MessageQueueLen)
}

func (this *UserData) SendMessage(message *Message) {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()

	for serverId, messageQueue := range this.serverMsgQueue {
		// 有可能用户已经退出，导致 messageQueue 满，阻塞
		if len(messageQueue) < MessageQueueLen {
			messageQueue <- message
		} else {
			logger.Infoln("server_id:", serverId, "had close")

			delete(this.serverMsgQueue, serverId)
		}
	}
}

// 用于 expvar 统计信息
type LoginUser struct {
	Uid            int    `json:"uid"`
	LastAccessTime string `json:"last_access_time"`
	OnlineDuartion string `json:"online_duration"`
}
type LoginUserSlice []*LoginUser

func (self LoginUserSlice) String() string {
	b, err := json.Marshal(self)
	if err != nil {
		return "[]"
	}

	return string(b)
}

var Book = &book{users: make(map[int]*UserData), uids: make(map[int]struct{})}

type book struct {
	users map[int]*UserData
	// 登录用户
	uids    map[int]struct{}
	rwMutex sync.RWMutex
}

// 增加一个用户到book中（有可能是用户的另一个请求）
// user为UID或IP地址的int表示
func (this *book) AddUser(user, serverId int, isUid bool) *UserData {
	var userData *UserData
	var ok bool
	this.rwMutex.Lock()
	if userData, ok = this.users[user]; ok {
		this.rwMutex.Unlock()

		userData.InitMessageQueue(serverId)
		userData.onlineDuartion += time.Now().Sub(userData.lastAccessTime)
		userData.lastAccessTime = time.Now()
	} else {
		userData = &UserData{
			serverMsgQueue: map[int]chan *Message{serverId: make(chan *Message, MessageQueueLen)},
			lastAccessTime: time.Now(),
		}
		this.users[user] = userData
		if isUid {
			this.uids[user] = struct{}{}
		}
		length := len(this.users)

		this.rwMutex.Unlock()

		logger.Infoln("user:", user, "had enter")

		onlineInfo := map[string]int{"online": length}
		// 在线人数超过历史最高
		if length > MaxOnlineNum() {
			maxRwMu.Lock()
			maxOnlineNum = length
			onlineInfo["maxonline"] = maxOnlineNum
			maxRwMu.Unlock()
			saveMaxOnlineNum()
		}
		// 广播给其他人：有新用户进来，包括可能的新历史最高
		message := NewMessage(WsMsgOnline, onlineInfo)
		go this.BroadcastToOthersMessage(message, user)
	}

	return userData
}

// 删除用户
func (this *book) DelUser(user, serverId int, isUid bool) {
	this.rwMutex.Lock()
	defer this.rwMutex.Unlock()

	// 已经不存在了
	if _, ok := this.users[user]; !ok {
		if isUid {
			delete(this.uids, user)
		}
		return
	}

	// 自己只有一个页面建立websocket连接
	if this.users[user].Len() == 1 {
		delete(this.users, user)
		if isUid {
			delete(this.uids, user)
		}
	} else {
		this.users[user].Remove(serverId)
	}
}

// 判断用户是否还在线（user 有可能是IP）
func (this *book) UserIsOnline(user int) bool {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	if _, ok := this.users[user]; ok {
		return true
	}
	return false
}

// 判断注册用户是否还在线（user 有可能是IP）
func (this *book) RegUserIsOnline(uid int) bool {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	if _, ok := this.uids[uid]; ok {
		return true
	}
	return false
}

// 在线用户数
func (this *book) Len() int {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	return len(this.users)
}

// 在线注册会员数
func (this *book) LoginLen() int {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	return len(this.uids)
}

func (this *book) LoginUserData() LoginUserSlice {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()

	loginUserData := LoginUserSlice(make([]*LoginUser, 0, len(this.uids)))
	for uid := range this.uids {
		user := this.users[uid]
		loginUserData = append(loginUserData, &LoginUser{
			Uid:            uid,
			LastAccessTime: times.Format("Y-m-d H:i:s", user.lastAccessTime),
			OnlineDuartion: user.onlineDuartion.String(),
		})
	}

	return loginUserData
}

// 给某个用户发送一条消息
func (this *book) PostMessage(uid int, message *Message) {
	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	if userData, ok := this.users[uid]; ok {
		logger.Infoln("post message to", uid, message)
		go userData.SendMessage(message)
	}
}

// 给所有用户广播消息
func (this *book) BroadcastAllUsersMessage(message *Message) {
	logger.Infoln("BroadcastAllUsersMessage message", message)

	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	for uid, userData := range this.users {
		if userData.Len() == 0 {
			delete(this.users, uid)
			delete(this.uids, uid)
		}
		userData.SendMessage(message)
	}
}

// 给除了自己的其他用户广播消息
func (this *book) BroadcastToOthersMessage(message *Message, myself int) {
	logger.Infoln("BroadcastToOthersMessage message", message)

	this.rwMutex.RLock()
	defer this.rwMutex.RUnlock()
	for uid, userData := range this.users {
		if uid == myself {
			continue
		}

		if userData.Len() == 0 {
			delete(this.users, uid)
			delete(this.uids, uid)
		}
		userData.SendMessage(message)
	}
}

var (
	// 保存历史最大在线用户数
	maxOnlineNum int
	maxRwMu      sync.RWMutex
)

func initMaxOnlineNum() {
	maxRwMu.Lock()
	defer maxRwMu.Unlock()
	if maxOnlineNum == 0 {
		data, err := ioutil.ReadFile(getDataFile())
		if err != nil {
			logger.Errorln("read data file error:", err)
			return
		}
		maxOnlineNum = goutils.MustInt(strings.TrimSpace(string(data)))
	}
}

// 获得历史最高在线人数
func MaxOnlineNum() int {
	initMaxOnlineNum()
	maxRwMu.RLock()
	defer maxRwMu.RUnlock()
	return maxOnlineNum
}

func saveMaxOnlineNum() {
	data := []byte(strconv.Itoa(MaxOnlineNum()))
	err := ioutil.WriteFile(getDataFile(), data, 0777)
	if err != nil {
		logger.Errorln("write data file error:", err)
		return
	}
}

var dataFile string

func getDataFile() string {
	if dataFile != "" {
		return dataFile
	}
	dataFile = config.ConfigFile.MustValue("global", "data_path")
	if !filepath.IsAbs(dataFile) {
		dataFile = config.ROOT + "/" + dataFile
	}
	// 文件夹不存在，则创建
	dataPath := filepath.Dir(dataFile)
	if err := os.MkdirAll(dataPath, 0777); err != nil {
		logger.Errorln("MkdirAll error:", err)
	}
	return dataFile
}
<|endoftext|>


<commit_before>package runtime

import (
	"github.com/ta2gch/iris/runtime/ilos"
	"github.com/ta2gch/iris/runtime/ilos/class"
	"github.com/ta2gch/iris/runtime/ilos/instance"
)

func UnsafeEndOfListIsNil(i ilos.Instance) bool {
	cdr := i
	for ilos.InstanceOf(cdr, class.Cons) {
		cdr = instance.UnsafeCdr(cdr) // Checked at the top of this loop
	}
	if ilos.InstanceOf(cdr, class.Null) {
		return true
	}
	return false
}

func UnsafeListLength(i ilos.Instance) int {
	cdr := i
	cnt := 0
	for ilos.InstanceOf(cdr, class.Cons) {
		cdr = instance.UnsafeCdr(cdr) // Checked at the top of this loop
		cnt++
	}
	return cnt
}
<commit_msg>Added some comments<commit_after>package runtime

import (
	"github.com/ta2gch/iris/runtime/ilos"
	"github.com/ta2gch/iris/runtime/ilos/class"
	"github.com/ta2gch/iris/runtime/ilos/instance"
)

// UnsafeEndOfListIsNil test a given instance ends with nil
// but doesn't work correctly if the given instance isn't a instance of list
// So you have to check the instance.
func UnsafeEndOfListIsNil(i ilos.Instance) bool {
	cdr := i
	for ilos.InstanceOf(cdr, class.Cons) {
		cdr = instance.UnsafeCdr(cdr) // Checked at the top of this loop
	}
	if ilos.InstanceOf(cdr, class.Null) {
		return true
	}
	return false
}

// UnsafeListLength return a length of list
// but doesn't work correctly if the given instance aren't a instance of list.
// So you have to check the instance.
func UnsafeListLength(i ilos.Instance) int {
	cdr := i
	cnt := 0
	for ilos.InstanceOf(cdr, class.Cons) {
		cdr = instance.UnsafeCdr(cdr) // Checked at the top of this loop
		cnt++
	}
	return cnt
}
<|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.

/*

Gofmt formats Go programs.

Without an explicit path, it processes the standard input.  Given a file,
it operates on that file; given a directory, it operates on all .go files in
that directory, recursively.  (Files starting with a period are ignored.)

Usage:
	gofmt [flags] [path ...]

The flags are:

	-l
		just list files whose formatting differs from gofmt's; generate no other output
		unless -w is also set.
	-r rule
		apply the rewrite rule to the source before reformatting.
	-w
		if set, overwrite each input file with its output.
	-spaces
		align with spaces instead of tabs.
	-tabwidth=8
		tab width in spaces.

Debugging flags:

	-trace
		print parse trace.
	-comments=true
		print comments; if false, all comments are elided from the output.

The rewrite rule specified with the -r flag must be a string of the form:

	pattern -> replacement

Both pattern and replacement must be valid Go expressions.
In the pattern, single-character lowercase identifers serve as
wildcards matching arbitrary subexpressions; those expressions
will be substituted for the same identifiers in the replacement.

Examples

To check files for unnecessary parentheses:

	gofmt -r '(a) -> a' -l *.go

To remove the parentheses:

	gofmt -r '(a) -> a' -w *.go

To convert the package tree from explicit slice upper bounds to implicit ones:

	gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src/pkg
*/
package documentation

// BUG(rsc): The implementation of -r is a bit slow.
<commit_msg>updated documentation for gofmt<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.

/*

Gofmt formats Go programs.

Without an explicit path, it processes the standard input.  Given a file,
it operates on that file; given a directory, it operates on all .go files in
that directory, recursively.  (Files starting with a period are ignored.)

Usage:
	gofmt [flags] [path ...]

The flags are:

	-l
		just list files whose formatting differs from gofmt's; generate no other output
		unless -w is also set.
	-r rule
		apply the rewrite rule to the source before reformatting.
	-w
		if set, overwrite each input file with its output.
	-spaces
		align with spaces instead of tabs.
	-tabindent
		indent with tabs independent of -spaces.
	-tabwidth=8
		tab width in spaces.

Debugging flags:

	-trace
		print parse trace.
	-comments=true
		print comments; if false, all comments are elided from the output.

The rewrite rule specified with the -r flag must be a string of the form:

	pattern -> replacement

Both pattern and replacement must be valid Go expressions.
In the pattern, single-character lowercase identifers serve as
wildcards matching arbitrary subexpressions; those expressions
will be substituted for the same identifiers in the replacement.

Examples

To check files for unnecessary parentheses:

	gofmt -r '(a) -> a' -l *.go

To remove the parentheses:

	gofmt -r '(a) -> a' -w *.go

To convert the package tree from explicit slice upper bounds to implicit ones:

	gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src/pkg
*/
package documentation

// BUG(rsc): The implementation of -r is a bit slow.
<|endoftext|>


<commit_before>// Copyright 2015 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 (
	"cmd/internal/obj"
	"cmd/link/internal/amd64"
	"cmd/link/internal/arm"
	"cmd/link/internal/arm64"
	"cmd/link/internal/mips64"
	"cmd/link/internal/ppc64"
	"cmd/link/internal/x86"
	"fmt"
	"os"
)

func main() {
	switch obj.Getgoarch() {
	default:
		fmt.Fprintf(os.Stderr, "link: unknown architecture %q\n", obj.Getgoarch())
		os.Exit(2)
	case "386":
		x86.Main()
	case "amd64", "amd64p32":
		amd64.Main()
	case "arm":
		arm.Main()
	case "arm64":
		arm64.Main()
	case "mips64", "mips64le":
		mips64.Main()
	case "ppc64", "ppc64le":
		ppc64.Main()
	}
}
<commit_msg>cmd/link: add s390x to link tool main function<commit_after>// Copyright 2015 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 (
	"cmd/internal/obj"
	"cmd/link/internal/amd64"
	"cmd/link/internal/arm"
	"cmd/link/internal/arm64"
	"cmd/link/internal/mips64"
	"cmd/link/internal/ppc64"
	"cmd/link/internal/s390x"
	"cmd/link/internal/x86"
	"fmt"
	"os"
)

func main() {
	switch obj.Getgoarch() {
	default:
		fmt.Fprintf(os.Stderr, "link: unknown architecture %q\n", obj.Getgoarch())
		os.Exit(2)
	case "386":
		x86.Main()
	case "amd64", "amd64p32":
		amd64.Main()
	case "arm":
		arm.Main()
	case "arm64":
		arm64.Main()
	case "mips64", "mips64le":
		mips64.Main()
	case "ppc64", "ppc64le":
		ppc64.Main()
	case "s390x":
		s390x.Main()
	}
}
<|endoftext|>


<commit_before>// The u-root shell is intended to be very simple, since builtins and extensions
// are written in Go. It should not need YACC. As in the JSON parser, we hope this
// simple state machine will do the job.
package main

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os/exec"
	"path"
	"strings"
)

type arg struct {
	val string
	mod string
}

// The Command struct is initially filled in by the parser. The shell itself
// adds to it as processing continues, and then uses it to creates os.Commands
type Command struct {
	*exec.Cmd
	// These are filled in by the parser.
	args  []arg
	fdmap map[int]string
	link  string
	bg    bool

	// These are set up by the shell as it evaluates the Commands
	// provided by the parser.
	// we separate the command so people don't have to put checks for the length
	// of argv in their builtins. We do that for them.
	cmd  string
	argv []string
}

var (
	cmds  []Command
	punct = "<>|&$ \t\n"
)

func pushback(b *bufio.Reader) {
	err := b.UnreadByte()
	if err != nil {
		panic(errors.New(fmt.Sprintf("unreading bufio: %v", err)))
	}
}

func one(b *bufio.Reader) byte {
	//fmt.Printf("next\n")
	c, err := b.ReadByte()
	//fmt.Printf("'%v' %v\n", c, err)
	if err == io.EOF {
		return 0
	}
	if err != nil {
		panic(errors.New(fmt.Sprintf("reading bufio: %v", err)))
	}
	return c
}

func next(b *bufio.Reader) byte {
	c := one(b)
	if c == '\\' {
		return one(b)
	}
	return byte(c)
}

// Tokenize stuff coming in from the stream. For everything but an arg, the
// type is just the thing itself, since we can switch on strings.
func tok(b *bufio.Reader) (string, string) {
	tokType, arg := "white", ""
	c := next(b)

	//fmt.Printf("TOK %v", c)
	switch c {
	case 0:
		return "EOF", ""
	case '>':
		return "FD", "1"
	case '<':
		return "FD", "0"
	// yes, I realize $ handling is still pretty hokey.
	case '$':
		arg = ""
		c = next(b)
		for {
			if strings.Index(punct, string(c)) > -1 {
				pushback(b)
				break
			}
			arg = arg + string(c)
			c = next(b)
		}
		return "ENV", arg
	case '\'':
		for {
			nc := next(b)
			if nc == '\'' {
				return "ARG", arg
			}
			arg = arg + string(nc)
		}
	case ' ', '\t':
		return "white", string(c)
	case '\n':
		//fmt.Printf("NEWLINE\n")
		return "EOL", ""
	case '|', '&':
		//fmt.Printf("LINK %v\n", c)
		// peek ahead. We need the literal, so don't use next()
		nc := one(b)
		if nc == c {
			//fmt.Printf("LINK %v\n", string(c)+string(c))
			return "LINK", string(c) + string(c)
		}
		pushback(b)
		if c == '&' {
			//fmt.Printf("BG\n")
			tokType = "BG"
			if nc == 0 {
				tokType = "EOL"
			}
			return "BG", tokType
		}
		//fmt.Printf("LINK %v\n", string(c))
		return "LINK", string(c)
	default:
		for {
			if strings.Index(punct, string(c)) > -1 {
				pushback(b)
				return "ARG", arg
			}
			arg = arg + string(c)
			c = next(b)
		}

	}
	return tokType, arg

}

// get an ARG. It has to work.
func getArg(b *bufio.Reader, what string) string {
	for {
		nt, s := tok(b)
		if nt == "EOF" || nt == "EOL" {
			panic(errors.New(fmt.Sprintf("%v requires an argument", what)))
		}
		if nt == "white" {
			continue
		}
		if nt != "ARG" {
			panic(errors.New(fmt.Sprintf("%v requires an argument, not %v", what, nt)))
		}
		return s
	}
}
func parsestring(b *bufio.Reader, c *Command) (*Command, string) {
	t, s := tok(b)
	if s == "\n" || t == "EOF" || t == "EOL" {
		return nil, t
	}
	for {
		switch t {
		case "ENV":
			if !path.IsAbs(s) {
				s = path.Join(envDir, s)
			}
			b, err := ioutil.ReadFile(s)
			if err != nil {
				panic(errors.New(fmt.Sprintf("%s: %v", s, err)))
			}
			f := bufio.NewReader(bytes.NewReader(b))
			// the whole string is consumed.
			parsestring(f, c)
		case "ARG":
			c.args = append(c.args, arg{s, t})
		case "white":
		case "FD":
			x := 0
			_, err := fmt.Sscanf(s, "%v", &x)
			if err != nil {
				panic(errors.New(fmt.Sprintf("bad FD on redirect: %v, %v", s, err)))
			}
			// whitespace is allowed
			c.fdmap[x] = getArg(b, t)
		// LINK and BG are similar save that LINK requires another command. If we don't get one, well.
		case "LINK":
			c.link = s
			//fmt.Printf("LINK %v %v\n", c, s)
			return c, t
		case "BG":
			c.bg = true
			return c, t
		case "EOF":
			return c, t
		case "EOL":
			return c, t
		default:
			panic(errors.New(fmt.Sprintf("unknown token type %v", t)))
		}
		t, s = tok(b)
	}
	return c, t
}
func parse(b *bufio.Reader) (*Command, string) {
	//fmt.Printf("%v %v\n", t, s)
	c := newCommand()
	return parsestring(b, c)
}

func newCommand() *Command {
	return &Command{fdmap: make(map[int]string)}
}

// Just eat it up until you have all the commands you need.
func parsecommands(b *bufio.Reader) ([]*Command, string) {
	cmds := make([]*Command, 0)
	for {
		c, t := parse(b)
		if c == nil {
			return cmds, t
		}
		//fmt.Printf("cmd  %v\n", *c)
		cmds = append(cmds, c)
		if t == "EOF" || t == "EOL" {
			return cmds, t
		}
	}
}

func getCommand(b *bufio.Reader) (c []*Command, t string, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = e.(error)
		}
	}()

	// TODO: put a recover here that just returns an error.
	c, t = parsecommands(b)
	// the rules.
	// For now, no empty commands.
	// Can't have a redir and a redirect for fd1.
	for i := range c {
		if len(c[i].args) == 0 {
			return nil, "", errors.New("empty commands not allowed (yet)\n")
		}
		if c[i].link == "|" && c[i].fdmap[1] != "" {
			return nil, "", errors.New("Can't have a pipe and > on one command\n")
		}
		if c[i].link == "|" && i == len(c)-1 {
			return nil, "", errors.New("Can't have a pipe to nowhere\n")
		}
		if i < len(c)-1 && c[i].link == "|" && c[i+1].fdmap[0] != "" {
			return nil, "", errors.New("Can't have a pipe to command with redirect on stdin\n")
		}
	}
	return c, t, err
}

/*
func main() {
	b := bufio.NewReader(os.Stdin)
	for {
	    c, t, err := getCommand(b)
		fmt.Printf("%v %v %v\n", c, t, err)
	    if t == "EOF" {
	       break
	       }
	       }
}
*/
<commit_msg>Fix parsing of files that have no newline at the end<commit_after>// The u-root shell is intended to be very simple, since builtins and extensions
// are written in Go. It should not need YACC. As in the JSON parser, we hope this
// simple state machine will do the job.
package main

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os/exec"
	"path"
	"strings"
)

type arg struct {
	val string
	mod string
}

// The Command struct is initially filled in by the parser. The shell itself
// adds to it as processing continues, and then uses it to creates os.Commands
type Command struct {
	*exec.Cmd
	// These are filled in by the parser.
	args  []arg
	fdmap map[int]string
	link  string
	bg    bool

	// These are set up by the shell as it evaluates the Commands
	// provided by the parser.
	// we separate the command so people don't have to put checks for the length
	// of argv in their builtins. We do that for them.
	cmd  string
	argv []string
}

var (
	cmds  []Command
	punct = "<>|&$ \t\n"
)

func pushback(b *bufio.Reader) {
	err := b.UnreadByte()
	if err != nil {
		panic(errors.New(fmt.Sprintf("unreading bufio: %v", err)))
	}
}

func one(b *bufio.Reader) byte {
	c, err := b.ReadByte()
	//fmt.Printf("one '%v' %v\n", c, err)
	if err == io.EOF {
		return 0
	}
	if err != nil {
		panic(errors.New(fmt.Sprintf("reading bufio: %v", err)))
	}
	return c
}

func next(b *bufio.Reader) byte {
	c := one(b)
	if c == '\\' {
		return one(b)
	}
	return byte(c)
}

// Tokenize stuff coming in from the stream. For everything but an arg, the
// type is just the thing itself, since we can switch on strings.
func tok(b *bufio.Reader) (string, string) {
	tokType, arg := "white", ""
	c := next(b)

	//fmt.Printf("TOK %v", c)
	switch c {
	case 0:
		return "EOF", ""
	case '>':
		return "FD", "1"
	case '<':
		return "FD", "0"
	// yes, I realize $ handling is still pretty hokey.
	case '$':
		arg = ""
		c = next(b)
		for {
			if c == 0 {
				break
			}
			if strings.Index(punct, string(c)) > -1 {
				pushback(b)
				break
			}
			arg = arg + string(c)
			c = next(b)
		}
		return "ENV", arg
	case '\'':
		for {
			nc := next(b)
			if nc == '\'' {
				return "ARG", arg
			}
			arg = arg + string(nc)
		}
	case ' ', '\t':
		return "white", string(c)
	case '\n':
		//fmt.Printf("NEWLINE\n")
		return "EOL", ""
	case '|', '&':
		//fmt.Printf("LINK %v\n", c)
		// peek ahead. We need the literal, so don't use next()
		nc := one(b)
		if nc == c {
			//fmt.Printf("LINK %v\n", string(c)+string(c))
			return "LINK", string(c) + string(c)
		}
		pushback(b)
		if c == '&' {
			//fmt.Printf("BG\n")
			tokType = "BG"
			if nc == 0 {
				tokType = "EOL"
			}
			return "BG", tokType
		}
		//fmt.Printf("LINK %v\n", string(c))
		return "LINK", string(c)
	default:
		for {
			if c == 0 {
				return "ARG", arg
			}
			if strings.Index(punct, string(c)) > -1 {
				pushback(b)
				return "ARG", arg
			}
			arg = arg + string(c)
			c = next(b)
		}

	}
	return tokType, arg

}

// get an ARG. It has to work.
func getArg(b *bufio.Reader, what string) string {
	for {
		nt, s := tok(b)
		if nt == "EOF" || nt == "EOL" {
			panic(errors.New(fmt.Sprintf("%v requires an argument", what)))
		}
		if nt == "white" {
			continue
		}
		if nt != "ARG" {
			panic(errors.New(fmt.Sprintf("%v requires an argument, not %v", what, nt)))
		}
		return s
	}
}
func parsestring(b *bufio.Reader, c *Command) (*Command, string) {
	t, s := tok(b)
	if s == "\n" || t == "EOF" || t == "EOL" {
		return nil, t
	}
	for {
		switch t {
		case "ENV":
			if !path.IsAbs(s) {
				s = path.Join(envDir, s)
			}
			b, err := ioutil.ReadFile(s)
			if err != nil {
				panic(errors.New(fmt.Sprintf("%s: %v", s, err)))
			}
			f := bufio.NewReader(bytes.NewReader(b))
			// the whole string is consumed.
			parsestring(f, c)
		case "ARG":
			c.args = append(c.args, arg{s, t})
		case "white":
		case "FD":
			x := 0
			_, err := fmt.Sscanf(s, "%v", &x)
			if err != nil {
				panic(errors.New(fmt.Sprintf("bad FD on redirect: %v, %v", s, err)))
			}
			// whitespace is allowed
			c.fdmap[x] = getArg(b, t)
		// LINK and BG are similar save that LINK requires another command. If we don't get one, well.
		case "LINK":
			c.link = s
			//fmt.Printf("LINK %v %v\n", c, s)
			return c, t
		case "BG":
			c.bg = true
			return c, t
		case "EOF":
			return c, t
		case "EOL":
			return c, t
		default:
			panic(errors.New(fmt.Sprintf("unknown token type %v", t)))
		}
		t, s = tok(b)
	}
	return c, t
}
func parse(b *bufio.Reader) (*Command, string) {
	//fmt.Printf("%v %v\n", t, s)
	c := newCommand()
	return parsestring(b, c)
}

func newCommand() *Command {
	return &Command{fdmap: make(map[int]string)}
}

// Just eat it up until you have all the commands you need.
func parsecommands(b *bufio.Reader) ([]*Command, string) {
	cmds := make([]*Command, 0)
	for {
		c, t := parse(b)
		if c == nil {
			return cmds, t
		}
		//fmt.Printf("cmd  %v\n", *c)
		cmds = append(cmds, c)
		if t == "EOF" || t == "EOL" {
			return cmds, t
		}
	}
}

func getCommand(b *bufio.Reader) (c []*Command, t string, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = e.(error)
		}
	}()

	// TODO: put a recover here that just returns an error.
	c, t = parsecommands(b)
	// the rules.
	// For now, no empty commands.
	// Can't have a redir and a redirect for fd1.
	for i := range c {
		if len(c[i].args) == 0 {
			return nil, "", errors.New("empty commands not allowed (yet)\n")
		}
		if c[i].link == "|" && c[i].fdmap[1] != "" {
			return nil, "", errors.New("Can't have a pipe and > on one command\n")
		}
		if c[i].link == "|" && i == len(c)-1 {
			return nil, "", errors.New("Can't have a pipe to nowhere\n")
		}
		if i < len(c)-1 && c[i].link == "|" && c[i+1].fdmap[0] != "" {
			return nil, "", errors.New("Can't have a pipe to command with redirect on stdin\n")
		}
	}
	return c, t, err
}

/*
func main() {
	b := bufio.NewReader(os.Stdin)
	for {
	    c, t, err := getCommand(b)
		fmt.Printf("%v %v %v\n", c, t, err)
	    if t == "EOF" {
	       break
	       }
	       }
}
*/
<|endoftext|>


<commit_before>package s3

import (
	"bytes"
	"io"
	"io/ioutil"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/graymeta/stow"
	"github.com/pkg/errors"
)

// Amazon S3 bucket contains a creation date and a name.
type container struct {
	// name is needed to retrieve items.
	name string
	// client is responsible for performing the requests.
	client *s3.S3
	// region describes the AWS Availability Zone of the S3 Bucket.
	region         string
	customEndpoint string
}

// ID returns a string value which represents the name of the container.
func (c *container) ID() string {
	return c.name
}

// Name returns a string value which represents the name of the container.
func (c *container) Name() string {
	return c.name
}

// Item returns a stow.Item instance of a container based on the
// name of the container and the key representing
func (c *container) Item(id string) (stow.Item, error) {
	return c.getItem(id)
}

// Items sends a request to retrieve a list of items that are prepended with
// the prefix argument. The 'cursor' variable facilitates pagination.
func (c *container) Items(prefix, startAfter string, count int) ([]stow.Item, string, error) {
	itemLimit := int64(count)

	params := &s3.ListObjectsV2Input{
		Bucket:     aws.String(c.Name()),
		StartAfter: &startAfter,
		MaxKeys:    &itemLimit,
		Prefix:     &prefix,
	}

	response, err := c.client.ListObjectsV2(params)
	if err != nil {
		return nil, "", errors.Wrap(err, "Items, listing objects")
	}

	containerItems := make([]stow.Item, len(response.Contents)) // Allocate space for the Item slice.

	for i, object := range response.Contents {
		etag := cleanEtag(*object.ETag) // Copy etag value and remove the strings.
		object.ETag = &etag             // Assign the value to the object field representing the item.

		containerItems[i] = &item{
			container: c,
			client:    c.client,
			properties: properties{
				ETag:         object.ETag,
				Key:          object.Key,
				LastModified: object.LastModified,
				Owner:        object.Owner,
				Size:         object.Size,
				StorageClass: object.StorageClass,
			},
		}
	}

	// Create a marker and determine if the list of items to retrieve is complete.
	// If not, the last file is the input to the value of after which item to start
	startAfter := ""
	if *response.IsTruncated {
		startAfter = containerItems[len(containerItems)-1].Name()
	}

	return containerItems, startAfter, nil
}

func (c *container) RemoveItem(id string) error {
	params := &s3.DeleteObjectInput{
		Bucket: aws.String(c.Name()),
		Key:    aws.String(id),
	}

	_, err := c.client.DeleteObject(params)
	if err != nil {
		return errors.Wrapf(err, "RemoveItem, deleting object %+v", params)
	}
	return nil
}

// Put sends a request to upload content to the container. The arguments
// received are the name of the item (S3 Object), a reader representing the
// content, and the size of the file. Many more attributes can be given to the
// file, including metadata. Keeping it simple for now.
func (c *container) Put(name string, r io.Reader, size int64, metadata map[string]interface{}) (stow.Item, error) {
	content, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, errors.Wrap(err, "unable to create or update item, reading content")
	}

	// Convert map[string]interface{} to map[string]*string
	mdPrepped, err := prepMetadata(metadata)
	if err != nil {
		return nil, errors.Wrap(err, "unable to create or update item, preparing metadata")
	}

	params := &s3.PutObjectInput{
		Bucket:        aws.String(c.name), // Required
		Key:           aws.String(name),   // Required
		ContentLength: aws.Int64(size),
		Body:          bytes.NewReader(content),
		Metadata:      mdPrepped, // map[string]*string
	}

	// Only Etag is returned.
	response, err := c.client.PutObject(params)
	if err != nil {
		return nil, errors.Wrap(err, "RemoveItem, deleting object")
	}
	etag := cleanEtag(*response.ETag)

	// Some fields are empty because this information isn't included in the response.
	// May have to involve sending a request if we want more specific information.
	// Keeping it simple for now.
	// s3.Object info: https://github.com/aws/aws-sdk-go/blob/master/service/s3/api.go#L7092-L7107
	// Response: https://github.com/aws/aws-sdk-go/blob/master/service/s3/api.go#L8193-L8227
	newItem := &item{
		container: c,
		client:    c.client,
		properties: properties{
			ETag: &etag,
			Key:  &name,
			Size: &size,
			//LastModified *time.Time
			//Owner        *s3.Owner
			//StorageClass *string
		},
	}

	return newItem, nil
}

// Region returns a string representing the region/availability zone of the container.
func (c *container) Region() string {
	return c.region
}

// A request to retrieve a single item includes information that is more specific than
// a PUT. Instead of doing a request within the PUT, make this method available so that the
// request can be made by the field retrieval methods when necessary. This is the case for
// fields that are left out, such as the object's last modified date. This also needs to be
// done only once since the requested information is retained.
// May be simpler to just stick it in PUT and and do a request every time, please vouch
// for this if so.
func (c *container) getItem(id string) (*item, error) {
	params := &s3.GetObjectInput{
		Bucket: aws.String(c.name),
		Key:    aws.String(id),
	}

	res, err := c.client.GetObject(params)
	if err != nil {
		// stow needs ErrNotFound to pass the test but amazon returns an opaque error
		if strings.Contains(err.Error(), "NoSuchKey") {
			return nil, stow.ErrNotFound
		}
		return nil, errors.Wrap(err, "getItem, getting the object")
	}
	defer res.Body.Close()

	etag := cleanEtag(*res.ETag) // etag string value contains quotations. Remove them.
	md, err := parseMetadata(res.Metadata)
	if err != nil {
		return nil, errors.Wrap(err, "unable to retrieve Item information, parsing metadata")
	}

	i := &item{
		container: c,
		client:    c.client,
		properties: properties{
			ETag:         &etag,
			Key:          &id,
			LastModified: res.LastModified,
			Owner:        nil, // not returned in the response.
			Size:         res.ContentLength,
			StorageClass: res.StorageClass,
			Metadata:     md,
		},
	}

	return i, nil
}

// Remove quotation marks from beginning and end. This includes quotations that
// are escaped. Also removes leading `W/` from prefix for weak Etags.
//
// Based on the Etag spec, the full etag value (<FULL ETAG VALUE>) can include:
// - W/"<ETAG VALUE>"
// - "<ETAG VALUE>"
// - ""
// Source: https://tools.ietf.org/html/rfc7232#section-2.3
//
// Based on HTTP spec, forward slash is a separator and must be enclosed in
// quotes to be used as a valid value. Hence, the returned value may include:
// - "<FULL ETAG VALUE>"
// - \"<FULL ETAG VALUE>\"
// Source: https://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
//
// This function contains a loop to check for the presence of the three possible
// filler characters and strips them, resulting in only the Etag value.
func cleanEtag(etag string) string {
	for {
		// Check if the filler characters are present
		if strings.HasPrefix(etag, `\"`) {
			etag = strings.Trim(etag, `\"`)

		} else if strings.HasPrefix(etag, `"`) {
			etag = strings.Trim(etag, `"`)

		} else if strings.HasPrefix(etag, `W/`) {
			etag = strings.Replace(etag, `W/`, "", 1)

		} else {
			break
		}
	}
	return etag
}

// prepMetadata parses a raw map into the native type required by S3 to set metadata (map[string]*string).
// TODO: validation for key values. This function also assumes that the value of a key value pair is a string.
func prepMetadata(md map[string]interface{}) (map[string]*string, error) {
	m := make(map[string]*string, len(md))
	for key, value := range md {
		strValue, valid := value.(string)
		if !valid {
			return nil, errors.Errorf(`value of key '%s' in metadata must be of type string`, key)
		}
		m[key] = aws.String(strValue)
	}
	return m, nil
}

// The first letter of a dash separated key value is capitalized, so perform a ToLower on it.
// This Key transformation of returning lowercase is consistent with other locations..
func parseMetadata(md map[string]*string) (map[string]interface{}, error) {
	m := make(map[string]interface{}, len(md))
	for key, value := range md {
		k := strings.ToLower(key)
		m[k] = *value
	}
	return m, nil
}
<commit_msg>Bring back use of cursor in s3 container<commit_after>package s3

import (
	"bytes"
	"io"
	"io/ioutil"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/graymeta/stow"
	"github.com/pkg/errors"
)

// Amazon S3 bucket contains a creation date and a name.
type container struct {
	// name is needed to retrieve items.
	name string
	// client is responsible for performing the requests.
	client *s3.S3
	// region describes the AWS Availability Zone of the S3 Bucket.
	region         string
	customEndpoint string
}

// ID returns a string value which represents the name of the container.
func (c *container) ID() string {
	return c.name
}

// Name returns a string value which represents the name of the container.
func (c *container) Name() string {
	return c.name
}

// Item returns a stow.Item instance of a container based on the
// name of the container and the key representing
func (c *container) Item(id string) (stow.Item, error) {
	return c.getItem(id)
}

// Items sends a request to retrieve a list of items that are prepended with
// the prefix argument. The 'cursor' variable facilitates pagination.
func (c *container) Items(prefix, cursor string, count int) ([]stow.Item, string, error) {
	itemLimit := int64(count)

	params := &s3.ListObjectsV2Input{
		Bucket:     aws.String(c.Name()),
		StartAfter: &cursor,
		MaxKeys:    &itemLimit,
		Prefix:     &prefix,
	}

	response, err := c.client.ListObjectsV2(params)
	if err != nil {
		return nil, "", errors.Wrap(err, "Items, listing objects")
	}

	containerItems := make([]stow.Item, len(response.Contents)) // Allocate space for the Item slice.

	for i, object := range response.Contents {
		etag := cleanEtag(*object.ETag) // Copy etag value and remove the strings.
		object.ETag = &etag             // Assign the value to the object field representing the item.

		containerItems[i] = &item{
			container: c,
			client:    c.client,
			properties: properties{
				ETag:         object.ETag,
				Key:          object.Key,
				LastModified: object.LastModified,
				Owner:        object.Owner,
				Size:         object.Size,
				StorageClass: object.StorageClass,
			},
		}
	}

	// Create a marker and determine if the list of items to retrieve is complete.
	// If not, the last file is the input to the value of after which item to start
	startAfter := ""
	if *response.IsTruncated {
		startAfter = containerItems[len(containerItems)-1].Name()
	}

	return containerItems, startAfter, nil
}

func (c *container) RemoveItem(id string) error {
	params := &s3.DeleteObjectInput{
		Bucket: aws.String(c.Name()),
		Key:    aws.String(id),
	}

	_, err := c.client.DeleteObject(params)
	if err != nil {
		return errors.Wrapf(err, "RemoveItem, deleting object %+v", params)
	}
	return nil
}

// Put sends a request to upload content to the container. The arguments
// received are the name of the item (S3 Object), a reader representing the
// content, and the size of the file. Many more attributes can be given to the
// file, including metadata. Keeping it simple for now.
func (c *container) Put(name string, r io.Reader, size int64, metadata map[string]interface{}) (stow.Item, error) {
	content, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, errors.Wrap(err, "unable to create or update item, reading content")
	}

	// Convert map[string]interface{} to map[string]*string
	mdPrepped, err := prepMetadata(metadata)
	if err != nil {
		return nil, errors.Wrap(err, "unable to create or update item, preparing metadata")
	}

	params := &s3.PutObjectInput{
		Bucket:        aws.String(c.name), // Required
		Key:           aws.String(name),   // Required
		ContentLength: aws.Int64(size),
		Body:          bytes.NewReader(content),
		Metadata:      mdPrepped, // map[string]*string
	}

	// Only Etag is returned.
	response, err := c.client.PutObject(params)
	if err != nil {
		return nil, errors.Wrap(err, "RemoveItem, deleting object")
	}
	etag := cleanEtag(*response.ETag)

	// Some fields are empty because this information isn't included in the response.
	// May have to involve sending a request if we want more specific information.
	// Keeping it simple for now.
	// s3.Object info: https://github.com/aws/aws-sdk-go/blob/master/service/s3/api.go#L7092-L7107
	// Response: https://github.com/aws/aws-sdk-go/blob/master/service/s3/api.go#L8193-L8227
	newItem := &item{
		container: c,
		client:    c.client,
		properties: properties{
			ETag: &etag,
			Key:  &name,
			Size: &size,
			//LastModified *time.Time
			//Owner        *s3.Owner
			//StorageClass *string
		},
	}

	return newItem, nil
}

// Region returns a string representing the region/availability zone of the container.
func (c *container) Region() string {
	return c.region
}

// A request to retrieve a single item includes information that is more specific than
// a PUT. Instead of doing a request within the PUT, make this method available so that the
// request can be made by the field retrieval methods when necessary. This is the case for
// fields that are left out, such as the object's last modified date. This also needs to be
// done only once since the requested information is retained.
// May be simpler to just stick it in PUT and and do a request every time, please vouch
// for this if so.
func (c *container) getItem(id string) (*item, error) {
	params := &s3.GetObjectInput{
		Bucket: aws.String(c.name),
		Key:    aws.String(id),
	}

	res, err := c.client.GetObject(params)
	if err != nil {
		// stow needs ErrNotFound to pass the test but amazon returns an opaque error
		if strings.Contains(err.Error(), "NoSuchKey") {
			return nil, stow.ErrNotFound
		}
		return nil, errors.Wrap(err, "getItem, getting the object")
	}
	defer res.Body.Close()

	etag := cleanEtag(*res.ETag) // etag string value contains quotations. Remove them.
	md, err := parseMetadata(res.Metadata)
	if err != nil {
		return nil, errors.Wrap(err, "unable to retrieve Item information, parsing metadata")
	}

	i := &item{
		container: c,
		client:    c.client,
		properties: properties{
			ETag:         &etag,
			Key:          &id,
			LastModified: res.LastModified,
			Owner:        nil, // not returned in the response.
			Size:         res.ContentLength,
			StorageClass: res.StorageClass,
			Metadata:     md,
		},
	}

	return i, nil
}

// Remove quotation marks from beginning and end. This includes quotations that
// are escaped. Also removes leading `W/` from prefix for weak Etags.
//
// Based on the Etag spec, the full etag value (<FULL ETAG VALUE>) can include:
// - W/"<ETAG VALUE>"
// - "<ETAG VALUE>"
// - ""
// Source: https://tools.ietf.org/html/rfc7232#section-2.3
//
// Based on HTTP spec, forward slash is a separator and must be enclosed in
// quotes to be used as a valid value. Hence, the returned value may include:
// - "<FULL ETAG VALUE>"
// - \"<FULL ETAG VALUE>\"
// Source: https://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
//
// This function contains a loop to check for the presence of the three possible
// filler characters and strips them, resulting in only the Etag value.
func cleanEtag(etag string) string {
	for {
		// Check if the filler characters are present
		if strings.HasPrefix(etag, `\"`) {
			etag = strings.Trim(etag, `\"`)

		} else if strings.HasPrefix(etag, `"`) {
			etag = strings.Trim(etag, `"`)

		} else if strings.HasPrefix(etag, `W/`) {
			etag = strings.Replace(etag, `W/`, "", 1)

		} else {
			break
		}
	}
	return etag
}

// prepMetadata parses a raw map into the native type required by S3 to set metadata (map[string]*string).
// TODO: validation for key values. This function also assumes that the value of a key value pair is a string.
func prepMetadata(md map[string]interface{}) (map[string]*string, error) {
	m := make(map[string]*string, len(md))
	for key, value := range md {
		strValue, valid := value.(string)
		if !valid {
			return nil, errors.Errorf(`value of key '%s' in metadata must be of type string`, key)
		}
		m[key] = aws.String(strValue)
	}
	return m, nil
}

// The first letter of a dash separated key value is capitalized, so perform a ToLower on it.
// This Key transformation of returning lowercase is consistent with other locations..
func parseMetadata(md map[string]*string) (map[string]interface{}, error) {
	m := make(map[string]interface{}, len(md))
	for key, value := range md {
		k := strings.ToLower(key)
		m[k] = *value
	}
	return m, nil
}
<|endoftext|>


<commit_before>package test

// Contains tests for render/rest

import (
	"encoding/xml"
	"strconv"
	"testing"

	"github.com/kataras/iris"
)

type renderTestInformationType struct {
	XMLName    xml.Name `xml:"info"`
	FirstAttr  string   `xml:"first,attr"`
	SecondAttr string   `xml:"second,attr"`
	Name       string   `xml:"name",json:"name"`
	Birth      string   `xml:"birth",json:"birth"`
	Stars      int      `xml:"stars",json:"stars"`
}

func TestREST(t *testing.T) {
	api := iris.New()

	dataContents := []byte("Some binary data here.")
	textContents := "Plain text here"
	JSONPContents := map[string]string{"hello": "jsonp"}
	JSONPCallback := "callbackName"
	JSONXMLContents := renderTestInformationType{
		XMLName:    xml.Name{Local: "info", Space: "info"}, // only need to verify that later
		FirstAttr:  "this is the first attr",
		SecondAttr: "this is the second attr",
		Name:       "Iris web framework",
		Birth:      "13 March 2016",
		Stars:      4064,
	}
	markdownContents := "# Hello dynamic markdown from Iris"

	api.Get("/data", func(ctx *iris.Context) {
		ctx.Data(iris.StatusOK, dataContents)
	})

	api.Get("/text", func(ctx *iris.Context) {
		ctx.Text(iris.StatusOK, textContents)
	})

	api.Get("/jsonp", func(ctx *iris.Context) {
		ctx.JSONP(iris.StatusOK, JSONPCallback, JSONPContents)
	})

	api.Get("/json", func(ctx *iris.Context) {
		ctx.JSON(iris.StatusOK, JSONXMLContents)
	})
	api.Get("/xml", func(ctx *iris.Context) {
		ctx.XML(iris.StatusOK, JSONXMLContents)
	})

	api.Get("/markdown", func(ctx *iris.Context) {
		ctx.Markdown(iris.StatusOK, markdownContents)
	})

	e := tester(api, t)
	dataT := e.GET("/data").Expect()
	dataT.Header("Content-Type").Equal("application/octet-stream")
	dataT.Body().Equal(string(dataContents))

	textT := e.GET("/text").Expect()
	textT.Header("Content-Type").Equal("text/plain; charset=UTF-8")
	textT.Body().Equal(textContents)

	/*	JSONPT := e.GET("/jsonp").Expect()
		dataT.Header("Content-Type").Equal("application/javascript; charset=UTF-8")
		dataT.Body().Equal(dataContents)*/

	JSONT := e.GET("/json").Expect()
	JSONT.Header("Content-Type").Equal("application/json; charset=UTF-8")
	JSONT.JSON().Object().Equal(JSONXMLContents)

	XMLT := e.GET("/xml").Expect()
	XMLT.Header("Content-Type").Equal("text/xml; charset=UTF-8")
	XMLT.Body().Equal(`<` + JSONXMLContents.XMLName.Local + ` first="` + JSONXMLContents.FirstAttr + `" second="` + JSONXMLContents.SecondAttr + `"><name>` + JSONXMLContents.Name + `</name><birth>` + JSONXMLContents.Birth + `</birth><stars>` + strconv.Itoa(JSONXMLContents.Stars) + `</stars></info>`)

	markdownT := e.GET("/markdown").Expect()
	markdownT.Header("Content-Type").Equal("text/html; charset=UTF-8")
	markdownT.Body().Equal("
" + markdownContents[2:] + "
\n")

}
<commit_msg>add test for JSONP<commit_after>package test

// Contains tests for render/rest & render/template

import (
	"encoding/xml"
	"strconv"
	"testing"

	"github.com/kataras/iris"
)

type renderTestInformationType struct {
	XMLName    xml.Name `xml:"info"`
	FirstAttr  string   `xml:"first,attr"`
	SecondAttr string   `xml:"second,attr"`
	Name       string   `xml:"name",json:"name"`
	Birth      string   `xml:"birth",json:"birth"`
	Stars      int      `xml:"stars",json:"stars"`
}

func TestREST(t *testing.T) {
	api := iris.New()

	dataContents := []byte("Some binary data here.")
	textContents := "Plain text here"
	JSONPContents := map[string]string{"hello": "jsonp"}
	JSONPCallback := "callbackName"
	JSONXMLContents := renderTestInformationType{
		XMLName:    xml.Name{Local: "info", Space: "info"}, // only need to verify that later
		FirstAttr:  "this is the first attr",
		SecondAttr: "this is the second attr",
		Name:       "Iris web framework",
		Birth:      "13 March 2016",
		Stars:      4064,
	}
	markdownContents := "# Hello dynamic markdown from Iris"

	api.Get("/data", func(ctx *iris.Context) {
		ctx.Data(iris.StatusOK, dataContents)
	})

	api.Get("/text", func(ctx *iris.Context) {
		ctx.Text(iris.StatusOK, textContents)
	})

	api.Get("/jsonp", func(ctx *iris.Context) {
		ctx.JSONP(iris.StatusOK, JSONPCallback, JSONPContents)
	})

	api.Get("/json", func(ctx *iris.Context) {
		ctx.JSON(iris.StatusOK, JSONXMLContents)
	})
	api.Get("/xml", func(ctx *iris.Context) {
		ctx.XML(iris.StatusOK, JSONXMLContents)
	})

	api.Get("/markdown", func(ctx *iris.Context) {
		ctx.Markdown(iris.StatusOK, markdownContents)
	})

	e := tester(api, t)
	dataT := e.GET("/data").Expect()
	dataT.Header("Content-Type").Equal("application/octet-stream")
	dataT.Body().Equal(string(dataContents))

	textT := e.GET("/text").Expect()
	textT.Header("Content-Type").Equal("text/plain; charset=UTF-8")
	textT.Body().Equal(textContents)

	JSONPT := e.GET("/jsonp").Expect()
	JSONPT.Header("Content-Type").Equal("application/javascript; charset=UTF-8")
	JSONPT.Body().Equal(JSONPCallback + `({"hello":"jsonp"});`)

	JSONT := e.GET("/json").Expect()
	JSONT.Header("Content-Type").Equal("application/json; charset=UTF-8")
	JSONT.JSON().Object().Equal(JSONXMLContents)

	XMLT := e.GET("/xml").Expect()
	XMLT.Header("Content-Type").Equal("text/xml; charset=UTF-8")
	XMLT.Body().Equal(`<` + JSONXMLContents.XMLName.Local + ` first="` + JSONXMLContents.FirstAttr + `" second="` + JSONXMLContents.SecondAttr + `"><name>` + JSONXMLContents.Name + `</name><birth>` + JSONXMLContents.Birth + `</birth><stars>` + strconv.Itoa(JSONXMLContents.Stars) + `</stars></info>`)

	markdownT := e.GET("/markdown").Expect()
	markdownT.Header("Content-Type").Equal("text/html; charset=UTF-8")
	markdownT.Body().Equal("
" + markdownContents[2:] + "
\n")

}
<|endoftext|>