<commit_before>// Copyright 2015
// Author: huangjunwei@youmi.net

package blog4go

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"strconv"
	"sync"
	"time"
)

// 各种日志结构接口
type LogWriter interface {
	// 关闭log writer的处理方法
	// 善后
	Close()

	// 用于内部写log的方法
	write(level Level, format string)
	writef(level Level, format string, args ...interface{})

	// 供用户强制刷日志到输出
	Flush()
}

var DefaultFileLogWriter *FileLogWriter = new(FileLogWriter)

const (
	// 好像buffer size 调大点benchmark效果更好
	DefaultBufferSize = 4096
	// 浮点数默认精确值
	DefaultPrecise = 2
)

// 装逼的logger
type FileLogWriter struct {
	level Level

	// log文件
	filename string
	file     *os.File
	writer   *bufio.Writer

	// TODO logrotate
	rotate bool

	lock   *sync.Mutex
	closed bool
}

// 时间格式化的cache
type timeFormatCacheType struct {
	now    time.Time // 这个字段暂时没什么作用, 好像可以尝试用来做logrotate
	format string
}

// 包初始化函数
func init() {
	timeCache.now = time.Now()
	timeCache.format = timeCache.now.Format("[2006/01/02:15:04:05]")
}

// 创建file writer
func NewFileLogWriter(filename string) (fileWriter *FileLogWriter, err error) {
	fileWriter = new(FileLogWriter)

	// 打开文件描述符
	file, err := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0644))
	if nil != err {
		return nil, err
	}
	fileWriter.filename = filename
	fileWriter.file = file
	fileWriter.writer = bufio.NewWriterSize(file, DefaultBufferSize)
	fileWriter.lock = new(sync.Mutex)
	fileWriter.closed = false

	go fileWriter.daemon()

	return fileWriter, nil
}

func (self *FileLogWriter) Close() {
	self.lock.Lock()
	self.writer.Flush()
	self.file.Close()
	self.writer = nil
	self.closed = true
	self.lock.Unlock()
}

func (self *FileLogWriter) Flush() {
	self.writer.Flush()
}

func (self *FileLogWriter) SetLevel(level Level) *FileLogWriter {
	self.level = level
	return self
}

func (self *FileLogWriter) GetLevel() Level {
	return self.level
}

// 用全局的timeCache好像比较好
// 实例的timeCache没那么好统一更新
var timeCache = timeFormatCacheType{}

// 常驻goroutine, 更新格式化的时间及logrotate
func (self *FileLogWriter) daemon() {
	t := time.Tick(1 * time.Second)

DaemonLoop:
	for {
		select {
		case <-t:
			if self.closed {
				break DaemonLoop
			}

			now := time.Now()
			timeCache.now = now
			timeCache.format = now.Format("[2006/01/02:15:04:05]")
		}
	}
}

func (self *FileLogWriter) write(level Level, format string, args ...interface{}) {
	if level < self.level {
		return
	}

	self.lock.Lock()
	defer self.lock.Unlock()

	if self.closed {
		return
	}

	self.writer.WriteString(timeCache.format)
	self.writer.WriteString(level.Prefix())
	self.writer.WriteString(format)
	self.writer.WriteString("\n")
}

// 格式化构造message
// 使用 % 作占位符
func (self *FileLogWriter) writef(level Level, format string, args ...interface{}) (err error) {
	if level < self.level {
		return
	}

	self.lock.Lock()
	defer self.lock.Unlock()

	if self.closed {
		return
	}

	self.writer.WriteString(timeCache.format)
	self.writer.WriteString(level.Prefix())

	// 识别占位符标记
	var tag bool = false
	var tagPos int = 0
	// 转义字符标记
	var escape bool = false
	// 在处理的args 下标
	var n int = 0
	// 未输出的，第一个普通字符位置
	var last int = 0 //

	for i := 0; i < len(format); i++ {
		if tag {
			switch format[i] {
			// 类型检查/ 特殊字符处理
			// 占位符，有意义部分
			// 字符串
			// %s
			case 's':
				if escape {
					escape = false
				}

				if str, ok := args[n].(string); ok {
					self.writer.WriteString(str)
					n++
					last = i + 1
				} else {
					return errors.New("Wrong format type.")
				}
				tag = false
			// 整型
			// %d
			case 'd':
				if escape {
					escape = false
				}

				// 判断数据类型
				if number, ok := args[n].(int); ok {
					self.writer.WriteString(strconv.Itoa(number))
					n++
					last = i + 1
				} else {
					return errors.New("Wrong format type.")
				}
				tag = false
			// 浮点型
			// %.xf
			case 'f':
				if escape {
					escape = false
				}

				// 还没想到好的解决方案，先用fmt自带的
				self.writer.WriteString(fmt.Sprintf(format[tagPos:i+1], args[n]))
				n++
				last = i + 1
				tag = false

				//if f, ok := args[n].(float64); ok {
				//// 获取精确度
				//prec, err := strconv.ParseInt(format[i-1:i], 10, 0)
				//if nil != err {
				//// 如果f前不是数字，是%
				//prec = DefaultPrecise
				//}

				//self.writer.WriteString(strconv.FormatFloat(f, 'f', int(prec), 64))
				////self.writer.WriteString(strconv.FormatFloat(f, 'f', -1, 64))
				//n++
				//last = i + 1
				//} else {
				//return errors.New("Wrong format type.")
				//}
			// Value
			// {xxx:xxx}
			case 'v':
				if escape {
					escape = false
				}

				// 还没想到好的解决方案，先用fmt自带的
				fmt.Println(format[tagPos : i+1])
				self.writer.WriteString(fmt.Sprintf(format[tagPos:i+1], args[n]))
				n++
				last = i + 1
				tag = false
			// 布尔型
			// %t
			case 't':
				if escape {
					escape = false
				}

				if b, ok := args[n].(bool); ok {
					self.writer.WriteString(strconv.FormatBool(b))
					n++
					last = i + 1
				} else {
					return errors.New("Wrong format type.")
				}
				tag = false
			//转义符
			case '\\':
				if escape {
					self.writer.WriteString("\\")
				}
				escape = !escape
			//默认
			default:

			}

		} else {
			// 占位符，百分号
			if '%' == format[i] && !escape {
				tag = true
				tagPos = i
				self.writer.WriteString(format[last:i])
				escape = false
			}
		}
	}
	self.writer.WriteString(format[last:])
	self.writer.WriteString("\n")

	return
}

func (self *FileLogWriter) Debug(format string) {
	self.write(DEBUG, format)
}

func (self *FileLogWriter) Debugf(format string, args ...interface{}) {
	self.writef(DEBUG, format, args...)
}

func (self *FileLogWriter) Trace(format string) {
	self.write(TRACE, format)
}

func (self *FileLogWriter) Tracef(format string, args ...interface{}) {
	self.writef(TRACE, format, args...)
}

func (self *FileLogWriter) Info(format string) {
	self.write(INFO, format)
}

func (self *FileLogWriter) Infof(format string, args ...interface{}) {
	self.writef(INFO, format, args...)
}

func (self *FileLogWriter) Warn(format string) {
	self.write(WARNING, format)
}

func (self *FileLogWriter) Warnf(format string, args ...interface{}) {
	self.writef(WARNING, format, args...)
}

func (self *FileLogWriter) Error(format string) {
	self.write(ERROR, format)
}

func (self *FileLogWriter) Errorf(format string, args ...interface{}) {
	self.writef(ERROR, format, args...)
}

func (self *FileLogWriter) Critical(format string) {
	self.write(CRITICAL, format)
}

func (self *FileLogWriter) Criticalf(format string, args ...interface{}) {
	self.writef(CRITICAL, format, args...)
}
<commit_msg>增加logrotate的代码<commit_after>// Copyright 2015
// Author: huangjunwei@youmi.net

package blog4go

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"strconv"
	"sync"
	"time"
)

// 各种日志结构接口
type LogWriter interface {
	// 关闭log writer的处理方法
	// 善后
	Close()

	// 用于内部写log的方法
	write(level Level, format string)
	writef(level Level, format string, args ...interface{})

	// 供用户强制刷日志到输出
	Flush()
}

var DefaultFileLogWriter *FileLogWriter = new(FileLogWriter)

const (
	// 好像buffer size 调大点benchmark效果更好
	DefaultBufferSize = 4096
	// 浮点数默认精确值
	DefaultPrecise = 2

	// 换行符
	// End Of Line
	EOL = "\n"

	// 时间前缀的格式
	PrefixTimeFormat = "[2006/01/02:15:04:05]"

	// Logrotate 后缀
	RotatePosfix = "-2006-01-02"
)

// 装逼的logger
type FileLogWriter struct {
	level Level

	// log文件
	filename string
	file     *os.File
	writer   *bufio.Writer

	// logrotate暂定一天一文件
	rotate bool

	// 互斥锁，用户互斥调用bufio
	lock *sync.Mutex

	// writer 关闭标识
	closed bool
}

// 时间格式化的cache
type timeFormatCacheType struct {
	now time.Time // 这个字段暂时没什么作用, 好像可以尝试用来做logrotate
	// 日期
	day int
	// 时间格式化结果
	format string
}

// 包初始化函数
func init() {
	timeCache.now = time.Now()
	timeCache.format = timeCache.now.Format(PrefixTimeFormat)
}

// 创建file writer
func NewFileLogWriter(filename string) (fileWriter *FileLogWriter, err error) {
	fileWriter = new(FileLogWriter)

	// 打开文件描述符
	file, err := os.OpenFile(filename+time.Now().Format(RotatePosfix), os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0644))
	if nil != err {
		return nil, err
	}
	fileWriter.filename = filename
	fileWriter.file = file
	fileWriter.writer = bufio.NewWriterSize(file, DefaultBufferSize)
	fileWriter.lock = new(sync.Mutex)
	fileWriter.closed = false

	go fileWriter.daemon()

	return fileWriter, nil
}

func (self *FileLogWriter) Close() {
	self.lock.Lock()
	self.writer.Flush()
	self.file.Close()
	self.writer = nil
	self.closed = true
	self.lock.Unlock()
}

func (self *FileLogWriter) Flush() {
	self.writer.Flush()
}

func (self *FileLogWriter) SetLevel(level Level) *FileLogWriter {
	self.level = level
	return self
}

func (self *FileLogWriter) GetLevel() Level {
	return self.level
}

// 用全局的timeCache好像比较好
// 实例的timeCache没那么好统一更新
var timeCache = timeFormatCacheType{}

// 常驻goroutine, 更新格式化的时间及logrotate
func (self *FileLogWriter) daemon() {
	t := time.Tick(1 * time.Second)

DaemonLoop:
	for {
		select {
		case <-t:
			if self.closed {
				break DaemonLoop
			}

			now := time.Now()
			day := now.Day()
			timeCache.now = now
			timeCache.day = day
			timeCache.format = now.Format(PrefixTimeFormat)

			// 判断是否需要logrotate
			if timeCache.day != day {
				// 此时需要logrotate, 切换bufio中的文件io.Writer
				file, err := os.OpenFile(self.filename+now.Format(RotatePosfix), os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0644))

				// TODO 文件打开错误要处理
				if nil != err {
					continue
				}

				self.file = file
				self.writer.Reset(file)
			}
		}
	}
}

func (self *FileLogWriter) write(level Level, format string, args ...interface{}) {
	if level < self.level {
		return
	}

	self.lock.Lock()
	defer self.lock.Unlock()

	if self.closed {
		return
	}

	self.writer.WriteString(timeCache.format)
	self.writer.WriteString(level.Prefix())
	self.writer.WriteString(format)
	self.writer.WriteString(EOL)
}

// 格式化构造message
// 使用 % 作占位符
func (self *FileLogWriter) writef(level Level, format string, args ...interface{}) (err error) {
	if level < self.level {
		return
	}

	self.lock.Lock()
	defer self.lock.Unlock()

	if self.closed {
		return
	}

	self.writer.WriteString(timeCache.format)
	self.writer.WriteString(level.Prefix())

	// 识别占位符标记
	var tag bool = false
	var tagPos int = 0
	// 转义字符标记
	var escape bool = false
	// 在处理的args 下标
	var n int = 0
	// 未输出的，第一个普通字符位置
	var last int = 0 //

	for i := 0; i < len(format); i++ {
		if tag {
			switch format[i] {
			// 类型检查/ 特殊字符处理
			// 占位符，有意义部分
			// 字符串
			// %s
			case 's':
				if escape {
					escape = false
				}

				if str, ok := args[n].(string); ok {
					self.writer.WriteString(str)
					n++
					last = i + 1
				} else {
					return errors.New("Wrong format type.")
				}
				tag = false
			// 整型
			// %d
			case 'd':
				if escape {
					escape = false
				}

				// 判断数据类型
				if number, ok := args[n].(int); ok {
					self.writer.WriteString(strconv.Itoa(number))
					n++
					last = i + 1
				} else {
					return errors.New("Wrong format type.")
				}
				tag = false
			// 浮点型
			// %.xf
			case 'f':
				if escape {
					escape = false
				}

				// 还没想到好的解决方案，先用fmt自带的
				self.writer.WriteString(fmt.Sprintf(format[tagPos:i+1], args[n]))
				n++
				last = i + 1
				tag = false

				//if f, ok := args[n].(float64); ok {
				//// 获取精确度
				//prec, err := strconv.ParseInt(format[i-1:i], 10, 0)
				//if nil != err {
				//// 如果f前不是数字，是%
				//prec = DefaultPrecise
				//}

				//self.writer.WriteString(strconv.FormatFloat(f, 'f', int(prec), 64))
				////self.writer.WriteString(strconv.FormatFloat(f, 'f', -1, 64))
				//n++
				//last = i + 1
				//} else {
				//return errors.New("Wrong format type.")
				//}
			// Value
			// {xxx:xxx}
			case 'v':
				if escape {
					escape = false
				}

				// 还没想到好的解决方案，先用fmt自带的
				self.writer.WriteString(fmt.Sprintf(format[tagPos:i+1], args[n]))
				n++
				last = i + 1
				tag = false
			// 布尔型
			// %t
			case 't':
				if escape {
					escape = false
				}

				if b, ok := args[n].(bool); ok {
					self.writer.WriteString(strconv.FormatBool(b))
					n++
					last = i + 1
				} else {
					return errors.New("Wrong format type.")
				}
				tag = false
			//转义符
			case '\\':
				if escape {
					self.writer.WriteString("\\")
				}
				escape = !escape
			//默认
			default:

			}

		} else {
			// 占位符，百分号
			if '%' == format[i] && !escape {
				tag = true
				tagPos = i
				self.writer.WriteString(format[last:i])
				escape = false
			}
		}
	}
	self.writer.WriteString(format[last:])
	self.writer.WriteString(EOL)

	return
}

