<commit_before>// Package lumberjack provides a rolling logger.
//
// Lumberjack is intended to be one part of a logging infrastructure.
// It is not an all-in-one solution, but instead is a pluggable
// component at the bottom of the logging stack that simply controls the files
// to which logs are written.
//
// Lumberjack plays well with any logger that can write to an io.Writer,
// including the standard library's log package.
//
// Lumberjack assumes that only one process is writing to the output files.
// Using the same lumberjack configuration from multiple processes on the same
// machine will result in improper behavior.
package lumberjack

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"time"
)

const (
	// Some helper constants to make your declarations easier to read.

	Megabyte = 1024 * 1024
	Gigabyte = 1024 * Megabyte

	// Note that lumberjack days and weeks may not exactly conform to calendar
	// days and weeks due to daylight savings, leap seconds, etc.

	Day  = 24 * time.Hour
	Week = 7 * Day

	defaultNameFormat = "2006-01-02T15-04-05.000.log"
	defaultMaxSize    = 100 * Megabyte
)

// ensure we always implement io.WriteCloser
var _ io.WriteCloser = (*Logger)(nil)

// Logger is an io.WriteCloser that writes to a log file in the given directory
// with the given NameFormat.  NameFormat should include a time formatting
// layout in it that produces a valid unique filename for the OS.  For more
// about time formatting layouts, read http://golang.org/pkg/time/#pkg-constants.
//
// The date encoded in the filename by NameFormat is used to determine which log
// files are most recent in several situations.
//
// Logger opens or creates a logfile on first Write.  It looks for files in the
// directory that match its name format, and if the one with the most recent
// NameFormat date is less than MaxSize, it will open and append to that file.
// If no such file exists, or the file is >= MaxSize, a new file is created
// using the current time with NameFormat to generate the filename.
//
// Whenever a write would cause the current log file exceed MaxSize, a new file
// is created using the current time.
//
// Cleaning Up Old Log Files
//
// Whenever a new file gets created, old log files may be deleted.  The log file
// directory is scanned for files that match NameFormat.  The most recent files
// according to their NameFormat date will be retained, up to a number equal to
// MaxBackups (or all of them if MaxBackups is 0).  Any files with a last
// modified time (based on FileInfo.ModTime) older than MaxAge are deleted,
// regardless of MaxBackups.
//
// If MaxBackups and MaxAge are both 0, no old log files will be deleted.
type Logger struct {
	// Dir determines the directory in which to store log files.
	// It defaults to os.TempDir() if empty.
	Dir string `json:"dir" yaml:"dir"`

	// NameFormat is the time formatting layout used to generate filenames.
	// It defaults to "2006-01-02T15-04-05.000.log".
	NameFormat string `json:"nameformat" yaml:"nameformat"`

	// MaxSize is the maximum size in bytes of the log file before it gets
	// rolled. It defaults to 100 megabytes.
	MaxSize int64 `json:"maxsize" yaml:"maxsize"`

	// MaxAge is the maximum time to retain old log files based on
	// FileInfo.ModTime.  The default is not to remove old log files based on
	// age.
	MaxAge time.Duration `json:"maxage" yaml:"maxage"`

	// MaxBackups is the maximum number of old log files to retain.  The default
	// is to retain all old log files (though MaxAge may still cause them to get
	// deleted.)
	MaxBackups int `json:"maxbackups" yaml:"maxbackups"`

	// LocalTime determines if the time used for formatting the filename is the
	// computer's local time.  The default is to use UTC time.
	LocalTime bool `json:"localtime" yaml:"localtime"`

	size int64
	file *os.File
	mu   sync.Mutex
}

// currentTime is only used for testing. Normally it's the time.Now() function.
var currentTime = time.Now

// Write implements io.Writer.  If a write would cause the log file to be larger
// than MaxSize, a new log file is created using the current time formatted with
// PathFormat.  If the length of the write is greater than MaxSize, an error is
// returned.
func (l *Logger) Write(p []byte) (n int, err error) {
	l.mu.Lock()
	defer l.mu.Unlock()

	writeLen := int64(len(p))
	if writeLen > l.max() {
		return 0, fmt.Errorf(
			"write length %d exceeds maximum file size %d", writeLen, l.max(),
		)
	}

	if l.file == nil {
		if err = l.openExistingOrNew(len(p)); err != nil {
			return 0, err
		}
	}

	if l.size+writeLen > l.max() {
		if err := l.rotate(); err != nil {
			return 0, err
		}
	}

	n, err = l.file.Write(p)
	l.size += int64(n)

	return n, err
}

// Close implements io.Closer, and closes the current logfile.
func (l *Logger) Close() error {
	l.mu.Lock()
	err := l.close()
	l.mu.Unlock()
	return err
}

// close closes the file if it is open.
func (l *Logger) close() error {
	if l.file == nil {
		return nil
	}
	err := l.file.Close()
	l.file = nil
	return err
}

// Rotate causes Logger to close the existing log file and immediately create a
// new one.  This is a helper function for applications that want to initiate
// rotations outside of the normal rotation rules, such as in response to
// SIGHUP.  After rotating, this initiates a cleanup of old log files according
// to the normal rules.
func (l *Logger) Rotate() error {
	l.mu.Lock()
	err := l.rotate()
	l.mu.Unlock()
	return err
}

// rotate closes the current file, if any, opens a new file, and then calls
// cleanup.
func (l *Logger) rotate() error {
	if err := l.close(); err != nil {
		return nil
	}
	if err := l.openNew(); err != nil {
		return err
	}
	return l.cleanup()
}

// openNew opens a new log file for writing.
func (l *Logger) openNew() error {
	err := os.MkdirAll(l.dir(), 0744)
	if err != nil {
		return fmt.Errorf("can't make directories for new logfile: %s", err)
	}
	f, err := os.OpenFile(l.genFilename(), os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return fmt.Errorf("can't open new logfile: %s", err)
	}
	info, err := f.Stat()
	if err != nil {
		// can't really do anything if close fails here
		_ = f.Close()
		return fmt.Errorf("can't get size of new logfile: %s", err)
	}
	l.size = info.Size()
	l.file = f
	return nil
}

// openExistingOrNew opens the most recently modified logfile in the log
// directory, if the current write would not put it over MaxSize.  If there is
// no such file or the write would put it over the MaxSize, a new file is
// created.
func (l *Logger) openExistingOrNew(writeLen int) error {
	if l.Dir == "" && l.NameFormat == "" {
		return l.openNew()
	}
	files, err := ioutil.ReadDir(l.dir())
	if os.IsNotExist(err) {
		return l.openNew()
	}
	if err != nil {
		return fmt.Errorf("can't read files in log file directory: %s", err)
	}
	sort.Sort(byFormatTime{files, l.format()})
	for _, info := range files {
		if info.IsDir() {
			continue
		}

		if !l.isLogFile(info) {
			continue
		}

		// the first file we find that matches our pattern will be the most
		// recently modified log file.
		if info.Size()+int64(writeLen) < l.max() {
			filename := filepath.Join(l.dir(), info.Name())
			file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0644)
			if err == nil {
				l.file = file
				return nil
			}
			// if we fail to open the old log file for some reason, just ignore
			// it and open a new log file.
		}
		break
	}
	return l.openNew()
}

// genFilename generates the name of the logfile from the current time.
func (l *Logger) genFilename() string {
	t := currentTime()
	if !l.LocalTime {
		t = t.UTC()
	}
	return filepath.Join(l.dir(), t.Format(l.format()))
}

// cleanup deletes old log files, keeping at most l.MaxBackups files, as long as
// none of them are older than MaxAge.
func (l *Logger) cleanup() error {
	if l.MaxBackups == 0 && l.MaxAge == 0 {
		return nil
	}

	files, err := l.oldLogFiles()
	if err != nil {
		return err
	}

	var deletes []os.FileInfo

	if l.MaxBackups > 0 {
		deletes = files[l.MaxBackups:]
		files = files[:l.MaxBackups]
	}
	if l.MaxAge > 0 {
		cutoff := currentTime().Add(-1 * l.MaxAge)

		for _, f := range files {
			if f.ModTime().Before(cutoff) {
				deletes = append(deletes, f)
			}
		}
	}

	if len(deletes) == 0 {
		return nil
	}

	go deleteAll(l.dir(), deletes)

	return nil
}

func deleteAll(dir string, files []os.FileInfo) {
	// remove files on a separate goroutine
	for _, f := range files {
		// what am I going to do, log this?
		_ = os.Remove(filepath.Join(dir, f.Name()))
	}
}

// oldLogFiles returns the list of backup log files stored in the same
// directory as the current log file, sorted by ModTime
func (l *Logger) oldLogFiles() ([]os.FileInfo, error) {
	files, err := ioutil.ReadDir(l.dir())
	if err != nil {
		return nil, fmt.Errorf("can't read log file directory: %s", err)
	}
	logFiles := []os.FileInfo{}

	for _, f := range files {
		if f.IsDir() {
			continue
		}
		if filepath.Base(f.Name()) == filepath.Base(l.file.Name()) {
			continue
		}

		if l.isLogFile(f) {
			logFiles = append(logFiles, f)
		}
	}

	sort.Sort(byFormatTime{logFiles, l.format()})

	return logFiles, nil
}

func (l *Logger) isLogFile(f os.FileInfo) bool {
	_, err := time.Parse(l.format(), filepath.Base(f.Name()))
	return err == nil
}

func (l *Logger) max() int64 {
	if l.MaxSize == 0 {
		return defaultMaxSize
	}
	return l.MaxSize
}

func (l *Logger) dir() string {
	if l.Dir != "" {
		return l.Dir
	}
	return os.TempDir()
}

func (l *Logger) format() string {
	if l.NameFormat != "" {
		return l.NameFormat
	}
	return defaultNameFormat
}

// byFormatTime sorts by newest time formatted in the name.
type byFormatTime struct {
	files  []os.FileInfo
	format string
}

func (b byFormatTime) Less(i, j int) bool {
	return b.time(i).After(b.time(j))
}

func (b byFormatTime) Swap(i, j int) {
	b.files[i], b.files[j] = b.files[j], b.files[i]
}

func (b byFormatTime) Len() int {
	return len(b.files)
}

func (b byFormatTime) time(i int) time.Time {
	t, err := time.Parse(b.format, filepath.Base(b.files[i].Name()))
	if err != nil {
		return time.Time{}
	}
	return t
}
<commit_msg>go back to using defer to unlock in close and rotate<commit_after>// Package lumberjack provides a rolling logger.
//
// Lumberjack is intended to be one part of a logging infrastructure.
// It is not an all-in-one solution, but instead is a pluggable
// component at the bottom of the logging stack that simply controls the files
// to which logs are written.
//
// Lumberjack plays well with any logger that can write to an io.Writer,
// including the standard library's log package.
//
// Lumberjack assumes that only one process is writing to the output files.
// Using the same lumberjack configuration from multiple processes on the same
// machine will result in improper behavior.
package lumberjack

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"time"
)

const (
	// Some helper constants to make your declarations easier to read.

	Megabyte = 1024 * 1024
	Gigabyte = 1024 * Megabyte

	// Note that lumberjack days and weeks may not exactly conform to calendar
	// days and weeks due to daylight savings, leap seconds, etc.

	Day  = 24 * time.Hour
	Week = 7 * Day

	defaultNameFormat = "2006-01-02T15-04-05.000.log"
	defaultMaxSize    = 100 * Megabyte
)

// ensure we always implement io.WriteCloser
var _ io.WriteCloser = (*Logger)(nil)

// Logger is an io.WriteCloser that writes to a log file in the given directory
// with the given NameFormat.  NameFormat should include a time formatting
// layout in it that produces a valid unique filename for the OS.  For more
// about time formatting layouts, read http://golang.org/pkg/time/#pkg-constants.
//
// The date encoded in the filename by NameFormat is used to determine which log
// files are most recent in several situations.
//
// Logger opens or creates a logfile on first Write.  It looks for files in the
// directory that match its name format, and if the one with the most recent
// NameFormat date is less than MaxSize, it will open and append to that file.
// If no such file exists, or the file is >= MaxSize, a new file is created
// using the current time with NameFormat to generate the filename.
//
// Whenever a write would cause the current log file exceed MaxSize, a new file
// is created using the current time.
//
// Cleaning Up Old Log Files
//
// Whenever a new file gets created, old log files may be deleted.  The log file
// directory is scanned for files that match NameFormat.  The most recent files
// according to their NameFormat date will be retained, up to a number equal to
// MaxBackups (or all of them if MaxBackups is 0).  Any files with a last
// modified time (based on FileInfo.ModTime) older than MaxAge are deleted,
// regardless of MaxBackups.
//
// If MaxBackups and MaxAge are both 0, no old log files will be deleted.
type Logger struct {
	// Dir determines the directory in which to store log files.
	// It defaults to os.TempDir() if empty.
	Dir string `json:"dir" yaml:"dir"`

	// NameFormat is the time formatting layout used to generate filenames.
	// It defaults to "2006-01-02T15-04-05.000.log".
	NameFormat string `json:"nameformat" yaml:"nameformat"`

	// MaxSize is the maximum size in bytes of the log file before it gets
	// rolled. It defaults to 100 megabytes.
	MaxSize int64 `json:"maxsize" yaml:"maxsize"`

	// MaxAge is the maximum time to retain old log files based on
	// FileInfo.ModTime.  The default is not to remove old log files based on
	// age.
	MaxAge time.Duration `json:"maxage" yaml:"maxage"`

	// MaxBackups is the maximum number of old log files to retain.  The default
	// is to retain all old log files (though MaxAge may still cause them to get
	// deleted.)
	MaxBackups int `json:"maxbackups" yaml:"maxbackups"`

	// LocalTime determines if the time used for formatting the filename is the
	// computer's local time.  The default is to use UTC time.
	LocalTime bool `json:"localtime" yaml:"localtime"`

	size int64
	file *os.File
	mu   sync.Mutex
}

