<commit_before>package main

import (
	"encoding/json"
	"flag"
	"io"
	"io/ioutil"
	"log"
	"os"
	"time"
)

import (
	"github.com/qiniu/api/conf"
	"github.com/qiniu/api/rsf"
)

type Item struct {
	Name     string    `json:"name"`
	Etag     string    `json:"etag"`
	Size     int64     `json:"size"`
	Time     time.Time `json:"time"`
	MimeType string    `json:"mime"`
	User     string    `json:"user"`
}

func convert(list []rsf.ListItem) (ret []Item) {
	for _, v := range list {
		var it Item
		it.Name = v.Key
		it.Etag = v.Hash
		it.Size = v.Fsize
		it.Time = time.Unix(v.PutTime/1e7, 0)
		it.MimeType = v.MimeType
		it.User = v.EndUser
		ret = append(ret, it)
	}
	return
}

func main() {
	ak := flag.String("ak", "", "access key")
	sk := flag.String("sk", "", "secret key")
	bucket := flag.String("bucket", "", "bucket")
	out := flag.String("o", "", "output file")
	flag.Parse()
	if *ak == "" || *sk == "" || *bucket == "" || *out == "" {
		flag.PrintDefaults()
		log.Fatalln("invalid args")
		return
	}
	conf.ACCESS_KEY = *ak
	conf.SECRET_KEY = *sk
	list := []rsf.ListItem{}
	client := rsf.New(nil)
	marker := ""
	var err error
	for {
		var ret []rsf.ListItem
		ret, marker, err = client.ListPrefix(nil, *bucket, "", marker, 1000)
		if err != nil && err != io.EOF {
			log.Fatalln("error occured!", err)
			return
		}
		list = append(list, ret...)
		if err == io.EOF {
			break
		}
	}
	data, err := json.MarshalIndent(convert(list), "", "")
	if err != nil {
		log.Fatalln("list data error", err)
		return
	}
	err = ioutil.WriteFile(*out, data, os.ModePerm)
	if err != nil {
		log.Fatalln("write file failed")
	}
}
<commit_msg>add filesize<commit_after>package main

import (
	"encoding/json"
	"flag"
	"io"
	"io/ioutil"
	"log"
	"os"
	"time"
)

import (
	"github.com/qiniu/api/conf"
	"github.com/qiniu/api/rsf"
)

type Item struct {
	Name     string    `json:"name"`
	Etag     string    `json:"etag"`
	Size     int64     `json:"size"`
	Time     time.Time `json:"time"`
	MimeType string    `json:"mime"`
	User     string    `json:"user"`
}

func convert(list []rsf.ListItem) (ret []Item, size int64) {
	for _, v := range list {
		var it Item
		it.Name = v.Key
		it.Etag = v.Hash
		it.Size = v.Fsize
		it.Time = time.Unix(v.PutTime/1e7, 0)
		it.MimeType = v.MimeType
		it.User = v.EndUser
		ret = append(ret, it)
		size += v.Fsize
	}
	return
}

func main() {
	ak := flag.String("ak", "", "access key")
	sk := flag.String("sk", "", "secret key")
	bucket := flag.String("bucket", "", "bucket")
	out := flag.String("o", "", "output file")
	flag.Parse()
	if *ak == "" || *sk == "" || *bucket == "" || *out == "" {
		flag.PrintDefaults()
		log.Fatalln("invalid args")
		return
	}
	conf.ACCESS_KEY = *ak
	conf.SECRET_KEY = *sk
	list := []rsf.ListItem{}
	client := rsf.New(nil)
	marker := ""
	var err error
	for {
		var ret []rsf.ListItem
		ret, marker, err = client.ListPrefix(nil, *bucket, "", marker, 1000)
		if err != nil && err != io.EOF {
			log.Fatalln("error occured!", err)
			return
		}
		list = append(list, ret...)
		if err == io.EOF {
			break
		}
	}
	items, size := convert(list)
	log.Println("total size", size)
	data, err := json.MarshalIndent(items, "", "")
	if err != nil {
		log.Fatalln("list data error", err)
		return
	}
	err = ioutil.WriteFile(*out, data, os.ModePerm)
	if err != nil {
		log.Fatalln("write file failed")
	}
}
<|endoftext|>


<commit_before>package dns

import (
	"strconv"
)

// EDNS0 Options and Do bit
const (
	OptionCodeLLQ  = 1      // Not used
	OptionCodeUL   = 2      // Not used
	OptionCodeNSID = 3      // NSID, RFC5001
	_DO            = 1 << 7 // dnssec ok
)

// An ENDS0 option rdata element.
type Option struct {
	Code uint16
	Data string "hex"
}

/*
 * EDNS extended RR.
 * This is the EDNS0 Header
 * 	Name          string "domain-name"
 * 	Opt           uint16 // was type, but is always TypeOPT
 * 	UDPSize       uint16 // was class
 * 	ExtendedRcode uint8  // was TTL
 * 	Version       uint8  // was TTL
 * 	Z             uint16 // was TTL (all flags should be put here)
 * 	Rdlength      uint16 // length of data after the header
 */

type RR_OPT struct {
	Hdr    RR_Header
	Option []Option "OPT" // Tag is used in pack and unpack
}

func (rr *RR_OPT) Header() *RR_Header {
	return &rr.Hdr
}

func (rr *RR_OPT) String() string {
	s := ";; EDNS: version " + strconv.Itoa(int(rr.Version(0, false))) + "; "
	if rr.DoBit(false, false) {
		s += "flags: do; "
	} else {
		s += "flags: ; "
	}
	s += "udp: " + strconv.Itoa(int(rr.UDPSize(0, false))) + ";"

	for _, o := range rr.Option {
		switch o.Code {
		case OptionCodeNSID:
			s += " nsid: " + o.Data + ";"
		}
	}
	return s
}

// Set the version of edns
func (rr *RR_OPT) Version(v uint8, set bool) uint8 {
	return 0
}

// Set/Get the UDP buffer size
func (rr *RR_OPT) UDPSize(size uint16, set bool) uint16 {
	if set {
		rr.Hdr.Class = size
	}
	return rr.Hdr.Class
}

/* from RFC 3225
             +0 (MSB)                +1 (LSB)
      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   0: |   EXTENDED-RCODE      |       VERSION         |
      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   2: |DO|                    Z                       |
      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/

// Set/Get the DoBit
func (rr *RR_OPT) DoBit(do, set bool) bool {
	if set {
		b1 := byte(rr.Hdr.Ttl >> 24)
		b2 := byte(rr.Hdr.Ttl >> 16)
		b3 := byte(rr.Hdr.Ttl >> 8)
		b4 := byte(rr.Hdr.Ttl)
		b3 |= _DO // Set it
		rr.Hdr.Ttl = uint32(b1)<<24 | uint32(b2)<<16 | uint32(b3)<<8 | uint32(b4)
		return true
	} else {
		return byte(rr.Hdr.Ttl >> 8) &_DO == _DO
	}
	return true // dead code, bug in Go
}

// when set is true, set the nsid, otherwise get it
func (rr *RR_OPT) Nsid(nsid string, set bool) string {
	// RR.Option[0] to be set
	return ""
}
<commit_msg>Make the edns api more Go like<commit_after>package dns

import (
	"strconv"
)

// EDNS0 Options and Do bit
const (
	OptionCodeLLQ  = 1      // Not used
	OptionCodeUL   = 2      // Not used
	OptionCodeNSID = 3      // NSID, RFC5001
	_DO            = 1 << 7 // dnssec ok
)

// An ENDS0 option rdata element.
type Option struct {
	Code uint16
	Data string "hex"
}

/*
 * EDNS extended RR.
 * This is the EDNS0 Header
 * 	Name          string "domain-name"
 * 	Opt           uint16 // was type, but is always TypeOPT
 * 	UDPSize       uint16 // was class
 * 	ExtendedRcode uint8  // was TTL
 * 	Version       uint8  // was TTL
 * 	Z             uint16 // was TTL (all flags should be put here)
 * 	Rdlength      uint16 // length of data after the header
 */

type RR_OPT struct {
	Hdr    RR_Header
	Option []Option "OPT" // Tag is used in pack and unpack
}

func (rr *RR_OPT) Header() *RR_Header {
	return &rr.Hdr
}

func (rr *RR_OPT) String() string {
	s := ";; EDNS: version " + strconv.Itoa(int(rr.Version())) + "; "
	if rr.Do() {
		s += "flags: do; "
	} else {
		s += "flags: ; "
	}
	s += "udp: " + strconv.Itoa(int(rr.UDPSize())) + ";"

	for _, o := range rr.Option {
		switch o.Code {
		case OptionCodeNSID:
			s += " nsid: " + o.Data + ";"
		}
	}
	return s
}

// Get the version
func (rr *RR_OPT) Version() uint8 {
        return 0
}

// Set the version of edns
func (rr *RR_OPT) SetVersion(v uint8) {
	return
}

// Get the UDP buffer size
func (rr *RR_OPT) UDPSize() uint16 {
	return rr.Hdr.Class
}

// Set/Get the UDP buffer size
func (rr *RR_OPT) SetUDPSize(size uint16) {
	rr.Hdr.Class = size
}