func (self *FileLogWriter) Debug(format string) {
	self.write(DEBUG, format)
}

func (self *FileLogWriter) Debugf(format string, args ...interface{}) {
	self.writef(DEBUG, format, args...)
}

func (self *FileLogWriter) Trace(format string) {
	self.write(TRACE, format)
}

func (self *FileLogWriter) Tracef(format string, args ...interface{}) {
	self.writef(TRACE, format, args...)
}

func (self *FileLogWriter) Info(format string) {
	self.write(INFO, format)
}

func (self *FileLogWriter) Infof(format string, args ...interface{}) {
	self.writef(INFO, format, args...)
}

func (self *FileLogWriter) Warn(format string) {
	self.write(WARNING, format)
}

func (self *FileLogWriter) Warnf(format string, args ...interface{}) {
	self.writef(WARNING, format, args...)
}

func (self *FileLogWriter) Error(format string) {
	self.write(ERROR, format)
}

func (self *FileLogWriter) Errorf(format string, args ...interface{}) {
	self.writef(ERROR, format, args...)
}

func (self *FileLogWriter) Critical(format string) {
	self.write(CRITICAL, format)
}

func (self *FileLogWriter) Criticalf(format string, args ...interface{}) {
	self.writef(CRITICAL, format, args...)
}
<|endoftext|>


<commit_before>// Copyright 2015
// Author: huangjunwei@youmi.net

package blog4go

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"os"
	"time"
)

type Level int

const (
	DEBUG Level = iota
	TRACE
	INFO
	WARNING
	ERROR
	CRITICAL
)

var (
	levelStrings = [...]string{"DEBUG", "TRAC", "INFO", "WARN", "ERROR", "CRITAL"}
)

func (self Level) valid() bool {
	if DEBUG > self || CRITICAL < self {
		return false
	}
	return true
}

func (self Level) String() string {
	if !self.valid() {
		return "UNKNOWN"
	}
	return levelStrings[self]
}

// 单条日志记录结构体
type LogRecord struct {
	level   Level
	message string
}

func (self *LogRecord) String() string {
	var b bytes.Buffer
	now := time.Now().Format("2006-01-02 15:04:05")
	b.WriteString(now)
	b.WriteString(" [" + self.level.String() + "] ")
	b.WriteString(self.message)

	return b.String()
}

// 各种日志结构接口
type LogWriter interface {
	// 关闭log writer的处理方法
	// 善后
	Close()

	// 用于内部写log的方法
	write(level Level, format string, args ...interface{})

	// 供用户强制刷日志到输出
	Flush()
}

// DefaultFileLogWriter.c 为无缓冲channel
var DefaultFileLogWriter *FileLogWriter = new(FileLogWriter)

const (
	DefaultBufferSize = 4096
)

// 装逼的logger
type FileLogWriter struct {
	level Level

	// log文件
	filename string
	file     *os.File
	writer   *bufio.Writer

	// logrotate
	rotate bool
}

// 包初始化函数
func init() {

}

func NewFileLogWriter(filename string) (fileWriter *FileLogWriter, err error) {
	fileWriter = new(FileLogWriter)

	// 打开文件描述符
	file, err := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0644))
	if nil != err {
		return nil, err
	}
	fileWriter.filename = filename
	fileWriter.file = file
	fileWriter.writer = bufio.NewWriterSize(file, DefaultBufferSize)

	return fileWriter, nil
}

func (self *FileLogWriter) Close() {
	self.writer.Flush()
	self.file.Close()
}

func (self *FileLogWriter) Flush() {
	self.writer.Flush()
}

func (self *FileLogWriter) SetLevel(level Level) *FileLogWriter {
	self.level = level
	return self
}

func (self *FileLogWriter) GetLevel() Level {
	return self.level
}

func (self *FileLogWriter) write(level Level, format string, args ...interface{}) {
	if level < self.level {
		return
	}

	// TODO 优化format
	now := time.Now().Format("[2006/01/02:15:04:05]")
	self.writer.WriteString(now + " [" + level.String() + "] ")
	self.writer.WriteString(format + "\n")

	//self.writer.Flush()
}

func (self *FileLogWriter) writef(level Level, format string, args ...interface{}) (err error) {
	// 格式化构造message
	// 使用 % 作占位符

	now := time.Now().Format("[2006/01/02:15:04:05]")
	self.writer.WriteString(now + " [" + level.String() + "] ")
	self.writer.WriteString(format + "\n")

	// 识别占位符标记
	var tag bool = false
	var n int = 0
	var last int = 0

	for i := 0; i < len(format); i++ {
		if tag {
			switch format[i] {
			// 类型检查
			// 占位符，有意义部分
			// 字符串
			case 's':
				if str, ok := args[n].(string); !ok {
					return errors.New("Wrong format type.")
				}
			// 数字型
			case 'd':
				if digit, ok := args[n].(int64); !ok {
					return errors.New("Wrong format type.")
				}
			//转义符
			case '\\':

			//默认
			default:

			}
			self.writer.Write(args[n].([]byte))
			tag = false
		} else {
			// 占位符，百分号
			if '%' == format[i] {
				tag = true
				self.writer.Write([]byte(format[last:i]))
				last = i + 1
				continue
			}
		}
	}

	return
}

func (self *FileLogWriter) Debug(format string) {
	self.write(DEBUG, format)
}

func (self *FileLogWriter) Debugf(format string, args ...interface{}) {
	self.writef(DEBUG, format, args...)
}
<commit_msg>sleep<commit_after>// Copyright 2015
// Author: huangjunwei@youmi.net

package blog4go

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"os"
	"time"
)

type Level int

const (
	DEBUG Level = iota
	TRACE
	INFO
	WARNING
	ERROR
	CRITICAL
)

var (
	levelStrings = [...]string{"DEBUG", "TRAC", "INFO", "WARN", "ERROR", "CRITAL"}
)

func (self Level) valid() bool {
	if DEBUG > self || CRITICAL < self {
		return false
	}
	return true
}

func (self Level) String() string {
	if !self.valid() {
		return "UNKNOWN"
	}
	return levelStrings[self]
}

// 单条日志记录结构体
type LogRecord struct {
	level   Level
	message string
}

func (self *LogRecord) String() string {
	var b bytes.Buffer
	now := time.Now().Format("2006-01-02 15:04:05")
	b.WriteString(now)
	b.WriteString(" [" + self.level.String() + "] ")
	b.WriteString(self.message)

	return b.String()
}

// 各种日志结构接口
type LogWriter interface {
	// 关闭log writer的处理方法
	// 善后
	Close()

	// 用于内部写log的方法
	write(level Level, format string, args ...interface{})

	// 供用户强制刷日志到输出
	Flush()
}

// DefaultFileLogWriter.c 为无缓冲channel
var DefaultFileLogWriter *FileLogWriter = new(FileLogWriter)

const (
	DefaultBufferSize = 4096
)

// 装逼的logger
type FileLogWriter struct {
	level Level

	// log文件
	filename string
	file     *os.File
	writer   *bufio.Writer

	// logrotate
	rotate bool
}

// 包初始化函数
func init() {

}

func NewFileLogWriter(filename string) (fileWriter *FileLogWriter, err error) {
	fileWriter = new(FileLogWriter)

	// 打开文件描述符
	file, err := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0644))
	if nil != err {
		return nil, err
	}
	fileWriter.filename = filename
	fileWriter.file = file
	fileWriter.writer = bufio.NewWriterSize(file, DefaultBufferSize)

	return fileWriter, nil
}

func (self *FileLogWriter) Close() {
	self.writer.Flush()
	self.file.Close()
}

func (self *FileLogWriter) Flush() {
	self.writer.Flush()
}

func (self *FileLogWriter) SetLevel(level Level) *FileLogWriter {
	self.level = level
	return self
}

func (self *FileLogWriter) GetLevel() Level {
	return self.level
}

func (self *FileLogWriter) write(level Level, format string, args ...interface{}) {
	if level < self.level {
		return
	}

	// TODO 优化format
	now := time.Now().Format("[2006/01/02:15:04:05]")
	self.writer.WriteString(now + " [" + level.String() + "] ")
	self.writer.WriteString(format + "\n")

	//self.writer.Flush()
}

func (self *FileLogWriter) writef(level Level, format string, args ...interface{}) (err error) {
	// 格式化构造message
	// 使用 % 作占位符

	now := time.Now().Format("[2006/01/02:15:04:05]")
	self.writer.WriteString(now + " [" + level.String() + "] ")
	self.writer.WriteString(format + "\n")

	// 识别占位符标记
	var tag bool = false
	// 转义字符标记
	var escape bool = false

	var n int = 0
	var last int = 0

	for i := 0; i < len(format); i++ {
		if tag {
			switch format[i] {
			// 类型检查/ 特殊字符处理
			// 占位符，有意义部分
			// 字符串
			case 's':
				if escape {

					escape = false
				}

				if _, ok := args[n].(string); !ok {
					return errors.New("Wrong format type.")
				}
			// 整型
			case 'd':
				if escape {

					escape = false
				}

				if _, ok := args[n].(int64); !ok {
					return errors.New("Wrong format type.")
				}
			// 浮点型
			case 'f':
				if escape {

					escape = false
				}

				if _, ok := args[n].(float64); !ok {
					return errors.New("Wrong format type.")
				}

			//转义符
			case '\\':
				escape = true

			//默认
			default:

			}
			self.writer.Write(args[n].([]byte))
			n++
			tag = false
		} else {
			// 占位符，百分号
			if '%' == format[i] {
				tag = true
				self.writer.Write([]byte(format[last:i]))
				last = i + 1
				continue
			}
		}
	}

	return
}

func (self *FileLogWriter) Debug(format string) {
	self.write(DEBUG, format)
}

func (self *FileLogWriter) Debugf(format string, args ...interface{}) {
	self.writef(DEBUG, format, args...)
}
<|endoftext|>


<commit_before>package strategy

import (
	bsmsg "github.com/jbenet/go-ipfs/bitswap/message"
	peer "github.com/jbenet/go-ipfs/peer"
	u "github.com/jbenet/go-ipfs/util"
)

type Strategy interface {
	Accountant

	// Returns a slice of Peers that
	Peers() []*peer.Peer

	// WantList returns the WantList for the given Peer
	BlockIsWantedByPeer(u.Key, *peer.Peer) bool

	// ShouldSendTo(Peer) decides whether to send data to this Peer
	ShouldSendBlockToPeer(u.Key, *peer.Peer) bool

	// Seed initializes the decider to a deterministic state
	Seed(int64)
}

type Accountant interface {
	// MessageReceived records receipt of message for accounting purposes
	MessageReceived(*peer.Peer, bsmsg.BitSwapMessage) error

	// MessageSent records sending of message for accounting purposes
	MessageSent(*peer.Peer, bsmsg.BitSwapMessage) error
}

type WantList interface {
	// Peer returns the owner of the WantList
	Peer() *peer.Peer

	// Intersection returns the keys common to both WantLists
	Intersection(WantList) WantList

	KeySet
}

// TODO(brian): potentially move this somewhere more generic. For now, it's
// useful in BitSwap operations.

type KeySet interface {
	Contains(u.Key) bool
	Keys() []u.Key
}
<commit_msg>style(bitswap) remove unnecessary interface<commit_after>package strategy

import (
	bsmsg "github.com/jbenet/go-ipfs/bitswap/message"
	peer "github.com/jbenet/go-ipfs/peer"
	u "github.com/jbenet/go-ipfs/util"
)

type Strategy interface {
	// Returns a slice of Peers that
	Peers() []*peer.Peer

	// WantList returns the WantList for the given Peer
	BlockIsWantedByPeer(u.Key, *peer.Peer) bool

	// ShouldSendTo(Peer) decides whether to send data to this Peer
	ShouldSendBlockToPeer(u.Key, *peer.Peer) bool

	// Seed initializes the decider to a deterministic state
	Seed(int64)

	// MessageReceived records receipt of message for accounting purposes
	MessageReceived(*peer.Peer, bsmsg.BitSwapMessage) error

	// MessageSent records sending of message for accounting purposes
	MessageSent(*peer.Peer, bsmsg.BitSwapMessage) error
}

type WantList interface {
	// Peer returns the owner of the WantList
	Peer() *peer.Peer

	// Intersection returns the keys common to both WantLists
	Intersection(WantList) WantList

	KeySet
}

// TODO(brian): potentially move this somewhere more generic. For now, it's
// useful in BitSwap operations.