// currentTime is only used for testing. Normally it's the time.Now() function.
var currentTime = time.Now

// Write implements io.Writer.  If a write would cause the log file to be larger
// than MaxSize, a new log file is created using the current time formatted with
// PathFormat.  If the length of the write is greater than MaxSize, an error is
// returned.
func (l *Logger) Write(p []byte) (n int, err error) {
	l.mu.Lock()
	defer l.mu.Unlock()

	writeLen := int64(len(p))
	if writeLen > l.max() {
		return 0, fmt.Errorf(
			"write length %d exceeds maximum file size %d", writeLen, l.max(),
		)
	}

	if l.file == nil {
		if err = l.openExistingOrNew(len(p)); err != nil {
			return 0, err
		}
	}

	if l.size+writeLen > l.max() {
		if err := l.rotate(); err != nil {
			return 0, err
		}
	}

	n, err = l.file.Write(p)
	l.size += int64(n)

	return n, err
}

// Close implements io.Closer, and closes the current logfile.
func (l *Logger) Close() error {
	l.mu.Lock()
	defer l.mu.Unlock()
	return l.close()
}

// close closes the file if it is open.
func (l *Logger) close() error {
	if l.file == nil {
		return nil
	}
	err := l.file.Close()
	l.file = nil
	return err
}

// Rotate causes Logger to close the existing log file and immediately create a
// new one.  This is a helper function for applications that want to initiate
// rotations outside of the normal rotation rules, such as in response to
// SIGHUP.  After rotating, this initiates a cleanup of old log files according
// to the normal rules.
func (l *Logger) Rotate() error {
	l.mu.Lock()
	defer l.mu.Unlock()
	return l.rotate()
}

// rotate closes the current file, if any, opens a new file, and then calls
// cleanup.
func (l *Logger) rotate() error {
	if err := l.close(); err != nil {
		return nil
	}
	if err := l.openNew(); err != nil {
		return err
	}
	return l.cleanup()
}

// openNew opens a new log file for writing.
func (l *Logger) openNew() error {
	err := os.MkdirAll(l.dir(), 0744)
	if err != nil {
		return fmt.Errorf("can't make directories for new logfile: %s", err)
	}
	f, err := os.OpenFile(l.genFilename(), os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return fmt.Errorf("can't open new logfile: %s", err)
	}
	info, err := f.Stat()
	if err != nil {
		// can't really do anything if close fails here
		_ = f.Close()
		return fmt.Errorf("can't get size of new logfile: %s", err)
	}
	l.size = info.Size()
	l.file = f
	return nil
}

// openExistingOrNew opens the most recently modified logfile in the log
// directory, if the current write would not put it over MaxSize.  If there is
// no such file or the write would put it over the MaxSize, a new file is
// created.
func (l *Logger) openExistingOrNew(writeLen int) error {
	if l.Dir == "" && l.NameFormat == "" {
		return l.openNew()
	}
	files, err := ioutil.ReadDir(l.dir())
	if os.IsNotExist(err) {
		return l.openNew()
	}
	if err != nil {
		return fmt.Errorf("can't read files in log file directory: %s", err)
	}
	sort.Sort(byFormatTime{files, l.format()})
	for _, info := range files {
		if info.IsDir() {
			continue
		}

		if !l.isLogFile(info) {
			continue
		}

		// the first file we find that matches our pattern will be the most
		// recently modified log file.
		if info.Size()+int64(writeLen) < l.max() {
			filename := filepath.Join(l.dir(), info.Name())
			file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0644)
			if err == nil {
				l.file = file
				return nil
			}
			// if we fail to open the old log file for some reason, just ignore
			// it and open a new log file.
		}
		break
	}
	return l.openNew()
}

// genFilename generates the name of the logfile from the current time.
func (l *Logger) genFilename() string {
	t := currentTime()
	if !l.LocalTime {
		t = t.UTC()
	}
	return filepath.Join(l.dir(), t.Format(l.format()))
}

// cleanup deletes old log files, keeping at most l.MaxBackups files, as long as
// none of them are older than MaxAge.
func (l *Logger) cleanup() error {
	if l.MaxBackups == 0 && l.MaxAge == 0 {
		return nil
	}

	files, err := l.oldLogFiles()
	if err != nil {
		return err
	}

	var deletes []os.FileInfo

	if l.MaxBackups > 0 {
		deletes = files[l.MaxBackups:]
		files = files[:l.MaxBackups]
	}
	if l.MaxAge > 0 {
		cutoff := currentTime().Add(-1 * l.MaxAge)

		for _, f := range files {
			if f.ModTime().Before(cutoff) {
				deletes = append(deletes, f)
			}
		}
	}

	if len(deletes) == 0 {
		return nil
	}

	go deleteAll(l.dir(), deletes)

	return nil
}

func deleteAll(dir string, files []os.FileInfo) {
	// remove files on a separate goroutine
	for _, f := range files {
		// what am I going to do, log this?
		_ = os.Remove(filepath.Join(dir, f.Name()))
	}
}

// oldLogFiles returns the list of backup log files stored in the same
// directory as the current log file, sorted by ModTime
func (l *Logger) oldLogFiles() ([]os.FileInfo, error) {
	files, err := ioutil.ReadDir(l.dir())
	if err != nil {
		return nil, fmt.Errorf("can't read log file directory: %s", err)
	}
	logFiles := []os.FileInfo{}

	for _, f := range files {
		if f.IsDir() {
			continue
		}
		if filepath.Base(f.Name()) == filepath.Base(l.file.Name()) {
			continue
		}

		if l.isLogFile(f) {
			logFiles = append(logFiles, f)
		}
	}

	sort.Sort(byFormatTime{logFiles, l.format()})

	return logFiles, nil
}

func (l *Logger) isLogFile(f os.FileInfo) bool {
	_, err := time.Parse(l.format(), filepath.Base(f.Name()))
	return err == nil
}

func (l *Logger) max() int64 {
	if l.MaxSize == 0 {
		return defaultMaxSize
	}
	return l.MaxSize
}

func (l *Logger) dir() string {
	if l.Dir != "" {
		return l.Dir
	}
	return os.TempDir()
}

func (l *Logger) format() string {
	if l.NameFormat != "" {
		return l.NameFormat
	}
	return defaultNameFormat
}

// byFormatTime sorts by newest time formatted in the name.
type byFormatTime struct {
	files  []os.FileInfo
	format string
}

func (b byFormatTime) Less(i, j int) bool {
	return b.time(i).After(b.time(j))
}

func (b byFormatTime) Swap(i, j int) {
	b.files[i], b.files[j] = b.files[j], b.files[i]
}

func (b byFormatTime) Len() int {
	return len(b.files)
}

func (b byFormatTime) time(i int) time.Time {
	t, err := time.Parse(b.format, filepath.Base(b.files[i].Name()))
	if err != nil {
		return time.Time{}
	}
	return t
}
<|endoftext|>


<commit_before><commit_msg>go fmt<commit_after><|endoftext|>


<commit_before>/*
 * Location management.
 *
 * The core methods behind detecting collisions.
 */

// Package locationmanager provides all abilities to detect other entities in an environment.
package locationmanager

import (
	"fmt"
	"github.com/DiscoViking/goBrains/entity"
	"math"
)

// Add a new entity.
// This is added to first empty entry in the array, else append a new entry.
func (cm *LocationManager) AddEntity(ent entity.Entity) {
	newHitbox := circleHitbox{
		active:      true,
		centre:      coord{0, 0},
		orientation: 0,
		radius:      ent.GetRadius(),
		entity:      ent,
	}

	entry := cm.findEmptyHitbox()
	if entry == nil {
		cm.hitboxes = append(cm.hitboxes, &newHitbox)
	} else {
		entry = &newHitbox
	}
}

// Remove an entity.
func (cm *LocationManager) RemoveEntity(ent entity.Entity) {
	hb := cm.findHitbox(ent)
	hb.setActive(false)
}

// Update the location of an entity.
func (cm *LocationManager) ChangeLocation(move CoordDelta, ent entity.Entity) {
	hb := cm.findHitbox(ent)
	hb.update(move)
}

// Update the radius of an entity.
func (cm *LocationManager) ChangeRadius(radius float64, ent entity.Entity) {
	hb := cm.findHitbox(ent)
	hb.setRadius(radius)
}

// Determine all entities which exist at a specific point.
func (cm *LocationManager) GetCollisions(offset CoordDelta, ent entity.Entity) []entity.Entity {
	collisions := make([]entity.Entity, 0)

	searcher := cm.findHitbox(ent)
	absLoc := searcher.getCoord()

	dX := offset.Distance * math.Cos(searcher.getOrient())
	dY := offset.Distance * math.Sin(searcher.getOrient())
	absLoc.update(dX, dY)

	for _, hb := range cm.hitboxes {
		if hb.isInside(absLoc) {
			collisions = append(collisions, hb.getEntity())
		}
	}

	return collisions
}

// Get the location and orientation of a specific entity.
func (cm *LocationManager) GetLocation(ent entity.Entity) (bool, float64, float64, float64) {
	hb := cm.findHitbox(ent)

	if hb == nil {
		return false, 0, 0, 0
	}

	coordinate := hb.getCoord()
	orientation := hb.getOrient()

	return true, coordinate.locX, coordinate.locY, orientation
}

// Find the hitbox associated with an entity.
func (cm *LocationManager) findHitbox(ent entity.Entity) locatable {
	for _, hb := range cm.hitboxes {
		if hb.getActive() && (hb.getEntity() == ent) {
			return hb
		}
	}
	return nil
}

// Find the first unused hitbox structure.
func (cm *LocationManager) findEmptyHitbox() locatable {
	for _, hb := range cm.hitboxes {
		if !hb.getActive() {
			return hb
		}
	}
	return nil
}

// Print debug information about information stored in the LocationManager.
func (cm *LocationManager) PrintDebug() {
	fmt.Printf("Location Manager: %v\n", cm)
	for ii, hb := range cm.hitboxes {
		fmt.Printf("  Hitbox %v\n", ii)
		hb.printDebug()
	}
	fmt.Printf("\n")
}

// Initialise the LocationManager.
func NewLocationManager() *LocationManager {
	return &LocationManager{hitboxes: make([]locatable, 0)}
}
<commit_msg>Found the bug!<commit_after>/*
 * Location management.
 *
 * The core methods behind detecting collisions.
 */

// Package locationmanager provides all abilities to detect other entities in an environment.
package locationmanager

import (
	"fmt"
	"github.com/DiscoViking/goBrains/entity"
	"math"
)

// Add a new entity.
// This is added to first empty entry in the array, else append a new entry.
func (cm *LocationManager) AddEntity(ent entity.Entity) {
	newHitbox := circleHitbox{
		active:      true,
		centre:      coord{0, 0},
		orientation: 0,
		radius:      ent.GetRadius(),
		entity:      ent,
	}

	entry := cm.findEmptyHitbox()
	if entry == nil {
		cm.hitboxes = append(cm.hitboxes, &newHitbox)
	} else {
		entry.setActive(true)
		entry = &newHitbox
	}
}

// Remove an entity.
func (cm *LocationManager) RemoveEntity(ent entity.Entity) {
	hb := cm.findHitbox(ent)
	hb.setActive(false)
}

// Update the location of an entity.
func (cm *LocationManager) ChangeLocation(move CoordDelta, ent entity.Entity) {
	hb := cm.findHitbox(ent)
	hb.update(move)
}

// Update the radius of an entity.
func (cm *LocationManager) ChangeRadius(radius float64, ent entity.Entity) {
	hb := cm.findHitbox(ent)
	hb.setRadius(radius)
}

// Determine all entities which exist at a specific point.
func (cm *LocationManager) GetCollisions(offset CoordDelta, ent entity.Entity) []entity.Entity {
	collisions := make([]entity.Entity, 0)

	searcher := cm.findHitbox(ent)
	absLoc := searcher.getCoord()

	dX := offset.Distance * math.Cos(searcher.getOrient())
	dY := offset.Distance * math.Sin(searcher.getOrient())
	absLoc.update(dX, dY)

	for _, hb := range cm.hitboxes {
		if hb.isInside(absLoc) {
			collisions = append(collisions, hb.getEntity())
		}
	}

	return collisions
}

// Get the location and orientation of a specific entity.
func (cm *LocationManager) GetLocation(ent entity.Entity) (bool, float64, float64, float64) {
	hb := cm.findHitbox(ent)

	if hb == nil {
		return false, 0, 0, 0
	}

	coordinate := hb.getCoord()
	orientation := hb.getOrient()

	return true, coordinate.locX, coordinate.locY, orientation
}