/* from RFC 3225
             +0 (MSB)                +1 (LSB)
      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   0: |   EXTENDED-RCODE      |       VERSION         |
      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   2: |DO|                    Z                       |
      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/

// Get the do bit
func (rr *RR_OPT) Do() bool {
        return byte(rr.Hdr.Ttl >> 8) &_DO == _DO
}

// Set the do bit
func (rr *RR_OPT) SetDo() {
        b1 := byte(rr.Hdr.Ttl >> 24)
        b2 := byte(rr.Hdr.Ttl >> 16)
        b3 := byte(rr.Hdr.Ttl >> 8)
        b4 := byte(rr.Hdr.Ttl)
        b3 |= _DO // Set it
        rr.Hdr.Ttl = uint32(b1)<<24 | uint32(b2)<<16 | uint32(b3)<<8 | uint32(b4)
}

// Return the NSID as hex string
func (rr *RR_OPT) Nsid(nsid string) string {
	// RR.Option[0] to be set
	return ""
}

// Set the NSID
func (rr *RR_OPT) SetNsid(nsid string) {

}
<|endoftext|>


<commit_before>// Package errs improves on the standard `error` by encapsulating stack traces, timestamps,
// optional internal information, and optional public user-facing messages.
//
// Usage
//
// Create an empty error with stack trace and timestamp:
//
//  err := errs.New(nil)
//  err.Time() // time.Time at time of creation
//  err.Stack() // output from debug.Stack() at time of creation
//
// Create an error with associated internal info and a user-facing message:
//
//  userEmail := "user@example.com"
//  emailExists := checkIfEmailExists(userEmail)
//  if emailExists {
//    err := errs.New(errs.Info{ "Email":userEmail }, userEmail, "is already taken. Try another!")
//    return err
//  }
//  ...
//  err.Info("Email") // "user@example.com"
//  err.PublicMsg() // "user@example.com is already taken. Try another!"
//
// Wrap a standard error:
//
//  err := errors.New("An error")
//  err = errs.Wrap(err, errs.Info{"Foo": "Bar"}, "User message")
//  ...
//  err.WrappedErr().Error() == "An error"
package errs

import (
	"fmt"
	"runtime/debug"
	"time"
)

// Err is a richer error interface
type Err interface {
	// Error is an alias for LogString.
	// (errs.Err implements the error interface).
	Error() string

	// Stack returns the result of debug.Stack() from the time when this Err was created.
	Stack() []byte

	// Time returns the time.Time at which this Err was created.
	Time() time.Time

	// If errs.Wrap was used then WrappedError returns the wrapped error.
	WrappedError() error

	// If errs.Wrap or errs.New was called with any publicMsg values
	// then PublicMsg returns a string representation of those values.
	// This is useful for bubbling up user-facing message strings,
	// e.g `errs.New(nil, userEmail, "is already taken. Try another!")`
	PublicMsg() string

	// If errs.Wrap or errs.New was called with an errs.Info object
	// then Info("Foo") return the value of errs.Info{"Foo":...}
	// This is useful for bubbling up internal-facing info,
	// e.g `errs.Wrap(sqlError, { "SqlString":sqlStr, "SqlArgs":sqlArgs })`
	Info(name string) interface{}

	// AllInfo returns all info key-value-pairs passed through errs.New or errs.Wrap
	AllInfo() Info

	// LogString returns a string suitable for logging
	LogString() string

	// IsUserError returns false if it was created with errs.UserError.
	// Useful for e.g escaping out of a call stack but not logging it as
	// an unexpected/critical error,
	// e.g `errs.UserError(nil, "Wrong username/password")`
	IsUserError() bool
}

// New creates a new Err with the given Info and optional public message
func New(info Info, publicMsg ...interface{}) Err {
	return newErr(nil, false, info, publicMsg)
}

// Wrap the given error in an errs.Err. If err is nil, Wrap returns nil.
// Use Err.WrappedError for direct access to the wrapped error.
func Wrap(wrapErr error, info Info, publicMsg ...interface{}) Err {
	if wrapErr == nil {
		return nil
	}
	if info == nil {
		info = Info{}
	}
	if errsErr, isErr := IsErr(wrapErr); isErr {
		if errStructErr, isErrsErr := errsErr.(*err); isErrsErr {
			errStructErr.mergeIn(info, publicMsg)
			return errStructErr
		}
		return errsErr
	}
	return newErr(wrapErr, false, info, publicMsg)
}

// UserError creates an errs.Err which returns true for IsUserError().
// See Err.IsUserError
func UserError(info Info, publicMsg ...interface{}) Err {
	return newErr(nil, true, info, publicMsg)
}

// Format creates and wraps an error with the given error string. Equivalent to:
// `errs.Wrap(fmt.Errorf(format, args...))`
func Format(info Info, format string, argv ...interface{}) Err {
	return newErr(fmt.Errorf(format, argv...), false, info, nil)
}

// Info allows for associating key-value-pair info with an error for debugging,
// e.g `errs.Wrap(sqlError, { "SqlString":sqlStr, "SqlArgs":sqlArgs })`
type Info map[string]interface{}

// IsErr checks if err is an errs.Err, and return it as an errs.Err if it is.
// This is equivalent to err.(errs.Err)
func IsErr(err error) (Err, bool) {
	errsErr, isErr := err.(Err)
	return errsErr, isErr
}

// Internal
///////////

// err implements Err
type err struct {
	stack      []byte
	time       time.Time
	wrappedErr error
	isUserErr  bool
	info       Info
	publicMsg  string
}

func newErr(wrappedErr error, isUserErr bool, info Info, publicMsgParts []interface{}) Err {
	publicMsg := concatArgs(publicMsgParts...)
	stack := debug.Stack()
	return &err{stack, time.Now(), wrappedErr, isUserErr, info, publicMsg}
}

// Implements Err
func (e *err) Stack() []byte       { return e.stack }
func (e *err) Time() time.Time     { return e.time }
func (e *err) WrappedError() error { return e.wrappedErr }
func (e *err) PublicMsg() string   { return e.publicMsg }
func (e *err) Error() string       { return e.LogString() }
func (e *err) String() string      { return e.LogString() }
func (e *err) AllInfo() Info       { return e.info }
func (e *err) IsUserError() bool   { return e.isUserErr }

// Implements Err
func (e *err) Info(key string) interface{} {
	if e.info == nil {
		return nil
	}
	return e.info[key]
}

// Implements Err
func (e *err) LogString() string {
	return concatArgs("Error",
		"| Time:", e.time,
		"| StdError:", e.wrappedErrStr(),
		"| Info:["+concatArgs(e.info)+"]",
		"| PublicMsg:", e.publicMsg,
		"| Stack:", string(e.stack),
	)
}

// Merge in the given info and public message parts into this error
func (e *err) mergeIn(info Info, publicMsgParts []interface{}) {
	for key, val := range info {
		for e.info[key] != nil {
			key = key + "_duplicate"
		}
		e.info[key] = val
	}
	publicMsgPrefix := concatArgs(publicMsgParts...)
	if publicMsgPrefix == "" {
		// do nothing
	} else if e.publicMsg == "" {
		e.publicMsg = publicMsgPrefix
	} else {
		e.publicMsg = publicMsgPrefix + " - " + e.publicMsg
	}
}

// Get the string representation of the wrapper error,
// or an empty string if wrappedErr is nil
func (e *err) wrappedErrStr() string {
	if e == nil {
		return ""
	}
	if e.wrappedErr == nil {
		return ""
	}
	return e.wrappedErr.Error()
}

// Helper to concatenate arguments into a string,
// with spaces between the arguments
func concatArgs(args ...interface{}) string {
	res := fmt.Sprintln(args...)
	return res[0 : len(res)-1] // Remove newline at the end
}
<commit_msg>Remove one line from the debug stack for easier readability<commit_after>// Package errs improves on the standard `error` by encapsulating stack traces, timestamps,
// optional internal information, and optional public user-facing messages.
//
// Usage
//
// Create an empty error with stack trace and timestamp:
//
//  err := errs.New(nil)
//  err.Time() // time.Time at time of creation
//  err.Stack() // output from debug.Stack() at time of creation
//
// Create an error with associated internal info and a user-facing message:
//
//  userEmail := "user@example.com"
//  emailExists := checkIfEmailExists(userEmail)
//  if emailExists {
//    err := errs.New(errs.Info{ "Email":userEmail }, userEmail, "is already taken. Try another!")
//    return err
//  }
//  ...
//  err.Info("Email") // "user@example.com"
//  err.PublicMsg() // "user@example.com is already taken. Try another!"
//
// Wrap a standard error:
//
//  err := errors.New("An error")
//  err = errs.Wrap(err, errs.Info{"Foo": "Bar"}, "User message")
//  ...
//  err.WrappedErr().Error() == "An error"
package errs

import (
	"fmt"
	"runtime/debug"
	"time"
)

// Err is a richer error interface
type Err interface {
	// Error is an alias for LogString.
	// (errs.Err implements the error interface).
	Error() string

	// Stack returns the result of debug.Stack() from the time when this Err was created.
	Stack() []byte

	// Time returns the time.Time at which this Err was created.
	Time() time.Time

	// If errs.Wrap was used then WrappedError returns the wrapped error.
	WrappedError() error

	// If errs.Wrap or errs.New was called with any publicMsg values
	// then PublicMsg returns a string representation of those values.
	// This is useful for bubbling up user-facing message strings,
	// e.g `errs.New(nil, userEmail, "is already taken. Try another!")`
	PublicMsg() string

	// If errs.Wrap or errs.New was called with an errs.Info object
	// then Info("Foo") return the value of errs.Info{"Foo":...}
	// This is useful for bubbling up internal-facing info,
	// e.g `errs.Wrap(sqlError, { "SqlString":sqlStr, "SqlArgs":sqlArgs })`
	Info(name string) interface{}

	// AllInfo returns all info key-value-pairs passed through errs.New or errs.Wrap
	AllInfo() Info

	// LogString returns a string suitable for logging
	LogString() string

	// IsUserError returns false if it was created with errs.UserError.
	// Useful for e.g escaping out of a call stack but not logging it as
	// an unexpected/critical error,
	// e.g `errs.UserError(nil, "Wrong username/password")`
	IsUserError() bool
}

// New creates a new Err with the given Info and optional public message
func New(info Info, publicMsg ...interface{}) Err {
	return newErr(debug.Stack(), nil, false, info, publicMsg)
}

// Wrap the given error in an errs.Err. If err is nil, Wrap returns nil.
// Use Err.WrappedError for direct access to the wrapped error.
func Wrap(wrapErr error, info Info, publicMsg ...interface{}) Err {
	if wrapErr == nil {
		return nil
	}
	if info == nil {
		info = Info{}
	}
	if errsErr, isErr := IsErr(wrapErr); isErr {
		if errStructErr, isErrsErr := errsErr.(*err); isErrsErr {
			errStructErr.mergeIn(info, publicMsg)
			return errStructErr
		}
		return errsErr
	}
	return newErr(debug.Stack(), wrapErr, false, info, publicMsg)
}

// UserError creates an errs.Err which returns true for IsUserError().
// See Err.IsUserError
func UserError(info Info, publicMsg ...interface{}) Err {
	return newErr(debug.Stack(), nil, true, info, publicMsg)
}

// Format creates and wraps an error with the given error string. Equivalent to:
// `errs.Wrap(fmt.Errorf(format, args...))`
func Format(info Info, format string, argv ...interface{}) Err {
	return newErr(debug.Stack(), fmt.Errorf(format, argv...), false, info, nil)
}

// Info allows for associating key-value-pair info with an error for debugging,
// e.g `errs.Wrap(sqlError, { "SqlString":sqlStr, "SqlArgs":sqlArgs })`
type Info map[string]interface{}

// IsErr checks if err is an errs.Err, and return it as an errs.Err if it is.
// This is equivalent to err.(errs.Err)
func IsErr(err error) (Err, bool) {
	errsErr, isErr := err.(Err)
	return errsErr, isErr
}

// Internal
///////////

// err implements Err
type err struct {
	stack      []byte
	time       time.Time
	wrappedErr error
	isUserErr  bool
	info       Info
	publicMsg  string
}

func newErr(stack []byte, wrappedErr error, isUserErr bool, info Info, publicMsgParts []interface{}) Err {
	publicMsg := concatArgs(publicMsgParts...)
	return &err{stack, time.Now(), wrappedErr, isUserErr, info, publicMsg}
}

// Implements Err
func (e *err) Stack() []byte       { return e.stack }
func (e *err) Time() time.Time     { return e.time }
func (e *err) WrappedError() error { return e.wrappedErr }
func (e *err) PublicMsg() string   { return e.publicMsg }
func (e *err) Error() string       { return e.LogString() }
func (e *err) String() string      { return e.LogString() }
func (e *err) AllInfo() Info       { return e.info }
func (e *err) IsUserError() bool   { return e.isUserErr }

// Implements Err
func (e *err) Info(key string) interface{} {
	if e.info == nil {
		return nil
	}
	return e.info[key]
}

// Implements Err
func (e *err) LogString() string {
	return concatArgs("Error",
		"| Time:", e.time,
		"| StdError:", e.wrappedErrStr(),
		"| Info:["+concatArgs(e.info)+"]",
		"| PublicMsg:", e.publicMsg,
		"| Stack:", string(e.stack),
	)
}

// Merge in the given info and public message parts into this error
func (e *err) mergeIn(info Info, publicMsgParts []interface{}) {
	for key, val := range info {
		for e.info[key] != nil {
			key = key + "_duplicate"
		}
		e.info[key] = val
	}
	publicMsgPrefix := concatArgs(publicMsgParts...)
	if publicMsgPrefix == "" {
		// do nothing
	} else if e.publicMsg == "" {
		e.publicMsg = publicMsgPrefix
	} else {
		e.publicMsg = publicMsgPrefix + " - " + e.publicMsg
	}
}

// Get the string representation of the wrapper error,
// or an empty string if wrappedErr is nil
func (e *err) wrappedErrStr() string {
	if e == nil {
		return ""
	}
	if e.wrappedErr == nil {
		return ""
	}
	return e.wrappedErr.Error()
}

// Helper to concatenate arguments into a string,
// with spaces between the arguments
func concatArgs(args ...interface{}) string {
	res := fmt.Sprintln(args...)
	return res[0 : len(res)-1] // Remove newline at the end
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"exec"
	"fmt"
	"io/ioutil"
	"os"
	"runtime"
	"sort"
	"strings"
	"syscall"
)

type TaskResourceUsage struct {
	// User time, in nanoseconds
	userTime int64

	// System time, in nanoseconds
	systemTime int64

	// The number of times the command was executed
	numInvocations int

	dontPrintAverage bool
}

func (t *TaskResourceUsage) TotalTime() int64 {
	return t.userTime + t.systemTime
}

func (t *TaskResourceUsage) AverageTime() float64 {
	return float64(t.TotalTime()) / float64(t.numInvocations)
}

var taskStats = make(map[string]*TaskResourceUsage)

func addResourceUsage(taskName string, usage *syscall.Rusage) {
	taskRUsage := taskStats[taskName]
	if taskRUsage == nil {
		taskRUsage = &TaskResourceUsage{
			userTime:       0,
			systemTime:     0,
			numInvocations: 0,
		}

		taskStats[taskName] = taskRUsage
	}

	taskRUsage.systemTime += int64(usage.Utime.Sec)*1e9 + int64(usage.Utime.Usec)*1e3
	taskRUsage.userTime += int64(usage.Stime.Sec)*1e9 + int64(usage.Stime.Usec)*1e3
	taskRUsage.numInvocations += 1
}

func addSelf() {
	// Remove any previous stats
	taskStats["(self)"] = nil, false
	taskStats["(self.gc)"] = nil, false

	// Enclose the following 5 lines in a comment
	// if the compiler is unable to find 'syscall.Getrusage'
	var usage syscall.Rusage
	errno := syscall.Getrusage(0, &usage)
	if errno == 0 {
		addResourceUsage("(self)", &usage)
	}

	taskStats["(self.gc)"] = &TaskResourceUsage{
		userTime:         int64(runtime.MemStats.PauseTotalNs),
		systemTime:       0,
		numInvocations:   int(runtime.MemStats.NumGC),
		dontPrintAverage: true,
	}
}

func printTimings(out *os.File) {
	addSelf()

	sortedNames := make([]string, len(taskStats))
	maxNameLength := 0
	{
		i := 0
		for name, _ := range taskStats {
			sortedNames[i] = name
			if len(name) > maxNameLength {
				maxNameLength = len(name)
			}
			i++
		}

		sort.StringArray(sortedNames).Sort()
	}

	fmt.Fprintf(out, "Run times:\n")
	for _, name := range sortedNames {
		var buf bytes.Buffer
		fmt.Fprintf(&buf, "    %s:", name)
		for i := len(name); i < maxNameLength; i++ {
			buf.WriteString(" ")
		}
		taskRUsage := taskStats[name]
		fmt.Fprintf(&buf, " %.3f secs", float64(taskRUsage.TotalTime())/1e9)
		if taskRUsage.numInvocations >= 2 {
			if !taskRUsage.dontPrintAverage {
				fmt.Fprintf(&buf, " (%d invocations, %.3f secs per invocation)",
					taskRUsage.numInvocations, taskRUsage.AverageTime()/1e9)
			} else {
				fmt.Fprintf(&buf, " (%d invocations)", taskRUsage.numInvocations)
			}
		}

		fmt.Fprintf(out, "%s\n", buf.String())
	}
}

type Executable struct {
	name     string
	noLookup bool
	fullPath string // Cached path obtained by calling 'exec.LookPath(name)'
}

type RunFlags struct {
	cmdChan_orNil         chan *exec.Cmd
	stdin, stdout, stderr int
	dontPrintCmd          bool
}

func (e *Executable) runSimply(argv []string, dir string, dontPrintCmd bool) os.Error {
	flags := RunFlags{
		cmdChan_orNil: nil,
		stdin:         exec.PassThrough,
		stdout:        exec.PassThrough,
		stderr:        exec.PassThrough,
		dontPrintCmd:  false,
	}

	return e.run_lowLevel(argv, dir, flags)
}

// Runs 'e' as separate process, waits until it finishes,
// and returns the data the process sent to its output(s).
// The argument 'in' comprises the command's input.
func (e *Executable) run(argv []string, dir string, in string, mergeStdoutAndStderr bool) (stdout string, stderr string, err os.Error) {
	cmdChan := make(chan *exec.Cmd)
	errChan := make(chan os.Error, 3)
	stdoutChan := make(chan []byte, 1)
	stderrChan := make(chan []byte, 1)
	go func() {
		cmd := <-cmdChan
		if cmd != nil {
			// A goroutine for feeding STDIN
			go func() {
				_, err := cmd.Stdin.WriteString(in)
				if err != nil {
					cmd.Stdin.Close()
				} else {
					err = cmd.Stdin.Close()
				}
				errChan <- err
			}()

			// A goroutine for consuming STDOUT.
			// Note: STDOUT is optionally merged with STDERR.
			go func() {
				stdout, err := ioutil.ReadAll(cmd.Stdout)
				if err != nil {
					cmd.Stdout.Close()
					errChan <- err
				} else {
					err = cmd.Stdout.Close()
					errChan <- err
					stdoutChan <- stdout
				}
			}()

			if cmd.Stderr != nil {
				// A goroutine for consuming STDERR
				go func() {
					stderr, err := ioutil.ReadAll(cmd.Stderr)
					if err != nil {
						cmd.Stderr.Close()
						errChan <- err
					} else {
						err = cmd.Stderr.Close()
						errChan <- err
						stderrChan <- stderr
					}
				}()
			} else {
				errChan <- nil
				stderrChan <- make([]byte, 0)
			}
		}
	}()

	var stderrHandling int
	if mergeStdoutAndStderr {
		stderrHandling = exec.MergeWithStdout
	} else {
		stderrHandling = exec.Pipe
	}

	flags := RunFlags{
		cmdChan_orNil: cmdChan,
		stdin:         exec.Pipe,
		stdout:        exec.Pipe,
		stderr:        stderrHandling,
		dontPrintCmd:  true,
	}

	err = e.run_lowLevel(argv, dir, flags)
	if err != nil {
		return "", "", err
	}

	err1 := <-errChan
	err2 := <-errChan
	err3 := <-errChan
	if (err1 != nil) || (err2 != nil) || (err3 != nil) {
		err = err1
		if err == nil {
			err = err2
		}
		if err == nil {
			err = err3
		}
		return "", "", err
	}

	stdout = string(<-stdoutChan)
	stderr = string(<-stderrChan)

	return stdout, stderr, nil
}

// Runs 'e' as separate process and waits until it finishes.
// If 'cmdChan_orNil' is not nil, it will receive the 'exec.Cmd' returned by 'exec.Run'.
func (e *Executable) run_lowLevel(argv []string, dir string, flags RunFlags) os.Error {
	// Resolve 'e.fullpath' (if not resolved yet)
	if len(e.fullPath) == 0 {
		if (e.noLookup == false) || !strings.HasPrefix(e.name, "./") {
			var err os.Error
			e.fullPath, err = exec.LookPath(e.name)
			if err != nil {
				msg := "failed to lookup executable \"" + e.name + "\": " + err.String()
				return os.NewError(msg)
			}
		} else {
			e.fullPath = e.name
		}
	}

	if dir == "." {
		dir = ""
	}

	if (*flag_verbose && !flags.dontPrintCmd) || *flag_debug {
		if len(dir) == 0 {
			fmt.Fprintf(os.Stdout, "(%s)\n", strings.Join(argv, " "))
		} else {
			fmt.Fprintf(os.Stdout, "(cd %s ; %s)\n", dir, strings.Join(argv, " "))
		}
	}

	cmd, err := exec.Run(e.fullPath, argv, os.Environ(), dir, flags.stdin, flags.stdout, flags.stderr)
	if err != nil {
		if flags.cmdChan_orNil != nil {
			flags.cmdChan_orNil <- nil
		}
		return err
	} else {
		if flags.cmdChan_orNil != nil {
			flags.cmdChan_orNil <- cmd
		}
	}

	waitMsg, err := cmd.Wait( /*options*/ os.WRUSAGE)
	if err != nil {
		return err
	}
	if *flag_timings {
		addResourceUsage(e.name, waitMsg.Rusage)
	}
	if !waitMsg.Exited() {
		return os.NewError(fmt.Sprintf("unable to obtain the exit status of \"%s\"", e.name))
	}
	if waitMsg.ExitStatus() != 0 {
		var errMsg string
		if len(dir) == 0 {
			errMsg = fmt.Sprintf("command \"%s\" returned an error", strings.Join(argv, " "))
		} else {
			errMsg = fmt.Sprintf("command \"%s\" run in directory \"%s\" returned an error", strings.Join(argv, " "), dir)
		}
		return os.NewError(errMsg)
	}

	return nil
}
<commit_msg>Update in response to Go weekly.2011-06-02<commit_after>package main