type KeySet interface {
	Contains(u.Key) bool
	Keys() []u.Key
}
<|endoftext|>


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

package daemon

import (
	"errors"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

// Service constants
const (
	success = "\t\t\t\t\t[  \033[32mOK\033[0m  ]" // Show colored "OK"
	failed  = "\t\t\t\t\t[\033[31mFAILED\033[0m]" // Show colored "FAILED"
)

var (
	ErrUnsupportedSystem = errors.New("Unsupported system")
	ErrRootPriveleges    = errors.New("You must have root user privileges. Possibly using 'sudo' command should help")
	ErrAlreadyInstalled  = errors.New("Service has already been installed")
	ErrNotInstalled      = errors.New("Service is not installed")
	ErrAlreadyStopped    = errors.New("Service has already been stopped")
	ErrAlreadyRunning    = errors.New("Service is alredy running")
)

// Lookup path for executable file
func executablePath(name string) (string, error) {
	if path, err := exec.LookPath(name); err == nil {
		_, err := os.Stat(path)
		if os.IsNotExist(err) {
			return execPath()
		}
		return path, nil
	}
	return execPath()
}

// Check root rights to use system service
func checkPrivileges() (bool, error) {

	if output, err := exec.Command("id", "-g").Output(); err == nil {
		if gid, parseErr := strconv.ParseUint(strings.TrimSpace(string(output)), 10, 32); parseErr == nil {
			if gid == 0 {
				return true, nil
			}
			return false, ErrRootPriveleges
		}
	}
	return false, ErrUnsupportedSystem
}
<commit_msg>Fix typo #44<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by
// license that can be found in the LICENSE file.

package daemon

import (
	"errors"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

// Service constants
const (
	success = "\t\t\t\t\t[  \033[32mOK\033[0m  ]" // Show colored "OK"
	failed  = "\t\t\t\t\t[\033[31mFAILED\033[0m]" // Show colored "FAILED"
)

var (
	ErrUnsupportedSystem = errors.New("Unsupported system")
	ErrRootPriveleges    = errors.New("You must have root user privileges. Possibly using 'sudo' command should help")
	ErrAlreadyInstalled  = errors.New("Service has already been installed")
	ErrNotInstalled      = errors.New("Service is not installed")
	ErrAlreadyStopped    = errors.New("Service has already been stopped")
	ErrAlreadyRunning    = errors.New("Service is already running")
)

// Lookup path for executable file
func executablePath(name string) (string, error) {
	if path, err := exec.LookPath(name); err == nil {
		_, err := os.Stat(path)
		if os.IsNotExist(err) {
			return execPath()
		}
		return path, nil
	}
	return execPath()
}

// Check root rights to use system service
func checkPrivileges() (bool, error) {

	if output, err := exec.Command("id", "-g").Output(); err == nil {
		if gid, parseErr := strconv.ParseUint(strings.TrimSpace(string(output)), 10, 32); parseErr == nil {
			if gid == 0 {
				return true, nil
			}
			return false, ErrRootPriveleges
		}
	}
	return false, ErrUnsupportedSystem
}
<|endoftext|>


<commit_before>package manager

import (
	"TOGY/updater"
	"fmt"
	"os"
	"strconv"
	"time"
)

func updateManager(mgr *Manager, t <-chan time.Time) {
	for {
		select {
		case _ = <-t:
			mgr.config.Log.Println("Getting update info")
			i, err := updater.GetInfo(mgr.config)
			if err != nil {
				mgr.config.Log.Println("Error while downloading info: ", err)
				continue
			}
			if i.Broadcast {
				mgr.config.Log.Println("Updating broadcast")
				err = updateBroadcast(mgr, i.FileType)
				if err != nil {
					mgr.config.Log.Println(err)
				}
				mgr.config.Log.Println("Broadcast updated")
			}

			if i.Config {
				mgr.config.Log.Println("Updating config")
				err = updateConfig(mgr)
				if err != nil {
					mgr.config.Log.Println(err)
				}
				mgr.reloadSignal <- true
				mgr.config.Log.Println("Config updated, restarting manager.")
				return
			}
		}
	}
}

func updateConfig(mgr *Manager) error {
	err := updater.DownloadConfig(mgr.config, mgr.config.CentralPath)
	if err != nil {
		return fmt.Errorf("Error while updating config: %v", err)
	}
	mgr.config.Log.Println("Config succesfully downloaded, announcing.")
	err = updater.AnnounceConfig(mgr.config)
	if err != nil {
		return fmt.Errorf("Error while announcing the download of config: %v", err)
	}
	return nil
}

func updateBroadcast(mgr *Manager, ft string) error {
	path, err := makeTempDir()
	if err != nil {
		return fmt.Errorf("Error while creating temporary directory: %v", err)
	}
	err = updater.DownloadBroadcast(mgr.config, ft, path)
	if err != nil {
		return fmt.Errorf("Error while downloading new broadcast: %v", err)
	}
	mgr.config.Log.Println("Broadcast successfully downloaded, announcing.")

	err = updater.AnnounceBroadcast(mgr.config)
	if err != nil {
		return fmt.Errorf("Error while announcing the download of broadcast: %v", err)
	}

	err = mgr.stopBroadcast()
	if err != nil {
		return fmt.Errorf("Error while stopping broadcast for update: %v", err)
	}

	mgr.block()
	defer mgr.unblock()

	mgr.config.Log.Println("Moving new broadcast into place")
	err = deleteAll(mgr.config.BroadcastDir)
	if err != nil {
		return fmt.Errorf("Error while deleting old broadcast: %v", err)
	}

	err = moveFiles(path, mgr.config.BroadcastDir)
	if err != nil {
		return fmt.Errorf("Error while moving new broadcast into place: %v", err)
	}

	err = mgr.startBroadcast()
	if err != nil {
		return fmt.Errorf("Error while starting new broadcast: %v", err)
	}
	return nil
}

func makeTempDir() (path string, err error) {
	path = os.TempDir() + string(os.PathSeparator) + "broadcast-download-" + strconv.Itoa(int(time.Now().Unix()))
	err = os.Mkdir(path, os.ModePerm)
	if err != nil {
		return
	}
	return
}

func moveFiles(src, dest string) (err error) {
	sf, err := os.Open(src)
	if err != nil {
		return
	}

	fns, err := sf.Readdirnames(0)
	if err != nil {
		return
	}

	for _, name := range fns {
		path := src + string(os.PathSeparator) + name
		err = os.Rename(path, dest+string(os.PathSeparator)+name)
		if err != nil {
			return
		}
	}
	return
}

func deleteAll(dir string) (err error) {
	d, err := os.Open(dir)
	if err != nil {
		return
	}

	fns, err := d.Readdirnames(0)
	if err != nil {
		return
	}

	for _, f := range fns {
		path := dir + string(os.PathSeparator) + f
		err = os.Remove(path)
		if err != nil {
			return
		}
	}
	return nil
}
<commit_msg>Added deleting of the temporary files<commit_after>package manager

import (
	"TOGY/updater"
	"fmt"
	"os"
	"strconv"
	"time"
)

func updateManager(mgr *Manager, t <-chan time.Time) {
	for {
		select {
		case _ = <-t:
			mgr.config.Log.Println("Getting update info")
			i, err := updater.GetInfo(mgr.config)
			if err != nil {
				mgr.config.Log.Println("Error while downloading info: ", err)
				continue
			}
			if i.Broadcast {
				mgr.config.Log.Println("Updating broadcast")
				err = updateBroadcast(mgr, i.FileType)
				if err != nil {
					mgr.config.Log.Println(err)
				}
				mgr.config.Log.Println("Broadcast updated")
			}

			if i.Config {
				mgr.config.Log.Println("Updating config")
				err = updateConfig(mgr)
				if err != nil {
					mgr.config.Log.Println(err)
				}
				mgr.reloadSignal <- true
				mgr.config.Log.Println("Config updated, restarting manager.")
				return
			}
		}
	}
}

func updateConfig(mgr *Manager) error {
	err := updater.DownloadConfig(mgr.config, mgr.config.CentralPath)
	if err != nil {
		return fmt.Errorf("Error while updating config: %v", err)
	}
	mgr.config.Log.Println("Config succesfully downloaded, announcing.")
	err = updater.AnnounceConfig(mgr.config)
	if err != nil {
		return fmt.Errorf("Error while announcing the download of config: %v", err)
	}
	return nil
}

func updateBroadcast(mgr *Manager, ft string) error {
	path, err := makeTempDir()
	if err != nil {
		return fmt.Errorf("Error while creating temporary directory: %v", err)
	}
	err = updater.DownloadBroadcast(mgr.config, ft, path)
	if err != nil {
		return fmt.Errorf("Error while downloading new broadcast: %v", err)
	}
	mgr.config.Log.Println("Broadcast successfully downloaded, announcing.")

	err = updater.AnnounceBroadcast(mgr.config)
	if err != nil {
		return fmt.Errorf("Error while announcing the download of broadcast: %v", err)
	}

	err = mgr.stopBroadcast()
	if err != nil {
		return fmt.Errorf("Error while stopping broadcast for update: %v", err)
	}

	mgr.block()
	defer mgr.unblock()

	mgr.config.Log.Println("Moving new broadcast into place")
	err = deleteAll(mgr.config.BroadcastDir)
	if err != nil {
		return fmt.Errorf("Error while deleting old broadcast: %v", err)
	}

	err = moveFiles(path, mgr.config.BroadcastDir)
	if err != nil {
		return fmt.Errorf("Error while moving new broadcast into place: %v", err)
	}

	err = mgr.startBroadcast()
	if err != nil {
		return fmt.Errorf("Error while starting new broadcast: %v", err)
	}

	_ = deleteAll(path)
	return nil
}

func makeTempDir() (path string, err error) {
	path = os.TempDir() + string(os.PathSeparator) + "broadcast-download-" + strconv.Itoa(int(time.Now().Unix()))
	err = os.Mkdir(path, os.ModePerm)
	if err != nil {
		return
	}
	return
}

func moveFiles(src, dest string) (err error) {
	sf, err := os.Open(src)
	if err != nil {
		return
	}

	fns, err := sf.Readdirnames(0)
	if err != nil {
		return
	}

	for _, name := range fns {
		path := src + string(os.PathSeparator) + name
		err = os.Rename(path, dest+string(os.PathSeparator)+name)
		if err != nil {
			return
		}
	}
	return
}

func deleteAll(dir string) (err error) {
	d, err := os.Open(dir)
	if err != nil {
		return
	}

	fns, err := d.Readdirnames(0)
	if err != nil {
		return
	}

	for _, f := range fns {
		path := dir + string(os.PathSeparator) + f
		err = os.Remove(path)
		if err != nil {
			return
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"math"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"sync"
	"time"
)

var NumCPU int
var CountTo float64

type testInfo struct {
	InstanceNumCPU int
	RequestNumCPU  int
	CountTo        float64
	CountToPer     int
	StartingTime   time.Time
	EndingTime     time.Time
	Duration       time.Duration
	Durationms     int64
}

func main() {
	NumCPU = runtime.NumCPU()

	var port string
	port = os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	runtime.GOMAXPROCS(NumCPU)
	http.HandleFunc("/", billion)
	fmt.Println("listening on port " + port)
	err := http.ListenAndServe(":"+port, nil)
	if err != nil {
		panic(err)
	}
}

func billion(res http.ResponseWriter, req *http.Request) {
	var testInfo testInfo
	testInfo.InstanceNumCPU = NumCPU
	testInfo.RequestNumCPU, _ = strconv.Atoi(req.URL.Query().Get("numcpu"))

	if testInfo.RequestNumCPU > testInfo.InstanceNumCPU || testInfo.RequestNumCPU < 1 {
		testInfo.RequestNumCPU = 1
	}

	testInfo.CountTo = 1000000000
	testInfo.CountToPer = int(math.Floor(testInfo.CountTo / float64(testInfo.RequestNumCPU)))
	var wg sync.WaitGroup
	testInfo.StartingTime = time.Now().UTC()
	for i := 0; i < testInfo.RequestNumCPU; i++ {
		wg.Add(1)
		go func() {
			fmt.Println(fmt.Sprintf("started %v", testInfo.CountToPer))
			for i := 0; i < testInfo.CountToPer; i++ {
			}
			wg.Done()
		}()

	}

	wg.Wait()
	EndingTime := time.Now().UTC()

	testInfo.Duration = EndingTime.Sub(testInfo.StartingTime)
	testInfo.Durationms = testInfo.Duration.Nanoseconds() / 1e6

	//fmt.Fprintln(res, fmt.Sprintf(`<html><head><title>Go counts to %.0f</title></head><body><pre>`, testInfo.CountTo))
	jsonOutput, err := json.Marshal(&testInfo)
	if err != nil {
		log.Fatalf("error marshaling: %s", err)
	}

	fmt.Fprintln(res, string(jsonOutput))
	//fmt.Fprintln(res, fmt.Sprintf(`</pre></body></html>`))

}
<commit_msg>bugfix and schema update<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"math"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"sync"
	"time"
)

var NumCPU int
var CountTo float64

type testInfo struct {
	InstanceNumCPU int           `json:"instanceNumCPU"`
	RequestNumCPU  int           `json:"requestNumCPU"`
	CountTo        float64       `json:"countTo"`
	CountToPer     int           `json:"countToPer"`
	StartingTime   time.Time     `json:"startingTime"`
	EndingTime     time.Time     `json:"endingTime"`
	Duration       time.Duration `json:"duration"`
	Durationms     int64         `json:"durationms"`
}

func main() {
	NumCPU = runtime.NumCPU()

	var port string
	port = os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	runtime.GOMAXPROCS(NumCPU)
	http.HandleFunc("/", billion)
	fmt.Println("listening on port " + port)
	err := http.ListenAndServe(":"+port, nil)
	if err != nil {
		panic(err)
	}
}

func billion(res http.ResponseWriter, req *http.Request) {
	var testInfo testInfo
	testInfo.InstanceNumCPU = NumCPU
	testInfo.RequestNumCPU, _ = strconv.Atoi(req.URL.Query().Get("numcpu"))

	if testInfo.RequestNumCPU > testInfo.InstanceNumCPU || testInfo.RequestNumCPU < 1 {
		testInfo.RequestNumCPU = 1
	}

	testInfo.CountTo = 1000000000
	testInfo.CountToPer = int(math.Floor(testInfo.CountTo / float64(testInfo.RequestNumCPU)))
	var wg sync.WaitGroup
	testInfo.StartingTime = time.Now().UTC()
	for i := 0; i < testInfo.RequestNumCPU; i++ {
		wg.Add(1)
		go func() {
			fmt.Println(fmt.Sprintf("started %v", testInfo.CountToPer))
			for i := 0; i < testInfo.CountToPer; i++ {
			}
			wg.Done()
		}()

	}

	wg.Wait()
	testInfo.EndingTime = time.Now().UTC()

	testInfo.Duration = testInfo.EndingTime.Sub(testInfo.StartingTime)
	testInfo.Durationms = testInfo.Duration.Nanoseconds() / 1e6

	//fmt.Fprintln(res, fmt.Sprintf(`<html><head><title>Go counts to %.0f</title></head><body><pre>`, testInfo.CountTo))
	jsonOutput, err := json.Marshal(&testInfo)
	if err != nil {
		log.Fatalf("error marshaling: %s", err)
	}

	fmt.Fprintln(res, string(jsonOutput))
	//fmt.Fprintln(res, fmt.Sprintf(`</pre></body></html>`))

}
<|endoftext|>


<commit_before>// gocovmerge takes the results from multiple `go test -coverprofile` runs and
// merges them into one profile
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"sort"

	"golang.org/x/tools/cover"
)

func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock) {
	var inserted, combined int
	i := sort.Search(len(p.Blocks), func(i int) bool {
		pi := p.Blocks[i]
		return pi.StartLine >= pb.StartLine && (pi.StartLine != pb.StartLine || pi.StartCol >= pb.StartCol)
	})
	if i < len(p.Blocks) && p.Blocks[i].StartLine == pb.StartLine && p.Blocks[i].StartCol == pb.StartCol {
		// TODO validate ends match
		p.Blocks[i].Count += pb.Count
		combined++
	} else {
		// TODO validate doesn't overlap with the adjacent blocks
		p.Blocks = append(p.Blocks, cover.ProfileBlock{})
		copy(p.Blocks[i+1:], p.Blocks[i:])
		p.Blocks[i] = pb
		inserted++
	}
}

func mergeProfile(profiles []*cover.Profile, p *cover.Profile) []*cover.Profile {
	i := sort.Search(len(profiles), func(i int) bool { return profiles[i].FileName >= p.FileName })
	if i < len(profiles) && profiles[i].FileName == p.FileName {
		if profiles[i].Mode != p.Mode {
			log.Fatalf("cannot merge profiles with different modes")
		}
		for _, b := range p.Blocks {
			// TODO we can be smarter if we keep track of where we are while merging
			// that way we don't have to re-do the binary search from the start for each entry
			mergeProfileBlock(profiles[i], b)
		}
		// log.Printf("merged: %v", p.FileName)
	} else {
		profiles = append(profiles, nil)
		copy(profiles[i+1:], profiles[i:])
		profiles[i] = p
	}
	return profiles
}

func dumpProfiles(profiles []*cover.Profile, out io.Writer) {
	if len(profiles) == 0 {
		return
	}
	fmt.Fprintf(out, "mode: %s\n", profiles[0].Mode)
	for _, p := range profiles {
		for _, b := range p.Blocks {
			fmt.Fprintf(out, "%s:%d.%d,%d.%d %d %d\n", p.FileName, b.StartLine, b.StartCol, b.EndLine, b.EndCol, b.NumStmt, b.Count)
		}
	}
}

func main() {
	flag.Parse()

	var merged []*cover.Profile

	for _, file := range flag.Args() {
		profiles, err := cover.ParseProfiles(file)
		if err != nil {
			log.Fatalf("failed to parse profiles: %v", err)
		}
		for _, p := range profiles {
			merged = mergeProfile(merged, p)
		}
	}

	dumpProfiles(merged, os.Stdout)
}
<commit_msg>optimize gocovmerge<commit_after>// gocovmerge takes the results from multiple `go test -coverprofile` runs and
// merges them into one profile
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"sort"

	"golang.org/x/tools/cover"
)