// Find the hitbox associated with an entity.
func (cm *LocationManager) findHitbox(ent entity.Entity) locatable {
	for _, hb := range cm.hitboxes {
		if hb.getActive() && (hb.getEntity() == ent) {
			return hb
		}
	}
	return nil
}

// Find the first unused hitbox structure.
func (cm *LocationManager) findEmptyHitbox() locatable {
	for _, hb := range cm.hitboxes {
		if !hb.getActive() {
			return hb
		}
	}
	return nil
}

// Print debug information about information stored in the LocationManager.
func (cm *LocationManager) PrintDebug() {
	fmt.Printf("Location Manager: %v\n", cm)
	for ii, hb := range cm.hitboxes {
		fmt.Printf("  Hitbox %v\n", ii)
		hb.printDebug()
	}
	fmt.Printf("\n")
}

// Initialise the LocationManager.
func NewLocationManager() *LocationManager {
	return &LocationManager{hitboxes: make([]locatable, 0)}
}
<|endoftext|>


<commit_before>package kvstore

import (
	"testing"

	"github.com/smancke/guble/testutil"
	"github.com/stretchr/testify/assert"
)

func BenchmarkPostgresKVStore_PutGet(b *testing.B) {
	kvs := NewPostgresKVStore(aPostgresConfig())
	kvs.Open()
	CommonBenchmarkPutGet(b, kvs)
}

func TestPostgresKVStore_PutGetDelete(t *testing.T) {
	kvs := NewPostgresKVStore(aPostgresConfig())
	kvs.Open()
	CommonTestPutGetDelete(t, kvs, kvs)
}

func TestPostgresKVStore_Iterate(t *testing.T) {
	kvs := NewPostgresKVStore(aPostgresConfig())
	kvs.Open()
	CommonTestIterate(t, kvs, kvs)
}

func TestPostgresKVStore_IterateKeys(t *testing.T) {
	kvs := NewPostgresKVStore(aPostgresConfig())
	kvs.Open()
	CommonTestIterateKeys(t, kvs, kvs)
}

func TestPostgresKVStore_Check(t *testing.T) {
	a := assert.New(t)

	kvs := NewPostgresKVStore(aPostgresConfig())
	kvs.Open()

	err := kvs.Check()
	a.NoError(err, "Db ping should work")

	kvs.Stop()

	err = kvs.Check()
	a.NotNil(err, "Check should fail because db was already closed")
}

func TestPostgresKVStore_Open(t *testing.T) {
	kvs := NewPostgresKVStore(invalidPostgresConfig())
	err := kvs.Open()
	assert.NotNil(t, err)
}

func TestPostgresKVStore_ParallelUsage(t *testing.T) {
	// test cant run because we cannot ensure the data has been written
	testutil.SkipIfShort(t)
	a := assert.New(t)

	kvs1 := NewPostgresKVStore(aPostgresConfig())
	err := kvs1.Open()
	a.NoError(err)

	kvs2 := NewPostgresKVStore(aPostgresConfig())
	err = kvs2.Open()
	a.NoError(err)

	CommonTestPutGetDelete(t, kvs1, kvs2)
	CommonTestIterate(t, kvs1, kvs2)
	CommonTestIterateKeys(t, kvs1, kvs2)
}

// This config assumes a postgresql running locally
func aPostgresConfig() PostgresConfig {
	return PostgresConfig{
		ConnParams: map[string]string{
			"host":     "localhost",
			"user":     "postgres",
			"password": "",
			"dbname":   "guble",
			"sslmode":  "disable",
		},
		MaxIdleConns: 1,
		MaxOpenConns: 1,
	}
}

func invalidPostgresConfig() PostgresConfig {
	return PostgresConfig{
		ConnParams: map[string]string{
			"host":     "localhost",
			"user":     "",
			"password": "",
			"dbname":   "",
			"sslmode":  "disable",
		},
		MaxIdleConns: 1,
		MaxOpenConns: 1,
	}
}
<commit_msg>removing incorrect test<commit_after>package kvstore

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

func BenchmarkPostgresKVStore_PutGet(b *testing.B) {
	kvs := NewPostgresKVStore(aPostgresConfig())
	kvs.Open()
	CommonBenchmarkPutGet(b, kvs)
}

func TestPostgresKVStore_PutGetDelete(t *testing.T) {
	kvs := NewPostgresKVStore(aPostgresConfig())
	kvs.Open()
	CommonTestPutGetDelete(t, kvs, kvs)
}

func TestPostgresKVStore_Iterate(t *testing.T) {
	kvs := NewPostgresKVStore(aPostgresConfig())
	kvs.Open()
	CommonTestIterate(t, kvs, kvs)
}

func TestPostgresKVStore_IterateKeys(t *testing.T) {
	kvs := NewPostgresKVStore(aPostgresConfig())
	kvs.Open()
	CommonTestIterateKeys(t, kvs, kvs)
}

func TestPostgresKVStore_Check(t *testing.T) {
	a := assert.New(t)

	kvs := NewPostgresKVStore(aPostgresConfig())
	kvs.Open()

	err := kvs.Check()
	a.NoError(err, "Db ping should work")

	kvs.Stop()

	err = kvs.Check()
	a.NotNil(err, "Check should fail because db was already closed")
}

func TestPostgresKVStore_Open(t *testing.T) {
	kvs := NewPostgresKVStore(invalidPostgresConfig())
	err := kvs.Open()
	assert.NotNil(t, err)
}

// This config assumes a postgresql running locally
func aPostgresConfig() PostgresConfig {
	return PostgresConfig{
		ConnParams: map[string]string{
			"host":     "localhost",
			"user":     "postgres",
			"password": "",
			"dbname":   "guble",
			"sslmode":  "disable",
		},
		MaxIdleConns: 1,
		MaxOpenConns: 1,
	}
}

func invalidPostgresConfig() PostgresConfig {
	return PostgresConfig{
		ConnParams: map[string]string{
			"host":     "localhost",
			"user":     "",
			"password": "",
			"dbname":   "",
			"sslmode":  "disable",
		},
		MaxIdleConns: 1,
		MaxOpenConns: 1,
	}
}
<|endoftext|>


<commit_before>// Package secureheader adds some HTTP header fields widely
// considered to improve safety of HTTP requests. These fields
// are documented as follows:
//
//   Strict Transport Security: https://tools.ietf.org/html/rfc6797
//   Frame Options:             https://tools.ietf.org/html/draft-ietf-websec-x-frame-options-00
//   Cross Site Scripting:      http://msdn.microsoft.com/en-us/library/dd565647%28v=vs.85%29.aspx
//   Content Type Options:      http://msdn.microsoft.com/en-us/library/ie/gg622941%28v=vs.85%29.aspx
//   Content Security Policy:   https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html
//
// The easiest way to use this package:
//
//   http.ListenAndServe(addr, secureheader.DefaultConfig)
//
// DefaultConfig is initialized with conservative (safer and more
// restrictive) behavior. If you want to change that, set its
// fields to different values before calling ListenAndServe. See
// the example code below.
//
// This package was inspired by Twitter's secureheaders Ruby
// library. See https://github.com/twitter/secureheaders.
package secureheader

import (
	"net"
	"net/http"
	"strconv"
	"time"
)

// DefaultConfig is initialized with conservative (safer and more
// restrictive) behavior.
var DefaultConfig = &Config{
	HTTPSRedirect:          true,
	HTTPSUseForwardedProto: ShouldUseForwardedProto(),

	PermitClearLoopback: false,

	ContentTypeOptions: true,

	CSP:          false,
	CSPBody:      "default-src 'self'",
	CSPReportURI: "",

	CSPReportOnly:          false,
	CSPReportOnlyBody:      "default-src 'self'",
	CSPReportOnlyReportURI: "",

	HSTS:                  true,
	HSTSMaxAge:            300 * 24 * time.Hour,
	HSTSIncludeSubdomains: true,
	HSTSPreload:           false,

	FrameOptions:       true,
	FrameOptionsPolicy: Deny,

	XSSProtection:      true,
	XSSProtectionBlock: false,
}

type Config struct {
	// If true, redirects any request with scheme http to the
	// equivalent https URL.
	HTTPSRedirect          bool
	HTTPSUseForwardedProto bool

	// Allow cleartext (non-HTTPS) HTTP connections to a loopback
	// address, even if HTTPSRedirect is true.
	PermitClearLoopback bool

	// If true, sets X-Content-Type-Options to "nosniff".
	ContentTypeOptions bool

	// If true, send a Content-Security-Policy header. For more
	// information on deploying CSP, see for example
	// https://medium.com/sourceclear/content-security-policy-with-sentry-efb04f336f59
	// Dsiabled by default. If you set CSP = true,
	// the default policy is "default-src 'self'" and reporting is disabled.
	// To enable reporting, set CSPReportURI to your reporting endpoint.
	CSP          bool
	CSPBody      string
	CSPReportURI string

	// If true, the browser will report CSP violations, but won't enforce them
	// It *is* meaningful to set both headers
	// Content-Security-Policy *AND* Content-Security-Policy-Report-Only
	// and give them different bodys & report-uri's. The browser will
	// enforce the former, but only generate warnings on the latter.
	// Like CSPBody, the default is "default-src 'self'", and
	// Set CSPReportOnlyReportURI to your reporting endpoint.
	CSPReportOnly          bool
	CSPReportOnlyBody      string
	CSPReportOnlyReportURI string

	// If true, sets the HTTP Strict Transport Security header
	// field, which instructs browsers to send future requests
	// over HTTPS, even if the URL uses the unencrypted http
	// scheme.
	HSTS                  bool
	HSTSMaxAge            time.Duration
	HSTSIncludeSubdomains bool
	HSTSPreload           bool

	// If true, sets X-Frame-Options, to control when the request
	// should be displayed inside an HTML frame.
	FrameOptions       bool
	FrameOptionsPolicy FramePolicy

	// If true, sets X-XSS-Protection to "1", optionally with
	// "mode=block". See the official documentation, linked above,
	// for the meaning of these values.
	XSSProtection      bool
	XSSProtectionBlock bool

	// Used by ServeHTTP, after setting any extra headers, to
	// reply to the request. Next is typically nil, in which case
	// http.DefaultServeMux is used instead.
	Next http.Handler
}

// ServeHTTP sets header fields on w according to the options in
// c, then either replies directly or runs c.Next to reply.
// Typically c.Next is nil, in which case http.DefaultServeMux is
// used instead.
func (c *Config) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if c.HTTPSRedirect && !c.isHTTPS(r) && !c.okloopback(r) {
		url := *r.URL
		url.Scheme = "https"
		url.Host = r.Host
		http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
		return
	}
	if c.ContentTypeOptions {
		w.Header().Set("X-Content-Type-Options", "nosniff")
	}
	if c.CSP {
		v := c.CSPBody
		if c.CSPReportURI != "" {
			v += "; report-uri " + c.CSPReportURI
		}
		w.Header().Set("Content-Security-Policy", v)
	}
	if c.CSPReportOnly {
		v := c.CSPReportOnlyBody
		if c.CSPReportOnlyReportURI != "" {
			v += "; report-uri " + c.CSPReportOnlyReportURI
		}
		w.Header().Set("Content-Security-Policy-Report-Only", v)
	}
	if c.HSTS && c.isHTTPS(r) {
		v := "max-age=" + strconv.FormatInt(int64(c.HSTSMaxAge/time.Second), 10)
		if c.HSTSIncludeSubdomains {
			v += "; includeSubDomains"
		}
		if c.HSTSPreload {
			v += "; preload"
		}
		w.Header().Set("Strict-Transport-Security", v)
	}
	if c.FrameOptions {
		w.Header().Set("X-Frame-Options", string(c.FrameOptionsPolicy))
	}
	if c.XSSProtection {
		v := "1"
		if c.XSSProtectionBlock {
			v += "; mode=block"
		}
		w.Header().Set("X-XSS-Protection", v)
	}
	next := c.Next
	if next == nil {
		next = http.DefaultServeMux
	}
	next.ServeHTTP(w, r)
}

// Given that r is cleartext (not HTTPS), okloopback returns
// whether r is on a permitted loopback connection.
func (c *Config) okloopback(r *http.Request) bool {
	return c.PermitClearLoopback && isLoopback(r)
}

func (c *Config) isHTTPS(r *http.Request) bool {
	if c.HTTPSUseForwardedProto {
		return r.Header.Get("X-Forwarded-Proto") == "https"
	}
	return r.TLS != nil
}

// FramePolicy tells the browser under what circumstances to allow
// the response to be displayed inside an HTML frame. There are
// three options:
//
//   Deny            do not permit display in a frame
//   SameOrigin      permit display in a frame from the same origin
//   AllowFrom(url)  permit display in a frame from the given url
type FramePolicy string

const (
	Deny       FramePolicy = "DENY"
	SameOrigin FramePolicy = "SAMEORIGIN"
)

// AllowFrom returns a FramePolicy specifying that the requested
// resource should be included in a frame from only the given url.
func AllowFrom(url string) FramePolicy {
	return FramePolicy("ALLOW-FROM: " + url)
}

// ShouldUseForwardedProto returns whether to trust the
// X-Forwarded-Proto header field.
// DefaultConfig.HTTPSUseForwardedProto is initialized to this
// value.
//
// This value depends on the particular environment where the
// package is built. It is currently true iff build constraint
// "heroku" is satisfied.
func ShouldUseForwardedProto() bool {
	return defaultUseForwardedProto
}