import (
	"bytes"
	"exec"
	"fmt"
	"io/ioutil"
	"os"
	"runtime"
	"sort"
	"strings"
	"syscall"
)

type TaskResourceUsage struct {
	// User time, in nanoseconds
	userTime int64

	// System time, in nanoseconds
	systemTime int64

	// The number of times the command was executed
	numInvocations int

	dontPrintAverage bool
}

func (t *TaskResourceUsage) TotalTime() int64 {
	return t.userTime + t.systemTime
}

func (t *TaskResourceUsage) AverageTime() float64 {
	return float64(t.TotalTime()) / float64(t.numInvocations)
}

var taskStats = make(map[string]*TaskResourceUsage)

func addResourceUsage(taskName string, usage *syscall.Rusage) {
	taskRUsage := taskStats[taskName]
	if taskRUsage == nil {
		taskRUsage = &TaskResourceUsage{
			userTime:       0,
			systemTime:     0,
			numInvocations: 0,
		}

		taskStats[taskName] = taskRUsage
	}

	taskRUsage.systemTime += int64(usage.Utime.Sec)*1e9 + int64(usage.Utime.Usec)*1e3
	taskRUsage.userTime += int64(usage.Stime.Sec)*1e9 + int64(usage.Stime.Usec)*1e3
	taskRUsage.numInvocations += 1
}

func addSelf() {
	// Remove any previous stats
	taskStats["(self)"] = nil, false
	taskStats["(self.gc)"] = nil, false

	// Enclose the following 5 lines in a comment
	// if the compiler is unable to find 'syscall.Getrusage'
	var usage syscall.Rusage
	errno := syscall.Getrusage(0, &usage)
	if errno == 0 {
		addResourceUsage("(self)", &usage)
	}

	taskStats["(self.gc)"] = &TaskResourceUsage{
		userTime:         int64(runtime.MemStats.PauseTotalNs),
		systemTime:       0,
		numInvocations:   int(runtime.MemStats.NumGC),
		dontPrintAverage: true,
	}
}

func printTimings(out *os.File) {
	addSelf()

	sortedNames := make([]string, len(taskStats))
	maxNameLength := 0
	{
		i := 0
		for name, _ := range taskStats {
			sortedNames[i] = name
			if len(name) > maxNameLength {
				maxNameLength = len(name)
			}
			i++
		}

		sort.StringArray(sortedNames).Sort()
	}

	fmt.Fprintf(out, "Run times:\n")
	for _, name := range sortedNames {
		var buf bytes.Buffer
		fmt.Fprintf(&buf, "    %s:", name)
		for i := len(name); i < maxNameLength; i++ {
			buf.WriteString(" ")
		}
		taskRUsage := taskStats[name]
		fmt.Fprintf(&buf, " %.3f secs", float64(taskRUsage.TotalTime())/1e9)
		if taskRUsage.numInvocations >= 2 {
			if !taskRUsage.dontPrintAverage {
				fmt.Fprintf(&buf, " (%d invocations, %.3f secs per invocation)",
					taskRUsage.numInvocations, taskRUsage.AverageTime()/1e9)
			} else {
				fmt.Fprintf(&buf, " (%d invocations)", taskRUsage.numInvocations)
			}
		}

		fmt.Fprintf(out, "%s\n", buf.String())
	}
}

type Executable struct {
	name     string
	noLookup bool
	fullPath string // Cached path obtained by calling 'exec.LookPath(name)'
}

type RunFlags struct {
	stdin, stdout, stderr *os.File
	dontPrintCmd          bool
}

func (e *Executable) runSimply(argv []string, dir string, dontPrintCmd bool) os.Error {
	flags := RunFlags{
		stdin:         nil,
		stdout:        os.Stdout,
		stderr:        os.Stderr,
		dontPrintCmd:  false,
	}

	return e.run_lowLevel(argv, dir, flags)
}