func mergeProfiles(p *cover.Profile, merge *cover.Profile) {
	if p.Mode != merge.Mode {
		log.Fatalf("cannot merge profiles with different modes")
	}
	// Since the blocks are sorted, we can keep track of where the last block
	// was inserted and only look at the blocks after that as targets for merge
	startIndex := 0
	for _, b := range merge.Blocks {
		startIndex = mergeProfileBlock(p, b, startIndex)
	}
}

func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock, startIndex int) int {
	var inserted, combined int
	sortFunc := func(i int) bool {
		pi := p.Blocks[i+startIndex]
		return pi.StartLine >= pb.StartLine && (pi.StartLine != pb.StartLine || pi.StartCol >= pb.StartCol)
	}

	i := 0
	if sortFunc(i) != true {
		i = sort.Search(len(p.Blocks)-startIndex, sortFunc)
	}
	i += startIndex
	if i < len(p.Blocks) && p.Blocks[i].StartLine == pb.StartLine && p.Blocks[i].StartCol == pb.StartCol {
		if p.Blocks[i].EndLine != pb.EndLine || p.Blocks[i].EndCol != pb.EndCol {
			log.Fatalf("OVERLAP MERGE: %v %v %v", p.FileName, p.Blocks[i], pb)
		}
		p.Blocks[i].Count += pb.Count
		combined++
	} else {
		if i > 0 {
			pa := p.Blocks[i-1]
			if pa.EndLine >= pb.EndLine && (pa.EndLine != pb.EndLine || pa.EndCol > pb.EndCol) {
				log.Fatalf("OVERLAP BEFORE: %v %v %v", p.FileName, pa, pb)
			}
		}
		if i < len(p.Blocks)-1 {
			pa := p.Blocks[i+1]
			if pa.StartLine <= pb.StartLine && (pa.StartLine != pb.StartLine || pa.StartCol < pb.StartCol) {
				log.Fatalf("OVERLAP AFTER: %v %v %v", p.FileName, pa, pb)
			}
		}
		p.Blocks = append(p.Blocks, cover.ProfileBlock{})
		copy(p.Blocks[i+1:], p.Blocks[i:])
		p.Blocks[i] = pb
		inserted++
	}
	return i + 1
}

func addProfile(profiles []*cover.Profile, p *cover.Profile) []*cover.Profile {
	i := sort.Search(len(profiles), func(i int) bool { return profiles[i].FileName >= p.FileName })
	if i < len(profiles) && profiles[i].FileName == p.FileName {
		mergeProfiles(profiles[i], p)
	} else {
		profiles = append(profiles, nil)
		copy(profiles[i+1:], profiles[i:])
		profiles[i] = p
	}
	return profiles
}

func dumpProfiles(profiles []*cover.Profile, out io.Writer) {
	if len(profiles) == 0 {
		return
	}
	fmt.Fprintf(out, "mode: %s\n", profiles[0].Mode)
	for _, p := range profiles {
		for _, b := range p.Blocks {
			fmt.Fprintf(out, "%s:%d.%d,%d.%d %d %d\n", p.FileName, b.StartLine, b.StartCol, b.EndLine, b.EndCol, b.NumStmt, b.Count)
		}
	}
}

func main() {
	flag.Parse()

	var merged []*cover.Profile

	for _, file := range flag.Args() {
		profiles, err := cover.ParseProfiles(file)
		if err != nil {
			log.Fatalf("failed to parse profiles: %v", err)
		}
		for _, p := range profiles {
			merged = addProfile(merged, p)
		}
	}

	dumpProfiles(merged, os.Stdout)
}
<|endoftext|>


<commit_before>// Package gocroaring is an wrapper for CRoaring in go
// It provides a fast compressed bitmap data structure.
// See http://roaringbitmap.org for details.
package gocroaring

/*
#cgo CFLAGS: -march=native -O3  -Wa,-q -std=c99
#include "roaring.h"

// mitigate cgo call overhead by adding many values at once.
void roaring_bitmap_add_many(roaring_bitmap_t *r, const uint32_t *vals, int n){
	// somewhat emprically found that about 1K is the break-even.
	if(n < 1000) {
		for(int i=0;i<n;i++){
			roaring_bitmap_add(r, vals[i]);
		}
		return;
	}
	roaring_bitmap_t *ro = roaring_bitmap_of_ptr((size_t)n, vals);
	roaring_bitmap_or_inplace(r, ro);
}

*/
import "C"
import (
	"bytes"
	"errors"
	"io"
	"os"
	"runtime"
)

import "unsafe"

func free(a *Bitmap) {
	C.roaring_bitmap_free(a.cpointer)
}

// Bitmap is the roaring bitmap
type Bitmap struct {
	cpointer *C.struct_roaring_bitmap_s
}

// New creates a new Bitmap with any number of initial values.
func New(x ...uint32) *Bitmap {
	var answer *Bitmap
	if len(x) > 0 {
		ptr := unsafe.Pointer(&x[0])
		answer = &Bitmap{C.roaring_bitmap_of_ptr(C.size_t(len(x)), (*C.uint32_t)(ptr))}
	} else {
		answer = &Bitmap{C.roaring_bitmap_create()}
	}
	runtime.SetFinalizer(answer, free)
	return answer
}

// Printf writes a description of the bitmap to stdout
func (rb *Bitmap) Printf() {
	C.roaring_bitmap_printf(rb.cpointer)
	C.fflush(C.stdout)
}

// Add the integer(s) x to the bitmap
func (rb *Bitmap) Add(x ...uint32) {
	if len(x) == 1 {
		C.roaring_bitmap_add(rb.cpointer, C.uint32_t(x[0]))
	} else {
		ptr := unsafe.Pointer(&x[0])
		C.roaring_bitmap_add_many(rb.cpointer, (*C.uint32_t)(ptr), C.int(len(x)))
	}

}

// RunOptimize the compression of the bitmap (call this after populating a new bitmap), return true if the bitmap was modified
func (rb *Bitmap) RunOptimize() bool {
	return bool(C.roaring_bitmap_run_optimize(rb.cpointer))
}

// RemoveRunCompression  Remove run-length encoding even when it is more space efficient return whether a change was applied
func (rb *Bitmap) RemoveRunCompression() bool {
	return bool(C.roaring_bitmap_remove_run_compression(rb.cpointer))
}

// FastOr computes the union between many bitmaps quickly, as opposed to having to call Or repeatedly.
// It might also be faster than calling Or repeatedly.
func FastOr(bitmaps ...*Bitmap) *Bitmap {
	number := len(bitmaps)
	po := make([]*C.struct_roaring_bitmap_s, number)
	for i, v := range bitmaps {
		po[i] = v.cpointer
	}
	b := &Bitmap{C.roaring_bitmap_or_many(C.size_t(number), (**C.struct_roaring_bitmap_s)(unsafe.Pointer(&po[0])))}
	runtime.SetFinalizer(b, free)
	return b
}

// Contains returns true if the integer is contained in the bitmap
func (rb *Bitmap) Contains(x uint32) bool {
	return bool(C.roaring_bitmap_contains(rb.cpointer, C.uint32_t(x)))
}

// Remove the integer x from the bitmap
func (rb *Bitmap) Remove(x uint32) {
	C.roaring_bitmap_remove(rb.cpointer, C.uint32_t(x))
}

// Cardinality returns the number of integers contained in the bitmap
func (rb *Bitmap) Cardinality() uint64 {
	return uint64(C.roaring_bitmap_get_cardinality(rb.cpointer))
}

// IsEmpty returns true if the Bitmap is empty (it is faster than doing (Cardinality() == 0))
func (rb *Bitmap) IsEmpty() bool {
	return bool(C.roaring_bitmap_is_empty(rb.cpointer))
}

// Equals returns true if the two bitmaps contain the same integers
func (rb *Bitmap) Equals(o interface{}) bool {
	srb, ok := o.(*Bitmap)
	if ok {
		return bool(C.roaring_bitmap_equals(rb.cpointer, srb.cpointer))
	}
	return false
}

// Clone creates a copy of the Bitmap
func (rb *Bitmap) Clone() *Bitmap {
	b := &Bitmap{C.roaring_bitmap_copy(rb.cpointer)}
	runtime.SetFinalizer(b, free)
	return b
}

// And computes the intersection between two bitmaps and stores the result in the current bitmap
func (rb *Bitmap) And(x2 *Bitmap) {
	C.roaring_bitmap_and_inplace(rb.cpointer, x2.cpointer)
}

// Xor computes the symmetric difference between two bitmaps and stores the result in the current bitmap
func (rb *Bitmap) Xor(x2 *Bitmap) {
	C.roaring_bitmap_xor_inplace(rb.cpointer, x2.cpointer)
}

// Or computes the union between two bitmaps and stores the result in the current bitmap
func (rb *Bitmap) Or(x2 *Bitmap) {
	C.roaring_bitmap_or_inplace(rb.cpointer, x2.cpointer)
}