func isLoopback(r *http.Request) bool {
	a, err := net.ResolveTCPAddr("tcp", r.RemoteAddr)
	return err == nil && a.IP.IsLoopback()
}
<commit_msg>Secure some securable references (#12)<commit_after>// Package secureheader adds some HTTP header fields widely
// considered to improve safety of HTTP requests. These fields
// are documented as follows:
//
//   Strict Transport Security: https://tools.ietf.org/html/rfc6797
//   Frame Options:             https://tools.ietf.org/html/draft-ietf-websec-x-frame-options-00
//   Cross Site Scripting:      https://msdn.microsoft.com/en-us/library/dd565647%28v=vs.85%29.aspx
//   Content Type Options:      https://msdn.microsoft.com/en-us/library/ie/gg622941%28v=vs.85%29.aspx
//   Content Security Policy:   https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html
//
// The easiest way to use this package:
//
//   http.ListenAndServe(addr, secureheader.DefaultConfig)
//
// DefaultConfig is initialized with conservative (safer and more
// restrictive) behavior. If you want to change that, set its
// fields to different values before calling ListenAndServe. See
// the example code below.
//
// This package was inspired by Twitter's secureheaders Ruby
// library. See https://github.com/twitter/secureheaders.
package secureheader

import (
	"net"
	"net/http"
	"strconv"
	"time"
)

// DefaultConfig is initialized with conservative (safer and more
// restrictive) behavior.
var DefaultConfig = &Config{
	HTTPSRedirect:          true,
	HTTPSUseForwardedProto: ShouldUseForwardedProto(),

	PermitClearLoopback: false,

	ContentTypeOptions: true,

	CSP:          false,
	CSPBody:      "default-src 'self'",
	CSPReportURI: "",

	CSPReportOnly:          false,
	CSPReportOnlyBody:      "default-src 'self'",
	CSPReportOnlyReportURI: "",

	HSTS:                  true,
	HSTSMaxAge:            300 * 24 * time.Hour,
	HSTSIncludeSubdomains: true,
	HSTSPreload:           false,

	FrameOptions:       true,
	FrameOptionsPolicy: Deny,

	XSSProtection:      true,
	XSSProtectionBlock: false,
}

type Config struct {
	// If true, redirects any request with scheme http to the
	// equivalent https URL.
	HTTPSRedirect          bool
	HTTPSUseForwardedProto bool

	// Allow cleartext (non-HTTPS) HTTP connections to a loopback
	// address, even if HTTPSRedirect is true.
	PermitClearLoopback bool

	// If true, sets X-Content-Type-Options to "nosniff".
	ContentTypeOptions bool

	// If true, send a Content-Security-Policy header. For more
	// information on deploying CSP, see for example
	// https://medium.com/sourceclear/content-security-policy-with-sentry-efb04f336f59
	// Dsiabled by default. If you set CSP = true,
	// the default policy is "default-src 'self'" and reporting is disabled.
	// To enable reporting, set CSPReportURI to your reporting endpoint.
	CSP          bool
	CSPBody      string
	CSPReportURI string

	// If true, the browser will report CSP violations, but won't enforce them
	// It *is* meaningful to set both headers
	// Content-Security-Policy *AND* Content-Security-Policy-Report-Only
	// and give them different bodys & report-uri's. The browser will
	// enforce the former, but only generate warnings on the latter.
	// Like CSPBody, the default is "default-src 'self'", and
	// Set CSPReportOnlyReportURI to your reporting endpoint.
	CSPReportOnly          bool
	CSPReportOnlyBody      string
	CSPReportOnlyReportURI string

	// If true, sets the HTTP Strict Transport Security header
	// field, which instructs browsers to send future requests
	// over HTTPS, even if the URL uses the unencrypted http
	// scheme.
	HSTS                  bool
	HSTSMaxAge            time.Duration
	HSTSIncludeSubdomains bool
	HSTSPreload           bool

	// If true, sets X-Frame-Options, to control when the request
	// should be displayed inside an HTML frame.
	FrameOptions       bool
	FrameOptionsPolicy FramePolicy

	// If true, sets X-XSS-Protection to "1", optionally with
	// "mode=block". See the official documentation, linked above,
	// for the meaning of these values.
	XSSProtection      bool
	XSSProtectionBlock bool

	// Used by ServeHTTP, after setting any extra headers, to
	// reply to the request. Next is typically nil, in which case
	// http.DefaultServeMux is used instead.
	Next http.Handler
}

// ServeHTTP sets header fields on w according to the options in
// c, then either replies directly or runs c.Next to reply.
// Typically c.Next is nil, in which case http.DefaultServeMux is
// used instead.
func (c *Config) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if c.HTTPSRedirect && !c.isHTTPS(r) && !c.okloopback(r) {
		url := *r.URL
		url.Scheme = "https"
		url.Host = r.Host
		http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
		return
	}
	if c.ContentTypeOptions {
		w.Header().Set("X-Content-Type-Options", "nosniff")
	}
	if c.CSP {
		v := c.CSPBody
		if c.CSPReportURI != "" {
			v += "; report-uri " + c.CSPReportURI
		}
		w.Header().Set("Content-Security-Policy", v)
	}
	if c.CSPReportOnly {
		v := c.CSPReportOnlyBody
		if c.CSPReportOnlyReportURI != "" {
			v += "; report-uri " + c.CSPReportOnlyReportURI
		}
		w.Header().Set("Content-Security-Policy-Report-Only", v)
	}
	if c.HSTS && c.isHTTPS(r) {
		v := "max-age=" + strconv.FormatInt(int64(c.HSTSMaxAge/time.Second), 10)
		if c.HSTSIncludeSubdomains {
			v += "; includeSubDomains"
		}
		if c.HSTSPreload {
			v += "; preload"
		}
		w.Header().Set("Strict-Transport-Security", v)
	}
	if c.FrameOptions {
		w.Header().Set("X-Frame-Options", string(c.FrameOptionsPolicy))
	}
	if c.XSSProtection {
		v := "1"
		if c.XSSProtectionBlock {
			v += "; mode=block"
		}
		w.Header().Set("X-XSS-Protection", v)
	}
	next := c.Next
	if next == nil {
		next = http.DefaultServeMux
	}
	next.ServeHTTP(w, r)
}

// Given that r is cleartext (not HTTPS), okloopback returns
// whether r is on a permitted loopback connection.
func (c *Config) okloopback(r *http.Request) bool {
	return c.PermitClearLoopback && isLoopback(r)
}

func (c *Config) isHTTPS(r *http.Request) bool {
	if c.HTTPSUseForwardedProto {
		return r.Header.Get("X-Forwarded-Proto") == "https"
	}
	return r.TLS != nil
}

// FramePolicy tells the browser under what circumstances to allow
// the response to be displayed inside an HTML frame. There are
// three options:
//
//   Deny            do not permit display in a frame
//   SameOrigin      permit display in a frame from the same origin
//   AllowFrom(url)  permit display in a frame from the given url
type FramePolicy string

const (
	Deny       FramePolicy = "DENY"
	SameOrigin FramePolicy = "SAMEORIGIN"
)

// AllowFrom returns a FramePolicy specifying that the requested
// resource should be included in a frame from only the given url.
func AllowFrom(url string) FramePolicy {
	return FramePolicy("ALLOW-FROM: " + url)
}

// ShouldUseForwardedProto returns whether to trust the
// X-Forwarded-Proto header field.
// DefaultConfig.HTTPSUseForwardedProto is initialized to this
// value.
//
// This value depends on the particular environment where the
// package is built. It is currently true iff build constraint
// "heroku" is satisfied.
func ShouldUseForwardedProto() bool {
	return defaultUseForwardedProto
}

func isLoopback(r *http.Request) bool {
	a, err := net.ResolveTCPAddr("tcp", r.RemoteAddr)
	return err == nil && a.IP.IsLoopback()
}
<|endoftext|>


<commit_before>package ginDoi

import (
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"html/template"
	"io/ioutil"
	"net/http"
	"path/filepath"
)

// Check the current user. Return a user if logged in
func loggedInUser(r *http.Request, pr *OauthProvider) (*DoiUser, error) {
	return &DoiUser{}, nil
}

func readBody(r *http.Request) (*string, error) {
	body, err := ioutil.ReadAll(r.Body)
	x := string(body)
	return &x, err
}