// Runs 'e' as separate process, waits until it finishes,
// and returns the data the process sent to its output(s).
// The argument 'in' comprises the command's input.
func (e *Executable) run(argv []string, dir string, in string, mergeStdoutAndStderr bool) (stdout string, stderr string, err os.Error) {
	stdin_r, stdin_w, err := os.Pipe()
	if err != nil {
		return "", "", err
	}

	stdout_r, stdout_w, err := os.Pipe()
	if err != nil {
		stdin_r.Close()
		stdin_w.Close()
		return "", "", err
	}

	var stderr_r, stderr_w *os.File
	if mergeStdoutAndStderr {
		stderr_r, stderr_w = stdout_r, stdout_w
	} else {
		stderr_r, stderr_w, err = os.Pipe()
		if err != nil {
			stdin_r.Close()
			stdin_w.Close()
			stdout_r.Close()
			stdout_w.Close()
			return "", "", err
		}
	}

	errChan := make(chan os.Error, 3)
	stdoutChan := make(chan []byte, 1)
	stderrChan := make(chan []byte, 1)
	{
		// A goroutine for feeding STDIN
		go func() {
			_, err := stdin_w.WriteString(in)
			if err != nil {
				stdin_w.Close()
			} else {
				err = stdin_w.Close()
			}
			errChan <- err
		}()

		// A goroutine for consuming STDOUT.
		// Note: STDOUT is optionally merged with STDERR.
		go func() {
			stdout, err := ioutil.ReadAll(stdout_r)
			if err != nil {
				stdout_r.Close()
				errChan <- err
			} else {
				err = stdout_r.Close()
				errChan <- err
				stdoutChan <- stdout
			}
		}()

		if !mergeStdoutAndStderr {
			// A goroutine for consuming STDERR
			go func() {
				stderr, err := ioutil.ReadAll(stderr_r)
				if err != nil {
					stderr_r.Close()
					errChan <- err
				} else {
					err = stderr_r.Close()
					errChan <- err
					stderrChan <- stderr
				}
			}()
		} else {
			errChan <- nil
			stderrChan <- make([]byte, 0)
		}
	}

	// Execute the command
	flags := RunFlags{
		stdin:         stdin_r,
		stdout:        stdout_w,
		stderr:        stderr_w,
		dontPrintCmd:  true,
	}
	err = e.run_lowLevel(argv, dir, flags)

	// Close our ends of the pipes, so that the I/O goroutines can finish
	stdin_r.Close()
	stdout_w.Close()
	if stderr_w != stdout_w {
		stderr_w.Close()
	}

	if err != nil {
		return "", "", err
	}

	err1 := <-errChan
	err2 := <-errChan
	err3 := <-errChan
	if (err1 != nil) || (err2 != nil) || (err3 != nil) {
		err = err1
		if err == nil {
			err = err2
		}
		if err == nil {
			err = err3
		}
		return "", "", err
	}

	stdout = string(<-stdoutChan)
	stderr = string(<-stderrChan)
	return stdout, stderr, nil
}

// Runs 'e' as a separate process and waits until it finishes
func (e *Executable) run_lowLevel(argv []string, dir string, flags RunFlags) os.Error {
	// Resolve 'e.fullpath' (if not resolved yet)
	if len(e.fullPath) == 0 {
		if (e.noLookup == false) || !strings.HasPrefix(e.name, "./") {
			var err os.Error
			e.fullPath, err = exec.LookPath(e.name)
			if err != nil {
				msg := "failed to lookup executable \"" + e.name + "\": " + err.String()
				return os.NewError(msg)
			}
		} else {
			e.fullPath = e.name
		}
	}

	if dir == "." {
		dir = ""
	}

	if (*flag_verbose && !flags.dontPrintCmd) || *flag_debug {
		if len(dir) == 0 {
			fmt.Fprintf(os.Stdout, "(%s)\n", strings.Join(argv, " "))
		} else {
			fmt.Fprintf(os.Stdout, "(cd %s ; %s)\n", dir, strings.Join(argv, " "))
		}
	}

	procAttr := os.ProcAttr{
		Dir:   dir,
		Files: []*os.File{flags.stdin, flags.stdout, flags.stderr},
	}
	process, err := os.StartProcess(e.fullPath, argv, &procAttr)
	if err != nil {
		return err
	}

	waitMsg, err := process.Wait( /*options*/ os.WRUSAGE)
	if err != nil {
		return err
	}
	if *flag_timings {
		addResourceUsage(e.name, waitMsg.Rusage)
	}
	if !waitMsg.Exited() {
		return os.NewError(fmt.Sprintf("unable to obtain the exit status of \"%s\"", e.name))
	}
	if waitMsg.ExitStatus() != 0 {
		var errMsg string
		if len(dir) == 0 {
			errMsg = fmt.Sprintf("command \"%s\" returned an error", strings.Join(argv, " "))
		} else {
			errMsg = fmt.Sprintf("command \"%s\" run in directory \"%s\" returned an error", strings.Join(argv, " "), dir)
		}
		return os.NewError(errMsg)
	}

	return nil
}
<|endoftext|>


<commit_before>package vfs

import (
	"os"
	"path"
	"sync"
	"time"
)

type EntryType uint8

const (
	EntryTypeFile EntryType = iota + 1
	EntryTypeDir
)

type Entry interface {
	Type() EntryType
	Size() int64
	FileMode() os.FileMode
	ModificationTime() time.Time
}

// Type File represents an in-memory file or
// directory. Most in-memory VFS should use this
// structure to represent their files, in order to
// save work.
type File struct {
	sync.RWMutex
	// Data contains the file data.
	Data []byte
	// Mode is the file or directory mode. Note that some filesystems
	// might ignore the permission bits.
	Mode os.FileMode
	// ModTime represents the last modification time to the file.
	ModTime time.Time
}

func (f *File) Type() EntryType {
	return EntryTypeFile
}

func (f *File) Size() int64 {
	f.RLock()
	defer f.RUnlock()
	return int64(len(f.Data))
}

func (f *File) FileMode() os.FileMode {
	return f.Mode
}

func (f *File) ModificationTime() time.Time {
	f.RLock()
	defer f.RUnlock()
	return f.ModTime
}

type Dir struct {
	sync.RWMutex
	// Mode is the file or directory mode. Note that some filesystems
	// might ignore the permission bits.
	Mode os.FileMode
	// ModTime represents the last modification time to directory.
	ModTime time.Time
	// Entry names in this directory, in order.
	EntryNames []string
	// Entries in the same order as EntryNames.
	Entries []Entry
}

func (d *Dir) Type() EntryType {
	return EntryTypeDir
}

func (d *Dir) Size() int64 {
	return 0
}

func (d *Dir) FileMode() os.FileMode {
	return d.Mode
}

func (d *Dir) ModificationTime() time.Time {
	d.RLock()
	defer d.RUnlock()
	return d.ModTime
}

func (d *Dir) Add(name string, entry Entry) error {
	// TODO: Binary search
	for ii, v := range d.EntryNames {
		if v > name {
			names := make([]string, len(d.EntryNames)+1)
			copy(names, d.EntryNames[:ii])
			names[ii] = name
			copy(names[ii+1:], d.EntryNames[ii:])
			d.EntryNames = names

			entries := make([]Entry, len(d.Entries)+1)
			copy(entries, d.Entries[:ii])
			entries[ii] = entry
			copy(entries[ii+1:], d.Entries[ii:])

			d.Entries = entries
			return nil
		}
		if v == name {
			return os.ErrExist
		}
	}
	// Not added yet, put at the end
	d.EntryNames = append(d.EntryNames, name)
	d.Entries = append(d.Entries, entry)
	return nil
}

func (d *Dir) Find(name string) (Entry, int, error) {
	for ii, v := range d.EntryNames {
		if v == name {
			return d.Entries[ii], ii, nil
		}
	}
	return nil, -1, os.ErrNotExist
}

// EntryInfo implements the os.FileInfo interface wrapping
// a given File and its Path in its VFS.
type EntryInfo struct {
	// Path is the full path to the entry in its VFS.
	Path string
	// Entry is the instance used by the VFS to represent
	// the in-memory entry.
	Entry Entry
}

func (info *EntryInfo) Name() string {
	return path.Base(info.Path)
}

func (info *EntryInfo) Size() int64 {
	return info.Entry.Size()
}

func (info *EntryInfo) Mode() os.FileMode {
	return info.Entry.FileMode()
}

func (info *EntryInfo) ModTime() time.Time {
	return info.Entry.ModificationTime()
}

func (info *EntryInfo) IsDir() bool {
	return info.Entry.Type() == EntryTypeDir
}

// Sys returns the underlying Entry.
func (info *EntryInfo) Sys() interface{} {
	return info.Entry
}

// FileInfos represents an slice of os.FileInfo which
// implements the sort.Sort protocol. This type is only
// exported for users who want to implement their own
// filesystems, since VFS.ReadDir requires the returned
// []os.FileInfo to be sorted by name.
type FileInfos []os.FileInfo

func (f FileInfos) Len() int {
	return len(f)
}

func (f FileInfos) Less(i, j int) bool {
	return f[i].Name() < f[j].Name()
}

func (f FileInfos) Swap(i, j int) {
	f[i], f[j] = f[j], f[i]
}
<commit_msg>Documentation updates<commit_after>package vfs

import (
	"os"
	"path"
	"sync"
	"time"
)

// EntryType indicates the type of the entry.
type EntryType uint8

const (
	// EntryTypeFile indicates the entry is a file.
	EntryTypeFile EntryType = iota + 1
	// EntryTypeDir indicates the entry is a directory.
	EntryTypeDir
)

// Entry is the interface implemented by the in-memory representations
// of files and directories.
type Entry interface {
	// Type returns the entry type, either EntryTypeFile or
	// EntryTypeDir.
	Type() EntryType
	// Size returns the file size. For directories, it's always zero.
	Size() int64
	// FileMode returns the file mode as an os.FileMode.
	FileMode() os.FileMode
	// ModificationTime returns the last time the file or the directory
	// was modified.
	ModificationTime() time.Time
}

// Type File represents an in-memory file. Most in-memory VFS implementations
// should use this structure to represent their files, in order to save work.
type File struct {
	sync.RWMutex
	// Data contains the file data.
	Data []byte
	// Mode is the file or directory mode. Note that some filesystems
	// might ignore the permission bits.
	Mode os.FileMode
	// ModTime represents the last modification time to the file.
	ModTime time.Time
}

func (f *File) Type() EntryType {
	return EntryTypeFile
}

func (f *File) Size() int64 {
	f.RLock()
	defer f.RUnlock()
	return int64(len(f.Data))
}

func (f *File) FileMode() os.FileMode {
	return f.Mode
}

func (f *File) ModificationTime() time.Time {
	f.RLock()
	defer f.RUnlock()
	return f.ModTime
}

// Type DIr represents an in-memory directory. Most in-memory VFS
// implementations should use this structure to represent their
// directories, in order to save work.
type Dir struct {
	sync.RWMutex
	// Mode is the file or directory mode. Note that some filesystems
	// might ignore the permission bits.
	Mode os.FileMode
	// ModTime represents the last modification time to directory.
	ModTime time.Time
	// Entry names in this directory, in order.
	EntryNames []string
	// Entries in the same order as EntryNames.
	Entries []Entry
}

func (d *Dir) Type() EntryType {
	return EntryTypeDir
}

func (d *Dir) Size() int64 {
	return 0
}

func (d *Dir) FileMode() os.FileMode {
	return d.Mode
}

func (d *Dir) ModificationTime() time.Time {
	d.RLock()
	defer d.RUnlock()
	return d.ModTime
}

// Add ads a new entry to the directory. If there's already an
// entry ith the same name, an error is returned.
func (d *Dir) Add(name string, entry Entry) error {
	// TODO: Binary search
	for ii, v := range d.EntryNames {
		if v > name {
			names := make([]string, len(d.EntryNames)+1)
			copy(names, d.EntryNames[:ii])
			names[ii] = name
			copy(names[ii+1:], d.EntryNames[ii:])
			d.EntryNames = names

			entries := make([]Entry, len(d.Entries)+1)
			copy(entries, d.Entries[:ii])
			entries[ii] = entry
			copy(entries[ii+1:], d.Entries[ii:])

			d.Entries = entries
			return nil
		}
		if v == name {
			return os.ErrExist
		}
	}
	// Not added yet, put at the end
	d.EntryNames = append(d.EntryNames, name)
	d.Entries = append(d.Entries, entry)
	return nil
}

// Find returns the entry with the given name and its index,
// or an error if an entry with that name does not exist in
// the directory.
func (d *Dir) Find(name string) (Entry, int, error) {
	for ii, v := range d.EntryNames {
		if v == name {
			return d.Entries[ii], ii, nil
		}
	}
	return nil, -1, os.ErrNotExist
}

// EntryInfo implements the os.FileInfo interface wrapping
// a given File and its Path in its VFS.
type EntryInfo struct {
	// Path is the full path to the entry in its VFS.
	Path string
	// Entry is the instance used by the VFS to represent
	// the in-memory entry.
	Entry Entry
}

func (info *EntryInfo) Name() string {
	return path.Base(info.Path)
}

func (info *EntryInfo) Size() int64 {
	return info.Entry.Size()
}

func (info *EntryInfo) Mode() os.FileMode {
	return info.Entry.FileMode()
}