// AndNot computes the difference between two bitmaps and stores the result in the current bitmap
func (rb *Bitmap) AndNot(x2 *Bitmap) {
	C.roaring_bitmap_andnot_inplace(rb.cpointer, x2.cpointer)
}

// Or computes the union between two bitmaps and returns the result
func Or(x1, x2 *Bitmap) *Bitmap {
	b := &Bitmap{C.roaring_bitmap_or(x1.cpointer, x2.cpointer)}
	runtime.SetFinalizer(b, free)
	return b
}

// And computes the intersection between two bitmaps and returns the result
func And(x1, x2 *Bitmap) *Bitmap {
	b := &Bitmap{C.roaring_bitmap_and(x1.cpointer, x2.cpointer)}
	runtime.SetFinalizer(b, free)
	return b
}

// Xor computes the symmetric difference between two bitmaps and returns the result
func Xor(x1, x2 *Bitmap) *Bitmap {
	b := &Bitmap{C.roaring_bitmap_xor(x1.cpointer, x2.cpointer)}
	runtime.SetFinalizer(b, free)
	return b
}

// AndNot computes the difference between two bitmaps and returns the result
func AndNot(x1, x2 *Bitmap) *Bitmap {
	b := &Bitmap{C.roaring_bitmap_andnot(x1.cpointer, x2.cpointer)}
	runtime.SetFinalizer(b, free)
	return b
}

// Flip negates the bits in the given range (i.e., [rangeStart,rangeEnd)), any integer present in this range and in the bitmap is removed,
func (rb *Bitmap) Flip(rangeStart, rangeEnd uint64) {
	C.roaring_bitmap_flip_inplace(rb.cpointer, C.uint64_t(rangeStart), C.uint64_t(rangeEnd))
}

// Flip negates the bits in the given range  (i.e., [rangeStart,rangeEnd)), any integer present in this range and in the bitmap is removed,
func Flip(bm *Bitmap, rangeStart, rangeEnd uint64) *Bitmap {
	b := &Bitmap{C.roaring_bitmap_flip(bm.cpointer, C.uint64_t(rangeStart), C.uint64_t(rangeEnd))}
	runtime.SetFinalizer(b, free)
	return b
}

// SerializedSizeInBytes computes the serialized size in bytes  the Bitmap.
func (rb *Bitmap) SerializedSizeInBytes() int {
	return int(C.roaring_bitmap_portable_size_in_bytes(rb.cpointer))
}

// Write writes a serialized version of this bitmap to stream (you should have enough space)
func (rb *Bitmap) Write(b []byte) error {
	if len(b) < rb.SerializedSizeInBytes() {
		return errors.New("not enough space")
	}
	bchar := (*C.char)(unsafe.Pointer(&b[0]))
	C.roaring_bitmap_portable_serialize(rb.cpointer, bchar)
	return nil
}

// ToArray creates a new slice containing all of the integers stored in the Bitmap in sorted order
func (rb *Bitmap) ToArray() []uint32 {
	array := make([]uint32, rb.Cardinality())
	C.roaring_bitmap_to_uint32_array(rb.cpointer, (*C.uint32_t)(unsafe.Pointer(&array[0])))
	return array
}

// String creates a string representation of the Bitmap
func (rb *Bitmap) String() string {
	old := os.Stdout // keep backup of the real stdout
	r, w, _ := os.Pipe()
	os.Stdout = w
	C.roaring_bitmap_printf(rb.cpointer)
	C.fflush(C.stdout)
	outC := make(chan string)
	// copy the output in a separate goroutine so printing can't block indefinitely
	go func() {
		var buf bytes.Buffer
		io.Copy(&buf, r)
		outC <- buf.String()
	}()

	// back to normal state
	w.Close()
	os.Stdout = old // restoring the real stdout
	out := <-outC
	return out
}

// Read reads a serialized version of the bitmap (you need to call Free on it once you are done)
func Read(b []byte) (*Bitmap, error) {
	bchar := (*C.char)(unsafe.Pointer(&b[0]))
	answer := &Bitmap{C.roaring_bitmap_portable_deserialize(bchar)}
	if answer.cpointer == nil {
		return nil, errors.New("failed to read roaring array")
	}
	runtime.SetFinalizer(answer, free)
	return answer, nil
}

// Stats returns some statistics about the roaring bitmap.
func (rb *Bitmap) Stats() map[string]uint64 {
	var stat C.roaring_statistics_t
	C.roaring_bitmap_statistics(rb.cpointer, &stat)
	return map[string]uint64{
		"cardinality":         uint64(stat.cardinality),
		"n_containers":        uint64(stat.n_containers),
		"n_array_containers":  uint64(stat.n_array_containers),
		"n_run_containers":    uint64(stat.n_run_containers),
		"n_bitset_containers": uint64(stat.n_bitset_containers),

		"n_bytes_array_containers":  uint64(stat.n_bytes_array_containers),
		"n_bytes_run_containers":    uint64(stat.n_bytes_run_containers),
		"n_bytes_bitset_containers": uint64(stat.n_bytes_bitset_containers),

		"n_values_array_containers":  uint64(stat.n_values_array_containers),
		"n_values_run_containers":    uint64(stat.n_values_run_containers),
		"n_values_bitset_containers": uint64(stat.n_values_bitset_containers),
	}
}
<commit_msg>free the bitmap if it's used in add_many.<commit_after>// Package gocroaring is an wrapper for CRoaring in go
// It provides a fast compressed bitmap data structure.
// See http://roaringbitmap.org for details.
package gocroaring

/*
#cgo CFLAGS: -march=native -O3  -Wa,-q -std=c99
#include "roaring.h"

// mitigate cgo call overhead by adding many values at once.
void roaring_bitmap_add_many(roaring_bitmap_t *r, const uint32_t *vals, int n){
	// somewhat emprically found that about 1K is the break-even.
	if(n < 1000) {
		for(int i=0;i<n;i++){
			roaring_bitmap_add(r, vals[i]);
		}
		return;
	}
	roaring_bitmap_t *ro = roaring_bitmap_of_ptr((size_t)n, vals);
	roaring_bitmap_or_inplace(r, ro);
	roaring_bitmap_free(ro);
}

*/
import "C"
import (
	"bytes"
	"errors"
	"io"
	"os"
	"runtime"
)

import "unsafe"

func free(a *Bitmap) {
	C.roaring_bitmap_free(a.cpointer)
}

// Bitmap is the roaring bitmap
type Bitmap struct {
	cpointer *C.struct_roaring_bitmap_s
}

// New creates a new Bitmap with any number of initial values.
func New(x ...uint32) *Bitmap {
	var answer *Bitmap
	if len(x) > 0 {
		ptr := unsafe.Pointer(&x[0])
		answer = &Bitmap{C.roaring_bitmap_of_ptr(C.size_t(len(x)), (*C.uint32_t)(ptr))}
	} else {
		answer = &Bitmap{C.roaring_bitmap_create()}
	}
	runtime.SetFinalizer(answer, free)
	return answer
}

// Printf writes a description of the bitmap to stdout
func (rb *Bitmap) Printf() {
	C.roaring_bitmap_printf(rb.cpointer)
	C.fflush(C.stdout)
}

// Add the integer(s) x to the bitmap
func (rb *Bitmap) Add(x ...uint32) {
	if len(x) == 1 {
		C.roaring_bitmap_add(rb.cpointer, C.uint32_t(x[0]))
	} else {
		ptr := unsafe.Pointer(&x[0])
		C.roaring_bitmap_add_many(rb.cpointer, (*C.uint32_t)(ptr), C.int(len(x)))
	}

}

// RunOptimize the compression of the bitmap (call this after populating a new bitmap), return true if the bitmap was modified
func (rb *Bitmap) RunOptimize() bool {
	return bool(C.roaring_bitmap_run_optimize(rb.cpointer))
}

// RemoveRunCompression  Remove run-length encoding even when it is more space efficient return whether a change was applied
func (rb *Bitmap) RemoveRunCompression() bool {
	return bool(C.roaring_bitmap_remove_run_compression(rb.cpointer))
}

// FastOr computes the union between many bitmaps quickly, as opposed to having to call Or repeatedly.
// It might also be faster than calling Or repeatedly.
func FastOr(bitmaps ...*Bitmap) *Bitmap {
	number := len(bitmaps)
	po := make([]*C.struct_roaring_bitmap_s, number)
	for i, v := range bitmaps {
		po[i] = v.cpointer
	}
	b := &Bitmap{C.roaring_bitmap_or_many(C.size_t(number), (**C.struct_roaring_bitmap_s)(unsafe.Pointer(&po[0])))}
	runtime.SetFinalizer(b, free)
	return b
}

// Contains returns true if the integer is contained in the bitmap
func (rb *Bitmap) Contains(x uint32) bool {
	return bool(C.roaring_bitmap_contains(rb.cpointer, C.uint32_t(x)))
}

// Remove the integer x from the bitmap
func (rb *Bitmap) Remove(x uint32) {
	C.roaring_bitmap_remove(rb.cpointer, C.uint32_t(x))
}

// Cardinality returns the number of integers contained in the bitmap
func (rb *Bitmap) Cardinality() uint64 {
	return uint64(C.roaring_bitmap_get_cardinality(rb.cpointer))
}

// IsEmpty returns true if the Bitmap is empty (it is faster than doing (Cardinality() == 0))
func (rb *Bitmap) IsEmpty() bool {
	return bool(C.roaring_bitmap_is_empty(rb.cpointer))
}

// Equals returns true if the two bitmaps contain the same integers
func (rb *Bitmap) Equals(o interface{}) bool {
	srb, ok := o.(*Bitmap)
	if ok {
		return bool(C.roaring_bitmap_equals(rb.cpointer, srb.cpointer))
	}
	return false
}

// Clone creates a copy of the Bitmap
func (rb *Bitmap) Clone() *Bitmap {
	b := &Bitmap{C.roaring_bitmap_copy(rb.cpointer)}
	runtime.SetFinalizer(b, free)
	return b
}

// And computes the intersection between two bitmaps and stores the result in the current bitmap
func (rb *Bitmap) And(x2 *Bitmap) {
	C.roaring_bitmap_and_inplace(rb.cpointer, x2.cpointer)
}

// Xor computes the symmetric difference between two bitmaps and stores the result in the current bitmap
func (rb *Bitmap) Xor(x2 *Bitmap) {
	C.roaring_bitmap_xor_inplace(rb.cpointer, x2.cpointer)
}

// Or computes the union between two bitmaps and stores the result in the current bitmap
func (rb *Bitmap) Or(x2 *Bitmap) {
	C.roaring_bitmap_or_inplace(rb.cpointer, x2.cpointer)
}

// AndNot computes the difference between two bitmaps and stores the result in the current bitmap
func (rb *Bitmap) AndNot(x2 *Bitmap) {
	C.roaring_bitmap_andnot_inplace(rb.cpointer, x2.cpointer)
}

// Or computes the union between two bitmaps and returns the result
func Or(x1, x2 *Bitmap) *Bitmap {
	b := &Bitmap{C.roaring_bitmap_or(x1.cpointer, x2.cpointer)}
	runtime.SetFinalizer(b, free)
	return b
}

// And computes the intersection between two bitmaps and returns the result
func And(x1, x2 *Bitmap) *Bitmap {
	b := &Bitmap{C.roaring_bitmap_and(x1.cpointer, x2.cpointer)}
	runtime.SetFinalizer(b, free)
	return b
}

// Xor computes the symmetric difference between two bitmaps and returns the result
func Xor(x1, x2 *Bitmap) *Bitmap {
	b := &Bitmap{C.roaring_bitmap_xor(x1.cpointer, x2.cpointer)}
	runtime.SetFinalizer(b, free)
	return b
}

// AndNot computes the difference between two bitmaps and returns the result
func AndNot(x1, x2 *Bitmap) *Bitmap {
	b := &Bitmap{C.roaring_bitmap_andnot(x1.cpointer, x2.cpointer)}
	runtime.SetFinalizer(b, free)
	return b
}

// Flip negates the bits in the given range (i.e., [rangeStart,rangeEnd)), any integer present in this range and in the bitmap is removed,
func (rb *Bitmap) Flip(rangeStart, rangeEnd uint64) {
	C.roaring_bitmap_flip_inplace(rb.cpointer, C.uint64_t(rangeStart), C.uint64_t(rangeEnd))
}

// Flip negates the bits in the given range  (i.e., [rangeStart,rangeEnd)), any integer present in this range and in the bitmap is removed,
func Flip(bm *Bitmap, rangeStart, rangeEnd uint64) *Bitmap {
	b := &Bitmap{C.roaring_bitmap_flip(bm.cpointer, C.uint64_t(rangeStart), C.uint64_t(rangeEnd))}
	runtime.SetFinalizer(b, free)
	return b
}

// SerializedSizeInBytes computes the serialized size in bytes  the Bitmap.
func (rb *Bitmap) SerializedSizeInBytes() int {
	return int(C.roaring_bitmap_portable_size_in_bytes(rb.cpointer))
}

// Write writes a serialized version of this bitmap to stream (you should have enough space)
func (rb *Bitmap) Write(b []byte) error {
	if len(b) < rb.SerializedSizeInBytes() {
		return errors.New("not enough space")
	}
	bchar := (*C.char)(unsafe.Pointer(&b[0]))
	C.roaring_bitmap_portable_serialize(rb.cpointer, bchar)
	return nil
}

// ToArray creates a new slice containing all of the integers stored in the Bitmap in sorted order
func (rb *Bitmap) ToArray() []uint32 {
	array := make([]uint32, rb.Cardinality())
	C.roaring_bitmap_to_uint32_array(rb.cpointer, (*C.uint32_t)(unsafe.Pointer(&array[0])))
	return array
}