func DoDoiJob(w http.ResponseWriter, r *http.Request, jobQueue chan DoiJob, storage LocalStorage, op OauthProvider) {
	// Make sure we can only be called with an HTTP POST request.
	if r.Method != "POST" {
		w.Header().Set("Allow", "POST")
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	dReq := DoiReq{}
	//ToDo Error checking
	body, _ := ioutil.ReadAll(r.Body)
	json.Unmarshal(body, &dReq)
	log.WithFields(log.Fields{
		"request": fmt.Sprintf("%+v", dReq),
		"source":  "DoDoiJob",
	}).Debug("Unmarshaled a doi request")

	ok, err := op.ValidateToken(dReq.OauthLogin, dReq.Token)
	if err != nil {
		log.WithFields(log.Fields{
			"request": fmt.Sprintf("%+v", dReq),
			"source":  "DoDoiJob",
			"error":   err,
		}).Debug("User authentication Failed")
		dReq.Mess = MS_NOLOGIN
		w.WriteHeader(http.StatusUnauthorized)
		return
	}
	if ! ok {
		log.WithFields(log.Fields{
			"request": fmt.Sprintf("%+v", dReq),
			"source":  "DoDoiJob",
		}).Debug("Token not valid")
		dReq.Mess = MS_NOLOGIN
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	user, err := op.getUser(dReq.OauthLogin, dReq.Token)
	if err != nil {
		log.WithFields(log.Fields{
			"request": fmt.Sprintf("%+v", dReq),
			"source":  "DoDoiJob",
			"error":   err,
		}).Debug("Could not get userdata")
		dReq.Mess = MS_NOLOGIN
		w.WriteHeader(http.StatusUnauthorized)
		return
	}
	dReq.User = DoiUser{MainOId: user}
	//ToDo Error checking
	ds, _ := storage.GetDataSource()
	uuid, _ := ds.MakeUUID(dReq.URI, user)
	if ok, doiInfo := ds.ValidDoiFile(dReq.URI, user); !ok {
		w.WriteHeader(http.StatusBadRequest)
		return
	} else {
		doiInfo.UUID = uuid
		doi := storage.DProvider.MakeDoi(doiInfo)
		dReq.DoiInfo = *doiInfo
		key, err := op.AuthorizePull(user)
		if err != nil {
			log.WithFields(log.Fields{
				"source": "DoDoiJob",
				"error":  err,
			}).Error("Could not Authorize Pull")
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		job := DoiJob{Source: dReq.URI, Storage: storage, User: user, DoiReq: dReq, Name: doiInfo.UUID, Key: *key}
		jobQueue <- job
		// Render success.
		w.WriteHeader(http.StatusCreated)
		w.Write([]byte(fmt.Sprintf(MS_SERVERWORKS, storage.HttpBase+uuid, doi)))
	}
}

func InitDoiJob(w http.ResponseWriter, r *http.Request, ds DataSource, op OauthProvider,
	tp string) {
	log.Infof("Got a new DOI request")
	if err := r.ParseForm(); err != nil {
		log.WithFields(log.Fields{
			"source": "Init",
		}).Debug("Could not parse form data")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	URI := r.Form.Get("repo")
	token := r.Form.Get("token")
	username := r.Form.Get("user")
	dReq := DoiReq{URI: URI, OauthLogin: username, Token: token}
	log.WithFields(log.Fields{
		"request": fmt.Sprintf("%+v", dReq),
		"source":  "Init",
	}).Debug("Got DOI Request")

	t, err := template.ParseFiles(filepath.Join(tp, "initjob.html")) // Parse template file.
	if err != nil {
		log.WithFields(log.Fields{
			"source": "DoDoiJob",
			"error":  err,
		}).Debug("Could not parse init template")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// Test whether URi was provided
	if !(len(URI) > 0) {
		log.WithFields(log.Fields{
			"request": dReq,
			"source":  "Init",
			"error":   err,
		}).Debug("No Repo URI provided")
		dReq.Mess = MS_URIINVALID
		err := t.Execute(w, dReq)
		if err != nil {
			log.WithFields(log.Fields{
				"request": dReq,
				"source":  "Init",
				"error":   err,
			}).Debug("Template not parsed")
			return
		}
		return
	}

	// Test whether token was provided
	if !(len(token) > 0) {
		dReq.Mess = MS_NOTOKEN
		log.WithFields(log.Fields{
			"request": dReq,
			"source":  "Init",
			"error":   err,
		}).Debug("No Token provided")
		err := t.Execute(w, dReq)
		if err != nil {
			log.Print(err)
			return
		}
		return
	}

	// Test whether username was provided
	if !(len(username) > 0) {
		dReq.Mess = MS_NOUSER
		err := t.Execute(w, dReq)
		if err != nil {
			log.Print(err)
			return
		}
		return
	}

	// test user login
	ok, err := op.ValidateToken(username, token)
	if err != nil {
		log.WithFields(log.Fields{
			"request": fmt.Sprintf("%+v", dReq),
			"source":  "InitDoiJob",
			"error":   err,
		}).Debug("User authentication Failed")
		dReq.Mess = MS_NOLOGIN
		w.WriteHeader(http.StatusUnauthorized)
		return
	}
	if ! ok {
		log.WithFields(log.Fields{
			"request": fmt.Sprintf("%+v", dReq),
			"source":  "InitDoiJob",
		}).Debug("Token not valid")
		dReq.Mess = MS_NOLOGIN
		w.WriteHeader(http.StatusUnauthorized)
		t.Execute(w, dReq)
		return
	}

	// get user
	user, err := op.getUser(username, token)
	if err != nil {
		log.WithFields(log.Fields{
			"request": dReq,
			"source":  "Init",
			"error":   err,
		}).Debug("Could not authenticate user")
		dReq.Mess = MS_NOLOGIN
		t.Execute(w, dReq)
		return
	}

	// check for doifile
	if ok, doiInfo := ds.ValidDoiFile(URI, user); ok {
		log.WithFields(log.Fields{
			"doiInfo": doiInfo,
			"source":  "Init",
		}).Debug("Received Doi information")
		dReq.DoiInfo = *doiInfo
		err := t.Execute(w, dReq)
		if err != nil {
			log.WithFields(log.Fields{
				"request": dReq,
				"source":  "Init",
				"error":   err,
			}).Error("Could not parse template")
			return
		}
	} else if doiInfo != nil {
		log.WithFields(log.Fields{
			"doiInfo": doiInfo,
			"source":  "Init",
			"error":   err,
		}).Debug("Doifile File invalid")
		if doiInfo.Missing != nil {
			dReq.Mess = MS_INVALIDDOIFILE + " Issue: " + doiInfo.Missing[0]
		} else {
			dReq.Mess = MS_INVALIDDOIFILE + MS_ENCODING
		}
		t.Execute(w, dReq)
		if err != nil {
			log.WithFields(log.Fields{
				"request": dReq,
				"source":  "Init",
				"error":   err,
			}).Error("Could not parse template")
			return
		}
		return
	} else {
		dReq.Mess = MS_INVALIDDOIFILE
		t.Execute(w, dReq)
		if err != nil {
			log.WithFields(log.Fields{
				"request": dReq,
				"source":  "Init",
				"error":   err,
			}).Error("Could not parse template")
			return
		}
		return
	}
}
<commit_msg>Doify info should not contain link to the dataset<commit_after>package ginDoi

import (
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"html/template"
	"io/ioutil"
	"net/http"
	"path/filepath"
)

// Check the current user. Return a user if logged in
func loggedInUser(r *http.Request, pr *OauthProvider) (*DoiUser, error) {
	return &DoiUser{}, nil
}

func readBody(r *http.Request) (*string, error) {
	body, err := ioutil.ReadAll(r.Body)
	x := string(body)
	return &x, err
}

func DoDoiJob(w http.ResponseWriter, r *http.Request, jobQueue chan DoiJob, storage LocalStorage, op OauthProvider) {
	// Make sure we can only be called with an HTTP POST request.
	if r.Method != "POST" {
		w.Header().Set("Allow", "POST")
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	dReq := DoiReq{}
	//ToDo Error checking
	body, _ := ioutil.ReadAll(r.Body)
	json.Unmarshal(body, &dReq)
	log.WithFields(log.Fields{
		"request": fmt.Sprintf("%+v", dReq),
		"source":  "DoDoiJob",
	}).Debug("Unmarshaled a doi request")

	ok, err := op.ValidateToken(dReq.OauthLogin, dReq.Token)
	if err != nil {
		log.WithFields(log.Fields{
			"request": fmt.Sprintf("%+v", dReq),
			"source":  "DoDoiJob",
			"error":   err,
		}).Debug("User authentication Failed")
		dReq.Mess = MS_NOLOGIN
		w.WriteHeader(http.StatusUnauthorized)
		return
	}
	if ! ok {
		log.WithFields(log.Fields{
			"request": fmt.Sprintf("%+v", dReq),
			"source":  "DoDoiJob",
		}).Debug("Token not valid")
		dReq.Mess = MS_NOLOGIN
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	user, err := op.getUser(dReq.OauthLogin, dReq.Token)
	if err != nil {
		log.WithFields(log.Fields{
			"request": fmt.Sprintf("%+v", dReq),
			"source":  "DoDoiJob",
			"error":   err,
		}).Debug("Could not get userdata")
		dReq.Mess = MS_NOLOGIN
		w.WriteHeader(http.StatusUnauthorized)
		return
	}
	dReq.User = DoiUser{MainOId: user}
	//ToDo Error checking
	ds, _ := storage.GetDataSource()
	uuid, _ := ds.MakeUUID(dReq.URI, user)
	if ok, doiInfo := ds.ValidDoiFile(dReq.URI, user); !ok {
		w.WriteHeader(http.StatusBadRequest)
		return
	} else {
		doiInfo.UUID = uuid
		doi := storage.DProvider.MakeDoi(doiInfo)
		dReq.DoiInfo = *doiInfo
		key, err := op.AuthorizePull(user)
		if err != nil {
			log.WithFields(log.Fields{
				"source": "DoDoiJob",
				"error":  err,
			}).Error("Could not Authorize Pull")
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		job := DoiJob{Source: dReq.URI, Storage: storage, User: user, DoiReq: dReq, Name: doiInfo.UUID, Key: *key}
		jobQueue <- job
		// Render success.
		w.WriteHeader(http.StatusCreated)
		w.Write([]byte(fmt.Sprintf(MS_SERVERWORKS, doi)))
	}
}

func InitDoiJob(w http.ResponseWriter, r *http.Request, ds DataSource, op OauthProvider,
	tp string) {
	log.Infof("Got a new DOI request")
	if err := r.ParseForm(); err != nil {
		log.WithFields(log.Fields{
			"source": "Init",
		}).Debug("Could not parse form data")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	URI := r.Form.Get("repo")
	token := r.Form.Get("token")
	username := r.Form.Get("user")
	dReq := DoiReq{URI: URI, OauthLogin: username, Token: token}
	log.WithFields(log.Fields{
		"request": fmt.Sprintf("%+v", dReq),
		"source":  "Init",
	}).Debug("Got DOI Request")

	t, err := template.ParseFiles(filepath.Join(tp, "initjob.html")) // Parse template file.
	if err != nil {
		log.WithFields(log.Fields{
			"source": "DoDoiJob",
			"error":  err,
		}).Debug("Could not parse init template")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// Test whether URi was provided
	if !(len(URI) > 0) {
		log.WithFields(log.Fields{
			"request": dReq,
			"source":  "Init",
			"error":   err,
		}).Debug("No Repo URI provided")
		dReq.Mess = MS_URIINVALID
		err := t.Execute(w, dReq)
		if err != nil {
			log.WithFields(log.Fields{
				"request": dReq,
				"source":  "Init",
				"error":   err,
			}).Debug("Template not parsed")
			return
		}
		return
	}

	// Test whether token was provided
	if !(len(token) > 0) {
		dReq.Mess = MS_NOTOKEN
		log.WithFields(log.Fields{
			"request": dReq,
			"source":  "Init",
			"error":   err,
		}).Debug("No Token provided")
		err := t.Execute(w, dReq)
		if err != nil {
			log.Print(err)
			return
		}
		return
	}

	// Test whether username was provided
	if !(len(username) > 0) {
		dReq.Mess = MS_NOUSER
		err := t.Execute(w, dReq)
		if err != nil {
			log.Print(err)
			return
		}
		return
	}

	// test user login
	ok, err := op.ValidateToken(username, token)
	if err != nil {
		log.WithFields(log.Fields{
			"request": fmt.Sprintf("%+v", dReq),
			"source":  "InitDoiJob",
			"error":   err,
		}).Debug("User authentication Failed")
		dReq.Mess = MS_NOLOGIN
		w.WriteHeader(http.StatusUnauthorized)
		return
	}
	if ! ok {
		log.WithFields(log.Fields{
			"request": fmt.Sprintf("%+v", dReq),
			"source":  "InitDoiJob",
		}).Debug("Token not valid")
		dReq.Mess = MS_NOLOGIN
		w.WriteHeader(http.StatusUnauthorized)
		t.Execute(w, dReq)
		return
	}

	// get user
	user, err := op.getUser(username, token)
	if err != nil {
		log.WithFields(log.Fields{
			"request": dReq,
			"source":  "Init",
			"error":   err,
		}).Debug("Could not authenticate user")
		dReq.Mess = MS_NOLOGIN
		t.Execute(w, dReq)
		return
	}

	// check for doifile
	if ok, doiInfo := ds.ValidDoiFile(URI, user); ok {
		log.WithFields(log.Fields{
			"doiInfo": doiInfo,
			"source":  "Init",
		}).Debug("Received Doi information")
		dReq.DoiInfo = *doiInfo
		err := t.Execute(w, dReq)
		if err != nil {
			log.WithFields(log.Fields{
				"request": dReq,
				"source":  "Init",
				"error":   err,
			}).Error("Could not parse template")
			return
		}
	} else if doiInfo != nil {
		log.WithFields(log.Fields{
			"doiInfo": doiInfo,
			"source":  "Init",
			"error":   err,
		}).Debug("Doifile File invalid")
		if doiInfo.Missing != nil {
			dReq.Mess = MS_INVALIDDOIFILE + " Issue: " + doiInfo.Missing[0]
		} else {
			dReq.Mess = MS_INVALIDDOIFILE + MS_ENCODING
		}
		t.Execute(w, dReq)
		if err != nil {
			log.WithFields(log.Fields{
				"request": dReq,
				"source":  "Init",
				"error":   err,
			}).Error("Could not parse template")
			return
		}
		return
	} else {
		dReq.Mess = MS_INVALIDDOIFILE
		t.Execute(w, dReq)
		if err != nil {
			log.WithFields(log.Fields{
				"request": dReq,
				"source":  "Init",
				"error":   err,
			}).Error("Could not parse template")
			return
		}
		return
	}
}
<|endoftext|>


<commit_before>package lib

import (
	"fmt"
	"strings"
    "sort"
    "os"
    "io"
    "io/ioutil"
    "net/http"
    "runtime"
)

var (
    vUpdateEndpoint = updateEndpoint
    vUpdateBucket = updateBucket
)

var specChineseUpdate = SpecText{

	synopsisText: "更新ossutil",

	paramText: "[options]",

	syntaxText: `
    ossutil update [-f]
`,

	detailHelpText: `
    该命令检查当前ossutil的版本与最新版本，输出两者的版本号，如果有更新版本，询问是否
    进行升级。如果指定了--force选项，则不询问，当有可用更新时，直接升级。

`,

	sampleText: `
    ossutil update
    ossutil update -f
`,
}

var specEnglishUpdate = SpecText{

	synopsisText: "Update ossutil",

	paramText: "[options]",

	syntaxText: `
    ossutil update [-f]
`,

	detailHelpText: `
    The command check version of current ossutil and get the latest version, output the
    versions, if any updated version exists, the command ask you for upgrading. If --force
    option is specified, the command upgrade without asking.
`,

	sampleText: `
    ossutil update
    ossutil update -f
`,
}

// UpdateCommand is the command update ossutil
type UpdateCommand struct {
	command Command
}

var updateCommand = UpdateCommand{
	command: Command{
		name:        "update",
		nameAlias:   []string{""},
		minArgc:     0,
		maxArgc:     0,
		specChinese: specChineseUpdate,
		specEnglish: specEnglishUpdate,
		group:       GroupTypeAdditionalCommand,
		validOptionNames: []string{
			OptionForce,
            OptionRetryTimes,
            OptionLanguage,
		},
	},
}

// function for RewriteLoadConfiger interface
func (uc *UpdateCommand) rewriteLoadConfig(configFile string) error {
    // read config file, if error exist, do not print error
    var err error
    if uc.command.configOptions, err = LoadConfig(configFile); err != nil {
        uc.command.configOptions = OptionMapType{}
    }
	return nil
}

// function for FormatHelper interface
func (uc *UpdateCommand) formatHelpForWhole() string {
	return uc.command.formatHelpForWhole()
}

func (uc *UpdateCommand) formatIndependHelp() string {
	return uc.command.formatIndependHelp()
}

// Init simulate inheritance, and polymorphism
func (uc *UpdateCommand) Init(args []string, options OptionMapType) error {
	return uc.command.Init(args, options, uc)
}

// RunCommand simulate inheritance, and polymorphism
func (uc *UpdateCommand) RunCommand() error {
    force, _ := GetBool(OptionForce, uc.command.options)
    language, _ := GetString(OptionLanguage, uc.command.options)
    language = strings.ToLower(language)

    // get lastest version
    version, err := uc.getLastestVersion()
    if err != nil {
        return fmt.Errorf("get lastest vsersion error, %s", err.Error())
    }

    if language == LEnglishLanguage {
        fmt.Printf("current version is: %s, the lastest version is: %s", Version, version)
    } else {
        fmt.Printf("当前版本为：%s，最新版本为：%s", Version, version)
    }
    if version == Version {
        if language == LEnglishLanguage {
            fmt.Println(", current version is the lastest version, no need to update.")
        } else {
            fmt.Println("，当前版本即为最新版本，无需更新。")
        }
        return nil
    }
    fmt.Println("")

    if !force {
        if language == LEnglishLanguage {
            fmt.Printf("sure to update ossutil(y or n)? ")
        } else {
            fmt.Printf("确定更新版本(y or n)? ")
        }

        var val string
        if _, err := fmt.Scanln(&val); err == nil && (val == "yes" || val == "y") {
            return uc.updateVersion(version, language)
        }

        if language == LEnglishLanguage {
            fmt.Printf("operation is canceled.")
        } else {
            fmt.Println("操作取消。")
        }
    } else {
        return uc.updateVersion(version, language)
    }
    return nil
}

func (uc *UpdateCommand) getLastestVersion() (string, error) {
    if err := uc.anonymousGetToFileRetry(vUpdateBucket, updateVersionObject, updateTmpVersionFile); err != nil {
        return "", err
    }

    v, err := ioutil.ReadFile(updateTmpVersionFile)
    if err != nil {
        return "", err
    }
    versionStr := strings.TrimSpace(strings.Trim(string(v), "\n"))

    // get version list and sort
    sli := strings.Split(versionStr, "\n")
    vl := []string{}
    for _, vstr := range sli {
        vl = append(vl, strings.TrimSpace(strings.Trim(string(vstr), "\n")))
    }
    sort.Strings(vl)
    version := vl[len(vl) - 1]

    os.Remove(updateTmpVersionFile)

    return version, nil
}

func (uc *UpdateCommand) anonymousGetToFileRetry(bucketName, objectName, filePath string) error {
    host := fmt.Sprintf("http://%s.%s/%s", bucketName, vUpdateEndpoint, objectName)
	retryTimes, _ := GetInt(OptionRetryTimes, uc.command.options)
	for i := 1; ; i++ {
        err := uc.ossAnonymousGetToFile(host, filePath)
		if err == nil {
			return err
		}
		if int64(i) >= retryTimes {
			return ObjectError{err, objectName}
		}
	}
}

func (uc *UpdateCommand) ossAnonymousGetToFile(host, filePath string) error {
    response, _ := http.Get(host)
    defer response.Body.Close()
    statusCode := response.StatusCode
    body, _ := ioutil.ReadAll(response.Body)
    if statusCode >= 300 {
        return fmt.Errorf(string(body))
    }

    fd, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0660)
    if err != nil {
        return err
    }
    defer fd.Close()

    _, err = io.WriteString(fd, string(body))
    if err != nil {
        return err
    }
    return nil
}

func (uc *UpdateCommand) updateVersion(version, language string) error {
    // get binary path
    filePath, renameFilePath := getBinaryPath()

    // get binary mode
    f, err := os.Stat(filePath)
    if err != nil {
        return err
    }
    mode := f.Mode()

    // rename the current binary to another one
    if err := os.Rename(filePath, renameFilePath); err != nil {
        return fmt.Errorf("update binary error, %s", err.Error())
    }

    // download the binary of the specified version
    if err := uc.getBinary(filePath, version); err != nil {
        fmt.Println("uc:", uc)
        uc.revertRename(filePath, renameFilePath)
        return fmt.Errorf("download binary of version: %s error, %s", version, err.Error())
    }

    if err := os.Chmod(filePath, mode); err != nil {
        uc.revertRename(filePath, renameFilePath)
        return fmt.Errorf("chmod binary error, %s", err.Error())
    }

    // remove the current one
    if runtime.GOOS != "windows" {
        if err := os.Remove(renameFilePath); err != nil {
            uc.revertRename(filePath, renameFilePath)
            return fmt.Errorf("remove old binary error, %s", err.Error())
        }
    }

    if language == LEnglishLanguage {
        fmt.Println("Update Success!")
    } else {
        fmt.Println("更新成功!")
    }
    return nil
}

func (uc *UpdateCommand) revertRename(filePath, renameFilePath string) error {
    if err := os.Remove(filePath); err != nil {
        return err
    }
    if err := os.Rename(renameFilePath, filePath); err != nil {
        return err
    }
    return nil
}

func (uc *UpdateCommand) getBinary(filePath, version string) error {
    // get os type
    var object string
    switch runtime.GOOS {
    case "darwin":
        object = updateBinaryMac64
    case "windows":
        object = updateBinaryWindow64
        if runtime.GOARCH == "386" {
            object = updateBinaryWindow32
        }
    default:
        object = updateBinaryLinux
    }

    object = version + "/" + object

    if err := uc.anonymousGetToFileRetry(vUpdateBucket, object, filePath); err != nil {
        return err
    }

    return nil
}

<commit_msg>modify case<commit_after>package lib

import (
	"fmt"
	"strings"
    "sort"
    "os"
    "io"
    "io/ioutil"
    "net/http"
    "runtime"
)

var (
    vUpdateEndpoint = updateEndpoint
    vUpdateBucket = updateBucket
)

var specChineseUpdate = SpecText{

	synopsisText: "更新ossutil",

	paramText: "[options]",

	syntaxText: `
    ossutil update [-f]
`,

	detailHelpText: `
    该命令检查当前ossutil的版本与最新版本，输出两者的版本号，如果有更新版本，询问是否
    进行升级。如果指定了--force选项，则不询问，当有可用更新时，直接升级。

`,

	sampleText: `
    ossutil update
    ossutil update -f
`,
}

var specEnglishUpdate = SpecText{

	synopsisText: "Update ossutil",

	paramText: "[options]",

	syntaxText: `
    ossutil update [-f]
`,

	detailHelpText: `
    The command check version of current ossutil and get the latest version, output the
    versions, if any updated version exists, the command ask you for upgrading. If --force
    option is specified, the command upgrade without asking.
`,

	sampleText: `
    ossutil update
    ossutil update -f
`,
}

// UpdateCommand is the command update ossutil
type UpdateCommand struct {
	command Command
}

var updateCommand = UpdateCommand{
	command: Command{
		name:        "update",
		nameAlias:   []string{""},
		minArgc:     0,
		maxArgc:     0,
		specChinese: specChineseUpdate,
		specEnglish: specEnglishUpdate,
		group:       GroupTypeAdditionalCommand,
		validOptionNames: []string{
			OptionForce,
            OptionRetryTimes,
            OptionLanguage,
		},
	},
}

// function for RewriteLoadConfiger interface
func (uc *UpdateCommand) rewriteLoadConfig(configFile string) error {
    // read config file, if error exist, do not print error
    var err error
    if uc.command.configOptions, err = LoadConfig(configFile); err != nil {
        uc.command.configOptions = OptionMapType{}
    }
	return nil
}

// function for FormatHelper interface
func (uc *UpdateCommand) formatHelpForWhole() string {
	return uc.command.formatHelpForWhole()
}

func (uc *UpdateCommand) formatIndependHelp() string {
	return uc.command.formatIndependHelp()
}

// Init simulate inheritance, and polymorphism
func (uc *UpdateCommand) Init(args []string, options OptionMapType) error {
	return uc.command.Init(args, options, uc)
}

// RunCommand simulate inheritance, and polymorphism
func (uc *UpdateCommand) RunCommand() error {
    force, _ := GetBool(OptionForce, uc.command.options)
    language, _ := GetString(OptionLanguage, uc.command.options)
    language = strings.ToLower(language)

    // get lastest version
    version, err := uc.getLastestVersion()
    if err != nil {
        return fmt.Errorf("get lastest vsersion error, %s", err.Error())
    }

    if language == LEnglishLanguage {
        fmt.Printf("current version is: %s, the lastest version is: %s", Version, version)
    } else {
        fmt.Printf("当前版本为：%s，最新版本为：%s", Version, version)
    }
    if version == Version {
        if language == LEnglishLanguage {
            fmt.Println(", current version is the lastest version, no need to update.")
        } else {
            fmt.Println("，当前版本即为最新版本，无需更新。")
        }
        return nil
    }
    fmt.Println("")

    if !force {
        if language == LEnglishLanguage {
            fmt.Printf("sure to update ossutil(y or n)? ")
        } else {
            fmt.Printf("确定更新版本(y or n)? ")
        }

        var val string
        if _, err := fmt.Scanln(&val); err == nil && (val == "yes" || val == "y") {
            return uc.updateVersion(version, language)
        }

        if language == LEnglishLanguage {
            fmt.Printf("operation is canceled.")
        } else {
            fmt.Println("操作取消。")
        }
    } else {
        return uc.updateVersion(version, language)
    }
    return nil
}

func (uc *UpdateCommand) getLastestVersion() (string, error) {
    if err := uc.anonymousGetToFileRetry(vUpdateBucket, updateVersionObject, updateTmpVersionFile); err != nil {
        return "", err
    }

    v, err := ioutil.ReadFile(updateTmpVersionFile)
    if err != nil {
        return "", err
    }
    versionStr := strings.TrimSpace(strings.Trim(string(v), "\n"))

    // get version list and sort
    sli := strings.Split(versionStr, "\n")
    vl := []string{}
    for _, vstr := range sli {
        vl = append(vl, strings.TrimSpace(strings.Trim(string(vstr), "\n")))
    }
    sort.Strings(vl)
    version := vl[len(vl) - 1]

    os.Remove(updateTmpVersionFile)

    return version, nil
}

func (uc *UpdateCommand) anonymousGetToFileRetry(bucketName, objectName, filePath string) error {
    host := fmt.Sprintf("http://%s.%s/%s", bucketName, vUpdateEndpoint, objectName)
	retryTimes, _ := GetInt(OptionRetryTimes, uc.command.options)
	for i := 1; ; i++ {
        err := uc.ossAnonymousGetToFile(host, filePath)
		if err == nil {
			return err
		}
		if int64(i) >= retryTimes {
			return ObjectError{err, objectName}
		}
	}
}

func (uc *UpdateCommand) ossAnonymousGetToFile(host, filePath string) error {
    response, _ := http.Get(host)
    defer response.Body.Close()
    statusCode := response.StatusCode
    body, _ := ioutil.ReadAll(response.Body)
    if statusCode >= 300 {
        return fmt.Errorf(string(body))
    }

    fd, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0660)
    if err != nil {
        return err
    }
    defer fd.Close()

    _, err = io.WriteString(fd, string(body))
    if err != nil {
        return err
    }
    return nil
}

func (uc *UpdateCommand) updateVersion(version, language string) error {
    // get binary path
    filePath, renameFilePath := getBinaryPath()

    // get binary mode
    f, err := os.Stat(filePath)
    if err != nil {
        return err
    }
    mode := f.Mode()

    // rename the current binary to another one
    if err := os.Rename(filePath, renameFilePath); err != nil {
        return fmt.Errorf("update binary error, %s", err.Error())
    }

    // download the binary of the specified version
    if err := uc.getBinary(filePath, version); err != nil {
        fmt.Println("uc:", uc)
        //uc.revertRename(filePath, renameFilePath)
        return fmt.Errorf("download binary of version: %s error, %s", version, err.Error())
    }

    if err := os.Chmod(filePath, mode); err != nil {
        uc.revertRename(filePath, renameFilePath)
        return fmt.Errorf("chmod binary error, %s", err.Error())
    }

    // remove the current one
    if runtime.GOOS != "windows" {
        if err := os.Remove(renameFilePath); err != nil {
            uc.revertRename(filePath, renameFilePath)
            return fmt.Errorf("remove old binary error, %s", err.Error())
        }
    }

    if language == LEnglishLanguage {
        fmt.Println("Update Success!")
    } else {
        fmt.Println("更新成功!")
    }
    return nil
}

func (uc *UpdateCommand) revertRename(filePath, renameFilePath string) error {
    if err := os.Remove(filePath); err != nil {
        return err
    }
    if err := os.Rename(renameFilePath, filePath); err != nil {
        return err
    }
    return nil
}

func (uc *UpdateCommand) getBinary(filePath, version string) error {
    // get os type
    var object string
    switch runtime.GOOS {
    case "darwin":
        object = updateBinaryMac64
    case "windows":
        object = updateBinaryWindow64
        if runtime.GOARCH == "386" {
            object = updateBinaryWindow32
        }
    default:
        object = updateBinaryLinux
    }

    object = version + "/" + object

    if err := uc.anonymousGetToFileRetry(vUpdateBucket, object, filePath); err != nil {
        return err
    }

    return nil
}

<|endoftext|>


<commit_before>package codewriter

import (
	"bytes"
	"fmt"
	"testing"
)

func TestSetFileName(t *testing.T) {
	testCases := []struct {
		filename string
		want     string
	}{
		{"", "// \n" + asmEnd},
		{"foo.txt", "// foo.txt\n" + asmEnd},
	}

	var buf bytes.Buffer
	cw := New(&buf)
	for _, tt := range testCases {
		if e := cw.SetFileName(tt.filename); e != nil {
			t.Fatalf("SetFileName failed: %v", e)
		}
		if e := cw.Close(); e != nil {
			t.Fatalf("Close failed: %v", e)
		}

		got := buf.String()
		if got != tt.want {
			t.Errorf("got = %q; want = %q", got, tt.want)
		}

		buf.Reset()
	}
}

func TestWriteArithmetic(t *testing.T) {
	testCases := []struct {
		cmd  string
		want string
	}{
		{"add", asmBinary("M=D+M") + asmEnd},
		{"sub", asmBinary("M=M-D") + asmEnd},
		{"and", asmBinary("M=D&M") + asmEnd},
		{"or", asmBinary("M=D|M") + asmEnd},
		{"neg", asmUnary("-") + asmEnd},
		{"not", asmUnary("!") + asmEnd},
		{"eq", asmCompare("JEQ", "LABEL0", "LABEL1") + asmEnd},
		{"gt", asmCompare("JGT", "LABEL0", "LABEL1") + asmEnd},
		{"lt", asmCompare("JLT", "LABEL0", "LABEL1") + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		if e := cw.WriteArithmetic(tt.cmd); e != nil {
			t.Fatalf("WriteArithmetic failed: %s", e.Error())
		}
		if e := cw.Close(); e != nil {
			t.Fatalf("Close failed: %s", e.Error())
		}

		got := buf.String()
		if got != tt.want {
			t.Errorf("src = %s\ngot =\n%s\nwant =\n%s", tt.cmd, got, tt.want)
		}
	}
}

func TestWriteArithmeticError(t *testing.T) {
	testCases := []struct {
		cmd string
	}{
		{"foo"},
		{"bar"},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		if e := cw.WriteArithmetic(tt.cmd); e == nil {
			t.Fatalf("WriteArithmetic should return error: cmd = %s", tt.cmd)
		}
	}
}

func TestWritePushPop(t *testing.T) {
	testCases := []struct {
		cmd  string
		seg  string
		idx  uint
		want string
	}{
		{"push", "constant", 0, asmPushConst(0) + asmEnd},
		{"push", "constant", 1, asmPushConst(1) + asmEnd},
		{"push", "local", 0, asmPush("LCL", 0) + asmEnd},
		{"push", "argument", 0, asmPush("ARG", 0) + asmEnd},
		{"push", "this", 0, asmPush("THIS", 0) + asmEnd},
		{"push", "that", 0, asmPush("THAT", 0) + asmEnd},
		{"push", "temp", 0, asmPush("R5", 0) + asmEnd},
		{"push", "temp", 7, asmPush("R5", 7) + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		if e := cw.WritePushPop(tt.cmd, tt.seg, tt.idx); e != nil {
			t.Fatalf("WritePushPop failed: %s", e.Error())
		}
		if e := cw.Close(); e != nil {
			t.Fatalf("Close failed: %s", e.Error())
		}

		got := buf.String()
		if got != tt.want {
			t.Errorf("src = %s %s %d\ngot =\n%s\nwant =\n%s", tt.cmd, tt.seg, tt.idx, got, tt.want)
		}

		buf.Reset()
		cw.err = nil
	}
}

func TestPushVal(t *testing.T) {
	testCases := []struct {
		v    uint
		want string
	}{
		{bitFalse, asmPushConst(bitFalse) + asmEnd},
		{1, asmPushConst(1) + asmEnd},
		{2, asmPushConst(2) + asmEnd},
	}

	var buf bytes.Buffer
	cw := New(&buf)
	for _, tt := range testCases {
		if cw.pushVal(tt.v); cw.err != nil {
			t.Fatalf("pushStack failed: %v", cw.err)
		}
		if e := cw.Close(); e != nil {
			t.Fatalf("Close failed: %s", e.Error())
		}

		got := buf.String()
		if got != tt.want {
			t.Errorf("v = %d\ngot =\n%s\nwant =\n%s", tt.v, got, tt.want)
		}

		buf.Reset()
		cw.err = nil
	}
}

func asmPushConst(v uint) string {
	tpl := `@%d
D=A
@SP
A=M
M=D
@SP
AM=M+1
`
	return fmt.Sprintf(tpl, v)
}

func asmPush(symb string, idx uint) string {
	tpl := `@%d
D=A
@%s
A=D+M
D=M
@SP
A=M
M=D
@SP
AM=M+1
`
	return fmt.Sprintf(tpl, idx, symb)
}

func asmUnary(op string) string {
	tpl := `@SP
AM=M-1
M=%sM
@SP
AM=M+1
`
	return fmt.Sprintf(tpl, op)
}

func TestUnary(t *testing.T) {
	testCases := []struct {
		cmd  string
		want string
	}{
		{"neg", asmUnary("-") + asmEnd},
		{"not", asmUnary("!") + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		cw.unary(tt.cmd)
		_ = cw.Close()

		got := buf.String()
		if got != tt.want {
			t.Errorf("cmd = %s\ngot =\n%s\nwant =\n%s", tt.cmd, got, tt.want)
		}
	}
}

func asmBinary(op string) string {
	tpl := `@SP
AM=M-1
D=M
@SP
AM=M-1
%s
@SP
AM=M+1
`
	return fmt.Sprintf(tpl, op)
}

func TestBinary(t *testing.T) {
	testCases := []struct {
		cmd  string
		want string
	}{
		{"add", asmBinary("M=D+M") + asmEnd},
		{"sub", asmBinary("M=M-D") + asmEnd},
		{"and", asmBinary("M=D&M") + asmEnd},
		{"or", asmBinary("M=D|M") + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		cw.binary(tt.cmd)
		_ = cw.Close()

		got := buf.String()
		if got != tt.want {
			t.Errorf("cmd = %s\ngot =\n%s\nwant =\n%s", tt.cmd, got, tt.want)
		}
	}
}

func asmCompare(op, labelJmp, labelEnd string) string {
	tpl := `@SP
AM=M-1
D=M
@SP
AM=M-1
D=M-D
@%s
D;%s
@0
D=A
@SP
A=M
M=D
@%s
0;JMP
(%s)
@SP
A=M
M=-1
(%s)
@SP
AM=M+1
`
	return fmt.Sprintf(tpl, labelJmp, op, labelEnd, labelJmp, labelEnd)
}

func TestCompare(t *testing.T) {
	testCases := []struct {
		cmd  string
		want string
	}{
		{"eq", asmCompare("JEQ", "LABEL0", "LABEL1") + asmEnd},
		{"gt", asmCompare("JGT", "LABEL0", "LABEL1") + asmEnd},
		{"lt", asmCompare("JLT", "LABEL0", "LABEL1") + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		cw.compare(tt.cmd)
		_ = cw.Close()

		got := buf.String()
		if got != tt.want {
			t.Errorf("cmd = %s\ngot =\n%s\nwant =\n%s", tt.cmd, got, tt.want)
		}
	}
}

func TestAcmd(t *testing.T) {
	testCases := []struct {
		addr string
		want string
	}{
		{"16", "@16\n" + asmEnd},
		{"i", "@i\n" + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		cw.acmd(tt.addr)
		if cw.err != nil {
			t.Fatalf("error writing aCommand: %s", cw.err)
		}
		_ = cw.Close()

		got := buf.String()
		if got != tt.want {
			t.Errorf("got = %s; want = %s", got, tt.want)
		}
	}
}

func TestCcmdj(t *testing.T) {
	testCases := []struct {
		dest, comp, jump string
		want             string
	}{
		{"M", "M+D", "", "M=M+D\n" + asmEnd},
		{"", "D", "JMP", "D;JMP\n" + asmEnd},
		{"AMD", "D|M", "JEQ", "AMD=D|M;JEQ\n" + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		cw.ccmdj(tt.dest, tt.comp, tt.jump)
		if cw.err != nil {
			t.Fatalf("error writing cCommand: %s", cw.err)
		}
		_ = cw.Close()

		got := buf.String()
		if got != tt.want {
			t.Errorf("got = %s; want = %s", got, tt.want)
		}
	}
}

func TestLcmd(t *testing.T) {
	testCases := []struct {
		label string
		want  string
	}{
		{"LABEL", "(LABEL)\n" + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		cw.lcmd(tt.label)
		if cw.err != nil {
			t.Fatalf("error writing lCommand: %s", cw.err)
		}
		_ = cw.Close()

		got := buf.String()
		if got != tt.want {
			t.Errorf("got = %s; want = %s", got, tt.want)
		}
	}
}

var asmEnd = `(END)
@END
0;JMP
`
<commit_msg>07/codewriter: add a test for `pop local`<commit_after>package codewriter

import (
	"bytes"
	"fmt"
	"testing"
)

func TestSetFileName(t *testing.T) {
	testCases := []struct {
		filename string
		want     string
	}{
		{"", "// \n" + asmEnd},
		{"foo.txt", "// foo.txt\n" + asmEnd},
	}

	var buf bytes.Buffer
	cw := New(&buf)
	for _, tt := range testCases {
		if e := cw.SetFileName(tt.filename); e != nil {
			t.Fatalf("SetFileName failed: %v", e)
		}
		if e := cw.Close(); e != nil {
			t.Fatalf("Close failed: %v", e)
		}

		got := buf.String()
		if got != tt.want {
			t.Errorf("got = %q; want = %q", got, tt.want)
		}

		buf.Reset()
	}
}

func TestWriteArithmetic(t *testing.T) {
	testCases := []struct {
		cmd  string
		want string
	}{
		{"add", asmBinary("M=D+M") + asmEnd},
		{"sub", asmBinary("M=M-D") + asmEnd},
		{"and", asmBinary("M=D&M") + asmEnd},
		{"or", asmBinary("M=D|M") + asmEnd},
		{"neg", asmUnary("-") + asmEnd},
		{"not", asmUnary("!") + asmEnd},
		{"eq", asmCompare("JEQ", "LABEL0", "LABEL1") + asmEnd},
		{"gt", asmCompare("JGT", "LABEL0", "LABEL1") + asmEnd},
		{"lt", asmCompare("JLT", "LABEL0", "LABEL1") + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		if e := cw.WriteArithmetic(tt.cmd); e != nil {
			t.Fatalf("WriteArithmetic failed: %s", e.Error())
		}
		if e := cw.Close(); e != nil {
			t.Fatalf("Close failed: %s", e.Error())
		}

		got := buf.String()
		if got != tt.want {
			t.Errorf("src = %s\ngot =\n%s\nwant =\n%s", tt.cmd, got, tt.want)
		}
	}
}

func TestWriteArithmeticError(t *testing.T) {
	testCases := []struct {
		cmd string
	}{
		{"foo"},
		{"bar"},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		if e := cw.WriteArithmetic(tt.cmd); e == nil {
			t.Fatalf("WriteArithmetic should return error: cmd = %s", tt.cmd)
		}
	}
}

func TestWritePushPop(t *testing.T) {
	testCases := []struct {
		cmd  string
		seg  string
		idx  uint
		want string
	}{
		{"push", "constant", 0, asmPushConst(0) + asmEnd},
		{"push", "constant", 1, asmPushConst(1) + asmEnd},
		{"push", "local", 0, asmPush("LCL", 0) + asmEnd},
		{"push", "argument", 0, asmPush("ARG", 0) + asmEnd},
		{"push", "this", 0, asmPush("THIS", 0) + asmEnd},
		{"push", "that", 0, asmPush("THAT", 0) + asmEnd},
		{"push", "temp", 0, asmPush("R5", 0) + asmEnd},
		{"push", "temp", 7, asmPush("R5", 7) + asmEnd},
		{"pop", "local", 0, asmPush("R5", 0) + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		if e := cw.WritePushPop(tt.cmd, tt.seg, tt.idx); e != nil {
			t.Fatalf("WritePushPop failed: %s", e.Error())
		}
		if e := cw.Close(); e != nil {
			t.Fatalf("Close failed: %s", e.Error())
		}

		got := buf.String()
		if got != tt.want {
			t.Errorf("src = %s %s %d\ngot =\n%s\nwant =\n%s", tt.cmd, tt.seg, tt.idx, got, tt.want)
		}

		buf.Reset()
		cw.err = nil
	}
}

func TestPushVal(t *testing.T) {
	testCases := []struct {
		v    uint
		want string
	}{
		{bitFalse, asmPushConst(bitFalse) + asmEnd},
		{1, asmPushConst(1) + asmEnd},
		{2, asmPushConst(2) + asmEnd},
	}

	var buf bytes.Buffer
	cw := New(&buf)
	for _, tt := range testCases {
		if cw.pushVal(tt.v); cw.err != nil {
			t.Fatalf("pushStack failed: %v", cw.err)
		}
		if e := cw.Close(); e != nil {
			t.Fatalf("Close failed: %s", e.Error())
		}

		got := buf.String()
		if got != tt.want {
			t.Errorf("v = %d\ngot =\n%s\nwant =\n%s", tt.v, got, tt.want)
		}

		buf.Reset()
		cw.err = nil
	}
}

func asmPushConst(v uint) string {
	tpl := `@%d
D=A
@SP
A=M
M=D
@SP
AM=M+1
`
	return fmt.Sprintf(tpl, v)
}

func asmPush(symb string, idx uint) string {
	tpl := `@%d
D=A
@%s
A=D+M
D=M
@SP
A=M
M=D
@SP
AM=M+1
`
	return fmt.Sprintf(tpl, idx, symb)
}

func asmPop(symb string, idx uint) string {
	tpl := `@%d
D=A
@%s
AD=D+M
@R13
M=D
@SP
AM=M-1
D=M
@R13
A=M
M=D
`
	return fmt.Sprintf(tpl, idx, symb)
}

func asmUnary(op string) string {
	tpl := `@SP
AM=M-1
M=%sM
@SP
AM=M+1
`
	return fmt.Sprintf(tpl, op)
}

func TestUnary(t *testing.T) {
	testCases := []struct {
		cmd  string
		want string
	}{
		{"neg", asmUnary("-") + asmEnd},
		{"not", asmUnary("!") + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		cw.unary(tt.cmd)
		_ = cw.Close()

		got := buf.String()
		if got != tt.want {
			t.Errorf("cmd = %s\ngot =\n%s\nwant =\n%s", tt.cmd, got, tt.want)
		}
	}
}

func asmBinary(op string) string {
	tpl := `@SP
AM=M-1
D=M
@SP
AM=M-1
%s
@SP
AM=M+1
`
	return fmt.Sprintf(tpl, op)
}

func TestBinary(t *testing.T) {
	testCases := []struct {
		cmd  string
		want string
	}{
		{"add", asmBinary("M=D+M") + asmEnd},
		{"sub", asmBinary("M=M-D") + asmEnd},
		{"and", asmBinary("M=D&M") + asmEnd},
		{"or", asmBinary("M=D|M") + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		cw.binary(tt.cmd)
		_ = cw.Close()

		got := buf.String()
		if got != tt.want {
			t.Errorf("cmd = %s\ngot =\n%s\nwant =\n%s", tt.cmd, got, tt.want)
		}
	}
}

func asmCompare(op, labelJmp, labelEnd string) string {
	tpl := `@SP
AM=M-1
D=M
@SP
AM=M-1
D=M-D
@%s
D;%s
@0
D=A
@SP
A=M
M=D
@%s
0;JMP
(%s)
@SP
A=M
M=-1
(%s)
@SP
AM=M+1
`
	return fmt.Sprintf(tpl, labelJmp, op, labelEnd, labelJmp, labelEnd)
}

func TestCompare(t *testing.T) {
	testCases := []struct {
		cmd  string
		want string
	}{
		{"eq", asmCompare("JEQ", "LABEL0", "LABEL1") + asmEnd},
		{"gt", asmCompare("JGT", "LABEL0", "LABEL1") + asmEnd},
		{"lt", asmCompare("JLT", "LABEL0", "LABEL1") + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		cw.compare(tt.cmd)
		_ = cw.Close()

		got := buf.String()
		if got != tt.want {
			t.Errorf("cmd = %s\ngot =\n%s\nwant =\n%s", tt.cmd, got, tt.want)
		}
	}
}

func TestAcmd(t *testing.T) {
	testCases := []struct {
		addr string
		want string
	}{
		{"16", "@16\n" + asmEnd},
		{"i", "@i\n" + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		cw.acmd(tt.addr)
		if cw.err != nil {
			t.Fatalf("error writing aCommand: %s", cw.err)
		}
		_ = cw.Close()

		got := buf.String()
		if got != tt.want {
			t.Errorf("got = %s; want = %s", got, tt.want)
		}
	}
}

func TestCcmdj(t *testing.T) {
	testCases := []struct {
		dest, comp, jump string
		want             string
	}{
		{"M", "M+D", "", "M=M+D\n" + asmEnd},
		{"", "D", "JMP", "D;JMP\n" + asmEnd},
		{"AMD", "D|M", "JEQ", "AMD=D|M;JEQ\n" + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		cw.ccmdj(tt.dest, tt.comp, tt.jump)
		if cw.err != nil {
			t.Fatalf("error writing cCommand: %s", cw.err)
		}
		_ = cw.Close()

		got := buf.String()
		if got != tt.want {
			t.Errorf("got = %s; want = %s", got, tt.want)
		}
	}
}

func TestLcmd(t *testing.T) {
	testCases := []struct {
		label string
		want  string
	}{
		{"LABEL", "(LABEL)\n" + asmEnd},
	}

	for _, tt := range testCases {
		var buf bytes.Buffer
		cw := New(&buf)

		cw.lcmd(tt.label)
		if cw.err != nil {
			t.Fatalf("error writing lCommand: %s", cw.err)
		}
		_ = cw.Close()

		got := buf.String()
		if got != tt.want {
			t.Errorf("got = %s; want = %s", got, tt.want)
		}
	}
}

var asmEnd = `(END)
@END
0;JMP
`
<|endoftext|>


<commit_before>// package kernctl provides access to the OSX Kext Control API for sending and
// receiving messages from kernel extensions.
package kernctl

// #include <stdlib.h>
// #include <sys/socket.h>
// #include <sys/kern_control.h>
// #include <string.h>
import "C"

import (
	"fmt"
	"syscall"
	"unicode/utf8"
	"unsafe"
)

const (
	SYSPROTO_CONTROL = 2
	AF_SYSTEM        = 32
	PF_SYSTEM        = AF_SYSTEM
	AF_SYS_CONTROL   = 2
	CTLIOCGINFO      = 3227799043
)

type Conn struct {
	CtlId uint32
	UnitId uint32
	fd int
}

type Message interface {
	Bytes() []byte
}

func (conn *Conn) socket() (int, error) {
	if conn.fd == 0 {
		fd, err := syscall.Socket(PF_SYSTEM, syscall.SOCK_DGRAM, SYSPROTO_CONTROL)
		fmt.Println("fd: ", fd)
		if err != nil {
			return 0, err
		}
		conn.fd = fd
	}

	return conn.fd, nil
}

// Connect will create a connection to the control socket for the
// kernel extension named in CtlName
func (conn *Conn) Connect() (error) {
	_, errno := conn.connect()
	var err error = nil
	if errno != 0 {
		err = fmt.Errorf("failed to connect to kext. errno: ", errno)
	}

	return err
}

// Close closes a connection to a kernel extension
func (conn *Conn) Close() {
	if conn.fd != 0 {
		syscall.Close(conn.fd)
	}
	conn.fd = 0
}

func (conn *Conn) SendCommand(msg Message) {
	fd, err := conn.socket()
	fmt.Println("sending ", msg, "(", msg.Bytes(), ") to ", fd)
	n, err := syscall.Write(fd, msg.Bytes()[:])
	fmt.Println("wrote ", n, " bytes. err: ", err)
}

func (conn *Conn) Select() error {
	fd, _ := conn.socket()
	timeout := &syscall.Timeval{
		Sec: 1,
		Usec: 0,
	}
	var r, w, e syscall.FdSet

	n := syscall.Select(fd, &r, &w, &e, timeout)
	fmt.Println("select:", n, fd, r, w, e)
	return nil
}

func  (conn *Conn) createSockAddr() C.struct_sockaddr_ctl {
	var sockaddr C.struct_sockaddr_ctl
	sockaddr.sc_len = C.u_char(unsafe.Sizeof(C.struct_sockaddr_ctl{}))
	sockaddr.sc_family = C.u_char(PF_SYSTEM)
	sockaddr.ss_sysaddr = C.u_int16_t(AF_SYS_CONTROL)
	sockaddr.sc_id = C.u_int32_t(conn.CtlId)
	sockaddr.sc_unit = C.u_int32_t(conn.UnitId)
	return sockaddr
}

func (conn *Conn) connect() (ret int64, err syscall.Errno) {
	sockLen := 32
	sa := conn.createSockAddr()
	fd, _ := conn.socket()
	r1, r2, e := syscall.Syscall(syscall.SYS_CONNECT, uintptr(fd), uintptr(unsafe.Pointer(&sa)), uintptr(sockLen))
	fmt.Println("connect response: ", r1, " :", r2, " e:", e)
	return int64(r1), e
}

// Create a new connection to a named kext's kernel control socket
func NewConnByName(CtlName string) *Conn {
	conn := new(Conn)
	fd, _ := conn.socket()
	conn.CtlId, _ = GetCtlId(fd, CtlName)
	return conn
}

func NewConnByCtlId(CtlId uint32, UnitId uint32) *Conn {
	conn := new(Conn)
	conn.CtlId = CtlId
	conn.UnitId = UnitId
	return conn
}

// GetCtlId retrieves the kext control id for the kext named in CtlName using
// the socket file descriptor fd.
func GetCtlId(fd int, CtlName string) (uint32, error) {
	var info C.struct_ctl_info
	info.ctl_id = 0
	C.memcpy(unsafe.Pointer(&info.ctl_name), unsafe.Pointer(C.CString(CtlName)),
		C.size_t(utf8.RuneCountInString(CtlName)))
	syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), CTLIOCGINFO,
		uintptr(unsafe.Pointer(&info)))
	fmt.Println("CtlId: ", uint32(info.ctl_id))
	return uint32(info.ctl_id), nil
}
<commit_msg>fix Select function to actually receive data<commit_after>// package kernctl provides access to the OSX Kext Control API for sending and
// receiving messages from kernel extensions.
package kernctl

// #include <stdlib.h>
// #include <sys/socket.h>
// #include <sys/kern_control.h>
// #include <string.h>
import "C"

import (
	"fmt"
	"syscall"
	"unicode/utf8"
	"unsafe"
)

const (
	SYSPROTO_CONTROL = 2
	AF_SYSTEM        = 32
	PF_SYSTEM        = AF_SYSTEM
	AF_SYS_CONTROL   = 2
	CTLIOCGINFO      = 3227799043
)

type Conn struct {
	CtlId  uint32
	UnitId uint32
	fd     int
}

type Message interface {
	Bytes() []byte
}

func (conn *Conn) socket() (int, error) {
	if conn.fd == 0 {
		fd, err := syscall.Socket(PF_SYSTEM, syscall.SOCK_DGRAM, SYSPROTO_CONTROL)
		fmt.Println("fd: ", fd)
		if err != nil {
			return 0, err
		}
		conn.fd = fd
	}

	return conn.fd, nil
}

// Connect will create a connection to the control socket for the
// kernel extension named in CtlName
func (conn *Conn) Connect() error {
	_, errno := conn.connect()
	var err error = nil
	if errno != 0 {
		err = fmt.Errorf("failed to connect to kext. errno: ", errno)
	}

	return err
}

// Close closes a connection to a kernel extension
func (conn *Conn) Close() {
	if conn.fd != 0 {
		syscall.Close(conn.fd)
	}
	conn.fd = 0
}

func (conn *Conn) SendCommand(msg Message) {
	fd, err := conn.socket()
	fmt.Println("sending ", msg, "(", msg.Bytes(), ") to ", fd)
	n, err := syscall.Write(fd, msg.Bytes()[:])
	fmt.Println("wrote ", n, " bytes. err: ", err)
}

func (conn *Conn) Select(readBuf []byte) (error, int) {
	fd, _ := conn.socket()

	timeout := &syscall.Timeval{
		Sec:  1,
		Usec: 0,
	}

	r := &syscall.FdSet{}
	FD_ZERO(r)
	FD_SET(r, fd)

	syscall.Select(fd+1, r, nil, nil, timeout)
	bytesRead := 0
	if FD_ISSET(r, fd) {
		n, _, err := syscall.Recvfrom(fd, readBuf, 0)
		bytesRead = n

		if err != nil {
			fmt.Println(err)
		}
	}
	return nil, bytesRead
}

func (conn *Conn) createSockAddr() C.struct_sockaddr_ctl {
	var sockaddr C.struct_sockaddr_ctl
	sockaddr.sc_len = C.u_char(unsafe.Sizeof(C.struct_sockaddr_ctl{}))
	sockaddr.sc_family = C.u_char(PF_SYSTEM)
	sockaddr.ss_sysaddr = C.u_int16_t(AF_SYS_CONTROL)
	sockaddr.sc_id = C.u_int32_t(conn.CtlId)
	sockaddr.sc_unit = C.u_int32_t(conn.UnitId)
	return sockaddr
}

func (conn *Conn) connect() (ret int64, err syscall.Errno) {
	sockLen := 32
	sa := conn.createSockAddr()
	fd, _ := conn.socket()
	r1, r2, e := syscall.Syscall(syscall.SYS_CONNECT, uintptr(fd), uintptr(unsafe.Pointer(&sa)), uintptr(sockLen))
	fmt.Println("connect response: ", r1, " :", r2, " e:", e)
	return int64(r1), e
}

// Create a new connection to a named kext's kernel control socket
func NewConnByName(CtlName string) *Conn {
	conn := new(Conn)
	fd, _ := conn.socket()
	conn.CtlId, _ = GetCtlId(fd, CtlName)
	return conn
}

func NewConnByCtlId(CtlId uint32, UnitId uint32) *Conn {
	conn := new(Conn)
	conn.CtlId = CtlId
	conn.UnitId = UnitId
	return conn
}

// GetCtlId retrieves the kext control id for the kext named in CtlName using
// the socket file descriptor fd.
func GetCtlId(fd int, CtlName string) (uint32, error) {
	var info C.struct_ctl_info
	info.ctl_id = 0
	C.memcpy(unsafe.Pointer(&info.ctl_name), unsafe.Pointer(C.CString(CtlName)),
		C.size_t(utf8.RuneCountInString(CtlName)))
	syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), CTLIOCGINFO,
		uintptr(unsafe.Pointer(&info)))
	fmt.Println("CtlId: ", uint32(info.ctl_id))
	return uint32(info.ctl_id), nil
}

// stolen from https://github.com/pebbe/zmq2
func FD_SET(p *syscall.FdSet, i int) {
	p.Bits[i/64] |= 1 << uint(i) % 64
}

func FD_ISSET(p *syscall.FdSet, i int) bool {
	return (p.Bits[i/64] & (1 << uint(i) % 64)) != 0
}

func FD_ZERO(p *syscall.FdSet) {
	for i := range p.Bits {
		p.Bits[i] = 0
	}
}
<|endoftext|>