func (info *EntryInfo) ModTime() time.Time {
	return info.Entry.ModificationTime()
}

func (info *EntryInfo) IsDir() bool {
	return info.Entry.Type() == EntryTypeDir
}

// Sys returns the underlying Entry.
func (info *EntryInfo) Sys() interface{} {
	return info.Entry
}

// FileInfos represents an slice of os.FileInfo which
// implements the sort.Interface. This type is only
// exported for users who want to implement their own
// filesystems, since VFS.ReadDir requires the returned
// []os.FileInfo to be sorted by name.
type FileInfos []os.FileInfo

func (f FileInfos) Len() int {
	return len(f)
}

func (f FileInfos) Less(i, j int) bool {
	return f[i].Name() < f[j].Name()
}

func (f FileInfos) Swap(i, j int) {
	f[i], f[j] = f[j], f[i]
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"os"
	"unicode/utf8"
)

// open reads a file and returns it as *Text.
// When the file is not exists, it will return error with nil *Text.
func open(f string) (*Text, error) {
	file, err := os.Open(f)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	// aggregate the text info.
	// tor uses tab (4 space) for indentation.
	// but when parse an exist file, follow the file's rule.
	lines := make([]Line, 0)
	tabToSpace := false
	tabWidth := 4

	findIndentLine := false
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		t := scanner.Text()
		if !findIndentLine {
			r, _ := utf8.DecodeRuneInString(t)
			if r == ' ' || r == '\t' {
				findIndentLine = true
				if r == ' ' {
					tabToSpace = true
					// calculate tab width
					tabWidth = 0
					remain := t
					for len(remain) != 0 {
						r, rlen := utf8.DecodeRuneInString(remain)
						remain = remain[rlen:]
						if r != ' ' {
							break
						}
						tabWidth++
					}
				}
			}
		}
		lines = append(lines, Line{t})
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	// `touch` cmd creates a file with no content.
	// avoid program panic from empty text.
	if len(lines) == 0 {
		lines = []Line{{""}}
	}

	return &Text{lines: lines, tabToSpace: tabToSpace, tabWidth: tabWidth}, nil
}

// save saves Text to a file.
func save(f string, t *Text) error {
	file, err := os.Create(f)
	if err != nil {
		return err
	}
	defer file.Close()
	for _, line := range t.lines {
		file.WriteString(line.data + "\n")
	}
	return nil
}

// exists returns whether the given file or directory exists or not
func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}
<commit_msg>remove exist, it is unused now.<commit_after>package main

import (
	"bufio"
	"os"
	"unicode/utf8"
)

// open reads a file and returns it as *Text.
// When the file is not exists, it will return error with nil *Text.
func open(f string) (*Text, error) {
	file, err := os.Open(f)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	// aggregate the text info.
	// tor uses tab (4 space) for indentation.
	// but when parse an exist file, follow the file's rule.
	lines := make([]Line, 0)
	tabToSpace := false
	tabWidth := 4

	findIndentLine := false
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		t := scanner.Text()
		if !findIndentLine {
			r, _ := utf8.DecodeRuneInString(t)
			if r == ' ' || r == '\t' {
				findIndentLine = true
				if r == ' ' {
					tabToSpace = true
					// calculate tab width
					tabWidth = 0
					remain := t
					for len(remain) != 0 {
						r, rlen := utf8.DecodeRuneInString(remain)
						remain = remain[rlen:]
						if r != ' ' {
							break
						}
						tabWidth++
					}
				}
			}
		}
		lines = append(lines, Line{t})
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	// `touch` cmd creates a file with no content.
	// avoid program panic from empty text.
	if len(lines) == 0 {
		lines = []Line{{""}}
	}

	return &Text{lines: lines, tabToSpace: tabToSpace, tabWidth: tabWidth}, nil
}

// save saves Text to a file.
func save(f string, t *Text) error {
	file, err := os.Create(f)
	if err != nil {
		return err
	}
	defer file.Close()
	for _, line := range t.lines {
		file.WriteString(line.data + "\n")
	}
	return nil
}
<|endoftext|>


<commit_before>package form

import (
	"bytes"
	"encoding/json"
	"github.com/cjtoolkit/i18n"
	"io"
	"net/http"
)

// Form Renderer and Validator interface!
type Form interface {
	// Renders 'structPtr' to 'w', panic if structPtr is not a struct with pointer.
	// Also renders validation errors if 'Validate' or 'MustValidate' was call before hand.
	Render(structPtr Interface, w io.Writer)
	// As Render but Return string
	RenderStr(structPtr Interface) string

	// http://api.jquery.com/serializearray/
	// Request Body must be in JSON format. 'JSON.stringify(object);' in Javascript.
	// Eg [{name:"",value:""},{name:"",value:""},{name:"",value:""}...]
	// To validate you must call 'Validate' or 'MustValidate' after 'ParseSerializeArray'.
	ParseSerializeArray(r *http.Request)

	// Validate User Input and Populate Field in struct with pointers.
	// Must use struct with pointers otherwise it will return an error.
	// To get structPtrs field to validate against itself, specify r as 'nil'
	Validate(r *http.Request, structPtrs ...Interface) (bool, error)
	// Same as validate but panic on error.
	MustValidate(r *http.Request, structPtrs ...Interface) bool

	// Validate Single Field
	ValidateSingle(structPtr Interface, name string, value []string) (err error)

	// Encode JSON into 'w'
	// {"valid": bool, "data":[{"valid":bool, "error":"", "warning":"", "name":"", "count":int}...]}
	// Must call Validate or MustValidate first, otherwise it's print invalid data.
	Json(w io.Writer)
}

// Create new form validator and renderer.
// Panic if unable to verify languageSources.
// To use Default Second Layer specify r as 'nil'.
//
// Note: Stick to one instant per user request,
// do not use it as a global variable, as it's not thread safe.
func New(r RenderSecondLayer, languageSources ...string) Form {
	if r == nil {
		r = DefaultRenderSecondLayer
	}

	return &form{
		T:        i18n.MustTfunc("cjtoolkit-form", languageSources...),
		R:        r,
		Data:     map[Interface]*formData{},
		JsonData: []map[string]interface{}{},
	}
}

type formData struct {
	Errors  map[string][]error
	Warning map[string][]string
}

func newData() *formData {
	return &formData{
		Errors:  map[string][]error{},
		Warning: map[string][]string{},
	}
}

func (f *formData) addError(name string, err error) {
	f.Errors[name] = append(f.Errors[name], err)
}

func (f *formData) addWarning(name, warning string) {
	f.Warning[name] = append(f.Warning[name], warning)
}

func (f *formData) shiftError(name string) (err error) {
	if len(f.Errors[name]) > 0 {
		if len(f.Errors[name]) <= 1 {
			if len(f.Errors[name]) == 1 {
				err = f.Errors[name][0]
			}
			delete(f.Errors, name)
		} else {
			err, f.Errors[name] = f.Errors[name][0], f.Errors[name][1:]
		}
	}
	return
}

func (f *formData) shiftWarning(name string) (warning string) {
	if len(f.Warning[name]) > 0 {
		if len(f.Warning[name]) <= 1 {
			if len(f.Warning[name]) == 1 {
				warning = f.Warning[name][0]
			}
			delete(f.Warning, name)
		} else {
			warning, f.Warning[name] = f.Warning[name][0], f.Warning[name][1:]
		}
	}
	return
}

type form struct {
	T         i18n.Translator
	R         RenderSecondLayer
	Data      map[Interface]*formData
	JsonValid bool
	JsonData  []map[string]interface{}
	Value     *value
	vcount    int
	rcount    int
}

func (f *form) Render(structPtr Interface, w io.Writer) {
	f.render(structPtr, w)
}

func (f *form) RenderStr(structPtr Interface) string {
	w := &bytes.Buffer{}
	defer w.Reset()
	f.Render(structPtr, w)
	return w.String()
}

func (f *form) ParseSerializeArray(r *http.Request) {
	if r == nil || f.Value != nil {
		return
	}
	f.Value = newValueSerializeArray(r)
}

func (f *form) Validate(r *http.Request, structPtrs ...Interface) (bool, error) {
	if r != nil && f.Value == nil {
		f.Value = newValue(r)
	}
	valid := true
	for _, structPtr := range structPtrs {
		b, err := f.validate(structPtr)
		if err != nil {
			return false, err
		}
		if !b {
			valid = false
		}
	}
	f.JsonValid = valid
	return valid, nil
}

func (f *form) MustValidate(r *http.Request, structPtrs ...Interface) bool {
	b, err := f.Validate(r, structPtrs...)
	if err != nil {
		panic(err)
	}
	return b
}

func (f *form) ValidateSingle(structPtr Interface, name string, value []string) (err error) {
	err = f.validateSingle(structPtr, name, value)
	return
}

func (f *form) Json(w io.Writer) {
	v := map[string]interface{}{
		"valid": f.JsonValid,
		"data":  f.JsonData,
	}
	enc := json.NewEncoder(w)
	enc.Encode(v)
}
<commit_msg>Updated comment<commit_after>package form

import (
	"bytes"
	"encoding/json"
	"github.com/cjtoolkit/i18n"
	"io"
	"net/http"
)

// Form Renderer and Validator interface!
type Form interface {
	// Renders 'structPtr' to 'w', panic if structPtr is not a struct with pointer.
	// Also renders validation errors if 'Validate' or 'MustValidate' was call before hand.
	Render(structPtr Interface, w io.Writer)
	// As Render but Return string
	RenderStr(structPtr Interface) string

	// http://api.jquery.com/serializearray/
	// Request Body must be in JSON format. 'JSON.stringify(object);' in Javascript.
	// Eg [{name:"",value:""},{name:"",value:""},{name:"",value:""}...]
	// To validate you must call 'Validate' or 'MustValidate' after 'ParseSerializeArray'.
	ParseSerializeArray(r *http.Request)

	// Validate User Input and Populate Field in struct with pointers.
	// Must use struct with pointers otherwise it will return an error.
	// To get structPtrs field to validate against itself, specify r as 'nil'
	Validate(r *http.Request, structPtrs ...Interface) (bool, error)
	// Same as validate but panic on error.
	MustValidate(r *http.Request, structPtrs ...Interface) bool

	// Validate Single Field, won't work with must match.
	ValidateSingle(structPtr Interface, name string, value []string) (err error)

	// Encode JSON into 'w'
	// {"valid": bool, "data":[{"valid":bool, "error":"", "warning":"", "name":"", "count":int}...]}
	// Must call Validate or MustValidate first, otherwise it's print invalid data.
	Json(w io.Writer)
}

// Create new form validator and renderer.
// Panic if unable to verify languageSources.
// To use Default Second Layer specify r as 'nil'.
//
// Note: Stick to one instant per user request,
// do not use it as a global variable, as it's not thread safe.
func New(r RenderSecondLayer, languageSources ...string) Form {
	if r == nil {
		r = DefaultRenderSecondLayer
	}

	return &form{
		T:        i18n.MustTfunc("cjtoolkit-form", languageSources...),
		R:        r,
		Data:     map[Interface]*formData{},
		JsonData: []map[string]interface{}{},
	}
}

type formData struct {
	Errors  map[string][]error
	Warning map[string][]string
}

func newData() *formData {
	return &formData{
		Errors:  map[string][]error{},
		Warning: map[string][]string{},
	}
}

func (f *formData) addError(name string, err error) {
	f.Errors[name] = append(f.Errors[name], err)
}

func (f *formData) addWarning(name, warning string) {
	f.Warning[name] = append(f.Warning[name], warning)
}

func (f *formData) shiftError(name string) (err error) {
	if len(f.Errors[name]) > 0 {
		if len(f.Errors[name]) <= 1 {
			if len(f.Errors[name]) == 1 {
				err = f.Errors[name][0]
			}
			delete(f.Errors, name)
		} else {
			err, f.Errors[name] = f.Errors[name][0], f.Errors[name][1:]
		}
	}
	return
}

func (f *formData) shiftWarning(name string) (warning string) {
	if len(f.Warning[name]) > 0 {
		if len(f.Warning[name]) <= 1 {
			if len(f.Warning[name]) == 1 {
				warning = f.Warning[name][0]
			}
			delete(f.Warning, name)
		} else {
			warning, f.Warning[name] = f.Warning[name][0], f.Warning[name][1:]
		}
	}
	return
}