// String creates a string representation of the Bitmap
func (rb *Bitmap) String() string {
	old := os.Stdout // keep backup of the real stdout
	r, w, _ := os.Pipe()
	os.Stdout = w
	C.roaring_bitmap_printf(rb.cpointer)
	C.fflush(C.stdout)
	outC := make(chan string)
	// copy the output in a separate goroutine so printing can't block indefinitely
	go func() {
		var buf bytes.Buffer
		io.Copy(&buf, r)
		outC <- buf.String()
	}()

	// back to normal state
	w.Close()
	os.Stdout = old // restoring the real stdout
	out := <-outC
	return out
}

// Read reads a serialized version of the bitmap (you need to call Free on it once you are done)
func Read(b []byte) (*Bitmap, error) {
	bchar := (*C.char)(unsafe.Pointer(&b[0]))
	answer := &Bitmap{C.roaring_bitmap_portable_deserialize(bchar)}
	if answer.cpointer == nil {
		return nil, errors.New("failed to read roaring array")
	}
	runtime.SetFinalizer(answer, free)
	return answer, nil
}

// Stats returns some statistics about the roaring bitmap.
func (rb *Bitmap) Stats() map[string]uint64 {
	var stat C.roaring_statistics_t
	C.roaring_bitmap_statistics(rb.cpointer, &stat)
	return map[string]uint64{
		"cardinality":         uint64(stat.cardinality),
		"n_containers":        uint64(stat.n_containers),
		"n_array_containers":  uint64(stat.n_array_containers),
		"n_run_containers":    uint64(stat.n_run_containers),
		"n_bitset_containers": uint64(stat.n_bitset_containers),

		"n_bytes_array_containers":  uint64(stat.n_bytes_array_containers),
		"n_bytes_run_containers":    uint64(stat.n_bytes_run_containers),
		"n_bytes_bitset_containers": uint64(stat.n_bytes_bitset_containers),

		"n_values_array_containers":  uint64(stat.n_values_array_containers),
		"n_values_run_containers":    uint64(stat.n_values_run_containers),
		"n_values_bitset_containers": uint64(stat.n_values_bitset_containers),
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/fatih/color"
	"time"
)

var newPlayer *Player

var yellow = color.New(color.FgYellow).SprintFunc()
var red = color.New(color.FgRed).SprintFunc()
var cyan = color.New(color.FgCyan).SprintFunc()

var tSecond = time.NewTicker(time.Second)

type Living struct {
	name         string
	age          int
	gender       string
	gold_on_hand int
	health       int
	race         string
	x            int
	y            int
	z            int
}

type Player struct {
	Living
	gold_in_bank int
	class        string
	right_hand   string
	left_hand    string
	possessive   string
	objective    string
}

type NPC struct {
	Living
}

func new_game() {
	var obj string
	var pos string
	// Create new SprintXxx functions for later
	fmt.Print("What is your name? ")
	playerName := ""
	fmt.Scanln(&playerName)
	fmt.Print("Gender? ")
	playerGender := ""
	fmt.Scanln(&playerGender)
	switch playerGender {
	case "male":
		obj = "him"
		pos = "his"
	case "female":
		obj = "her"
		pos = "her"
	default:
		obj = "them"
		pos = "their"
	}
	newPlayer = new(Player)
	newPlayer.name = playerName
	newPlayer.age = 25
	newPlayer.gender = playerGender
	newPlayer.gold_on_hand = 0
	newPlayer.gold_in_bank = 0
	newPlayer.health = 100
	newPlayer.race = "human"
	newPlayer.class = "adventurer"
	newPlayer.right_hand = "nothing"
	newPlayer.left_hand = "nothing"
	newPlayer.possessive = pos
	newPlayer.objective = obj
	newPlayer.x = 0
	newPlayer.y = 0
	newPlayer.z = 0
	fmt.Printf("%s the %s %s has %s.\n", yellow(newPlayer.name), cyan(newPlayer.race), cyan(newPlayer.class), red("entered the world"))
}

func tick() {
	fmt.Println("tick")
}

func main() {
	new_game()
	fmt.Println(newPlayer.name, "begins", newPlayer.possessive, "adventure.")
	// Initiate game loop
	for {
		tick()
		<-tSecond.C
	}
}
<commit_msg>Player actions and NPC actions evaluated each 2s tick<commit_after>package main

import (
	"fmt"
	"github.com/fatih/color"
	"time"
)

var newPlayer *Player

var yellow = color.New(color.FgYellow).SprintFunc()
var red = color.New(color.FgRed).SprintFunc()
var cyan = color.New(color.FgCyan).SprintFunc()

var tSecond = time.NewTicker(time.Second * 2)

type Living struct {
	name         string
	age          int
	gender       string
	gold_on_hand int
	health       int
	race         string
	x            int
	y            int
	z            int
}

type Player struct {
	Living
	gold_in_bank int
	class        string
	right_hand   string
	left_hand    string
	possessive   string
	objective    string
}

type NPC struct {
	Living
}

func new_game() {
	var obj string
	var pos string
	// Create new SprintXxx functions for later
	fmt.Print("What is your name? ")
	playerName := ""
	fmt.Scanln(&playerName)
	fmt.Print("Gender? ")
	playerGender := ""
	fmt.Scanln(&playerGender)
	switch playerGender {
	case "male":
		obj = "him"
		pos = "his"
	case "female":
		obj = "her"
		pos = "her"
	default:
		obj = "them"
		pos = "their"
	}
	newPlayer = new(Player)
	newPlayer.name = playerName
	newPlayer.age = 25
	newPlayer.gender = playerGender
	newPlayer.gold_on_hand = 0
	newPlayer.gold_in_bank = 0
	newPlayer.health = 100
	newPlayer.race = "human"
	newPlayer.class = "adventurer"
	newPlayer.right_hand = "nothing"
	newPlayer.left_hand = "nothing"
	newPlayer.possessive = pos
	newPlayer.objective = obj
	newPlayer.x = 0
	newPlayer.y = 0
	newPlayer.z = 0
	fmt.Printf("%s the %s %s has %s.\n", yellow(newPlayer.name), cyan(newPlayer.race), cyan(newPlayer.class), red("entered the world"))
}

func ProcessPlayerActions() {
	fmt.Println("Player action.")
}

func ProcessNPCActions() {
	fmt.Println("NPC action.")
}

func tick() {
	ProcessPlayerActions()
	ProcessNPCActions()
}

func main() {
	new_game()
	fmt.Println(newPlayer.name, "begins", newPlayer.possessive, "adventure.")
	// Initiate game loop
	for {
		tick()
		<-tSecond.C
	}
}
<|endoftext|>


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

package main

import (
	"flag"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/mb0/lab/golab/gosrc"
	"github.com/mb0/lab/golab/hub"
	"github.com/mb0/lab/ws"
)

var (
	httpstart = flag.Bool("http", false, "start http server")
	httpaddr  = flag.String("addr", "localhost:8910", "http server addr")
)

func init() {
	flag.Parse()
	if !*httpstart {
		return
	}
	log.Printf("starting http://%s/\n", *httpaddr)
	serveclient()
	lab.mods = append(lab.mods, &httpmod{})
}

type httpmod struct {
	*hub.Hub
}

func (mod *httpmod) Start() {
	mod.Hub = hub.New(func(h *hub.Hub, m hub.Msg, id hub.Id) {
		switch m.Head {
		case hub.Signon.Head:
			// send reports for all working packages
			msg, err := hub.Marshal("reports", lab.src.AllReports())
			if err != nil {
				log.Println(err)
				return
			}
			h.SendMsg(msg, id)
		case "stat":
			var path string
			err := m.Unmarshal(&path)
			if err != nil {
				log.Println(err)
				return
			}
			msg, err := apistat(path)
			if err != nil {
				log.Println(err)
				return
			}
			h.SendMsg(msg, id)
		default:
			// echo messages
			h.SendMsg(m, id)
		}
	})
	lab.src.SignalReports(func(r *gosrc.Report) {
		m, err := hub.Marshal("report", r)
		if err != nil {
			log.Println(err)
			return
		}
		mod.SendMsg(m, hub.Group)
	})
	http.Handle("/ws", mod.Hub)
	err := http.ListenAndServe(*httpaddr, nil)
	if err != nil {
		log.Fatalf("http %s\n", err)
	}
}

func (mod *httpmod) Filter(r *ws.Res) bool {
	return false
}

func (mod *httpmod) Handle(op ws.Op, r *ws.Res) {
}

func apistat(path string) (hub.Msg, error) {
	res := apires{ws.NewId(path), path}
	if r := lab.ws.Res(res.Id); r != nil {
		res.Name = r.Name
		if r.Dir != nil {
			cs := make([]apires, 0, len(r.Children))
			for _, c := range r.Children {
				cs = append(cs, apires{c.Id, c.Name})
			}
			return hub.Marshal("stat", struct {
				apires
				Path     string
				Children []apires
			}{res, path, cs})
		}
		return hub.Marshal("stat", struct {
			apires
			Path string
		}{res, path})
	}
	return hub.Marshal("stat.err", struct {
		apires
		Path  string
		Error string
	}{res, path, "not found"})
}

type apires struct {
	Id   ws.Id `json:",string"`
	Name string
}

func serveclient() {
	http.HandleFunc("/", index)
	if static := findstatic(); static == "" {
		indexbytes = []byte("cannot find client files.")
	} else {
		http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(static))))
	}
}

func index(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/" {
		http.NotFound(w, r)
		return
	}
	if r.Method != "GET" {
		http.Error(w, "Method nod allowed", 405)
		return
	}
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.Write(indexbytes)
}

var indexbytes = []byte(`<!DOCTYPE html>

	<title>golab</title>
	<meta charset="utf-8">
	<link href="/static/main.css" rel="stylesheet">

`)

func findstatic() string {
	for _, dir := range lab.dirs {
		path := filepath.Join(dir, "github.com/mb0/lab/golab/static")
		_, err := os.Stat(path)
		if err == nil {
			return path
		}
	}
	return ""
}
<commit_msg>http server serves raw file contents of workspace resources.<commit_after>// Copyright 2013 Martin Schnabel. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/mb0/lab/golab/gosrc"
	"github.com/mb0/lab/golab/hub"
	"github.com/mb0/lab/ws"
)

var (
	httpstart = flag.Bool("http", false, "start http server")
	httpaddr  = flag.String("addr", "localhost:8910", "http server addr")
)

func init() {
	flag.Parse()
	if !*httpstart {
		return
	}
	log.Printf("starting http://%s/\n", *httpaddr)
	serveclient()
	lab.mods = append(lab.mods, &httpmod{})
}

type httpmod struct {
	*hub.Hub
}

func (mod *httpmod) Start() {
	mod.Hub = hub.New(func(h *hub.Hub, m hub.Msg, id hub.Id) {
		switch m.Head {
		case hub.Signon.Head:
			// send reports for all working packages
			msg, err := hub.Marshal("reports", lab.src.AllReports())
			if err != nil {
				log.Println(err)
				return
			}
			h.SendMsg(msg, id)
		case "stat":
			var path string
			err := m.Unmarshal(&path)
			if err != nil {
				log.Println(err)
				return
			}
			msg, err := apistat(path)
			if err != nil {
				log.Println(err)
				return
			}
			h.SendMsg(msg, id)
		default:
			// echo messages
			h.SendMsg(m, id)
		}
	})
	lab.src.SignalReports(func(r *gosrc.Report) {
		m, err := hub.Marshal("report", r)
		if err != nil {
			log.Println(err)
			return
		}
		mod.SendMsg(m, hub.Group)
	})
	http.Handle("/ws", mod.Hub)
	err := http.ListenAndServe(*httpaddr, nil)
	if err != nil {
		log.Fatalf("http %s\n", err)
	}
}

func (mod *httpmod) Filter(r *ws.Res) bool {
	return false
}

func (mod *httpmod) Handle(op ws.Op, r *ws.Res) {
}

func apistat(path string) (hub.Msg, error) {
	res := apires{ws.NewId(path), path, false}
	if r := lab.ws.Res(res.Id); r != nil {
		r.Lock()
		defer r.Unlock()
		res.Name, res.IsDir = r.Name, r.Flag&ws.FlagDir != 0
		if r.Dir != nil {
			cs := make([]apires, 0, len(r.Children))
			for _, c := range r.Children {
				if c.Flag&ws.FlagIgnore == 0 {
					cs = append(cs, apires{c.Id, c.Name, c.Flag&ws.FlagDir != 0})
				}
			}
			return hub.Marshal("stat", struct {
				apires
				Path     string
				Children []apires
			}{res, path, cs})
		}
		return hub.Marshal("stat", struct {
			apires
			Path string
		}{res, path})
	}
	return hub.Marshal("stat.err", struct {
		apires
		Path  string
		Error string
	}{res, path, "not found"})
}

type apires struct {
	Id    ws.Id `json:",string"`
	Name  string
	IsDir bool
}

func serveclient() {
	http.HandleFunc("/", index)
	if static := findstatic(); static == "" {
		indexbytes = []byte("cannot find client files.")
	} else {
		http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(static))))
	}
	http.HandleFunc("/raw/", raw)
}

func index(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/" {
		http.NotFound(w, r)
		return
	}
	if r.Method != "GET" {
		http.Error(w, "Method nod allowed", 405)
		return
	}
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.Write(indexbytes)
}

var indexbytes = []byte(`<!DOCTYPE html>

	<title>golab</title>
	<meta charset="utf-8">
	<link href="/static/main.css" rel="stylesheet">

`)