type form struct {
	T         i18n.Translator
	R         RenderSecondLayer
	Data      map[Interface]*formData
	JsonValid bool
	JsonData  []map[string]interface{}
	Value     *value
	vcount    int
	rcount    int
}

func (f *form) Render(structPtr Interface, w io.Writer) {
	f.render(structPtr, w)
}

func (f *form) RenderStr(structPtr Interface) string {
	w := &bytes.Buffer{}
	defer w.Reset()
	f.Render(structPtr, w)
	return w.String()
}

func (f *form) ParseSerializeArray(r *http.Request) {
	if r == nil || f.Value != nil {
		return
	}
	f.Value = newValueSerializeArray(r)
}

func (f *form) Validate(r *http.Request, structPtrs ...Interface) (bool, error) {
	if r != nil && f.Value == nil {
		f.Value = newValue(r)
	}
	valid := true
	for _, structPtr := range structPtrs {
		b, err := f.validate(structPtr)
		if err != nil {
			return false, err
		}
		if !b {
			valid = false
		}
	}
	f.JsonValid = valid
	return valid, nil
}

func (f *form) MustValidate(r *http.Request, structPtrs ...Interface) bool {
	b, err := f.Validate(r, structPtrs...)
	if err != nil {
		panic(err)
	}
	return b
}

func (f *form) ValidateSingle(structPtr Interface, name string, value []string) (err error) {
	err = f.validateSingle(structPtr, name, value)
	return
}

func (f *form) Json(w io.Writer) {
	v := map[string]interface{}{
		"valid": f.JsonValid,
		"data":  f.JsonData,
	}
	enc := json.NewEncoder(w)
	enc.Encode(v)
}
<|endoftext|>


<commit_before>package ftls

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net"
)

var (
	DefaultMinVersion = uint16(tls.VersionTLS12)
	DefaultCipherSet  = []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384}
)

type Ftsl struct {
	c *Config
}

type Config struct {
	StrictClientVerify bool
	MutualTLS          bool
	InsecureSkipVerify bool
	MinVersion         uint16
	CipherSet          []uint16
	CertFile           string
	KeyFile            string
	CAFile             string
}

// DefaultServerFTLSConf returns a ftls config with the most commonly used config set.
func DefaultServerFTLSConf(CertFile, KeyFile, CAFile string) *Config {
	return &Config{
		StrictClientVerify: true,
		MutualTLS:          true,
		InsecureSkipVerify: false,
		MinVersion:         DefaultMinVersion,
		CipherSet:          DefaultCipherSet,
		CertFile:           CertFile,
		KeyFile:            KeyFile,
		CAFile:             CAFile,
	}
}

// DefaultClientFTLSConf returns a ftls config with the most commonly used config set.
func DefaultClientFTLSConf(CertFile, KeyFile, CAFile string) *Config {
	return &Config{
		MutualTLS:          true,
		InsecureSkipVerify: false,
		MinVersion:         DefaultMinVersion,
		CertFile:           CertFile,
		KeyFile:            KeyFile,
		CAFile:             CAFile,
	}
}

// NewClientTLSConfig constructs a client tls.Conf from provide ftls Config.
func NewClientTLSConfig(c *Config) (*tls.Config, error) {
	cert, err := tls.LoadX509KeyPair(c.CertFile, c.KeyFile)
	if err != nil {
		return &tls.Config{}, fmt.Errorf("Unable to load cert %s %s: %s", c.CertFile, c.KeyFile, err.Error())
	}
	clientCertPool := x509.NewCertPool()
	if c.CAFile != "" {
		clientCACert, err := ioutil.ReadFile(c.CAFile)
		if err != nil {
			return &tls.Config{}, fmt.Errorf("Unable to load CA cert %s: %s", c.CAFile, err.Error())
		}
		clientCertPool.AppendCertsFromPEM(clientCACert)
	}
	if c.MutualTLS {
		tlsConf := &tls.Config{
			Certificates: []tls.Certificate{cert},
			RootCAs:      clientCertPool,
			CipherSuites: c.CipherSet,
			MinVersion:   DefaultMinVersion,
		}
		tlsConf.BuildNameToCertificate()
		return tlsConf, nil
	}
	return &tls.Config{RootCAs: clientCertPool, InsecureSkipVerify: c.InsecureSkipVerify}, nil
}

// NewServerTLSConfig constructs a server tls.Conf from the provided ftls Config.
func NewServerTLSConfig(c *Config) (*tls.Config, error) {
	var tlsConf *tls.Config
	if c.MutualTLS {
		caCert, err := ioutil.ReadFile(c.CAFile)
		if err != nil {
			return nil, fmt.Errorf("Unable to load ca cert %s: %s", c.CAFile, err.Error())
		}
		clientCertPool := x509.NewCertPool()
		if ok := clientCertPool.AppendCertsFromPEM(caCert); !ok {
			return nil, fmt.Errorf("Unable to append cert %s to pool.", c.CAFile)
		}
		strictness := tls.RequireAndVerifyClientCert
		if !c.StrictClientVerify {
			strictness = tls.RequireAnyClientCert
		}
		tlsConf = &tls.Config{
			ClientAuth:               strictness,
			ClientCAs:                clientCertPool,
			CipherSuites:             c.CipherSet,
			PreferServerCipherSuites: true,
			MinVersion:               DefaultMinVersion,
		}
		tlsConf.BuildNameToCertificate()
	}
	cert, err := tls.LoadX509KeyPair(c.CertFile, c.KeyFile)
	if err != nil {
		return nil, err
	}
	tlsConf.Certificates = []tls.Certificate{cert}
	tlsConf.InsecureSkipVerify = c.InsecureSkipVerify
	return tlsConf, nil
}

func VerifyClientAddrMatch(c *tls.Conn) error {
	err := c.Handshake()
	if err != nil {
		return err
	}
	addr, _, err := net.SplitHostPort(c.RemoteAddr().String())
	if err != nil {
		return err
	}
	return c.ConnectionState().VerifiedChains[0][0].VerifyHostname(addr)
}
<commit_msg>fix nil if no mutual tls<commit_after>package ftls

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net"
)

var (
	DefaultMinVersion = uint16(tls.VersionTLS12)
	DefaultCipherSet  = []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384}
)

type Ftsl struct {
	c *Config
}

type Config struct {
	StrictClientVerify bool
	MutualTLS          bool
	InsecureSkipVerify bool
	MinVersion         uint16
	CipherSet          []uint16
	CertFile           string
	KeyFile            string
	CAFile             string
}

// DefaultServerFTLSConf returns a ftls config with the most commonly used config set.
func DefaultServerFTLSConf(CertFile, KeyFile, CAFile string) *Config {
	return &Config{
		StrictClientVerify: true,
		MutualTLS:          true,
		InsecureSkipVerify: false,
		MinVersion:         DefaultMinVersion,
		CipherSet:          DefaultCipherSet,
		CertFile:           CertFile,
		KeyFile:            KeyFile,
		CAFile:             CAFile,
	}
}

// DefaultClientFTLSConf returns a ftls config with the most commonly used config set.
func DefaultClientFTLSConf(CertFile, KeyFile, CAFile string) *Config {
	return &Config{
		MutualTLS:          true,
		InsecureSkipVerify: false,
		MinVersion:         DefaultMinVersion,
		CertFile:           CertFile,
		KeyFile:            KeyFile,
		CAFile:             CAFile,
	}
}

// NewClientTLSConfig constructs a client tls.Conf from provide ftls Config.
func NewClientTLSConfig(c *Config) (*tls.Config, error) {
	cert, err := tls.LoadX509KeyPair(c.CertFile, c.KeyFile)
	if err != nil {
		return &tls.Config{}, fmt.Errorf("Unable to load cert %s %s: %s", c.CertFile, c.KeyFile, err.Error())
	}
	clientCertPool := x509.NewCertPool()
	if c.CAFile != "" {
		clientCACert, err := ioutil.ReadFile(c.CAFile)
		if err != nil {
			return &tls.Config{}, fmt.Errorf("Unable to load CA cert %s: %s", c.CAFile, err.Error())
		}
		clientCertPool.AppendCertsFromPEM(clientCACert)
	}
	if c.MutualTLS {
		tlsConf := &tls.Config{
			Certificates: []tls.Certificate{cert},
			RootCAs:      clientCertPool,
			CipherSuites: c.CipherSet,
			MinVersion:   DefaultMinVersion,
		}
		tlsConf.BuildNameToCertificate()
		return tlsConf, nil
	}
	return &tls.Config{RootCAs: clientCertPool, InsecureSkipVerify: c.InsecureSkipVerify}, nil
}

// NewServerTLSConfig constructs a server tls.Conf from the provided ftls Config.
func NewServerTLSConfig(c *Config) (*tls.Config, error) {
	tlsConfig := &tls.Config{}
	if c.MutualTLS {
		caCert, err := ioutil.ReadFile(c.CAFile)
		if err != nil {
			return nil, fmt.Errorf("Unable to load ca cert %s: %s", c.CAFile, err.Error())
		}
		clientCertPool := x509.NewCertPool()
		if ok := clientCertPool.AppendCertsFromPEM(caCert); !ok {
			return nil, fmt.Errorf("Unable to append cert %s to pool.", c.CAFile)
		}
		strictness := tls.RequireAndVerifyClientCert
		if !c.StrictClientVerify {
			strictness = tls.RequireAnyClientCert
		}
		tlsConf = &tls.Config{
			ClientAuth:               strictness,
			ClientCAs:                clientCertPool,
			CipherSuites:             c.CipherSet,
			PreferServerCipherSuites: true,
			MinVersion:               DefaultMinVersion,
		}
		tlsConf.BuildNameToCertificate()
	}
	cert, err := tls.LoadX509KeyPair(c.CertFile, c.KeyFile)
	if err != nil {
		return nil, err
	}
	tlsConf.Certificates = []tls.Certificate{cert}
	tlsConf.InsecureSkipVerify = c.InsecureSkipVerify
	return tlsConf, nil
}

func VerifyClientAddrMatch(c *tls.Conn) error {
	err := c.Handshake()
	if err != nil {
		return err
	}
	addr, _, err := net.SplitHostPort(c.RemoteAddr().String())
	if err != nil {
		return err
	}
	return c.ConnectionState().VerifiedChains[0][0].VerifyHostname(addr)
}
<|endoftext|>


<commit_before>// See LICENSE.txt for licensing information.

package main

import (
	"archive/zip"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"
)

import (
	"code.cloudfoundry.org/bytefmt"
)

type Game struct {
	Name  string    // user-given mnemonic, also used for making save storage directory
	Path  string    // absolute path to the source save file/directory
	Root  string    // absolute path to the saved saves directory
	Stamp time.Time // last modification datetime stamp
	Saves []*Save   // slice of saves
	Size  uint64    `json:Size,omitempty` // total size of all saves, MINVER:1
}

func (g *Game) PrintHeader() {
	fmt.Printf("%-32s %-9s %24s %8s\n", "Name", "# Backups", "Last backup", "Size")
}

func (g *Game) Print() {
	fmt.Printf("%-32s %-9d %24s %8s\n", g.Name, len(g.Saves), g.Stamp.Format(timeFmt), bytefmt.ByteSize(g.Size))
}

func (g *Game) PrintWhole() {
	g.PrintHeader()
	g.Print()
	fmt.Println()
	if len(g.Saves) > 0 {
		fmt.Printf("%3s ", "ID")
		g.Saves[0].PrintHeader()
		for i, s := range g.Saves {
			fmt.Printf("%3d ", i+1)
			s.Print()
		}
		fmt.Println()
	}
}

func (g *Game) Delete(from, to int) (n int, err error) {
	if from < 1 {
		return n, fmt.Errorf("Index from %d out of range", from)
	}
	if to > len(g.Saves) {
		return n, fmt.Errorf("Index to %d out of range", to)
	}
	spinner.Msg("Deleting saves...")
	from--
	i := from
	for ; i < to; i++ {
		s := g.Saves[i]
		if flagVerbose {
			spinner.Msg(fmt.Sprintf("removing save %d from %s", i+1, s.Stamp.Format(timeFmt)))
		}
		spinner.Tick()
		err = os.Remove(s.Path)
		if err != nil {
			break
		}
		g.Size -= s.Size
		n++
	}
	spinner.Finish()
	copy(g.Saves[from:], g.Saves[i:])
	for k, n := len(g.Saves)-i+from, len(g.Saves); k < n; k++ {
		g.Saves[k] = nil
	}
	g.Saves = g.Saves[:len(g.Saves)-i+from]
	return n, err
}

// Backup copies a save file, or zips a save directory.
// Note that currently proper file closing is depending on total
// exit of the program on any file operation failure.
func (g *Game) Backup() (sv *Save, err error) {
	fi, err := os.Stat(g.Path)
	if err != nil {
		return nil, err
	}
	s := time.Now()
	p := filepath.Join(g.Root, s.Format(fileFmt))
	o, err := os.Create(p)
	if err != nil {
		return nil, err
	}
	defer func() {
		o.Close()
		if err != nil {
			if lerr := os.Remove(p); lerr != nil {
				fmt.Fprintln(os.Stderr, "Error removing partial backup file:", lerr)
			}
		}
	}()
	// single file
	if fi.Mode().IsRegular() {
		i, err := os.Open(g.Path)
		if err != nil {
			return nil, err
		}
		defer i.Close()
		fmt.Println("Backing up", i.Name(), "...")
		if flagVerbose {
			fmt.Fprintf(os.Stderr, "copying %s to %s\n", g.Path, p)
		}
		n, err := io.Copy(o, i)
		if err != nil {
			if flagVerbose {
				fmt.Fprintf(os.Stderr, "error, copied %d\n", n)
			}
			return nil, err
		}
		if flagVerbose {
			fmt.Fprintf(os.Stderr, "ok, copied %d\n", n)
		}
	} else {
		// directory
		spinner.Msg("Backing up save directory...")
		if flagVerbose {
			spinner.Msg(fmt.Sprintf("zipping %s to %s", g.Path, p))
		}
		z := zip.NewWriter(o)
		err = filepath.Walk(g.Path, func(path string, info os.FileInfo, ierr error) (err error) {
			if ierr != nil {
				return ierr
			}
			if info.IsDir() {
				if flagVerbose {
					spinner.Msg(fmt.Sprintf("skipping %s", path))
				}
				return nil
			}
			rp, err := filepath.Rel(g.Path, path)
			if err != nil {
				return err
			}
			rp = filepath.ToSlash(rp)
			i, err := os.Open(path)
			if err != nil {
				return err
			}
			o, err := z.Create(rp)
			if err != nil {
				return err
			}
			if flagVerbose {
				spinner.Msg(fmt.Sprintf("compressing %s", rp))
			}
			spinner.Tick()
			_, err = io.Copy(o, i)
			i.Close()
			return err
		})
		spinner.Finish()
		if err != nil {
			return nil, err
		}
		if err = z.Close(); err != nil {
			return nil, err
		}
	}
	si, err := o.Stat()
	if err != nil {
		return nil, err
	}
	size := uint64(si.Size())
	sv = &Save{
		Stamp: s,
		Path:  p,
		Size:  size,
	}
	g.Saves = append(g.Saves, sv)
	g.Size += size
	return sv, nil
}

// Restore removes the current save file or directory, and repopulates
// it with the data from given backup save file.
// Note that currently proper file closing is depending on total
// exit of the program on any file operation failure.
func (g *Game) Restore(index int) (sv *Save, err error) {
	var i int

	if index > 0 {
		i = index - 1
		if i > len(g.Saves) {
			return nil, fmt.Errorf("Save ID %d out of range (1 ~ %d)", index, len(g.Saves))
		}
	} else {
		i = len(g.Saves) - 1 + index
		if i < 0 {
			return nil, fmt.Errorf("Save offset %d out of range (%d ~ 0)", index, -len(g.Saves)+1)
		}
	}
	sv = g.Saves[i]
	fi, err := os.Stat(g.Path)
	if err != nil {
		return nil, err
	}
	// single file
	if fi.Mode().IsRegular() {
		i, err := os.Open(sv.Path)
		if err != nil {
			return nil, err
		}
		defer i.Close()
		o, err := os.Create(g.Path)
		if err != nil {
			return nil, err
		}
		defer o.Close()
		fmt.Println("Restoring save from", sv.Path, "...")
		if flagVerbose {
			fmt.Fprintf(os.Stderr, "copying %s to %s\n", sv.Path, g.Path)
		}
		n, err := io.Copy(o, i)
		if flagVerbose {
			if err != nil {
				fmt.Fprintf(os.Stderr, "error, copied %d\n", n)
			} else {
				fmt.Fprintf(os.Stderr, "ok, copied %d\n", n)
			}
		}
		return sv, nil
	}
	// directory
	fmt.Println("Restoring save directory from", sv.Stamp.Format(timeFmt), "...")
	if flagVerbose {
		fmt.Fprintf(os.Stderr, "removing all from %s\n", g.Path)
	}
	err = os.RemoveAll(g.Path)
	if err != nil {
		return nil, err
	}
	if flagVerbose {
		fmt.Fprintf(os.Stderr, "unzipping %s to %s\n", sv.Path, g.Path)
	}
	z, err := zip.OpenReader(sv.Path)
	if err != nil {
		return nil, err
	}
	defer z.Close()
	for _, f := range z.File {
		i, err := f.Open()
		if err != nil {
			return nil, err
		}
		tp := filepath.Join(g.Path, filepath.FromSlash(f.Name))
		dp := filepath.Dir(tp)
		err = os.MkdirAll(dp, 0777)
		if err != nil {
			return nil, err
		}
		o, err := os.Create(tp)
		if err != nil {
			return nil, err
		}
		if flagVerbose {
			fmt.Fprintf(os.Stderr, "decompressing %s\n", f.Name)
		}
		n, err := io.Copy(o, i)
		if err != nil {
			if flagVerbose {
				fmt.Fprintf(os.Stderr, "error, copied %d\n", n)
			}
			return nil, err
		}
		o.Close()
		i.Close()
		if flagVerbose {
			fmt.Fprintf(os.Stderr, "ok, copied %d\n", n)
		}
	}
	return sv, nil
}
<commit_msg>Add spinner to restore too<commit_after>// See LICENSE.txt for licensing information.

package main

import (
	"archive/zip"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"
)

import (
	"code.cloudfoundry.org/bytefmt"
)

type Game struct {
	Name  string    // user-given mnemonic, also used for making save storage directory
	Path  string    // absolute path to the source save file/directory
	Root  string    // absolute path to the saved saves directory
	Stamp time.Time // last modification datetime stamp
	Saves []*Save   // slice of saves
	Size  uint64    `json:Size,omitempty` // total size of all saves, MINVER:1
}

func (g *Game) PrintHeader() {
	fmt.Printf("%-32s %-9s %24s %8s\n", "Name", "# Backups", "Last backup", "Size")
}

func (g *Game) Print() {
	fmt.Printf("%-32s %-9d %24s %8s\n", g.Name, len(g.Saves), g.Stamp.Format(timeFmt), bytefmt.ByteSize(g.Size))
}

func (g *Game) PrintWhole() {
	g.PrintHeader()
	g.Print()
	fmt.Println()
	if len(g.Saves) > 0 {
		fmt.Printf("%3s ", "ID")
		g.Saves[0].PrintHeader()
		for i, s := range g.Saves {
			fmt.Printf("%3d ", i+1)
			s.Print()
		}
		fmt.Println()
	}
}

func (g *Game) Delete(from, to int) (n int, err error) {
	if from < 1 {
		return n, fmt.Errorf("Index from %d out of range", from)
	}
	if to > len(g.Saves) {
		return n, fmt.Errorf("Index to %d out of range", to)
	}
	spinner.Msg("Deleting saves...")
	from--
	i := from
	for ; i < to; i++ {
		s := g.Saves[i]
		if flagVerbose {
			spinner.Msg(fmt.Sprintf("removing save %d from %s", i+1, s.Stamp.Format(timeFmt)))
		}
		spinner.Tick()
		err = os.Remove(s.Path)
		if err != nil {
			break
		}
		g.Size -= s.Size
		n++
	}
	spinner.Finish()
	copy(g.Saves[from:], g.Saves[i:])
	for k, n := len(g.Saves)-i+from, len(g.Saves); k < n; k++ {
		g.Saves[k] = nil
	}
	g.Saves = g.Saves[:len(g.Saves)-i+from]
	return n, err
}

// Backup copies a save file, or zips a save directory.
// Note that currently proper file closing is depending on total
// exit of the program on any file operation failure.
func (g *Game) Backup() (sv *Save, err error) {
	fi, err := os.Stat(g.Path)
	if err != nil {
		return nil, err
	}
	s := time.Now()
	p := filepath.Join(g.Root, s.Format(fileFmt))
	o, err := os.Create(p)
	if err != nil {
		return nil, err
	}
	defer func() {
		o.Close()
		if err != nil {
			if lerr := os.Remove(p); lerr != nil {
				fmt.Fprintln(os.Stderr, "Error removing partial backup file:", lerr)
			}
		}
	}()
	// single file
	if fi.Mode().IsRegular() {
		i, err := os.Open(g.Path)
		if err != nil {
			return nil, err
		}
		defer i.Close()
		fmt.Println("Backing up", i.Name(), "...")
		if flagVerbose {
			fmt.Fprintf(os.Stderr, "copying %s to %s\n", g.Path, p)
		}
		n, err := io.Copy(o, i)
		if err != nil {
			if flagVerbose {
				fmt.Fprintf(os.Stderr, "error, copied %d\n", n)
			}
			return nil, err
		}
		if flagVerbose {
			fmt.Fprintf(os.Stderr, "ok, copied %d\n", n)
		}
	} else {
		// directory
		spinner.Msg("Backing up save directory...")
		if flagVerbose {
			spinner.Msg(fmt.Sprintf("zipping %s to %s", g.Path, p))
		}
		z := zip.NewWriter(o)
		err = filepath.Walk(g.Path, func(path string, info os.FileInfo, ierr error) (err error) {
			if ierr != nil {
				return ierr
			}
			if info.IsDir() {
				if flagVerbose {
					spinner.Msg(fmt.Sprintf("skipping %s", path))
				}
				return nil
			}
			rp, err := filepath.Rel(g.Path, path)
			if err != nil {
				return err
			}
			rp = filepath.ToSlash(rp)
			i, err := os.Open(path)
			if err != nil {
				return err
			}
			o, err := z.Create(rp)
			if err != nil {
				return err
			}
			if flagVerbose {
				spinner.Msg(fmt.Sprintf("compressing %s", rp))
			}
			spinner.Tick()
			_, err = io.Copy(o, i)
			i.Close()
			return err
		})
		spinner.Finish()
		if err != nil {
			return nil, err
		}
		if err = z.Close(); err != nil {
			return nil, err
		}
	}
	si, err := o.Stat()
	if err != nil {
		return nil, err
	}
	size := uint64(si.Size())
	sv = &Save{
		Stamp: s,
		Path:  p,
		Size:  size,
	}
	g.Saves = append(g.Saves, sv)
	g.Size += size
	return sv, nil
}