func findstatic() string {
	for _, dir := range lab.dirs {
		path := filepath.Join(dir, "github.com/mb0/lab/golab/static")
		_, err := os.Stat(path)
		if err == nil {
			return path
		}
	}
	return ""
}

func raw(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path[:5] != "/raw/" {
		http.NotFound(w, r)
		return
	}
	if r.Method != "GET" {
		http.Error(w, "Method nod allowed", 405)
		return
	}
	path := r.URL.Path[4:]
	if res := lab.ws.Res(ws.NewId(path)); res == nil || res.Flag&(ws.FlagDir|ws.FlagIgnore) != 0 {
		http.NotFound(w, r)
		return
	}
	f, err := os.Open(path)
	if err != nil {
		http.NotFound(w, r)
		return
	}
	defer f.Close()
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	_, err = io.Copy(w, f)
	if err != nil {
		log.Println(err)
	}
}
<|endoftext|>


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

package packages

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"strings"

	"github.com/GoogleCloudPlatform/osconfig/clog"
	"github.com/GoogleCloudPlatform/osconfig/osinfo"
	"github.com/GoogleCloudPlatform/osconfig/util"
)

var (
	dpkg      string
	dpkgQuery string
	dpkgDeb   string
	aptGet    string

	dpkgInstallArgs   = []string{"--install"}
	dpkgQueryArgs     = []string{"-W", "-f", "${Package} ${Architecture} ${Version} ${db:Status-Status}\n"}
	dpkgRepairArgs    = []string{"--configure", "-a"}
	aptGetInstallArgs = []string{"install", "-y"}
	aptGetRemoveArgs  = []string{"remove", "-y"}
	aptGetUpdateArgs  = []string{"update"}

	aptGetUpgradeCmd     = "upgrade"
	aptGetFullUpgradeCmd = "full-upgrade"
	aptGetDistUpgradeCmd = "dist-upgrade"
	aptGetUpgradableArgs = []string{"--just-print", "-qq"}
	allowDowngradesArg   = "--allow-downgrades"

	dpkgErr = []byte("dpkg --configure -a")
)

func init() {
	if runtime.GOOS != "windows" {
		dpkg = "/usr/bin/dpkg"
		dpkgQuery = "/usr/bin/dpkg-query"
		dpkgDeb = "/usr/bin/dpkg-deb"
		aptGet = "/usr/bin/apt-get"
	}
	AptExists = util.Exists(aptGet)
	DpkgExists = util.Exists(dpkg)
	DpkgQueryExists = util.Exists(dpkgQuery)
}

// AptUpgradeType is the apt upgrade type.
type AptUpgradeType int

const (
	// AptGetUpgrade specifies apt-get upgrade should be run.
	AptGetUpgrade AptUpgradeType = iota
	// AptGetDistUpgrade specifies apt-get dist-upgrade should be run.
	AptGetDistUpgrade
	// AptGetFullUpgrade specifies apt-get full-upgrade should be run.
	AptGetFullUpgrade
)

type aptGetUpgradeOpts struct {
	upgradeType     AptUpgradeType
	showNew         bool
	allowDowngrades bool
}

// AptGetUpgradeOption is an option for apt-get upgrade.
type AptGetUpgradeOption func(*aptGetUpgradeOpts)

// AptGetUpgradeType returns a AptGetUpgradeOption that specifies upgrade type.
func AptGetUpgradeType(upgradeType AptUpgradeType) AptGetUpgradeOption {
	return func(args *aptGetUpgradeOpts) {
		args.upgradeType = upgradeType
	}
}

// AptGetUpgradeShowNew returns a AptGetUpgradeOption that indicates whether 'new' packages should be returned.
func AptGetUpgradeShowNew(showNew bool) AptGetUpgradeOption {
	return func(args *aptGetUpgradeOpts) {
		args.showNew = showNew
	}
}

// AptGetUpgradeAllowDowngrades returns a AptGetUpgradeOption that specifies AllowDowngrades.
func AptGetUpgradeAllowDowngrades(allowDowngrades bool) AptGetUpgradeOption {
	return func(args *aptGetUpgradeOpts) {
		args.allowDowngrades = allowDowngrades
	}
}

func dpkgRepair(ctx context.Context, out []byte) bool {
	// Error code 100 may occur for non repairable errors, just check the output.
	if !bytes.Contains(out, dpkgErr) {
		return false
	}
	clog.Debugf(ctx, "apt-get error, attempting dpkg repair.")
	// Ignore error here, just log and rerun apt-get.
	run(ctx, dpkg, dpkgRepairArgs)

	return true
}

func parseDpkgDeb(data []byte) (*PkgInfo, error) {
	/*
	   new Debian package, version 2.0.
	   size 6731954 bytes: control archive=2138 bytes.
	       498 bytes,    12 lines      control
	      3465 bytes,    31 lines      md5sums
	      2793 bytes,    65 lines   *  postinst             #!/bin/sh
	       938 bytes,    28 lines   *  postrm               #!/bin/sh
	       216 bytes,     7 lines   *  prerm                #!/bin/sh
	   Package: google-guest-agent
	   Version: 1:1dummy-g1
	   Architecture: amd64
	   Maintainer: Google Cloud Team <gc-team@google.com>
	   Installed-Size: 23279
	   Depends: init-system-helpers (>= 1.18~)
	   Conflicts: python-google-compute-engine, python3-google-compute-engine
	   Section: misc
	   Priority: optional
	   Description: Google Compute Engine Guest Agent
	    Contains the guest agent and metadata script runner binaries.
	   Git: https://github.com/GoogleCloudPlatform/guest-agent/tree/c3d526e650c4e45ae3258c07836fd72f85fd9fc8
	*/

	lines := bytes.Split(bytes.TrimSpace(data), []byte("\n"))
	info := &PkgInfo{}
	for _, ln := range lines {
		if info.Name != "" && info.Version != "" && info.Arch != "" {
			break
		}
		fields := bytes.Fields(ln)
		if len(fields) != 2 {
			continue
		}
		if bytes.Contains(fields[0], []byte("Package:")) {
			info.Name = string(fields[1])
			continue
		}
		if bytes.Contains(fields[0], []byte("Version:")) {
			info.Version = string(fields[1])
			continue
		}
		if bytes.Contains(fields[0], []byte("Architecture:")) {
			info.Arch = osinfo.Architecture(string(fields[1]))
			continue
		}
	}
	if info.Name == "" || info.Version == "" || info.Arch == "" {
		return nil, fmt.Errorf("could not parse dpkg-deb output: %q", data)
	}
	return info, nil
}

// DebPkgInfo gets PkgInfo from a deb package.
func DebPkgInfo(ctx context.Context, path string) (*PkgInfo, error) {
	out, err := run(ctx, dpkgDeb, []string{"-I", path})
	if err != nil {
		return nil, err
	}

	return parseDpkgDeb(out)
}

// InstallAptPackages installs apt packages.
func InstallAptPackages(ctx context.Context, pkgs []string) error {
	args := append(aptGetInstallArgs, pkgs...)
	install := exec.CommandContext(ctx, aptGet, args...)
	install.Env = append(os.Environ(),
		"DEBIAN_FRONTEND=noninteractive",
	)
	stdout, stderr, err := runner.Run(ctx, install)
	if err != nil {
		if dpkgRepair(ctx, stderr) {
			stdout, stderr, err = runner.Run(ctx, install)
		}
	}
	if err != nil {
		err = fmt.Errorf("error running %s with args %q: %v, stdout: %q, stderr: %q", aptGet, args, err, stdout, stderr)
	}
	return err
}

// RemoveAptPackages removes apt packages.
func RemoveAptPackages(ctx context.Context, pkgs []string) error {
	args := append(aptGetRemoveArgs, pkgs...)
	remove := exec.CommandContext(ctx, aptGet, args...)
	remove.Env = append(os.Environ(),
		"DEBIAN_FRONTEND=noninteractive",
	)
	stdout, stderr, err := runner.Run(ctx, remove)
	if err != nil {
		if dpkgRepair(ctx, stderr) {
			stdout, stderr, err = runner.Run(ctx, remove)
		}
	}
	if err != nil {
		err = fmt.Errorf("error running %s with args %q: %v, stdout: %q, stderr: %q", aptGet, args, err, stdout, stderr)
	}
	return err
}

func parseAptUpdates(ctx context.Context, data []byte, showNew bool) []*PkgInfo {
	/*
		Inst libldap-common [2.4.45+dfsg-1ubuntu1.2] (2.4.45+dfsg-1ubuntu1.3 Ubuntu:18.04/bionic-updates, Ubuntu:18.04/bionic-security [all])
		Inst firmware-linux-free (3.4 Debian:9.9/stable [all]) []
		Inst google-cloud-sdk [245.0.0-0] (246.0.0-0 cloud-sdk-stretch:cloud-sdk-stretch [all])
		Inst linux-image-4.9.0-9-amd64 (4.9.168-1+deb9u2 Debian-Security:9/stable [amd64])
		Inst linux-image-amd64 [4.9+80+deb9u6] (4.9+80+deb9u7 Debian:9.9/stable [amd64])
		Conf firmware-linux-free (3.4 Debian:9.9/stable [all])
		Conf google-cloud-sdk (246.0.0-0 cloud-sdk-stretch:cloud-sdk-stretch [all])
		Conf linux-image-4.9.0-9-amd64 (4.9.168-1+deb9u2 Debian-Security:9/stable [amd64])
		Conf linux-image-amd64 (4.9+80+deb9u7 Debian:9.9/stable [amd64])
	*/

	lines := bytes.Split(bytes.TrimSpace(data), []byte("\n"))

	var pkgs []*PkgInfo
	for _, ln := range lines {
		pkg := bytes.Fields(ln)
		if len(pkg) < 5 || string(pkg[0]) != "Inst" {
			continue
		}
		// Inst google-cloud-sdk [245.0.0-0] (246.0.0-0 cloud-sdk-stretch:cloud-sdk-stretch [all])
		pkg = pkg[1:] // ==> google-cloud-sdk [245.0.0-0] (246.0.0-0 cloud-sdk-stretch:cloud-sdk-stretch [all])
		if bytes.HasPrefix(pkg[1], []byte("[")) {
			pkg = append(pkg[:1], pkg[2:]...) // ==> google-cloud-sdk (246.0.0-0 cloud-sdk-stretch:cloud-sdk-stretch [all])
		} else if !showNew {
			// This is a newly installed package and not an upgrade, ignore if showNew is false.
			continue
		}
		// Drop trailing "[]" if they exist.
		if bytes.Contains(pkg[len(pkg)-1], []byte("[]")) {
			pkg = pkg[:len(pkg)-1]
		}
		if !bytes.HasPrefix(pkg[1], []byte("(")) || !bytes.HasSuffix(pkg[len(pkg)-1], []byte(")")) {
			continue
		}
		ver := bytes.Trim(pkg[1], "(")             // (246.0.0-0 => 246.0.0-0
		arch := bytes.Trim(pkg[len(pkg)-1], "[])") // [all]) => all
		pkgs = append(pkgs, &PkgInfo{Name: string(pkg[0]), Arch: osinfo.Architecture(string(arch)), Version: string(ver)})
	}
	return pkgs
}

// AptUpdates returns all the packages that will be installed when running
// apt-get [dist-|full-]upgrade.
func AptUpdates(ctx context.Context, opts ...AptGetUpgradeOption) ([]*PkgInfo, error) {
	aptOpts := &aptGetUpgradeOpts{
		upgradeType:     AptGetUpgrade,
		showNew:         false,
		allowDowngrades: false,
	}

	for _, opt := range opts {
		opt(aptOpts)
	}

	args := aptGetUpgradableArgs
	switch aptOpts.upgradeType {
	case AptGetUpgrade:
		args = append(aptGetUpgradableArgs, aptGetUpgradeCmd)
	case AptGetDistUpgrade:
		args = append(aptGetUpgradableArgs, aptGetDistUpgradeCmd)
	case AptGetFullUpgrade:
		args = append(aptGetUpgradableArgs, aptGetFullUpgradeCmd)
	default:
		return nil, fmt.Errorf("unknown upgrade type: %q", aptOpts.upgradeType)
	}

	if _, err := AptUpdate(ctx); err != nil {
		return nil, err
	}

	out, err := run(ctx, aptGet, args)
	if err != nil {
		return nil, err
	}

	return parseAptUpdates(ctx, out, aptOpts.showNew), nil
}

// AptUpdate runs apt-get update.
func AptUpdate(ctx context.Context) ([]byte, error) {
	return run(ctx, aptGet, aptGetUpdateArgs)
}

func parseInstalledDebpackages(data []byte) []*PkgInfo {
	/*
	   foo amd64 1.2.3-4 installed
	   bar noarch 1.2.3-4 installed
	   baz noarch 1.2.3-4 config-files
	   ...
	*/
	lines := strings.Split(strings.TrimSpace(string(data)), "\n")

	var pkgs []*PkgInfo
	for _, ln := range lines {
		pkg := strings.Fields(ln)
		if len(pkg) != 4 {
			continue
		}

		// Only report on installed packages.
		if pkg[3] != "installed" {
			continue
		}

		pkgs = append(pkgs, &PkgInfo{Name: pkg[0], Arch: osinfo.Architecture(pkg[1]), Version: pkg[2]})
	}
	return pkgs
}

// InstalledDebPackages queries for all installed deb packages.
func InstalledDebPackages(ctx context.Context) ([]*PkgInfo, error) {
	out, err := run(ctx, dpkgQuery, dpkgQueryArgs)
	if err != nil {
		return nil, err
	}
	return parseInstalledDebpackages(out), nil
}