// Restore removes the current save file or directory, and repopulates
// it with the data from given backup save file.
// Note that currently proper file closing is depending on total
// exit of the program on any file operation failure.
func (g *Game) Restore(index int) (sv *Save, err error) {
	var i int

	if index > 0 {
		i = index - 1
		if i > len(g.Saves) {
			return nil, fmt.Errorf("Save ID %d out of range (1 ~ %d)", index, len(g.Saves))
		}
	} else {
		i = len(g.Saves) - 1 + index
		if i < 0 {
			return nil, fmt.Errorf("Save offset %d out of range (%d ~ 0)", index, -len(g.Saves)+1)
		}
	}
	sv = g.Saves[i]
	fi, err := os.Stat(g.Path)
	if err != nil {
		return nil, err
	}
	// single file
	if fi.Mode().IsRegular() {
		i, err := os.Open(sv.Path)
		if err != nil {
			return nil, err
		}
		defer i.Close()
		o, err := os.Create(g.Path)
		if err != nil {
			return nil, err
		}
		defer o.Close()
		fmt.Println("Restoring save from", sv.Path, "...")
		if flagVerbose {
			fmt.Fprintf(os.Stderr, "copying %s to %s\n", sv.Path, g.Path)
		}
		n, err := io.Copy(o, i)
		if flagVerbose {
			if err != nil {
				fmt.Fprintf(os.Stderr, "error, copied %d\n", n)
			} else {
				fmt.Fprintf(os.Stderr, "ok, copied %d\n", n)
			}
		}
		return sv, nil
	}
	// directory
	spinner.Msg(fmt.Sprint("Restoring save directory from ", sv.Stamp.Format(timeFmt), "..."))
	if flagVerbose {
		spinner.Msg(fmt.Sprintf("removing all from %s", g.Path))
	}
	err = os.RemoveAll(g.Path)
	if err != nil {
		return nil, err
	}
	if flagVerbose {
		spinner.Msg(fmt.Sprintf("unzipping %s to %s", sv.Path, g.Path))
	}
	z, err := zip.OpenReader(sv.Path)
	if err != nil {
		return nil, err
	}
	defer z.Close()
	for _, f := range z.File {
		i, err := f.Open()
		if err != nil {
			return nil, err
		}
		tp := filepath.Join(g.Path, filepath.FromSlash(f.Name))
		dp := filepath.Dir(tp)
		err = os.MkdirAll(dp, 0777)
		if err != nil {
			return nil, err
		}
		o, err := os.Create(tp)
		if err != nil {
			return nil, err
		}
		if flagVerbose {
			spinner.Msg(fmt.Sprintf("decompressing %s", f.Name))
		}
		spinner.Tick()
		n, err := io.Copy(o, i)
		if err != nil {
			if flagVerbose {
				fmt.Fprintf(os.Stderr, "error, copied %d\n", n)
			}
			return nil, err
		}
		o.Close()
		i.Close()
		if flagVerbose {
			spinner.Msg(fmt.Sprintf("ok, copied %d", n))
		}
	}
	spinner.Finish()
	return sv, nil
}
<|endoftext|>


<commit_before>// Package gdbm provides support for the GNU database manager.
package gdbm

/*
#cgo LDFLAGS: -lgdbm
#include <gdbm.h>
#include <stdlib.h>
*/
import "C"

import (
	"unsafe"
)

type Mode int

const (
	// Open as a reader.
	Reader Mode = C.GDBM_READER
	// Open as a writer. The database must already exist.
	Writer = C.GDBM_WRITER
	// Open as a writer and create the database if it does not exist.
	Create = C.GDBM_WRCREAT
	// Open as a writer and create a new database regardless if one already exists.
	ForceCreate = C.GDBM_NEWDB
)

type Database struct {
	dbf C.GDBM_FILE
}

type Config struct {
	// Name of the database file.
	File string
	// Permission bits to use when creating a new database file.
	Perm int
	// How to open the database. See the documentation for the Mode type.
	Mode Mode
	// If true, all database operations will be synchronized to the disk.
	Sync bool
	// If true, no file locking will be performed on the database.
	DontLock bool
	// When creating new databases, BlockSize refers to the size of a single
	// transfer from disk to memory. The minimum value is 512. If BlockSize
	// is zero, then the file system default will be used.
	BlockSize int
	// CacheSize sets the size of the internal bucket cache. If zero, the cache size
	// will be set to 100.
	CacheSize int
}

// Opens a database using the given configuration options.
func OpenConfig(config *Config) (*Database, error) {
	cname := C.CString(config.File)
	defer C.free(unsafe.Pointer(cname))
	mode := C.int(config.Mode)
	if config.Sync {
		mode |= C.GDBM_SYNC
	}
	if config.DontLock {
		mode |= C.GDBM_NOLOCK
	}
	dbf, err := C.gdbm_open(cname, C.int(config.BlockSize), mode, C.int(config.Perm), nil)
	if dbf != nil {
		if config.CacheSize != 0 {
			val := C.int(config.CacheSize)
			C.gdbm_setopt(dbf, C.GDBM_CACHESIZE, &val, C.int(unsafe.Sizeof(val)))
		}
		return &Database{dbf}, nil
	}
	return nil, err
}

// Opens a database with the default options. The database will be
// created if it does not exist (with permission bits 0666, before umask).
func Open(file string) (*Database, error) {
	return OpenConfig(&Config{
		File: file,
		Perm: 0666,
		Mode: Create,
	})
}

// Closes the database and releases all associated resources.
func (d *Database) Close() {
	C.gdbm_close(d.dbf)
}

// Returns the data associated with a given key, or nil if the key is not
// present in the database.
func (d *Database) Fetch(key []byte) (value []byte) {
	dkey := toDatum(key)
	dval := C.gdbm_fetch(d.dbf, dkey)
	if dval.dptr != nil {
		defer C.free(unsafe.Pointer(dval.dptr))
		value = C.GoBytes(unsafe.Pointer(dval.dptr), dval.dsize)
	}
	return
}

// Stores data for a specified key. Any existing data for the key will be replaced.
// If `data` is nil, then the key will be deleted if it exists in the database.
func (d *Database) Store(key, data []byte) {
	dkey := toDatum(key)
	if data == nil {
		C.gdbm_delete(d.dbf, dkey)
	} else {
		C.gdbm_store(d.dbf, dkey, toDatum(data), C.GDBM_REPLACE)
	}
}

// Returns true if the specified key is found in the database.
func (d *Database) Exists(key []byte) bool {
	ret := C.gdbm_exists(d.dbf, toDatum(key))
	return ret != 0
}

// Iterates through all the keys in the database. The callback will be called for
// each key. The callback should return true unless it wants to cancel the iteration.
// Keys will be traversed in an unspecified order.
func (d *Database) Iterate(callback func(key []byte) (cont bool)) {
	key := C.gdbm_firstkey(d.dbf)
	for key.dptr != nil {
		key = d.iterKey(callback, key)
	}
}

func (d *Database) iterKey(callback func([]byte) bool, key C.datum) (next C.datum) {
	defer C.free(unsafe.Pointer(key.dptr))
	bytes := C.GoBytes(unsafe.Pointer(key.dptr), key.dsize)
	if callback(bytes) {
		next = C.gdbm_nextkey(d.dbf, key)
	}
	return
}

// Reorganizes the database file in order to reduce its size by reusing free space.
// This should be used very infrequently, and will only be useful after a lot of deletions
// have been made.
func (d *Database) Reorganize() {
	C.gdbm_reorganize(d.dbf)
}

// Synchronizes the database to disk. Sync will only return once the database has been
// physically written to the disk.
func (d *Database) Sync() {
	C.gdbm_sync(d.dbf)
}

func toDatum(data []byte) C.datum {
	var ptr unsafe.Pointer
	var size int
	if len(data) > 0 {
		ptr = unsafe.Pointer(&data[0])
		size = len(data)
	} else if data != nil {
		// GDBM requires a non-NULL pointer, but the size is zero so it can be arbitrary
		ptr = unsafe.Pointer(uintptr(1))
	}
	return C.datum{
		dptr:  (*C.char)(ptr),
		dsize: C.int(size),
	}
}

func fromDatum(datum C.datum) (bytes []byte) {
	if datum.dptr != nil {
		defer C.free(unsafe.Pointer(datum.dptr))
		bytes = C.GoBytes(unsafe.Pointer(datum.dptr), datum.dsize)
	}
	return
}
<commit_msg>Added Database.File() and fixed a bug in Fetch<commit_after>// Package gdbm provides support for the GNU database manager.
package gdbm

/*
#cgo LDFLAGS: -lgdbm
#include <gdbm.h>
#include <stdlib.h>
*/
import "C"

import (
	"unsafe"
)

type Mode int

const (
	// Open as a reader.
	Reader Mode = C.GDBM_READER
	// Open as a writer. The database must already exist.
	Writer = C.GDBM_WRITER
	// Open as a writer and create the database if it does not exist.
	Create = C.GDBM_WRCREAT
	// Open as a writer and create a new database regardless if one already exists.
	ForceCreate = C.GDBM_NEWDB
)

type Database struct {
	file string
	dbf  C.GDBM_FILE
}

type Config struct {
	// Name of the database file.
	File string
	// Permission bits to use when creating a new database file.
	Perm int
	// How to open the database. See the documentation for the Mode type.
	Mode Mode
	// If true, all database operations will be synchronized to the disk.
	Sync bool
	// If true, no file locking will be performed on the database.
	DontLock bool
	// When creating new databases, BlockSize refers to the size of a single
	// transfer from disk to memory. The minimum value is 512. If BlockSize
	// is zero, then the file system default will be used.
	BlockSize int
	// CacheSize sets the size of the internal bucket cache. If zero, the cache size
	// will be set to 100.
	CacheSize int
}

// Opens a database using the given configuration options.
func OpenConfig(config *Config) (*Database, error) {
	cname := C.CString(config.File)
	defer C.free(unsafe.Pointer(cname))
	mode := C.int(config.Mode)
	if config.Sync {
		mode |= C.GDBM_SYNC
	}
	if config.DontLock {
		mode |= C.GDBM_NOLOCK
	}
	dbf, err := C.gdbm_open(cname, C.int(config.BlockSize), mode, C.int(config.Perm), nil)
	if dbf != nil {
		if config.CacheSize != 0 {
			val := C.int(config.CacheSize)
			C.gdbm_setopt(dbf, C.GDBM_CACHESIZE, &val, C.int(unsafe.Sizeof(val)))
		}
		return &Database{config.File, dbf}, nil
	}
	return nil, err
}

// Opens a database with the default options. The database will be
// created if it does not exist (with permission bits 0666, before umask).
func Open(file string) (*Database, error) {
	return OpenConfig(&Config{
		File: file,
		Perm: 0666,
		Mode: Create,
	})
}

// Closes the database and releases all associated resources.
func (d *Database) Close() {
	C.gdbm_close(d.dbf)
}

func (d *Database) File() string {
	return d.file
}

// Returns the data associated with a given key, or nil if the key is not
// present in the database.
func (d *Database) Fetch(key []byte) (value []byte) {
	dkey := toDatum(key)
	dval := C.gdbm_fetch(d.dbf, dkey)
	if dval.dptr != nil {
		defer C.free(unsafe.Pointer(dval.dptr))
		value = C.GoBytes(unsafe.Pointer(dval.dptr), dval.dsize)
	}
	return
}

// Stores data for a specified key. Any existing data for the key will be replaced.
// If `data` is nil, then the key will be deleted if it exists in the database.
func (d *Database) Store(key, data []byte) {
	dkey := toDatum(key)
	if data == nil {
		C.gdbm_delete(d.dbf, dkey)
	} else {
		C.gdbm_store(d.dbf, dkey, toDatum(data), C.GDBM_REPLACE)
	}
}

// Returns true if the specified key is found in the database.
func (d *Database) Exists(key []byte) bool {
	ret := C.gdbm_exists(d.dbf, toDatum(key))
	return ret != 0
}

// Iterates through all the keys in the database. The callback will be called for
// each key. The callback should return true unless it wants to cancel the iteration.
// Keys will be traversed in an unspecified order.
func (d *Database) Iterate(callback func(key []byte) (cont bool)) {
	key := C.gdbm_firstkey(d.dbf)
	for key.dptr != nil {
		key = d.iterKey(callback, key)
	}
}

func (d *Database) iterKey(callback func([]byte) bool, key C.datum) (next C.datum) {
	defer C.free(unsafe.Pointer(key.dptr))
	bytes := C.GoBytes(unsafe.Pointer(key.dptr), key.dsize)
	if callback(bytes) {
		next = C.gdbm_nextkey(d.dbf, key)
	}
	return
}

// Reorganizes the database file in order to reduce its size by reusing free space.
// This should be used very infrequently, and will only be useful after a lot of deletions
// have been made.
func (d *Database) Reorganize() {
	C.gdbm_reorganize(d.dbf)
}

// Synchronizes the database to disk. Sync will only return once the database has been
// physically written to the disk.
func (d *Database) Sync() {
	C.gdbm_sync(d.dbf)
}

func toDatum(data []byte) C.datum {
	var ptr unsafe.Pointer
	var size int
	if len(data) > 0 {
		ptr = unsafe.Pointer(&data[0])
		size = len(data)
	} else if data != nil {
		// GDBM requires a non-NULL pointer, but the size is zero so it can be arbitrary
		ptr = unsafe.Pointer(&data)
	}
	return C.datum{
		dptr:  (*C.char)(ptr),
		dsize: C.int(size),
	}
}

func fromDatum(datum C.datum) (bytes []byte) {
	if datum.dptr != nil {
		defer C.free(unsafe.Pointer(datum.dptr))
		bytes = C.GoBytes(unsafe.Pointer(datum.dptr), datum.dsize)
	}
	return
}
<|endoftext|>