// DpkgInstall installs a deb package.
func DpkgInstall(ctx context.Context, path string) error {
	_, err := run(ctx, dpkg, append(dpkgInstallArgs, path))
	return err
}
<commit_msg>Return lower-case package name (#377)<commit_after>//  Copyright 2019 Google Inc. All Rights Reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

package packages

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"strings"

	"github.com/GoogleCloudPlatform/osconfig/clog"
	"github.com/GoogleCloudPlatform/osconfig/osinfo"
	"github.com/GoogleCloudPlatform/osconfig/util"
)

var (
	dpkg      string
	dpkgQuery string
	dpkgDeb   string
	aptGet    string

	dpkgInstallArgs   = []string{"--install"}
	dpkgQueryArgs     = []string{"-W", "-f", "${Package} ${Architecture} ${Version} ${db:Status-Status}\n"}
	dpkgRepairArgs    = []string{"--configure", "-a"}
	aptGetInstallArgs = []string{"install", "-y"}
	aptGetRemoveArgs  = []string{"remove", "-y"}
	aptGetUpdateArgs  = []string{"update"}

	aptGetUpgradeCmd     = "upgrade"
	aptGetFullUpgradeCmd = "full-upgrade"
	aptGetDistUpgradeCmd = "dist-upgrade"
	aptGetUpgradableArgs = []string{"--just-print", "-qq"}
	allowDowngradesArg   = "--allow-downgrades"

	dpkgErr = []byte("dpkg --configure -a")
)

func init() {
	if runtime.GOOS != "windows" {
		dpkg = "/usr/bin/dpkg"
		dpkgQuery = "/usr/bin/dpkg-query"
		dpkgDeb = "/usr/bin/dpkg-deb"
		aptGet = "/usr/bin/apt-get"
	}
	AptExists = util.Exists(aptGet)
	DpkgExists = util.Exists(dpkg)
	DpkgQueryExists = util.Exists(dpkgQuery)
}

// AptUpgradeType is the apt upgrade type.
type AptUpgradeType int

const (
	// AptGetUpgrade specifies apt-get upgrade should be run.
	AptGetUpgrade AptUpgradeType = iota
	// AptGetDistUpgrade specifies apt-get dist-upgrade should be run.
	AptGetDistUpgrade
	// AptGetFullUpgrade specifies apt-get full-upgrade should be run.
	AptGetFullUpgrade
)

type aptGetUpgradeOpts struct {
	upgradeType     AptUpgradeType
	showNew         bool
	allowDowngrades bool
}

// AptGetUpgradeOption is an option for apt-get upgrade.
type AptGetUpgradeOption func(*aptGetUpgradeOpts)

// AptGetUpgradeType returns a AptGetUpgradeOption that specifies upgrade type.
func AptGetUpgradeType(upgradeType AptUpgradeType) AptGetUpgradeOption {
	return func(args *aptGetUpgradeOpts) {
		args.upgradeType = upgradeType
	}
}

// AptGetUpgradeShowNew returns a AptGetUpgradeOption that indicates whether 'new' packages should be returned.
func AptGetUpgradeShowNew(showNew bool) AptGetUpgradeOption {
	return func(args *aptGetUpgradeOpts) {
		args.showNew = showNew
	}
}

// AptGetUpgradeAllowDowngrades returns a AptGetUpgradeOption that specifies AllowDowngrades.
func AptGetUpgradeAllowDowngrades(allowDowngrades bool) AptGetUpgradeOption {
	return func(args *aptGetUpgradeOpts) {
		args.allowDowngrades = allowDowngrades
	}
}

func dpkgRepair(ctx context.Context, out []byte) bool {
	// Error code 100 may occur for non repairable errors, just check the output.
	if !bytes.Contains(out, dpkgErr) {
		return false
	}
	clog.Debugf(ctx, "apt-get error, attempting dpkg repair.")
	// Ignore error here, just log and rerun apt-get.
	run(ctx, dpkg, dpkgRepairArgs)

	return true
}

func parseDpkgDeb(data []byte) (*PkgInfo, error) {
	/*
	   new Debian package, version 2.0.
	   size 6731954 bytes: control archive=2138 bytes.
	       498 bytes,    12 lines      control
	      3465 bytes,    31 lines      md5sums
	      2793 bytes,    65 lines   *  postinst             #!/bin/sh
	       938 bytes,    28 lines   *  postrm               #!/bin/sh
	       216 bytes,     7 lines   *  prerm                #!/bin/sh
	   Package: google-guest-agent
	   Version: 1:1dummy-g1
	   Architecture: amd64
	   Maintainer: Google Cloud Team <gc-team@google.com>
	   Installed-Size: 23279
	   Depends: init-system-helpers (>= 1.18~)
	   Conflicts: python-google-compute-engine, python3-google-compute-engine
	   Section: misc
	   Priority: optional
	   Description: Google Compute Engine Guest Agent
	    Contains the guest agent and metadata script runner binaries.
	   Git: https://github.com/GoogleCloudPlatform/guest-agent/tree/c3d526e650c4e45ae3258c07836fd72f85fd9fc8
	*/

	lines := bytes.Split(bytes.TrimSpace(data), []byte("\n"))
	info := &PkgInfo{}
	for _, ln := range lines {
		if info.Name != "" && info.Version != "" && info.Arch != "" {
			break
		}
		fields := bytes.Fields(ln)
		if len(fields) != 2 {
			continue
		}
		if bytes.Contains(fields[0], []byte("Package:")) {
			// Some packages do not adhere to the Debian Policy and might have mix-cased names
			// And dpkg will register the package with lower case anyway so use lower-case package name
			// This is necessary because the compliance check is done between the .deb file descriptor value
			// and the internal dpkg db which register a lower-cased package name
			info.Name = strings.ToLower(string(fields[1]))
			continue
		}
		if bytes.Contains(fields[0], []byte("Version:")) {
			info.Version = string(fields[1])
			continue
		}
		if bytes.Contains(fields[0], []byte("Architecture:")) {
			info.Arch = osinfo.Architecture(string(fields[1]))
			continue
		}
	}
	if info.Name == "" || info.Version == "" || info.Arch == "" {
		return nil, fmt.Errorf("could not parse dpkg-deb output: %q", data)
	}
	return info, nil
}

// DebPkgInfo gets PkgInfo from a deb package.
func DebPkgInfo(ctx context.Context, path string) (*PkgInfo, error) {
	out, err := run(ctx, dpkgDeb, []string{"-I", path})
	if err != nil {
		return nil, err
	}

	return parseDpkgDeb(out)
}

// InstallAptPackages installs apt packages.
func InstallAptPackages(ctx context.Context, pkgs []string) error {
	args := append(aptGetInstallArgs, pkgs...)
	install := exec.CommandContext(ctx, aptGet, args...)
	install.Env = append(os.Environ(),
		"DEBIAN_FRONTEND=noninteractive",
	)
	stdout, stderr, err := runner.Run(ctx, install)
	if err != nil {
		if dpkgRepair(ctx, stderr) {
			stdout, stderr, err = runner.Run(ctx, install)
		}
	}
	if err != nil {
		err = fmt.Errorf("error running %s with args %q: %v, stdout: %q, stderr: %q", aptGet, args, err, stdout, stderr)
	}
	return err
}

// RemoveAptPackages removes apt packages.
func RemoveAptPackages(ctx context.Context, pkgs []string) error {
	args := append(aptGetRemoveArgs, pkgs...)
	remove := exec.CommandContext(ctx, aptGet, args...)
	remove.Env = append(os.Environ(),
		"DEBIAN_FRONTEND=noninteractive",
	)
	stdout, stderr, err := runner.Run(ctx, remove)
	if err != nil {
		if dpkgRepair(ctx, stderr) {
			stdout, stderr, err = runner.Run(ctx, remove)
		}
	}
	if err != nil {
		err = fmt.Errorf("error running %s with args %q: %v, stdout: %q, stderr: %q", aptGet, args, err, stdout, stderr)
	}
	return err
}

func parseAptUpdates(ctx context.Context, data []byte, showNew bool) []*PkgInfo {
	/*
		Inst libldap-common [2.4.45+dfsg-1ubuntu1.2] (2.4.45+dfsg-1ubuntu1.3 Ubuntu:18.04/bionic-updates, Ubuntu:18.04/bionic-security [all])
		Inst firmware-linux-free (3.4 Debian:9.9/stable [all]) []
		Inst google-cloud-sdk [245.0.0-0] (246.0.0-0 cloud-sdk-stretch:cloud-sdk-stretch [all])
		Inst linux-image-4.9.0-9-amd64 (4.9.168-1+deb9u2 Debian-Security:9/stable [amd64])
		Inst linux-image-amd64 [4.9+80+deb9u6] (4.9+80+deb9u7 Debian:9.9/stable [amd64])
		Conf firmware-linux-free (3.4 Debian:9.9/stable [all])
		Conf google-cloud-sdk (246.0.0-0 cloud-sdk-stretch:cloud-sdk-stretch [all])
		Conf linux-image-4.9.0-9-amd64 (4.9.168-1+deb9u2 Debian-Security:9/stable [amd64])
		Conf linux-image-amd64 (4.9+80+deb9u7 Debian:9.9/stable [amd64])
	*/

	lines := bytes.Split(bytes.TrimSpace(data), []byte("\n"))

	var pkgs []*PkgInfo
	for _, ln := range lines {
		pkg := bytes.Fields(ln)
		if len(pkg) < 5 || string(pkg[0]) != "Inst" {
			continue
		}
		// Inst google-cloud-sdk [245.0.0-0] (246.0.0-0 cloud-sdk-stretch:cloud-sdk-stretch [all])
		pkg = pkg[1:] // ==> google-cloud-sdk [245.0.0-0] (246.0.0-0 cloud-sdk-stretch:cloud-sdk-stretch [all])
		if bytes.HasPrefix(pkg[1], []byte("[")) {
			pkg = append(pkg[:1], pkg[2:]...) // ==> google-cloud-sdk (246.0.0-0 cloud-sdk-stretch:cloud-sdk-stretch [all])
		} else if !showNew {
			// This is a newly installed package and not an upgrade, ignore if showNew is false.
			continue
		}
		// Drop trailing "[]" if they exist.
		if bytes.Contains(pkg[len(pkg)-1], []byte("[]")) {
			pkg = pkg[:len(pkg)-1]
		}
		if !bytes.HasPrefix(pkg[1], []byte("(")) || !bytes.HasSuffix(pkg[len(pkg)-1], []byte(")")) {
			continue
		}
		ver := bytes.Trim(pkg[1], "(")             // (246.0.0-0 => 246.0.0-0
		arch := bytes.Trim(pkg[len(pkg)-1], "[])") // [all]) => all
		pkgs = append(pkgs, &PkgInfo{Name: string(pkg[0]), Arch: osinfo.Architecture(string(arch)), Version: string(ver)})
	}
	return pkgs
}

// AptUpdates returns all the packages that will be installed when running
// apt-get [dist-|full-]upgrade.
func AptUpdates(ctx context.Context, opts ...AptGetUpgradeOption) ([]*PkgInfo, error) {
	aptOpts := &aptGetUpgradeOpts{
		upgradeType:     AptGetUpgrade,
		showNew:         false,
		allowDowngrades: false,
	}

	for _, opt := range opts {
		opt(aptOpts)
	}

	args := aptGetUpgradableArgs
	switch aptOpts.upgradeType {
	case AptGetUpgrade:
		args = append(aptGetUpgradableArgs, aptGetUpgradeCmd)
	case AptGetDistUpgrade:
		args = append(aptGetUpgradableArgs, aptGetDistUpgradeCmd)
	case AptGetFullUpgrade:
		args = append(aptGetUpgradableArgs, aptGetFullUpgradeCmd)
	default:
		return nil, fmt.Errorf("unknown upgrade type: %q", aptOpts.upgradeType)
	}

	if _, err := AptUpdate(ctx); err != nil {
		return nil, err
	}

	out, err := run(ctx, aptGet, args)
	if err != nil {
		return nil, err
	}

	return parseAptUpdates(ctx, out, aptOpts.showNew), nil
}

// AptUpdate runs apt-get update.
func AptUpdate(ctx context.Context) ([]byte, error) {
	return run(ctx, aptGet, aptGetUpdateArgs)
}

func parseInstalledDebpackages(data []byte) []*PkgInfo {
	/*
	   foo amd64 1.2.3-4 installed
	   bar noarch 1.2.3-4 installed
	   baz noarch 1.2.3-4 config-files
	   ...
	*/
	lines := strings.Split(strings.TrimSpace(string(data)), "\n")

	var pkgs []*PkgInfo
	for _, ln := range lines {
		pkg := strings.Fields(ln)
		if len(pkg) != 4 {
			continue
		}

		// Only report on installed packages.
		if pkg[3] != "installed" {
			continue
		}

		pkgs = append(pkgs, &PkgInfo{Name: pkg[0], Arch: osinfo.Architecture(pkg[1]), Version: pkg[2]})
	}
	return pkgs
}

// InstalledDebPackages queries for all installed deb packages.
func InstalledDebPackages(ctx context.Context) ([]*PkgInfo, error) {
	out, err := run(ctx, dpkgQuery, dpkgQueryArgs)
	if err != nil {
		return nil, err
	}
	return parseInstalledDebpackages(out), nil
}

// DpkgInstall installs a deb package.
func DpkgInstall(ctx context.Context, path string) error {
	_, err := run(ctx, dpkg, append(dpkgInstallArgs, path))
	return err
}
<|endoftext|>