<commit_before>/*
Package fake is the fake data generatror for go (Golang), heavily inspired by forgery and ffaker Ruby gems

Most data and methods are ported from forgery/ffaker Ruby gems.

Currently english and russian languages are available.

For the list of available methods please look at https://godoc.org/github.com/syscrusher/fake.

Fake embeds samples data files unless you call UseExternalData(true) in order to be able to work without external files dependencies when compiled, so, if you add new data files or make changes to existing ones don't forget to regenerate data.go file using github.com/mjibson/esc tool and esc -o data.go -pkg fake data command (or you can just use go generate command if you are using Go 1.4 or later).

Examples:
	name := fake.FirstName()
	fullname = := fake.FullName()
	product := fake.Product()

Changing language:
	err := fake.SetLang("ru")
	if err != nil {
		panic(err)
	}
	password := fake.SimplePassword()

Using english fallback:
	err := fake.SetLang("ru")
	if err != nil {
		panic(err)
	}
	fake.EnFallback(true)
	password := fake.Paragraph()

Using external data:
	fake.UseExternalData(true)
	password := fake.Paragraph()
*/
package fake

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"strconv"
	"strings"
	"sync"
	"time"
)

//go:generate go get github.com/mjibson/esc
//go:generate esc -o data.go -pkg fake data

// cat/subcat/lang/samples
type samplesTree map[string]map[string][]string

var samplesLock sync.Mutex
var samplesCache = make(samplesTree)
var r = rand.New(&rndSrc{src: rand.NewSource(time.Now().UnixNano())})
var lang = "en"
var useExternalData = false
var enFallback = true
var availLangs = GetLangs()

var (
	// ErrNoLanguageFn is the error that indicates that given language is not available
	ErrNoLanguageFn = func(lang string) error { return fmt.Errorf("The language passed (%s) is not available", lang) }
	// ErrNoSamplesFn is the error that indicates that there are no samples for the given language
	ErrNoSamplesFn = func(lang string) error { return fmt.Errorf("No samples found for language: %s", lang) }
)

type rndSrc struct {
	mtx sync.Mutex
	src rand.Source
}

func (s *rndSrc) Int63() int64 {
	s.mtx.Lock()
	n := s.src.Int63()
	s.mtx.Unlock()
	return n
}

func (s *rndSrc) Seed(n int64) {
	s.mtx.Lock()
	s.src.Seed(n)
	s.mtx.Unlock()
}

// GetLangs returns a slice of available languages
func GetLangs() []string {
	var langs []string
	for k, v := range _escData {
		if v.isDir && k != "/" && k != "/data" {
			langs = append(langs, strings.Replace(k, "/data/", "", 1))
		}
	}
	return langs
}

// SetLang sets the language in which the data should be generated
// returns error if passed language is not available
func SetLang(newLang string) error {
	found := false
	for _, l := range availLangs {
		if newLang == l {
			found = true
			break
		}
	}
	if !found {
		return ErrNoLanguageFn(newLang)
	}
	lang = newLang
	return nil
}

// UseExternalData sets the flag that allows using of external files as data providers (fake uses embedded ones by default)
func UseExternalData(flag bool) {
	useExternalData = flag
}

// EnFallback sets the flag that allows fake to fallback to englsh samples if the ones for the used languaged are not available
func EnFallback(flag bool) {
	enFallback = flag
}

func (st samplesTree) hasKeyPath(lang, cat string) bool {
	if _, ok := st[lang]; ok {
		if _, ok = st[lang][cat]; ok {
			return true
		}
	}
	return false
}

func join(parts ...string) string {
	var filtered []string
	for _, part := range parts {
		if part != "" {
			filtered = append(filtered, part)
		}
	}
	return strings.Join(filtered, " ")
}

func generate(lag, cat string, fallback bool) string {
	format := lookup(lang, cat+"_format", fallback)
	var result string
	for _, ru := range format {
		if ru != '#' {
			result += string(ru)
		} else {
			result += strconv.Itoa(r.Intn(10))
		}
	}
	return result
}

func lookup(lang, cat string, fallback bool) string {
	samplesLock.Lock()
	s := _lookup(lang, cat, fallback)
	samplesLock.Unlock()
	return s
}

func _lookup(lang, cat string, fallback bool) string {
	var samples []string

	if samplesCache.hasKeyPath(lang, cat) {
		samples = samplesCache[lang][cat]
	} else {
		var err error
		samples, err = populateSamples(lang, cat)
		if err != nil {
			if lang != "en" && fallback && enFallback && err.Error() == ErrNoSamplesFn(lang).Error() {
				return _lookup("en", cat, false)
			}
			return ""
		}
	}

	return samples[r.Intn(len(samples))]
}

func populateSamples(lang, cat string) ([]string, error) {
	data, err := readFile(lang, cat)
	if err != nil {
		return nil, err
	}

	if _, ok := samplesCache[lang]; !ok {
		samplesCache[lang] = make(map[string][]string)
	}

	samples := strings.Split(strings.TrimSpace(string(data)), "\n")

	samplesCache[lang][cat] = samples
	return samples, nil
}

func readFile(lang, cat string) ([]byte, error) {
	fullpath := fmt.Sprintf("/data/%s/%s", lang, cat)
	file, err := FS(useExternalData).Open(fullpath)
	if err != nil {
		return nil, ErrNoSamplesFn(lang)
	}
	defer file.Close()

	return ioutil.ReadAll(file)
}
<commit_msg>Check for errors on file closing<commit_after>/*
Package fake is the fake data generatror for go (Golang), heavily inspired by forgery and ffaker Ruby gems

Most data and methods are ported from forgery/ffaker Ruby gems.

Currently english and russian languages are available.

For the list of available methods please look at https://godoc.org/github.com/syscrusher/fake.

Fake embeds samples data files unless you call UseExternalData(true) in order to be able to work without external files dependencies when compiled, so, if you add new data files or make changes to existing ones don't forget to regenerate data.go file using github.com/mjibson/esc tool and esc -o data.go -pkg fake data command (or you can just use go generate command if you are using Go 1.4 or later).

Examples:
	name := fake.FirstName()
	fullname = := fake.FullName()
	product := fake.Product()

Changing language:
	err := fake.SetLang("ru")
	if err != nil {
		panic(err)
	}
	password := fake.SimplePassword()

Using english fallback:
	err := fake.SetLang("ru")
	if err != nil {
		panic(err)
	}
	fake.EnFallback(true)
	password := fake.Paragraph()

Using external data:
	fake.UseExternalData(true)
	password := fake.Paragraph()
*/
package fake

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"strconv"
	"strings"
	"sync"
	"time"
)

//go:generate go get github.com/mjibson/esc
//go:generate esc -o data.go -pkg fake data

// cat/subcat/lang/samples
type samplesTree map[string]map[string][]string

var samplesLock sync.Mutex
var samplesCache = make(samplesTree)
var r = rand.New(&rndSrc{src: rand.NewSource(time.Now().UnixNano())})
var lang = "en"
var useExternalData = false
var enFallback = true
var availLangs = GetLangs()

var (
	// ErrNoLanguageFn is the error that indicates that given language is not available
	ErrNoLanguageFn = func(lang string) error { return fmt.Errorf("The language passed (%s) is not available", lang) }
	// ErrNoSamplesFn is the error that indicates that there are no samples for the given language
	ErrNoSamplesFn = func(lang string) error { return fmt.Errorf("No samples found for language: %s", lang) }
)

type rndSrc struct {
	mtx sync.Mutex
	src rand.Source
}

func (s *rndSrc) Int63() int64 {
	s.mtx.Lock()
	n := s.src.Int63()
	s.mtx.Unlock()
	return n
}

func (s *rndSrc) Seed(n int64) {
	s.mtx.Lock()
	s.src.Seed(n)
	s.mtx.Unlock()
}

// GetLangs returns a slice of available languages
func GetLangs() []string {
	var langs []string
	for k, v := range _escData {
		if v.isDir && k != "/" && k != "/data" {
			langs = append(langs, strings.Replace(k, "/data/", "", 1))
		}
	}
	return langs
}

// SetLang sets the language in which the data should be generated
// returns error if passed language is not available
func SetLang(newLang string) error {
	found := false
	for _, l := range availLangs {
		if newLang == l {
			found = true
			break
		}
	}
	if !found {
		return ErrNoLanguageFn(newLang)
	}
	lang = newLang
	return nil
}

// UseExternalData sets the flag that allows using of external files as data providers (fake uses embedded ones by default)
func UseExternalData(flag bool) {
	useExternalData = flag
}

// EnFallback sets the flag that allows fake to fallback to englsh samples if the ones for the used languaged are not available
func EnFallback(flag bool) {
	enFallback = flag
}

func (st samplesTree) hasKeyPath(lang, cat string) bool {
	if _, ok := st[lang]; ok {
		if _, ok = st[lang][cat]; ok {
			return true
		}
	}
	return false
}

func join(parts ...string) string {
	var filtered []string
	for _, part := range parts {
		if part != "" {
			filtered = append(filtered, part)
		}
	}
	return strings.Join(filtered, " ")
}

func generate(lag, cat string, fallback bool) string {
	format := lookup(lang, cat+"_format", fallback)
	var result string
	for _, ru := range format {
		if ru != '#' {
			result += string(ru)
		} else {
			result += strconv.Itoa(r.Intn(10))
		}
	}
	return result
}

func lookup(lang, cat string, fallback bool) string {
	samplesLock.Lock()
	s := _lookup(lang, cat, fallback)
	samplesLock.Unlock()
	return s
}

func _lookup(lang, cat string, fallback bool) string {
	var samples []string

	if samplesCache.hasKeyPath(lang, cat) {
		samples = samplesCache[lang][cat]
	} else {
		var err error
		samples, err = populateSamples(lang, cat)
		if err != nil {
			if lang != "en" && fallback && enFallback && err.Error() == ErrNoSamplesFn(lang).Error() {
				return _lookup("en", cat, false)
			}
			return ""
		}
	}

	return samples[r.Intn(len(samples))]
}

func populateSamples(lang, cat string) ([]string, error) {
	data, err := readFile(lang, cat)
	if err != nil {
		return nil, err
	}

	if _, ok := samplesCache[lang]; !ok {
		samplesCache[lang] = make(map[string][]string)
	}

	samples := strings.Split(strings.TrimSpace(string(data)), "\n")

	samplesCache[lang][cat] = samples
	return samples, nil
}

func readFile(lang, cat string) (f []byte, err error) {
	fullpath := fmt.Sprintf("/data/%s/%s", lang, cat)
	file, err := FS(useExternalData).Open(fullpath)
	if err != nil {
		return nil, ErrNoSamplesFn(lang)
	}
	defer func() {
		// overwrites named return value
		err = file.Close()
	}()

	return ioutil.ReadAll(file)
}
<|endoftext|>


<commit_before>package u

import (
	"bufio"
	"crypto/sha1"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

// PathExists returns true if a filesystem path exists
// Treats any error (e.g. lack of access due to permissions) as non-existence
func PathExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

// FileExists returns true if a given path exists and is a file
func FileExists(path string) bool {
	st, err := os.Stat(path)
	return err == nil && !st.IsDir() && st.Mode().IsRegular()
}

// DirExists returns true if a given path exists and is a directory
func DirExists(path string) bool {
	st, err := os.Stat(path)
	return err == nil && st.IsDir()
}

// PathIsDir returns true if a path exists and is a directory
// Returns false, nil if a path exists and is not a directory (e.g. a file)
// Returns undefined, error if there was an error e.g. because a path doesn't exists
func PathIsDir(path string) (isDir bool, err error) {
	fi, err := os.Stat(path)
	if err != nil {
		return false, err
	}
	return fi.IsDir(), nil
}

// GetFileSize returns size of the file
func GetFileSize(path string) (int64, error) {
	fi, err := os.Lstat(path)
	if err != nil {
		return 0, err
	}
	return fi.Size(), nil
}

// CreateDirIfNotExists creates a directory if it doesn't exist
func CreateDirIfNotExists(dir string) error {
	return os.MkdirAll(dir, 0755)
}

// CreateDirIfNotExistsMust creates a directory. Panics on error
func CreateDirIfNotExistsMust(dir string) string {
	err := os.MkdirAll(dir, 0755)
	PanicIfErr(err)
	return dir
}

// CreateDirMust creates a directory. Panics on error
func CreateDirMust(path string) {
	err := CreateDirIfNotExists(path)
	PanicIfErr(err)
}

// CreateDirForFile creates intermediary directories for a file
func CreateDirForFile(path string) error {
	dir := filepath.Dir(path)
	return CreateDirIfNotExists(dir)
}

// CreateDirForFileMust is like CreateDirForFile. Panics on error.
func CreateDirForFileMust(path string) string {
	dir := filepath.Dir(path)
	err := CreateDirIfNotExists(dir)
	PanicIfErr(err)
	return dir
}

// WriteBytesToFile is like ioutil.WriteFile() but also creates intermediary
// directories
func WriteBytesToFile(d []byte, path string) error {
	if err := CreateDirIfNotExists(filepath.Dir(path)); err != nil {
		return err
	}
	return ioutil.WriteFile(path, d, 0644)
}

// ListFilesInDir returns a list of files in a directory
func ListFilesInDir(dir string, recursive bool) []string {
	files := make([]string, 0)
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		isDir, err := PathIsDir(path)
		if err != nil {
			return err
		}
		if isDir {
			if recursive || path == dir {
				return nil
			}
			return filepath.SkipDir
		}
		files = append(files, path)
		return nil
	})
	return files
}

// CopyFile copies a file
func CopyFile(dst, src string) error {
	fsrc, err := os.Open(src)
	if err != nil {
		return err
	}
	defer fsrc.Close()
	fdst, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer fdst.Close()
	if _, err = io.Copy(fdst, fsrc); err != nil {
		return err
	}
	return nil
}

// ReadLinesFromReader reads all lines from io.Reader. Newlines are not included.
func ReadLinesFromReader(r io.Reader) ([]string, error) {
	res := make([]string, 0)
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		res = append(res, scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		return res, err
	}
	return res, nil
}

// ReadLinesFromFile reads all lines from a file. Newlines are not included.
func ReadLinesFromFile(path string) ([]string, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return ReadLinesFromReader(f)
}

// Sha1OfFile returns 20-byte sha1 of file content
func Sha1OfFile(path string) ([]byte, error) {
	f, err := os.Open(path)
	if err != nil {
		//fmt.Printf("os.Open(%s) failed with %s\n", path, err.Error())
		return nil, err
	}
	defer f.Close()
	h := sha1.New()
	_, err = io.Copy(h, f)
	if err != nil {
		//fmt.Printf("io.Copy() failed with %s\n", err.Error())
		return nil, err
	}
	return h.Sum(nil), nil
}

// Sha1HexOfFile returns 40-byte hex sha1 of file content
func Sha1HexOfFile(path string) (string, error) {
	sha1, err := Sha1OfFile(path)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", sha1), nil
}

// PathMatchesExtensions returns true if path matches any of the extensions
func PathMatchesExtensions(path string, extensions []string) bool {
	if len(extensions) == 0 {
		return true
	}
	ext := strings.ToLower(filepath.Ext(path))
	for _, allowed := range extensions {
		if ext == allowed {
			return true
		}
	}
	return false
}
<commit_msg>add DeleteFilesIf<commit_after>package u

import (
	"bufio"
	"crypto/sha1"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

// PathExists returns true if a filesystem path exists
// Treats any error (e.g. lack of access due to permissions) as non-existence
func PathExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

// FileExists returns true if a given path exists and is a file
func FileExists(path string) bool {
	st, err := os.Stat(path)
	return err == nil && !st.IsDir() && st.Mode().IsRegular()
}

// DirExists returns true if a given path exists and is a directory
func DirExists(path string) bool {
	st, err := os.Stat(path)
	return err == nil && st.IsDir()
}

// PathIsDir returns true if a path exists and is a directory
// Returns false, nil if a path exists and is not a directory (e.g. a file)
// Returns undefined, error if there was an error e.g. because a path doesn't exists
func PathIsDir(path string) (isDir bool, err error) {
	fi, err := os.Stat(path)
	if err != nil {
		return false, err
	}
	return fi.IsDir(), nil
}

// GetFileSize returns size of the file
func GetFileSize(path string) (int64, error) {
	fi, err := os.Lstat(path)
	if err != nil {
		return 0, err
	}
	return fi.Size(), nil
}

// CreateDirIfNotExists creates a directory if it doesn't exist
func CreateDirIfNotExists(dir string) error {
	return os.MkdirAll(dir, 0755)
}

// CreateDirIfNotExistsMust creates a directory. Panics on error
func CreateDirIfNotExistsMust(dir string) string {
	err := os.MkdirAll(dir, 0755)
	PanicIfErr(err)
	return dir
}

// CreateDirMust creates a directory. Panics on error
func CreateDirMust(path string) {
	err := CreateDirIfNotExists(path)
	PanicIfErr(err)
}

// CreateDirForFile creates intermediary directories for a file
func CreateDirForFile(path string) error {
	dir := filepath.Dir(path)
	return CreateDirIfNotExists(dir)
}

// CreateDirForFileMust is like CreateDirForFile. Panics on error.
func CreateDirForFileMust(path string) string {
	dir := filepath.Dir(path)
	err := CreateDirIfNotExists(dir)
	PanicIfErr(err)
	return dir
}

// WriteBytesToFile is like ioutil.WriteFile() but also creates intermediary
// directories
func WriteBytesToFile(d []byte, path string) error {
	if err := CreateDirIfNotExists(filepath.Dir(path)); err != nil {
		return err
	}
	return ioutil.WriteFile(path, d, 0644)
}

// ListFilesInDir returns a list of files in a directory
func ListFilesInDir(dir string, recursive bool) []string {
	files := make([]string, 0)
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		isDir, err := PathIsDir(path)
		if err != nil {
			return err
		}
		if isDir {
			if recursive || path == dir {
				return nil
			}
			return filepath.SkipDir
		}
		files = append(files, path)
		return nil
	})
	return files
}

// CopyFile copies a file
func CopyFile(dst, src string) error {
	fsrc, err := os.Open(src)
	if err != nil {
		return err
	}
	defer fsrc.Close()
	fdst, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer fdst.Close()
	if _, err = io.Copy(fdst, fsrc); err != nil {
		return err
	}
	return nil
}

// ReadLinesFromReader reads all lines from io.Reader. Newlines are not included.
func ReadLinesFromReader(r io.Reader) ([]string, error) {
	res := make([]string, 0)
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		res = append(res, scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		return res, err
	}
	return res, nil
}

// ReadLinesFromFile reads all lines from a file. Newlines are not included.
func ReadLinesFromFile(path string) ([]string, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return ReadLinesFromReader(f)
}

// Sha1OfFile returns 20-byte sha1 of file content
func Sha1OfFile(path string) ([]byte, error) {
	f, err := os.Open(path)
	if err != nil {
		//fmt.Printf("os.Open(%s) failed with %s\n", path, err.Error())
		return nil, err
	}
	defer f.Close()
	h := sha1.New()
	_, err = io.Copy(h, f)
	if err != nil {
		//fmt.Printf("io.Copy() failed with %s\n", err.Error())
		return nil, err
	}
	return h.Sum(nil), nil
}

// Sha1HexOfFile returns 40-byte hex sha1 of file content
func Sha1HexOfFile(path string) (string, error) {
	sha1, err := Sha1OfFile(path)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", sha1), nil
}

// PathMatchesExtensions returns true if path matches any of the extensions
func PathMatchesExtensions(path string, extensions []string) bool {
	if len(extensions) == 0 {
		return true
	}
	ext := strings.ToLower(filepath.Ext(path))
	for _, allowed := range extensions {
		if ext == allowed {
			return true
		}
	}
	return false
}

// DeleteFilesIf deletes a files in a given directory if shouldDelete callback
// returns true
func DeleteFilesIf(dir string, shouldDelete func(os.FileInfo) bool) error {
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}
	for _, fi := range files {
		if fi.IsDir() || !fi.Mode().IsRegular() {
			continue
		}
		if shouldDelete(fi) {
			path := filepath.Join(dir, fi.Name())
			err = os.Remove(path)
			// Maybe: keep deleting?
			if err != nil {
				return err
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package dbox

import (
	"github.com/gebv/typed"
    "time"
)

var _ Object = (*File)(nil)

var (
	MapDataIDMetaKey = "MapDataID"
	RawDataIDMetaKey = "RawDataID"
	CreatedAtKey        = "CreatedAt"
	UpdatedAtKey = "UpdatedAt"
    NameKey = "NameKey"
)

func NewFile(store Store) *File {

	return &File{
		store:     store,
        MapObject: NewMapObject(store),
	}
}

type File struct {
	*MapObject

	mdata *MapObject
	rdata Object

	store Store

    err error

    // storeRawData Store
    // storeStructData Store
    // storeMetaFileData Store
}

func (f File) String() string {
    return f.Name()
}

func (f File) Error() error {
    return f.err
}

func (f File) Name() string {
    return f.Meta().String(NameKey)
}

func (f File) SetName(v string) {
    f.Meta().Set(NameKey, v)
}

func (f File) UpdatedAt() time.Time {
    return f.Meta().Time(UpdatedAtKey)
}

func (f File) CreatedAt() time.Time {
    return f.Meta().Time(CreatedAtKey)
}

// Meta meta data file
func (f File) Meta() *typed.Typed {

	return f.Map()
}

// MapData struct data file
func (f *File) MapData() *typed.Typed {
	if f.mdata == nil {
		f.mdata = NewMapObject(f.store)

		err := f.store.Get(f.mapDataID(), f.mdata)

		if err == ErrNotFound || len(f.mapDataID()) == 0 {
			f.mdata.Sync()
			f.setMapDataID(f.mdata.ID())
			f.MapObject.Sync() // update file props
		} else if err != nil {
			// handler error
            f.err = err

            // TODO: How to address the error?
		}
	}

	return f.mdata.Map()
}

// RawData raw data file
func (f *File) RawData() Object {
	if f.rdata == nil {
		f.rdata = NewRawObject(f.store)

		err := f.store.Get(f.rawDataID(), f.rdata)

		if err == ErrNotFound || len(f.rawDataID()) == 0 {
			f.rdata.Sync()
			f.setRawDataID(f.rdata.ID())
			f.MapObject.Sync() // update file props
		} else if err != nil {
			// handler error
            f.err = err

            // TODO: How to address the error?
		}
	}

	return f.rdata
}

func (f *File) Delete() error {
    if f.IsNew() {
        return ErrEmptyID
    }

    if len(f.Name()) == 0 {
        return ErrEmptyName
    }

    f.MapData()
    if err := f.store.Delete(f.mdata); err != nil {
        return err
    }

    f.RawData()
    if err := f.store.Delete(f.rdata); err != nil {
        return err
    }

    return f.store.Delete(f)
}

func (f *File) Sync() error {
    if f.IsNew() {
        f.id = NewUUID()
        f.BeforeCreate()
    }

    if len(f.Name()) == 0 {
        f.SetName(f.ID())
    }

    f.BeforeUpdate()

	if f.mdata != nil {
		return f.mdata.Sync()
	}

	if f.rdata != nil {
		return f.rdata.Sync()
	}

	// after save related objects

	if err := f.Encode(); err != nil {
		return err
	}

	return f.store.Save(f)
}

//----------
// helpfull
//---------

func (f *File) BeforeCreate() {
	f.Meta().Set(CreatedAtKey, time.Now())
}

func (f *File) BeforeUpdate()  {
	f.Meta().Set(UpdatedAtKey, time.Now())
}

func (f File) mapDataID() string {
	return f.Meta().String(MapDataIDMetaKey)
}

func (f File) setMapDataID(id string) {
	f.Meta().Set(MapDataIDMetaKey, id)
}

func (f File) rawDataID() string {
	return f.Meta().String(RawDataIDMetaKey)
}

func (f File) setRawDataID(id string) {
	f.Meta().Set(RawDataIDMetaKey, id)
}

// --------

func NewFileID(id string, store Store) (*File, error) {
    file := NewFile(store)

	return file, store.Get(id, file)
}

func NewFileName(name string, store Store) (*File, error) {
    file := NewFile(store)

	return file, store.(FileStore).GetByName(name, file)
}<commit_msg>consistency file (not the best solution)<commit_after>package dbox

import (
	"github.com/gebv/typed"
	"time"
)

var _ Object = (*File)(nil)

var (
	MapDataIDMetaKey = "MapDataID"
	RawDataIDMetaKey = "RawDataID"
	CreatedAtKey     = "CreatedAt"
	UpdatedAtKey     = "UpdatedAt"
	NameKey          = "NameKey"
)

func NewFile(store Store) *File {

	return &File{
		store:     store,
		MapObject: NewMapObject(store),
	}
}

type File struct {
	*MapObject

	mdata *MapObject
	rdata Object

	store Store

	invalid       bool
	reasoninvalid error
}

func (f File) String() string {
	return f.Name()
}
func (f File) Name() string {
	return f.Meta().String(NameKey)
}

func (f File) SetName(v string) {
	f.Meta().Set(NameKey, v)
}

func (f File) UpdatedAt() time.Time {
	return f.Meta().Time(UpdatedAtKey)
}

func (f File) CreatedAt() time.Time {
	return f.Meta().Time(CreatedAtKey)
}

// Meta meta data file
func (f File) Meta() *typed.Typed {

	return f.Map()
}

// MapData struct data file
func (f *File) MapData() *typed.Typed {
	if f.mdata == nil {
		f.mdata = NewMapObject(f.store)

		var err error

		if len(f.mapDataID()) != 0 {
			err = f.store.Get(f.mapDataID(), f.mdata)
		}

		if err == ErrNotFound || len(f.mapDataID()) == 0 {
			f.mdata.Sync()
			f.setMapDataID(f.mdata.ID())
			f.MapObject.Sync() // update file props
		} else if err != nil {
			// handler error
			f.invalid = true
			f.reasoninvalid = err

			// TODO: How to address the error?
		}
	}

	return f.mdata.Map()
}

// RawData raw data file
func (f *File) RawData() Object {
	if f.rdata == nil {
		f.rdata = NewRawObject(f.store)
		var err error

		if len(f.rawDataID()) != 0 {
			err = f.store.Get(f.rawDataID(), f.rdata)
		}

		if err == ErrNotFound || len(f.rawDataID()) == 0 {
			f.rdata.Sync()
			f.setRawDataID(f.rdata.ID())
			f.MapObject.Sync() // update file props
		} else if err != nil {
			// handler error
			f.invalid = true
			f.reasoninvalid = err

			// TODO: How to address the error?
		}
	}

	return f.rdata
}

func (f *File) Delete() error {
	if f.invalid {
		return f.reasoninvalid
	}

	if f.IsNew() {
		return ErrEmptyID
	}

	if len(f.Name()) == 0 {
		return ErrEmptyName
	}

	f.MapData()
	if err := f.store.Delete(f.mdata); err != nil {
		return err
	}

	f.RawData()
	if err := f.store.Delete(f.rdata); err != nil {
		return err
	}

	return f.store.Delete(f)
}

func (f *File) Sync() error {
	if f.invalid {
		return f.reasoninvalid
	}

	if f.IsNew() {
		f.id = NewUUID()
		f.BeforeCreate()
	}

	if len(f.Name()) == 0 {
		f.SetName(f.ID())
	}

	f.BeforeUpdate()

	if f.mdata != nil {
		return f.mdata.Sync()
	}

	if f.rdata != nil {
		return f.rdata.Sync()
	}

	// after save related objects

	if err := f.Encode(); err != nil {
		return err
	}

	return f.store.Save(f)
}

//----------
// helpfull
//---------

func (f *File) BeforeCreate() {
	f.Meta().Set(CreatedAtKey, time.Now())
}

func (f *File) BeforeUpdate() {
	f.Meta().Set(UpdatedAtKey, time.Now())
}

func (f File) mapDataID() string {
	return f.Meta().String(MapDataIDMetaKey)
}

func (f File) setMapDataID(id string) {
	f.Meta().Set(MapDataIDMetaKey, id)
}

func (f File) rawDataID() string {
	return f.Meta().String(RawDataIDMetaKey)
}

func (f File) setRawDataID(id string) {
	f.Meta().Set(RawDataIDMetaKey, id)
}

// --------

func NewFileID(id string, store Store) (*File, error) {
	file := NewFile(store)

	return file, store.Get(id, file)
}

func NewFileName(name string, store Store) (*File, error) {
	file := NewFile(store)

	return file, store.(FileStore).GetByName(name, file)
}
<|endoftext|>


<commit_before>// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tag

import (
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
)

// BlockType is a type which represents an enumeration of valid FLAC blocks
type BlockType byte

const (
	StreamInfoBlock    BlockType = 0
	PaddingBlock                 = 1
	ApplicationBlock             = 2
	SeektableBlock               = 3
	VorbisCommentBlock           = 4 // Supported
	CueSheetBlock                = 5
	PictureBlock                 = 6 // Supported
)

// ReadFLACTags reads FLAC metadata from the io.ReadSeeker, returning the resulting
// metadata in a Metadata implementation, or non-nil error if there was a problem.
func ReadFLACTags(r io.ReadSeeker) (Metadata, error) {
	_, err := r.Seek(0, os.SEEK_SET)
	if err != nil {
		return nil, err
	}

	flac, err := readString(r, 4)
	if err != nil {
		return nil, err
	}
	if flac != "fLaC" {
		return nil, errors.New("expected 'fLaC'")
	}

	m := &metadataFLAC{
		c: make(map[string]string),
	}

	for {
		last, err := m.readFLACMetadataBlock(r)
		if err != nil {
			return nil, err
		}

		if last {
			break
		}
	}
	return m, nil
}

type metadataFLAC struct {
	c map[string]string // the vorbis comments
	p *Picture
}

func (m *metadataFLAC) readFLACMetadataBlock(r io.ReadSeeker) (last bool, err error) {
	blockHeader, err := readBytes(r, 1)
	if err != nil {
		log.Println(err)
		return
	}

	if getBit(blockHeader[0], 7) {
		blockHeader[0] ^= (1 << 7)
		last = true
	}

	blockLen, err := readInt(r, 3)
	if err != nil {
		return
	}

	switch BlockType(blockHeader[0]) {
	case VorbisCommentBlock:
		err = m.readVorbisComment(r)

	case PictureBlock:
		err = m.readPictureBlock(r)

	default:
		_, err = r.Seek(int64(blockLen), os.SEEK_CUR)
	}
	return
}

func (m *metadataFLAC) readVorbisComment(r io.Reader) error {
	vendorLen, err := readInt32LittleEndian(r)
	if err != nil {
		return err
	}

	vendor, err := readString(r, vendorLen)
	if err != nil {
		return err
	}
	m.c["vendor"] = vendor

	commentsLen, err := readInt32LittleEndian(r)
	if err != nil {
		return err
	}

	for i := 0; i < commentsLen; i++ {
		l, err := readInt32LittleEndian(r)
		if err != nil {
			return err
		}
		s, err := readString(r, l)
		if err != nil {
			return err
		}
		k, v, err := parseComment(s)
		if err != nil {
			return err
		}
		m.c[strings.ToLower(k)] = v
	}
	return nil
}

func (m *metadataFLAC) readPictureBlock(r io.Reader) error {
	b, err := readInt(r, 4)
	if err != nil {
		return err
	}
	pictureType, ok := pictureTypes[byte(b)]
	if !ok {
		return fmt.Errorf("invalid picture type: %v", b)
	}
	mimeLen, err := readInt(r, 4)
	if err != nil {
		return err
	}
	mime, err := readString(r, mimeLen)
	if err != nil {
		return err
	}

	ext := ""
	switch mime {
	case "image/jpeg":
		ext = "jpg"
	case "image/png":
		ext = "png"
	case "image/gif":
		ext = "gif"
	}

	descLen, err := readInt(r, 4)
	if err != nil {
		return err
	}
	desc, err := readString(r, descLen)
	if err != nil {
		return err
	}

	// We skip width <32>, height <32>, colorDepth <32>, coloresUsed <32>
	_, err = readInt(r, 4) // width
	if err != nil {
		return err
	}
	_, err = readInt(r, 4) // height
	if err != nil {
		return err
	}
	_, err = readInt(r, 4) // color depth
	if err != nil {
		return err
	}
	_, err = readInt(r, 4) // colors used
	if err != nil {
		return err
	}

	dataLen, err := readInt(r, 4)
	if err != nil {
		return err
	}
	data := make([]byte, dataLen)
	_, err = io.ReadFull(r, data)
	if err != nil {
		return err
	}

	m.p = &Picture{
		Ext:         ext,
		MIMEType:    mime,
		Type:        pictureType,
		Description: desc,
		Data:        data,
	}
	return nil
}

func parseComment(c string) (k, v string, err error) {
	kv := strings.SplitN(c, "=", 2)
	if len(kv) != 2 {
		err = errors.New("vorbis comment must contain '='")
		return
	}
	k = kv[0]
	v = kv[1]
	return
}

func (m *metadataFLAC) Format() Format {
	return FLAC
}

func (m *metadataFLAC) Raw() map[string]interface{} {
	raw := make(map[string]interface{}, len(m.c))
	for k, v := range m.c {
		raw[k] = v
	}
	return raw
}

func (m *metadataFLAC) Title() string {
	return m.c["title"]
}

func (m *metadataFLAC) Artist() string {
	// PERFORMER
	// The artist(s) who performed the work. In classical music this would be the
	// conductor, orchestra, soloists. In an audio book it would be the actor who
	// did the reading. In popular music this is typically the same as the ARTIST
	// and is omitted.
	if m.c["performer"] != "" {
		return m.c["performer"]
	}
	return m.c["artist"]
}

func (m *metadataFLAC) Album() string {
	return m.c["album"]
}

func (m *metadataFLAC) AlbumArtist() string {
	// This field isn't included in the standard.
	return ""
}

func (m *metadataFLAC) Composer() string {
	// ARTIST
	// The artist generally considered responsible for the work. In popular music
	// this is usually the performing band or singer. For classical music it would
	// be the composer. For an audio book it would be the author of the original text.
	if m.c["composer"] != "" {
		return m.c["composer"]
	}
	if m.c["performer"] == "" {
		return ""
	}
	return m.c["artist"]
}

func (m *metadataFLAC) Genre() string {
	return m.c["genre"]
}

func (m *metadataFLAC) Year() int {
	// FIXME: try to parse the date in m.c["date"] to extract this
	return 0
}

func (m *metadataFLAC) Track() (int, int) {
	x, _ := strconv.Atoi(m.c["tracknumber"])
	// https://wiki.xiph.org/Field_names
	n, _ := strconv.Atoi(m.c["tracktotal"])
	return x, n
}

func (m *metadataFLAC) Disc() (int, int) {
	// https://wiki.xiph.org/Field_names
	x, _ := strconv.Atoi(m.c["discnumber"])
	n, _ := strconv.Atoi(m.c["disctotal"])
	return x, n
}

func (m *metadataFLAC) Picture() *Picture {
	return m.p
}
<commit_msg>Remove log statement<commit_after>// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tag

import (
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
)

// BlockType is a type which represents an enumeration of valid FLAC blocks
type BlockType byte

const (
	StreamInfoBlock    BlockType = 0
	PaddingBlock                 = 1
	ApplicationBlock             = 2
	SeektableBlock               = 3
	VorbisCommentBlock           = 4 // Supported
	CueSheetBlock                = 5
	PictureBlock                 = 6 // Supported
)

// ReadFLACTags reads FLAC metadata from the io.ReadSeeker, returning the resulting
// metadata in a Metadata implementation, or non-nil error if there was a problem.
func ReadFLACTags(r io.ReadSeeker) (Metadata, error) {
	_, err := r.Seek(0, os.SEEK_SET)
	if err != nil {
		return nil, err
	}

	flac, err := readString(r, 4)
	if err != nil {
		return nil, err
	}
	if flac != "fLaC" {
		return nil, errors.New("expected 'fLaC'")
	}

	m := &metadataFLAC{
		c: make(map[string]string),
	}

	for {
		last, err := m.readFLACMetadataBlock(r)
		if err != nil {
			return nil, err
		}

		if last {
			break
		}
	}
	return m, nil
}

type metadataFLAC struct {
	c map[string]string // the vorbis comments
	p *Picture
}

func (m *metadataFLAC) readFLACMetadataBlock(r io.ReadSeeker) (last bool, err error) {
	blockHeader, err := readBytes(r, 1)
	if err != nil {
		return
	}

	if getBit(blockHeader[0], 7) {
		blockHeader[0] ^= (1 << 7)
		last = true
	}

	blockLen, err := readInt(r, 3)
	if err != nil {
		return
	}

	switch BlockType(blockHeader[0]) {
	case VorbisCommentBlock:
		err = m.readVorbisComment(r)

	case PictureBlock:
		err = m.readPictureBlock(r)

	default:
		_, err = r.Seek(int64(blockLen), os.SEEK_CUR)
	}
	return
}

func (m *metadataFLAC) readVorbisComment(r io.Reader) error {
	vendorLen, err := readInt32LittleEndian(r)
	if err != nil {
		return err
	}

	vendor, err := readString(r, vendorLen)
	if err != nil {
		return err
	}
	m.c["vendor"] = vendor

	commentsLen, err := readInt32LittleEndian(r)
	if err != nil {
		return err
	}

	for i := 0; i < commentsLen; i++ {
		l, err := readInt32LittleEndian(r)
		if err != nil {
			return err
		}
		s, err := readString(r, l)
		if err != nil {
			return err
		}
		k, v, err := parseComment(s)
		if err != nil {
			return err
		}
		m.c[strings.ToLower(k)] = v
	}
	return nil
}

func (m *metadataFLAC) readPictureBlock(r io.Reader) error {
	b, err := readInt(r, 4)
	if err != nil {
		return err
	}
	pictureType, ok := pictureTypes[byte(b)]
	if !ok {
		return fmt.Errorf("invalid picture type: %v", b)
	}
	mimeLen, err := readInt(r, 4)
	if err != nil {
		return err
	}
	mime, err := readString(r, mimeLen)
	if err != nil {
		return err
	}

	ext := ""
	switch mime {
	case "image/jpeg":
		ext = "jpg"
	case "image/png":
		ext = "png"
	case "image/gif":
		ext = "gif"
	}

	descLen, err := readInt(r, 4)
	if err != nil {
		return err
	}
	desc, err := readString(r, descLen)
	if err != nil {
		return err
	}

	// We skip width <32>, height <32>, colorDepth <32>, coloresUsed <32>
	_, err = readInt(r, 4) // width
	if err != nil {
		return err
	}
	_, err = readInt(r, 4) // height
	if err != nil {
		return err
	}
	_, err = readInt(r, 4) // color depth
	if err != nil {
		return err
	}
	_, err = readInt(r, 4) // colors used
	if err != nil {
		return err
	}

	dataLen, err := readInt(r, 4)
	if err != nil {
		return err
	}
	data := make([]byte, dataLen)
	_, err = io.ReadFull(r, data)
	if err != nil {
		return err
	}

	m.p = &Picture{
		Ext:         ext,
		MIMEType:    mime,
		Type:        pictureType,
		Description: desc,
		Data:        data,
	}
	return nil
}

func parseComment(c string) (k, v string, err error) {
	kv := strings.SplitN(c, "=", 2)
	if len(kv) != 2 {
		err = errors.New("vorbis comment must contain '='")
		return
	}
	k = kv[0]
	v = kv[1]
	return
}

func (m *metadataFLAC) Format() Format {
	return FLAC
}

func (m *metadataFLAC) Raw() map[string]interface{} {
	raw := make(map[string]interface{}, len(m.c))
	for k, v := range m.c {
		raw[k] = v
	}
	return raw
}

func (m *metadataFLAC) Title() string {
	return m.c["title"]
}

func (m *metadataFLAC) Artist() string {
	// PERFORMER
	// The artist(s) who performed the work. In classical music this would be the
	// conductor, orchestra, soloists. In an audio book it would be the actor who
	// did the reading. In popular music this is typically the same as the ARTIST
	// and is omitted.
	if m.c["performer"] != "" {
		return m.c["performer"]
	}
	return m.c["artist"]
}

func (m *metadataFLAC) Album() string {
	return m.c["album"]
}

func (m *metadataFLAC) AlbumArtist() string {
	// This field isn't included in the standard.
	return ""
}

func (m *metadataFLAC) Composer() string {
	// ARTIST
	// The artist generally considered responsible for the work. In popular music
	// this is usually the performing band or singer. For classical music it would
	// be the composer. For an audio book it would be the author of the original text.
	if m.c["composer"] != "" {
		return m.c["composer"]
	}
	if m.c["performer"] == "" {
		return ""
	}
	return m.c["artist"]
}

func (m *metadataFLAC) Genre() string {
	return m.c["genre"]
}

func (m *metadataFLAC) Year() int {
	// FIXME: try to parse the date in m.c["date"] to extract this
	return 0
}

func (m *metadataFLAC) Track() (int, int) {
	x, _ := strconv.Atoi(m.c["tracknumber"])
	// https://wiki.xiph.org/Field_names
	n, _ := strconv.Atoi(m.c["tracktotal"])
	return x, n
}

func (m *metadataFLAC) Disc() (int, int) {
	// https://wiki.xiph.org/Field_names
	x, _ := strconv.Atoi(m.c["discnumber"])
	n, _ := strconv.Atoi(m.c["disctotal"])
	return x, n
}

func (m *metadataFLAC) Picture() *Picture {
	return m.p
}
<|endoftext|>


<commit_before>package flit

import (
	"errors"
	"fmt"
	"os"
	"os/user"
	"strings"
	"time"

	"code.google.com/p/go-uuid/uuid"
	"github.com/ianremmler/dgrl"
)

type Flit struct {
	issues *dgrl.Branch
}

func New() *Flit {
	return &Flit{issues: dgrl.NewRoot()}
}

func (f *Flit) InitFile() error {
	issueFile, err := os.OpenFile("issues", os.O_RDWR|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	issueFile.Close()
	return nil
}

func (f *Flit) Load() error {
	issueFile, err := os.Open("issues")
	if err != nil {
		return err
	}
	defer issueFile.Close()
	issues := dgrl.NewParser().Parse(issueFile)
	if issues == nil {
		return errors.New("error parsing issue file")
	}
	f.issues = issues
	return nil
}

func (f *Flit) Store() error {
	if f.issues == nil {
		return errors.New("issues not initialized")
	}
	issueFile, err := os.Create("issues")
	if err != nil {
		return err
	}
	fmt.Fprintln(issueFile, f.issues)
	issueFile.Close()
	return nil
}

func (f *Flit) AppendIssues() error {
	if f.issues == nil {
		return errors.New("issues not initialized")
	}
	issueFile, err := os.OpenFile("issues", os.O_RDWR|os.O_APPEND, 0666)
	if err != nil {
		return err
	}
	fmt.Fprintln(issueFile, f.issues)
	issueFile.Close()
	return nil
}

func (f *Flit) IssueIds() []string {
	if f.issues == nil {
		return []string{}
	}
	issueIds := []string{}
	for _, node := range f.issues.Kids() {
		if node.Type() == dgrl.BranchType {
			issueIds = append(issueIds, node.Key())
		}
	}
	return issueIds
}

func (f *Flit) NewIssue() (string, error) {
	if f.issues == nil {
		return "", errors.New("issues not initialized")
	}
	id := uuid.New()
	issue := dgrl.NewBranch(id)
	issue.Append(dgrl.NewLeaf("created", Stamp()))
	issue.Append(dgrl.NewLeaf("updated", Stamp()))
	issue.Append(dgrl.NewLeaf("closed", ""))
	issue.Append(dgrl.NewLeaf("summary", ""))
	issue.Append(dgrl.NewLeaf("tags", ""))
	issue.Append(dgrl.NewLeaf("status", ""))
	issue.Append(dgrl.NewLeaf("priority", ""))
	issue.Append(dgrl.NewLeaf("assigned", ""))
	issue.Append(dgrl.NewLongLeaf("description", "\n"))
	f.issues.Append(issue)

	return id, nil
}

func (f *Flit) Issue(id string) *dgrl.Branch {
	for _, node := range f.issues.Kids() {
		if node.Type() == dgrl.BranchType && node.Key() == id {
			return node.(*dgrl.Branch)
		}
	}
	return nil
}

func (f *Flit) Match(key, val string, doesMatch bool) []string {
	matches := []string{}

	for _, node := range f.issues.Kids() {
		if issue, ok := node.(*dgrl.Branch); ok {
			if IssueContains(issue, key, val) {
				matches = append(matches, issue.Key())
			}
		}
	}
	return matches
}

func IssueContains(issue *dgrl.Branch, key, val string) bool {
	for _, kid := range issue.Kids() {
		if leaf, ok := kid.(*dgrl.Leaf); ok {
			if strings.Contains(leaf.Key(), key) && strings.Contains(leaf.Value(), val) {
				return true
			}
		}
	}
	return false
}

func Get(issue *dgrl.Branch, key string) (string, bool) {
	for _, kid := range issue.Kids() {
		if leaf, ok := kid.(*dgrl.Leaf); ok {
			if strings.Contains(leaf.Key(), key) {
				return leaf.Value(), true
			}
		}
	}
	return "", false
}

func Set(issue *dgrl.Branch, key, val string) bool {
	for _, kid := range issue.Kids() {
		if leaf, ok := kid.(*dgrl.Leaf); ok {
			if strings.Contains(leaf.Key(), key) {
				leaf.SetValue(val)
				return true
			}
		}
	}
	return false
}

func curTime() string {
	return time.Now().Format(time.RFC3339)
}

func curUser() (string, error) {
	user, err := user.Current()
	if err != nil {
		return "", err
	}
	return user.Username, nil
}

func Stamp() string {
	user, err := curUser()
	if err != nil {
		user = "?"
	}
	return user + " " + curTime()
}
<commit_msg>Changed IssueContains to use Get.<commit_after>package flit

import (
	"errors"
	"fmt"
	"os"
	"os/user"
	"strings"
	"time"

	"code.google.com/p/go-uuid/uuid"
	"github.com/ianremmler/dgrl"
)

type Flit struct {
	issues *dgrl.Branch
}

func New() *Flit {
	return &Flit{issues: dgrl.NewRoot()}
}

func (f *Flit) InitFile() error {
	issueFile, err := os.OpenFile("issues", os.O_RDWR|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	issueFile.Close()
	return nil
}

func (f *Flit) Load() error {
	issueFile, err := os.Open("issues")
	if err != nil {
		return err
	}
	defer issueFile.Close()
	issues := dgrl.NewParser().Parse(issueFile)
	if issues == nil {
		return errors.New("error parsing issue file")
	}
	f.issues = issues
	return nil
}

func (f *Flit) Store() error {
	if f.issues == nil {
		return errors.New("issues not initialized")
	}
	issueFile, err := os.Create("issues")
	if err != nil {
		return err
	}
	fmt.Fprintln(issueFile, f.issues)
	issueFile.Close()
	return nil
}

func (f *Flit) AppendIssues() error {
	if f.issues == nil {
		return errors.New("issues not initialized")
	}
	issueFile, err := os.OpenFile("issues", os.O_RDWR|os.O_APPEND, 0666)
	if err != nil {
		return err
	}
	fmt.Fprintln(issueFile, f.issues)
	issueFile.Close()
	return nil
}

func (f *Flit) IssueIds() []string {
	if f.issues == nil {
		return []string{}
	}
	issueIds := []string{}
	for _, node := range f.issues.Kids() {
		if node.Type() == dgrl.BranchType {
			issueIds = append(issueIds, node.Key())
		}
	}
	return issueIds
}

func (f *Flit) NewIssue() (string, error) {
	if f.issues == nil {
		return "", errors.New("issues not initialized")
	}
	id := uuid.New()
	issue := dgrl.NewBranch(id)
	issue.Append(dgrl.NewLeaf("created", Stamp()))
	issue.Append(dgrl.NewLeaf("updated", Stamp()))
	issue.Append(dgrl.NewLeaf("closed", ""))
	issue.Append(dgrl.NewLeaf("summary", ""))
	issue.Append(dgrl.NewLeaf("tags", ""))
	issue.Append(dgrl.NewLeaf("status", ""))
	issue.Append(dgrl.NewLeaf("priority", ""))
	issue.Append(dgrl.NewLeaf("assigned", ""))
	issue.Append(dgrl.NewLongLeaf("description", "\n"))
	f.issues.Append(issue)

	return id, nil
}

func (f *Flit) Issue(id string) *dgrl.Branch {
	for _, node := range f.issues.Kids() {
		if node.Type() == dgrl.BranchType && node.Key() == id {
			return node.(*dgrl.Branch)
		}
	}
	return nil
}

func (f *Flit) Match(key, val string, doesMatch bool) []string {
	matches := []string{}
	for _, node := range f.issues.Kids() {
		if issue, ok := node.(*dgrl.Branch); ok {
			if IssueContains(issue, key, val) {
				matches = append(matches, issue.Key())
			}
		}
	}
	return matches
}

func Get(issue *dgrl.Branch, key string) (string, bool) {
	for _, kid := range issue.Kids() {
		if leaf, ok := kid.(*dgrl.Leaf); ok {
			if strings.Contains(leaf.Key(), key) {
				return leaf.Value(), true
			}
		}
	}
	return "", false
}

func IssueContains(issue *dgrl.Branch, key, val string) bool {
	if issueVal, ok := Get(issue, key); ok {
		if strings.Contains(issueVal, val) {
			return true
		}
	}
	return false
}

func Set(issue *dgrl.Branch, key, val string) bool {
	for _, kid := range issue.Kids() {
		if leaf, ok := kid.(*dgrl.Leaf); ok {
			if strings.Contains(leaf.Key(), key) {
				leaf.SetValue(val)
				return true
			}
		}
	}
	return false
}

func curTime() string {
	return time.Now().Format(time.RFC3339)
}

func curUser() (string, error) {
	user, err := user.Current()
	if err != nil {
		return "", err
	}
	return user.Username, nil
}

func Stamp() string {
	user, err := curUser()
	if err != nil {
		user = "?"
	}
	return user + " " + curTime()
}
<|endoftext|>


<commit_before>package sdlgui

import (
	"github.com/kasworld/go-sdl2/sdl"
	"github.com/kasworld/go-sdl2/sdl_ttf"
	"github.com/kasworld/htmlcolors"
	"github.com/kasworld/log"
)

func init() {
	ttf.Init()
}

type FontKey struct {
	co   htmlcolors.RGBA
	text string
}
type Font struct {
	Font  *ttf.Font
	Cache map[FontKey]*sdl.Surface
}

func LoadFont(filename string, size int) *Font {
	font, err := ttf.OpenFont(filename, size)
	if err != nil {
		log.Fatalf("font open fail %v\n", err)
	}
	return &Font{
		Font:  font,
		Cache: make(map[FontKey]*sdl.Surface),
	}
}
func (f *Font) MakeSurface(co htmlcolors.RGBA, text string) (*sdl.Surface, sdl.Rect) {
	surface := f.Font.RenderUTF8_Blended(text, RGBA2SDL(co))
	// surface := f.Font.RenderText_Solid(text, co.SdlColor())
	// surface := f.Font.RenderText_Shaded(text, co.SdlColor(), co.Neg().SdlColor())
	if surface == nil {
		log.Printf("%v %v", co, text)
		log.Fatalf("Failed to create surface: %s\n", sdl.GetError())
	}
	srcRect := sdl.Rect{}
	surface.GetClipRect(&srcRect)
	return surface, srcRect
}
func (f *Font) MakeSurface2(fg, bg htmlcolors.RGBA, text string) (*sdl.Surface, sdl.Rect) {
	surface := f.Font.RenderText_Shaded(text, RGBA2SDL(fg), RGBA2SDL(bg))
	if surface == nil {
		// log.Printf("%v %v", co, text)
		log.Fatalf("Failed to create surface: %s\n", sdl.GetError())
	}
	srcRect := sdl.Rect{}
	surface.GetClipRect(&srcRect)
	return surface, srcRect
}
func (f *Font) GetSurfaceWithCache(co htmlcolors.RGBA, text string) (*sdl.Surface, sdl.Rect) {
	surface := f.Cache[FontKey{co, text}]
	if surface != nil {
		srcRect := sdl.Rect{}
		surface.GetClipRect(&srcRect)
		return surface, srcRect
	} else {
		surface, srcRect := f.MakeSurface(co, text)
		f.Cache[FontKey{co, text}] = surface
		return surface, srcRect
	}
}
func (f *Font) MakeTexture(co htmlcolors.RGBA, text string, rend *sdl.Renderer) (*sdl.Texture, sdl.Rect) {
	surface, srcRect := f.MakeSurface(co, text)
	defer surface.Free()
	t, err := rend.CreateTextureFromSurface(surface)
	if err != nil {
		log.Fatalf("Failed to create Texture: %s\n", err)
	}
	return t, srcRect
}
<commit_msg>remove unused code<commit_after>package sdlgui

import (
	"github.com/kasworld/go-sdl2/sdl"
	"github.com/kasworld/go-sdl2/sdl_ttf"
	"github.com/kasworld/htmlcolors"
	"github.com/kasworld/log"
)

func init() {
	ttf.Init()
}

type FontKey struct {
	co   htmlcolors.RGBA
	text string
}
type Font struct {
	Font  *ttf.Font
	Cache map[FontKey]*sdl.Surface
}

func LoadFont(filename string, size int) *Font {
	font, err := ttf.OpenFont(filename, size)
	if err != nil {
		log.Fatalf("font open fail %v\n", err)
	}
	return &Font{
		Font:  font,
		Cache: make(map[FontKey]*sdl.Surface),
	}
}
func (f *Font) MakeSurface(co htmlcolors.RGBA, text string) (*sdl.Surface, sdl.Rect) {
	surface := f.Font.RenderUTF8_Blended(text, RGBA2SDL(co))
	if surface == nil {
		log.Printf("%v %v", co, text)
		log.Fatalf("Failed to create surface: %s\n", sdl.GetError())
	}
	srcRect := sdl.Rect{}
	surface.GetClipRect(&srcRect)
	return surface, srcRect
}

func (f *Font) GetSurfaceWithCache(co htmlcolors.RGBA, text string) (*sdl.Surface, sdl.Rect) {
	surface := f.Cache[FontKey{co, text}]
	if surface != nil {
		srcRect := sdl.Rect{}
		surface.GetClipRect(&srcRect)
		return surface, srcRect
	} else {
		surface, srcRect := f.MakeSurface(co, text)
		f.Cache[FontKey{co, text}] = surface
		return surface, srcRect
	}
}
func (f *Font) MakeTexture(co htmlcolors.RGBA, text string, rend *sdl.Renderer) (*sdl.Texture, sdl.Rect) {
	surface, srcRect := f.MakeSurface(co, text)
	defer surface.Free()
	t, err := rend.CreateTextureFromSurface(surface)
	if err != nil {
		log.Fatalf("Failed to create Texture: %s\n", err)
	}
	return t, srcRect
}
<|endoftext|>


<commit_before>package easyreq

import (
	"encoding/base64"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

// Structure that contains form fields and upload files,
// can optionally have request headers.
type Form struct {
	fields url.Values
	files  url.Values
	header http.Header
}

// Inteface implemeted by Form, Json structures
type Requester interface {
	Header() http.Header
	Request(string, string) (*http.Request, error)
}

// Creates new Form with given fields and files
func NewForm(fields, files url.Values) *Form {
	f := new(Form)
	if fields != nil {
		f.fields = fields
	}
	if files != nil {
		f.files = files
	}
	return f
}

// Returns url.Values which should be used to Add form field
func (f *Form) Field() url.Values {
	if f.fields == nil {
		f.fields = make(url.Values)
	}
	return f.fields
}

// Returns url.Values which should be used to Add form file
func (f *Form) File() url.Values {
	if f.files == nil {
		f.files = make(url.Values)
	}
	return f.files
}

func (f *Form) Header() http.Header {
	if f.header == nil {
		f.header = make(http.Header)
	}
	return f.header
}

// Helper to set Basic Auth header for request
func (f *Form) SetBasicAuth(username, password string) {
	setBasicAuth(f, username, password)
}

func setBasicAuth(r Requester, username, password string) {
	s := username + ":" + password
	r.Header().Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
}

// Helper funcation send requests using http.DefaultClient
func (f *Form) Do(verb, urlStr string) (*http.Response, error) {
	return do(f, verb, urlStr)
}

func do(r Requester, verb, urlStr string) (*http.Response, error) {
	req, err := r.Request(verb, urlStr)
	if err != nil {
		return nil, err
	}
	return http.DefaultClient.Do(req)
}

// Returns request based on current Fields and Files assoicated with form
// Request will always have correct Content-Type set for POST and PUT
func (form *Form) Request(verb, urlStr string) (*http.Request, error) {
	// GET handled differently then POST, PUT
	if verb == "GET" {
		return getRequest(form, verb, urlStr)
	}

	// no files take shortcut
	if len(form.files) == 0 {
		return postWithoutFiles(form, verb, urlStr)
	}

	body, err := ioutil.TempFile(os.TempDir(), "easyreq-request")
	if err != nil {
		return nil, err
	}
	bodyWriter := multipart.NewWriter(body)

	for key, paths := range form.files {
		for _, pathVal := range paths {
			pathVal = filepath.Clean(pathVal)
			file, err := os.Open(pathVal)
			if err != nil {
				return nil, err
			}

			mimePart, err := bodyWriter.CreateFormFile(key, filepath.Base(pathVal))
			if err != nil {
				return nil, err
			}

			if _, err = io.Copy(mimePart, file); err != nil {
				return nil, err
			}

			if err = file.Close(); err != nil {
				return nil, err
			}
		}
	}

	for key, values := range form.fields {
		for _, value := range values {
			if err = bodyWriter.WriteField(key, value); err != nil {
				return nil, err
			}
		}
	}

	if err = bodyWriter.Close(); err != nil {
		return nil, err
	}
	if err = body.Close(); err != nil {
		return nil, err
	}
	log.Println(body.Name())
	body, err = os.Open(body.Name())
	req, err := http.NewRequest(verb, urlStr, body)
	if err != nil {
		return nil, err
	}
	req.Header = form.Header()
	req.Header.Set("Content-Type", bodyWriter.FormDataContentType())

	return req, nil
}

// send urlencoded fields in body
func postWithoutFiles(form *Form, verb, urlStr string) (*http.Request, error) {
	data := strings.NewReader(form.fields.Encode())
	req, err := http.NewRequest(verb, urlStr, data)
	if err != nil {
		return nil, err
	}
	req.Header = form.Header()
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	return req, nil
}

// send urlencoded fields in url
func getRequest(form *Form, verb, urlStr string) (*http.Request, error) {
	u, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	// bother only if new fields are added
	if len(form.fields) != 0 {
		if len(u.Query()) != 0 {
			// if original url had encoded fields join
			urlStr += "&"
		} else {
			// else start
			urlStr += "?"
		}
		urlStr += form.fields.Encode()
	}

	req, err := http.NewRequest(verb, urlStr, nil)
	if err != nil {
		return nil, err
	}
	req.Header = form.Header()

	return req, nil
}
<commit_msg>Send content length from temp file<commit_after>package easyreq

import (
	"encoding/base64"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

// Structure that contains form fields and upload files,
// can optionally have request headers.
type Form struct {
	fields url.Values
	files  url.Values
	header http.Header
}

// Inteface implemeted by Form, Json structures
type Requester interface {
	Header() http.Header
	Request(string, string) (*http.Request, error)
}

// Creates new Form with given fields and files
func NewForm(fields, files url.Values) *Form {
	f := new(Form)
	if fields != nil {
		f.fields = fields
	}
	if files != nil {
		f.files = files
	}
	return f
}

// Returns url.Values which should be used to Add form field
func (f *Form) Field() url.Values {
	if f.fields == nil {
		f.fields = make(url.Values)
	}
	return f.fields
}

// Returns url.Values which should be used to Add form file
func (f *Form) File() url.Values {
	if f.files == nil {
		f.files = make(url.Values)
	}
	return f.files
}

func (f *Form) Header() http.Header {
	if f.header == nil {
		f.header = make(http.Header)
	}
	return f.header
}

// Helper to set Basic Auth header for request
func (f *Form) SetBasicAuth(username, password string) {
	setBasicAuth(f, username, password)
}

func setBasicAuth(r Requester, username, password string) {
	s := username + ":" + password
	r.Header().Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
}

// Helper funcation send requests using http.DefaultClient
func (f *Form) Do(verb, urlStr string) (*http.Response, error) {
	return do(f, verb, urlStr)
}

func do(r Requester, verb, urlStr string) (*http.Response, error) {
	req, err := r.Request(verb, urlStr)
	if err != nil {
		return nil, err
	}
	return http.DefaultClient.Do(req)
}

// Returns request based on current Fields and Files assoicated with form
// Request will always have correct Content-Type set for POST and PUT
func (form *Form) Request(verb, urlStr string) (*http.Request, error) {
	// GET handled differently then POST, PUT
	if verb == "GET" {
		return getRequest(form, verb, urlStr)
	}

	// no files take shortcut
	if len(form.files) == 0 {
		return postWithoutFiles(form, verb, urlStr)
	}

	body, err := ioutil.TempFile(os.TempDir(), "easyreq-request")
	if err != nil {
		return nil, err
	}
	bodyWriter := multipart.NewWriter(body)

	for key, paths := range form.files {
		for _, pathVal := range paths {
			pathVal = filepath.Clean(pathVal)
			file, err := os.Open(pathVal)
			if err != nil {
				return nil, err
			}

			mimePart, err := bodyWriter.CreateFormFile(key, filepath.Base(pathVal))
			if err != nil {
				return nil, err
			}

			if _, err = io.Copy(mimePart, file); err != nil {
				return nil, err
			}

			if err = file.Close(); err != nil {
				return nil, err
			}
		}
	}

	for key, values := range form.fields {
		for _, value := range values {
			if err = bodyWriter.WriteField(key, value); err != nil {
				return nil, err
			}
		}
	}

	if err = bodyWriter.Close(); err != nil {
		return nil, err
	}
	if err = body.Close(); err != nil {
		return nil, err
	}
	// log.Println(body.Name())
	body, err = os.Open(body.Name())
	req, err := http.NewRequest(verb, urlStr, body)
	if err != nil {
		return nil, err
	}
	req.Header = form.Header()
	if info, err := body.Stat(); err != nil {
		return nil, err
	} else {
		req.ContentLength = info.Size()
	}
	// log.Println(req.ContentLength)
	req.Header.Set("Content-Type", bodyWriter.FormDataContentType())

	return req, nil
}

// send urlencoded fields in body
func postWithoutFiles(form *Form, verb, urlStr string) (*http.Request, error) {
	data := strings.NewReader(form.fields.Encode())
	req, err := http.NewRequest(verb, urlStr, data)
	if err != nil {
		return nil, err
	}
	req.Header = form.Header()
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	return req, nil
}

// send urlencoded fields in url
func getRequest(form *Form, verb, urlStr string) (*http.Request, error) {
	u, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	// bother only if new fields are added
	if len(form.fields) != 0 {
		if len(u.Query()) != 0 {
			// if original url had encoded fields join
			urlStr += "&"
		} else {
			// else start
			urlStr += "?"
		}
		urlStr += form.fields.Encode()
	}

	req, err := http.NewRequest(verb, urlStr, nil)
	if err != nil {
		return nil, err
	}
	req.Header = form.Header()

	return req, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"syscall"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"

	"github.com/rfjakob/gocryptfs/internal/exitcodes"
	"github.com/rfjakob/gocryptfs/internal/fusefrontend"
	"github.com/rfjakob/gocryptfs/internal/tlog"
)

type fsckObj struct {
	fs *fusefrontend.FS
	// List of corrupt files
	corruptList []string
	// List of skipped files
	skippedList []string
	// Protects corruptList
	listLock sync.Mutex
	// stop a running watchMitigatedCorruptions thread
	watchDone chan struct{}
	// Inode numbers of hard-linked files (Nlink > 1) that we have already checked
	seenInodes map[uint64]struct{}
}

func runsAsRoot() bool {
	return syscall.Geteuid() == 0
}

func (ck *fsckObj) markCorrupt(path string) {
	ck.listLock.Lock()
	ck.corruptList = append(ck.corruptList, path)
	ck.listLock.Unlock()
}

func (ck *fsckObj) markSkipped(path string) {
	ck.listLock.Lock()
	ck.skippedList = append(ck.skippedList, path)
	ck.listLock.Unlock()
}

// Watch for mitigated corruptions that occur during OpenDir()
func (ck *fsckObj) watchMitigatedCorruptionsOpenDir(path string) {
	for {
		select {
		case item := <-ck.fs.MitigatedCorruptions:
			fmt.Printf("fsck: corrupt entry in dir %q: %q\n", path, item)
			ck.markCorrupt(filepath.Join(path, item))
		case <-ck.watchDone:
			return
		}
	}
}

// Recursively check dir for corruption
func (ck *fsckObj) dir(path string) {
	tlog.Debug.Printf("ck.dir %q\n", path)
	ck.xattrs(path)
	// Run OpenDir and catch transparently mitigated corruptions
	go ck.watchMitigatedCorruptionsOpenDir(path)
	entries, status := ck.fs.OpenDir(path, nil)
	ck.watchDone <- struct{}{}
	// Also catch non-mitigated corruptions
	if !status.Ok() {
		fmt.Printf("fsck: error opening dir %q: %v\n", path, status)
		if status == fuse.EACCES && !runsAsRoot() {
			ck.markSkipped(path)
		} else {
			ck.markCorrupt(path)
		}
		return
	}
	// Sort alphabetically
	sort.Sort(sortableDirEntries(entries))
	for _, entry := range entries {
		if entry.Name == "." || entry.Name == ".." {
			continue
		}
		nextPath := filepath.Join(path, entry.Name)
		filetype := entry.Mode & syscall.S_IFMT
		//fmt.Printf("  %q %x\n", entry.Name, entry.Mode)
		switch filetype {
		case syscall.S_IFDIR:
			ck.dir(nextPath)
		case syscall.S_IFREG:
			ck.file(nextPath)
		case syscall.S_IFLNK:
			ck.symlink(nextPath)
		case syscall.S_IFIFO, syscall.S_IFSOCK, syscall.S_IFBLK, syscall.S_IFCHR:
			// nothing to check
		default:
			fmt.Printf("fsck: unhandled file type %x\n", filetype)
		}
	}
}

func (ck *fsckObj) symlink(path string) {
	_, status := ck.fs.Readlink(path, nil)
	if !status.Ok() {
		ck.markCorrupt(path)
		fmt.Printf("fsck: error reading symlink %q: %v\n", path, status)
	}
}

// Watch for mitigated corruptions that occur during Read()
func (ck *fsckObj) watchMitigatedCorruptionsRead(path string) {
	for {
		select {
		case item := <-ck.fs.MitigatedCorruptions:
			fmt.Printf("fsck: corrupt file %q (inode %s)\n", path, item)
			ck.markCorrupt(path)
		case <-ck.watchDone:
			return
		}
	}
}

// Check file for corruption
func (ck *fsckObj) file(path string) {
	tlog.Debug.Printf("ck.file %q\n", path)
	attr, status := ck.fs.GetAttr(path, nil)
	if !status.Ok() {
		ck.markCorrupt(path)
		fmt.Printf("fsck: error stating file %q: %v\n", path, status)
		return
	}
	if attr.Nlink > 1 {
		// Due to hard links, we may have already checked this file.
		if _, ok := ck.seenInodes[attr.Ino]; ok {
			tlog.Debug.Printf("ck.file : skipping %q (inode number %d already seen)\n", path, attr.Ino)
			return
		}
		ck.seenInodes[attr.Ino] = struct{}{}
	}
	ck.xattrs(path)
	f, status := ck.fs.Open(path, syscall.O_RDONLY, nil)
	if !status.Ok() {
		fmt.Printf("fsck: error opening file %q: %v\n", path, status)
		if status == fuse.EACCES && !runsAsRoot() {
			ck.markSkipped(path)
		} else {
			ck.markCorrupt(path)
		}
		return
	}
	defer f.Release()
	allZero := make([]byte, fuse.MAX_KERNEL_WRITE)
	buf := make([]byte, fuse.MAX_KERNEL_WRITE)
	var off int64
	// Read() through the whole file and catch transparently mitigated corruptions
	go ck.watchMitigatedCorruptionsRead(path)
	defer func() { ck.watchDone <- struct{}{} }()
	for {
		tlog.Debug.Printf("ck.file: read %d bytes from offset %d\n", len(buf), off)
		result, status := f.Read(buf, off)
		if !status.Ok() {
			ck.markCorrupt(path)
			fmt.Printf("fsck: error reading file %q (inum %d): %v\n", path, inum(f), status)
			return
		}
		// EOF
		if result.Size() == 0 {
			return
		}
		off += int64(result.Size())
		// If we seem to be in the middle of a file hole, try to skip to the next
		// data section.
		if bytes.Equal(buf, allZero) {
			tlog.Debug.Printf("ck.file: trying to skip file hole\n")
			f2 := f.(*fusefrontend.File)
			nextOff, err := f2.SeekData(off)
			if err == nil {
				off = nextOff
			}
		}
	}
}

// Watch for mitigated corruptions that occur during ListXAttr()
func (ck *fsckObj) watchMitigatedCorruptionsListXAttr(path string) {
	for {
		select {
		case item := <-ck.fs.MitigatedCorruptions:
			fmt.Printf("fsck: corrupt xattr name on file %q: %q\n", path, item)
			ck.markCorrupt(path + " xattr:" + item)
		case <-ck.watchDone:
			return
		}
	}
}

// Check xattrs on file/dir at path
func (ck *fsckObj) xattrs(path string) {
	// Run ListXAttr() and catch transparently mitigated corruptions
	go ck.watchMitigatedCorruptionsListXAttr(path)
	attrs, status := ck.fs.ListXAttr(path, nil)
	ck.watchDone <- struct{}{}
	// Also catch non-mitigated corruptions
	if !status.Ok() {
		fmt.Printf("fsck: error listing xattrs on %q: %v\n", path, status)
		ck.markCorrupt(path)
		return
	}
	for _, a := range attrs {
		_, status := ck.fs.GetXAttr(path, a, nil)
		if !status.Ok() {
			fmt.Printf("fsck: error reading xattr %q from %q: %v\n", a, path, status)
			if status == fuse.EACCES && !runsAsRoot() {
				ck.markSkipped(path)
			} else {
				ck.markCorrupt(path)
			}
		}
	}
}

func fsck(args *argContainer) {
	if args.reverse {
		tlog.Fatal.Printf("Running -fsck with -reverse is not supported")
		os.Exit(exitcodes.Usage)
	}
	args.allow_other = false
	pfs, wipeKeys := initFuseFrontend(args)
	fs := pfs.(*fusefrontend.FS)
	fs.MitigatedCorruptions = make(chan string)
	ck := fsckObj{
		fs:         fs,
		watchDone:  make(chan struct{}),
		seenInodes: make(map[uint64]struct{}),
	}
	ck.dir("")
	wipeKeys()
	if len(ck.corruptList) == 0 && len(ck.skippedList) == 0 {
		tlog.Info.Printf("fsck summary: no problems found\n")
		return
	}
	if len(ck.skippedList) > 0 {
		tlog.Warn.Printf("fsck: re-run this program as root to check all files!\n")
	}
	fmt.Printf("fsck summary: %d corrupt files, %d files skipped\n", len(ck.corruptList), len(ck.skippedList))
	os.Exit(exitcodes.FsckErrors)
}

type sortableDirEntries []fuse.DirEntry

func (s sortableDirEntries) Len() int {
	return len(s)
}

func (s sortableDirEntries) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s sortableDirEntries) Less(i, j int) bool {
	return strings.Compare(s[i].Name, s[j].Name) < 0
}

func inum(f nodefs.File) uint64 {
	var a fuse.Attr
	f.GetAttr(&a)
	return a.Ino
}
<commit_msg>fsck: don't misdetect a file hole at EOF<commit_after>package main

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"syscall"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"

	"github.com/rfjakob/gocryptfs/internal/exitcodes"
	"github.com/rfjakob/gocryptfs/internal/fusefrontend"
	"github.com/rfjakob/gocryptfs/internal/tlog"
)

type fsckObj struct {
	fs *fusefrontend.FS
	// List of corrupt files
	corruptList []string
	// List of skipped files
	skippedList []string
	// Protects corruptList
	listLock sync.Mutex
	// stop a running watchMitigatedCorruptions thread
	watchDone chan struct{}
	// Inode numbers of hard-linked files (Nlink > 1) that we have already checked
	seenInodes map[uint64]struct{}
}

func runsAsRoot() bool {
	return syscall.Geteuid() == 0
}

func (ck *fsckObj) markCorrupt(path string) {
	ck.listLock.Lock()
	ck.corruptList = append(ck.corruptList, path)
	ck.listLock.Unlock()
}

func (ck *fsckObj) markSkipped(path string) {
	ck.listLock.Lock()
	ck.skippedList = append(ck.skippedList, path)
	ck.listLock.Unlock()
}

// Watch for mitigated corruptions that occur during OpenDir()
func (ck *fsckObj) watchMitigatedCorruptionsOpenDir(path string) {
	for {
		select {
		case item := <-ck.fs.MitigatedCorruptions:
			fmt.Printf("fsck: corrupt entry in dir %q: %q\n", path, item)
			ck.markCorrupt(filepath.Join(path, item))
		case <-ck.watchDone:
			return
		}
	}
}

// Recursively check dir for corruption
func (ck *fsckObj) dir(path string) {
	tlog.Debug.Printf("ck.dir %q\n", path)
	ck.xattrs(path)
	// Run OpenDir and catch transparently mitigated corruptions
	go ck.watchMitigatedCorruptionsOpenDir(path)
	entries, status := ck.fs.OpenDir(path, nil)
	ck.watchDone <- struct{}{}
	// Also catch non-mitigated corruptions
	if !status.Ok() {
		fmt.Printf("fsck: error opening dir %q: %v\n", path, status)
		if status == fuse.EACCES && !runsAsRoot() {
			ck.markSkipped(path)
		} else {
			ck.markCorrupt(path)
		}
		return
	}
	// Sort alphabetically
	sort.Sort(sortableDirEntries(entries))
	for _, entry := range entries {
		if entry.Name == "." || entry.Name == ".." {
			continue
		}
		nextPath := filepath.Join(path, entry.Name)
		filetype := entry.Mode & syscall.S_IFMT
		//fmt.Printf("  %q %x\n", entry.Name, entry.Mode)
		switch filetype {
		case syscall.S_IFDIR:
			ck.dir(nextPath)
		case syscall.S_IFREG:
			ck.file(nextPath)
		case syscall.S_IFLNK:
			ck.symlink(nextPath)
		case syscall.S_IFIFO, syscall.S_IFSOCK, syscall.S_IFBLK, syscall.S_IFCHR:
			// nothing to check
		default:
			fmt.Printf("fsck: unhandled file type %x\n", filetype)
		}
	}
}

func (ck *fsckObj) symlink(path string) {
	_, status := ck.fs.Readlink(path, nil)
	if !status.Ok() {
		ck.markCorrupt(path)
		fmt.Printf("fsck: error reading symlink %q: %v\n", path, status)
	}
}

// Watch for mitigated corruptions that occur during Read()
func (ck *fsckObj) watchMitigatedCorruptionsRead(path string) {
	for {
		select {
		case item := <-ck.fs.MitigatedCorruptions:
			fmt.Printf("fsck: corrupt file %q (inode %s)\n", path, item)
			ck.markCorrupt(path)
		case <-ck.watchDone:
			return
		}
	}
}

// Check file for corruption
func (ck *fsckObj) file(path string) {
	tlog.Debug.Printf("ck.file %q\n", path)
	attr, status := ck.fs.GetAttr(path, nil)
	if !status.Ok() {
		ck.markCorrupt(path)
		fmt.Printf("fsck: error stating file %q: %v\n", path, status)
		return
	}
	if attr.Nlink > 1 {
		// Due to hard links, we may have already checked this file.
		if _, ok := ck.seenInodes[attr.Ino]; ok {
			tlog.Debug.Printf("ck.file : skipping %q (inode number %d already seen)\n", path, attr.Ino)
			return
		}
		ck.seenInodes[attr.Ino] = struct{}{}
	}
	ck.xattrs(path)
	f, status := ck.fs.Open(path, syscall.O_RDONLY, nil)
	if !status.Ok() {
		fmt.Printf("fsck: error opening file %q: %v\n", path, status)
		if status == fuse.EACCES && !runsAsRoot() {
			ck.markSkipped(path)
		} else {
			ck.markCorrupt(path)
		}
		return
	}
	defer f.Release()
	// 128 kiB of zeros
	allZero := make([]byte, fuse.MAX_KERNEL_WRITE)
	buf := make([]byte, fuse.MAX_KERNEL_WRITE)
	var off int64
	// Read() through the whole file and catch transparently mitigated corruptions
	go ck.watchMitigatedCorruptionsRead(path)
	defer func() { ck.watchDone <- struct{}{} }()
	for {
		tlog.Debug.Printf("ck.file: read %d bytes from offset %d\n", len(buf), off)
		result, status := f.Read(buf, off)
		if !status.Ok() {
			ck.markCorrupt(path)
			fmt.Printf("fsck: error reading file %q (inum %d): %v\n", path, inum(f), status)
			return
		}
		n := result.Size()
		// EOF
		if n == 0 {
			return
		}
		off += int64(n)
		// If we seem to be in the middle of a file hole, try to skip to the next
		// data section.
		data := buf[:n]
		if bytes.Equal(data, allZero) {
			tlog.Debug.Printf("ck.file: trying to skip file hole\n")
			f2 := f.(*fusefrontend.File)
			nextOff, err := f2.SeekData(off)
			if err == nil {
				off = nextOff
			}
		}
	}
}

// Watch for mitigated corruptions that occur during ListXAttr()
func (ck *fsckObj) watchMitigatedCorruptionsListXAttr(path string) {
	for {
		select {
		case item := <-ck.fs.MitigatedCorruptions:
			fmt.Printf("fsck: corrupt xattr name on file %q: %q\n", path, item)
			ck.markCorrupt(path + " xattr:" + item)
		case <-ck.watchDone:
			return
		}
	}
}

// Check xattrs on file/dir at path
func (ck *fsckObj) xattrs(path string) {
	// Run ListXAttr() and catch transparently mitigated corruptions
	go ck.watchMitigatedCorruptionsListXAttr(path)
	attrs, status := ck.fs.ListXAttr(path, nil)
	ck.watchDone <- struct{}{}
	// Also catch non-mitigated corruptions
	if !status.Ok() {
		fmt.Printf("fsck: error listing xattrs on %q: %v\n", path, status)
		ck.markCorrupt(path)
		return
	}
	for _, a := range attrs {
		_, status := ck.fs.GetXAttr(path, a, nil)
		if !status.Ok() {
			fmt.Printf("fsck: error reading xattr %q from %q: %v\n", a, path, status)
			if status == fuse.EACCES && !runsAsRoot() {
				ck.markSkipped(path)
			} else {
				ck.markCorrupt(path)
			}
		}
	}
}

func fsck(args *argContainer) {
	if args.reverse {
		tlog.Fatal.Printf("Running -fsck with -reverse is not supported")
		os.Exit(exitcodes.Usage)
	}
	args.allow_other = false
	pfs, wipeKeys := initFuseFrontend(args)
	fs := pfs.(*fusefrontend.FS)
	fs.MitigatedCorruptions = make(chan string)
	ck := fsckObj{
		fs:         fs,
		watchDone:  make(chan struct{}),
		seenInodes: make(map[uint64]struct{}),
	}
	ck.dir("")
	wipeKeys()
	if len(ck.corruptList) == 0 && len(ck.skippedList) == 0 {
		tlog.Info.Printf("fsck summary: no problems found\n")
		return
	}
	if len(ck.skippedList) > 0 {
		tlog.Warn.Printf("fsck: re-run this program as root to check all files!\n")
	}
	fmt.Printf("fsck summary: %d corrupt files, %d files skipped\n", len(ck.corruptList), len(ck.skippedList))
	os.Exit(exitcodes.FsckErrors)
}

type sortableDirEntries []fuse.DirEntry

func (s sortableDirEntries) Len() int {
	return len(s)
}

func (s sortableDirEntries) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s sortableDirEntries) Less(i, j int) bool {
	return strings.Compare(s[i].Name, s[j].Name) < 0
}

func inum(f nodefs.File) uint64 {
	var a fuse.Attr
	f.GetAttr(&a)
	return a.Ino
}
<|endoftext|>


<commit_before>package gamedayapi

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	s "strings"
	"time"
)

// Game is the top-level abstraction, the starting point for clients.
// A game is obtained using the GameFor function.
// From a game, clients can navigate to all data: Innings, Boxscore, etc.
type Game struct {
	AwayAPMP        string `xml:"away_ampm,attr"`
	AwayCode        string `xml:"away_code,attr"`
	AwayLoss        string `xml:"away_loss,attr"`
	AwayTeamCity    string `xml:"away_team_city,attr"`
	AwayTeamID      string `xml:"away_team_id,attr"`
	AwayTeamName    string `xml:"away_team_name,attr"`
	AwayTime        string `xml:"away_time,attr"`
	AwayTimezone    string `xml:"away_time_zone,attr"`
	AwayWin         string `xml:"away_win,attr"`
	CalendarEventID string `xml:"calendar_event_id,attr"`
	HomeAMPM        string `xml:"home_ampm,attr"`
	HomeCode        string `xml:"home_code,attr"`
	HomeLoss        string `xml:"home_loss,attr"`
	HomeTeamCity    string `xml:"home_team_city,attr"`
	HomeTeamID      string `xml:"home_team_id,attr"`
	HomeTeamName    string `xml:"home_team_name,attr"`
	HomeTime        string `xml:"home_time,attr"`
	HomeTimezone    string `xml:"home_time_zone,attr"`
	HomeWin         string `xml:"home_win,attr"`
	ID              string `xml:"id,attr"`
	Gameday         string `xml:"gameday,attr"`
	GamePk          string `xml:"game_pk,attr"`
	GameType        string `xml:"game_type,attr"`
	TimeDate        string `xml:"time_date,attr"`
	Timezone        string `xml:"time_zone,attr"`
	Venue           string `xml:"venue,attr"`

	// GameDataDirectory does not always point to where the files are.
	// Use FetchableDataDirectory for building requests to gameday servers.
	GameDataDirectory string `xml:"game_data_directory,attr"`

	allInnings AllInnings
	boxscore   Boxscore
	hitChart   HitChart
}

// GameFor will return a pointer to a game instance for the team code and date provided.
// This is the place to start for interacting with a game.
// Does not account for doubleheaders. Use GamesFor if doubleheader support is needed.
func GameFor(teamCode string, date time.Time) (*Game, error) {
	epg := EpgFor(date)
	game, err := epg.GameForTeam(teamCode)
	if err != nil {
		return &Game{}, err
	}
	return game, nil
}

// GamesFor will return a collection of pointers to games for the team code and date provided.
// Accounts for doubleheaders. In most cases, the collection will only have one game in it.
func GamesFor(teamCode string, date time.Time) ([]*Game, error) {
	epg := EpgFor(date)
	games, err := epg.GamesForTeam(teamCode)
	if err != nil {
		return games, err
	}
	return games, nil
}

// AllInnings fetches the inning/innings_all.xml file from gameday servers and fills in all the
// structs beneath, all the way down to the pitches.
func (game *Game) AllInnings() *AllInnings {
	if len(game.allInnings.AtBat) == 0 {
		game.load("/inning/inning_all.xml", &game.allInnings)
	}
	return &game.allInnings
}

// Boxscore fetches the boxscore.xml file from the gameday servers and fills in all the structs beneath.
func (game *Game) Boxscore() *Boxscore {
	if len(game.boxscore.GameID) == 0 {
		game.load("/boxscore.xml", &game.boxscore)
	}
	return &game.boxscore
}

// HitChart fetches the inning/inning_hit.xml file from gameday servers
func (game *Game) HitChart() *HitChart {
	if len(game.hitChart.Hips) == 0 {
		game.load("/inning/inning_hit.xml", &game.hitChart)
	}
	return &game.hitChart
}

//func (game *Game) InningScores() *InningScores {}

// EagerLoad will eagerly load all of the files that the library pulls from the MLB gameday servers.
// Otherwise, files are lazily loaded as clients interact with the API.
func (game *Game) EagerLoad() {
	game.AllInnings()
	game.Boxscore()
	game.HitChart()
}

func (game Game) load(fileName string, val interface{}) {
	filePath := game.FetchableDataDirectory() + fileName
	localFilePath := BaseCachePath() + filePath
	if _, err := os.Stat(localFilePath); os.IsNotExist(err) {
		log.Println("Cache miss on " + localFilePath)
		fetchAndCache(filePath, val)
	} else {
		log.Println("Cache hit on " + localFilePath)
		body, _ := ioutil.ReadFile(localFilePath)
		xml.Unmarshal(body, val)
	}
}

// FetchableDataDirectory builds a data directory to the game files using the game's ID.
// GameDataDirectory is not always reliable (see epg on 2013-04-11 vs 2013-04-10 as an example).
func (game Game) FetchableDataDirectory() string {
	idPieces := s.Split(game.ID, "/")
	var buffer bytes.Buffer
	buffer.WriteString(GamedayBasePath)
	buffer.WriteString(fmt.Sprintf("/year_%s/month_%s/day_%s/gid_", idPieces[0], idPieces[1], idPieces[2]))
	buffer.WriteString(game.Gameday)
	return buffer.String()
}

func fetchAndCache(filePath string, val interface{}) {
	urlToFetch := GamedayHostname + filePath
	log.Println("Fetching " + urlToFetch)
	resp, err := http.Get(urlToFetch)
	check(err)
	if resp.StatusCode != 200 {
		log.Fatal(resp.Status + " fetching " + urlToFetch)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	check(err)
	xml.Unmarshal(body, val)
	cacheFile(filePath, body)
}

func cacheFile(filePath string, body []byte) {
	localCachePath := BaseCachePath() + filePath[0:s.LastIndex(filePath, "/")]
	os.MkdirAll(localCachePath, (os.FileMode)(0775))
	f, err := os.Create(localCachePath + filePath[s.LastIndex(filePath, "/"):])
	f.Write(body)
	check(err)
	defer f.Close()
}
<commit_msg>Only pull files for games that are final.<commit_after>package gamedayapi

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	s "strings"
	"time"
)

// Game is the top-level abstraction, the starting point for clients.
// A game is obtained using the GameFor function.
// From a game, clients can navigate to all data: Innings, Boxscore, etc.
type Game struct {
	AwayAPMP        string `xml:"away_ampm,attr"`
	AwayCode        string `xml:"away_code,attr"`
	AwayLoss        string `xml:"away_loss,attr"`
	AwayTeamCity    string `xml:"away_team_city,attr"`
	AwayTeamID      string `xml:"away_team_id,attr"`
	AwayTeamName    string `xml:"away_team_name,attr"`
	AwayTime        string `xml:"away_time,attr"`
	AwayTimezone    string `xml:"away_time_zone,attr"`
	AwayWin         string `xml:"away_win,attr"`
	CalendarEventID string `xml:"calendar_event_id,attr"`
	HomeAMPM        string `xml:"home_ampm,attr"`
	HomeCode        string `xml:"home_code,attr"`
	HomeLoss        string `xml:"home_loss,attr"`
	HomeTeamCity    string `xml:"home_team_city,attr"`
	HomeTeamID      string `xml:"home_team_id,attr"`
	HomeTeamName    string `xml:"home_team_name,attr"`
	HomeTime        string `xml:"home_time,attr"`
	HomeTimezone    string `xml:"home_time_zone,attr"`
	HomeWin         string `xml:"home_win,attr"`
	ID              string `xml:"id,attr"`
	Gameday         string `xml:"gameday,attr"`
	GamePk          string `xml:"game_pk,attr"`
	GameType        string `xml:"game_type,attr"`
	Status          string `xml:"status,attr"`
	TimeDate        string `xml:"time_date,attr"`
	Timezone        string `xml:"time_zone,attr"`
	Venue           string `xml:"venue,attr"`

	// GameDataDirectory does not always point to where the files are.
	// Use FetchableDataDirectory for building requests to gameday servers.
	GameDataDirectory string `xml:"game_data_directory,attr"`

	allInnings AllInnings
	boxscore   Boxscore
	hitChart   HitChart
}

// GameFor will return a pointer to a game instance for the team code and date provided.
// This is the place to start for interacting with a game.
// Does not account for doubleheaders. Use GamesFor if doubleheader support is needed.
func GameFor(teamCode string, date time.Time) (*Game, error) {
	epg := EpgFor(date)
	game, err := epg.GameForTeam(teamCode)
	if err != nil {
		return &Game{}, err
	}
	return game, nil
}

// GamesFor will return a collection of pointers to games for the team code and date provided.
// Accounts for doubleheaders. In most cases, the collection will only have one game in it.
func GamesFor(teamCode string, date time.Time) ([]*Game, error) {
	epg := EpgFor(date)
	games, err := epg.GamesForTeam(teamCode)
	if err != nil {
		return games, err
	}
	return games, nil
}

// IsFinal returns true if the game status is Final.
func (game *Game) IsFinal() bool {
	return game.Status == "Final"
}

// AllInnings fetches the inning/innings_all.xml file from gameday servers and fills in all the
// structs beneath, all the way down to the pitches.
func (game *Game) AllInnings() *AllInnings {
	if game.IsFinal() && len(game.allInnings.AtBat) == 0 {
		game.load("/inning/inning_all.xml", &game.allInnings)
	}
	return &game.allInnings
}

// Boxscore fetches the boxscore.xml file from the gameday servers and fills in all the structs beneath.
func (game *Game) Boxscore() *Boxscore {
	if game.IsFinal() && len(game.boxscore.GameID) == 0 {
		game.load("/boxscore.xml", &game.boxscore)
	}
	return &game.boxscore
}

// HitChart fetches the inning/inning_hit.xml file from gameday servers
func (game *Game) HitChart() *HitChart {
	if game.IsFinal() && len(game.hitChart.Hips) == 0 {
		game.load("/inning/inning_hit.xml", &game.hitChart)
	}
	return &game.hitChart
}

//func (game *Game) InningScores() *InningScores {}

// EagerLoad will eagerly load all of the files that the library pulls from the MLB gameday servers.
// Otherwise, files are lazily loaded as clients interact with the API.
func (game *Game) EagerLoad() {
	game.AllInnings()
	game.Boxscore()
	game.HitChart()
}

func (game Game) load(fileName string, val interface{}) {
	filePath := game.FetchableDataDirectory() + fileName
	localFilePath := BaseCachePath() + filePath
	if _, err := os.Stat(localFilePath); os.IsNotExist(err) {
		log.Println("Cache miss on " + localFilePath)
		fetchAndCache(filePath, val)
	} else {
		log.Println("Cache hit on " + localFilePath)
		body, _ := ioutil.ReadFile(localFilePath)
		xml.Unmarshal(body, val)
	}
}

// FetchableDataDirectory builds a data directory to the game files using the game's ID.
// GameDataDirectory is not always reliable (see epg on 2013-04-11 vs 2013-04-10 as an example).
func (game Game) FetchableDataDirectory() string {
	idPieces := s.Split(game.ID, "/")
	var buffer bytes.Buffer
	buffer.WriteString(GamedayBasePath)
	buffer.WriteString(fmt.Sprintf("/year_%s/month_%s/day_%s/gid_", idPieces[0], idPieces[1], idPieces[2]))
	buffer.WriteString(game.Gameday)
	return buffer.String()
}

func fetchAndCache(filePath string, val interface{}) {
	urlToFetch := GamedayHostname + filePath
	log.Println("Fetching " + urlToFetch)
	resp, err := http.Get(urlToFetch)
	check(err)
	if resp.StatusCode != 200 {
		log.Fatal(resp.Status + " fetching " + urlToFetch)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	check(err)
	xml.Unmarshal(body, val)
	cacheFile(filePath, body)
}

func cacheFile(filePath string, body []byte) {
	localCachePath := BaseCachePath() + filePath[0:s.LastIndex(filePath, "/")]
	os.MkdirAll(localCachePath, (os.FileMode)(0775))
	f, err := os.Create(localCachePath + filePath[s.LastIndex(filePath, "/"):])
	f.Write(body)
	check(err)
	defer f.Close()
}
<|endoftext|>


<commit_before>package goav

/*
	Use of this source code is governed by a MIT license that can be found in the LICENSE file.
	by Giorgis (habtom@giorgis.io)

	Contains:

	libavcodec: 	encoding/decoding library
	libavfilter: 	graph-based frame editing library
	libavformat: 	I/O and muxing/demuxing library
	libavdevice: 	special devices muxing/demuxing library
	libavutil: 		common utility library
	libswresample: 	audio resampling, format conversion and mixing
	libpostproc: 	post processing library
	libswscale: 	color conversion and scaling library

*/
<commit_msg>godoc compatible docs<commit_after>//Use of this source code is governed by a MIT license that can be found in the LICENSE file.
//Giorgis (habtom@giorgis.io)

/*
	Golang binding for FFmpeg: A comprehensive binding to the ffmpeg video/audio manipulation library.

	https://www.ffmpeg.org/

	Contains:

	libavcodec: 	encoding/decoding library
	libavfilter: 	graph-based frame editing library
	libavformat: 	I/O and muxing/demuxing library
	libavdevice: 	special devices muxing/demuxing library
	libavutil: 		common utility library
	libswresample: 	audio resampling, format conversion and mixing
	libpostproc: 	post processing library
	libswscale: 	color conversion and scaling library

*/
package goav
<|endoftext|>


<commit_before>package main

//go:generate go-bindata -o modules/glue/generated_lua.go -pkg=glue -prefix "./lua" ./lua

import (
	"./modules/luaconf"
	"./modules/middleware"
	"./modules/scheduler"
	"./modules/static"
	"bufio"
	"github.com/DeedleFake/Go-PhysicsFS/physfs"
	"github.com/gin-gonic/contrib/gzip"
	"github.com/gin-gonic/gin"
	"github.com/namsral/flag"
	"github.com/pmylund/go-cache"
	"log"
	"net/http"
	"path/filepath"
	"runtime"
	"strconv"
	"time"
)

// General
var jobs *int

// Cache
var cfe *cache.Cache

func cacheRead(c *cache.Cache, file string) (string, error) {
	res := ""
	data_tmp, found := c.Get(file)
	if found == false {
		data, err := fileRead(file)
		if err != nil {
			return "", err
		}
		c.Set(file, data, cache.DefaultExpiration)
	} else {
		debug("Using cache for %s" + file)
		res = data_tmp.(string)
	}
	return res, nil
}

// File sytem functions
var filesystem http.FileSystem

func initPhysFS(path string) http.FileSystem {
	err := physfs.Init()
	if err != nil {
		panic(err)
	}
	err = physfs.Mount(path, "/", true)
	if err != nil {
		panic(err)
	}
	return physfs.FileSystem()
}

/*func fileExists(file string) bool {
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return false
	}
	return true
}*/
func fileRead(file string) (string, error) {
	f, err := filesystem.Open(file)
	defer f.Close()
	if err != nil {
		return "", err
	}
	fi, err := f.Stat()
	if err != nil {
		return "", err
	}
	r := bufio.NewReader(f)
	buf := make([]byte, fi.Size())
	_, err = r.Read(buf)
	if err != nil {
		return "", err
	}
	return string(buf), err
}
func fileExists(file string) bool {
	return physfs.Exists(file)
}
func cacheFileExists(file string) bool {
	data_tmp, found := cfe.Get(file)
	if found == false {
		exists := fileExists(file)
		cfe.Set(file, exists, cache.DefaultExpiration)
		return exists
	} else {
		return data_tmp.(bool)
	}
}

// Logging
var doLog bool = false

func debug(str string) {
	if doLog {
		log.Print(str)
	}
}

// Server
func new_server() *gin.Engine {
	return gin.New()
}
func bootstrap(srv *gin.Engine, dir string, cfe *cache.Cache) {
	switcher := middleware.ExtRoute(middleware.Plan{
		".lua": middleware.Lua(),
		"***":  staticServe.ServeCached("", staticServe.PhysFS("", true, true), cfe),
	})
	/*srv.GET(`/:file`, switcher)
	srv.POST(`/:file`, switcher)*/
	srv.Use(switcher)
	/*st := staticServe.ServeCached("", staticServe.PhysFS("", true, true), cfe)
	lr := luaroute(dir)
	srv.Use(lr)
	srv.Use(st)*/
}

func main() {
	cfe = cache.New(5*time.Minute, 30*time.Second) // File-Exists Cache

	// Use config
	flag.String("config", "", "Parse Config File")
	var script = flag.String("script", "", "Parse Lua Script as initialization")

	var host = flag.String("host", "", "IP of Host to bind the Webserver on")
	var port = flag.Int("port", 8080, "Port to run Webserver on (HTTP)")
	jobs = flag.Int("states", runtime.NumCPU(), "Number of Preinitialized Lua States")
	var workers = flag.Int("workers", runtime.NumCPU(), "Number of Worker threads.")
	var webroot = flag.String("root", ".", "Path to Web Root")

	// Middleware options
	useRecovery := flag.Bool("recovery", false, "Recover from Panics")
	useLogger := flag.Bool("logger", true, "Log Requests and Cache information")
	useGzip := flag.Bool("gzip", false, "Use GZIP")

	flag.Parse()

	runtime.GOMAXPROCS(*workers)

	root, _ := filepath.Abs(*webroot)
	filesystem = initPhysFS(root)
	defer physfs.Deinit()
	go scheduler.Run()        // Run the scheduler.
	go middleware.Preloader() // Run the Preloader.
	middleware.Init(*jobs)    // Run init sequence.

	if *script == "" {
		srv := new_server()
		if *useLogger {
			doLog = true
			srv.Use(gin.Logger())
		}
		if *useRecovery {
			srv.Use(gin.Recovery())
		}
		if *useGzip {
			srv.Use(gzip.Gzip(gzip.DefaultCompression))
		}
		bootstrap(srv, "", cfe)
		srv.Run(*host + ":" + strconv.Itoa(*port))
	} else {
		srv, err := luaconf.Configure(*script, cfe, *webroot)
		if err != nil {
			panic(err)
		}
		srv.Run(*host + ":" + strconv.Itoa(*port))
	}
}
<commit_msg>Add https support<commit_after>package main

//go:generate go-bindata -o modules/glue/generated_lua.go -pkg=glue -prefix "./lua" ./lua

import (
	"./modules/luaconf"
	"./modules/middleware"
	"./modules/scheduler"
	"./modules/static"
	"bufio"
	"github.com/DeedleFake/Go-PhysicsFS/physfs"
	"github.com/gin-gonic/contrib/gzip"
	"github.com/gin-gonic/gin"
	"github.com/namsral/flag"
	"github.com/pmylund/go-cache"
	"log"
	"net/http"
	"path/filepath"
	"runtime"
	"strconv"
	"time"
)

// General
var jobs *int

// Cache
var cfe *cache.Cache

func cacheRead(c *cache.Cache, file string) (string, error) {
	res := ""
	data_tmp, found := c.Get(file)
	if found == false {
		data, err := fileRead(file)
		if err != nil {
			return "", err
		}
		c.Set(file, data, cache.DefaultExpiration)
	} else {
		debug("Using cache for %s" + file)
		res = data_tmp.(string)
	}
	return res, nil
}

// File sytem functions
var filesystem http.FileSystem

func initPhysFS(path string) http.FileSystem {
	err := physfs.Init()
	if err != nil {
		panic(err)
	}
	err = physfs.Mount(path, "/", true)
	if err != nil {
		panic(err)
	}
	return physfs.FileSystem()
}

/*func fileExists(file string) bool {
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return false
	}
	return true
}*/
func fileRead(file string) (string, error) {
	f, err := filesystem.Open(file)
	defer f.Close()
	if err != nil {
		return "", err
	}
	fi, err := f.Stat()
	if err != nil {
		return "", err
	}
	r := bufio.NewReader(f)
	buf := make([]byte, fi.Size())
	_, err = r.Read(buf)
	if err != nil {
		return "", err
	}
	return string(buf), err
}
func fileExists(file string) bool {
	return physfs.Exists(file)
}
func cacheFileExists(file string) bool {
	data_tmp, found := cfe.Get(file)
	if found == false {
		exists := fileExists(file)
		cfe.Set(file, exists, cache.DefaultExpiration)
		return exists
	} else {
		return data_tmp.(bool)
	}
}

// Logging
var doLog bool = false

func debug(str string) {
	if doLog {
		log.Print(str)
	}
}

// Server
func new_server() *gin.Engine {
	return gin.New()
}
func bootstrap(srv *gin.Engine, dir string, cfe *cache.Cache) {
	switcher := middleware.ExtRoute(middleware.Plan{
		".lua": middleware.Lua(),
		"***":  staticServe.ServeCached("", staticServe.PhysFS("", true, true), cfe),
	})
	/*srv.GET(`/:file`, switcher)
	srv.POST(`/:file`, switcher)*/
	srv.Use(switcher)
	/*st := staticServe.ServeCached("", staticServe.PhysFS("", true, true), cfe)
	lr := luaroute(dir)
	srv.Use(lr)
	srv.Use(st)*/
}

func serve(srv *gin.Engine, en_http bool, en_https bool, bind string, binds string, cert string, key string) {
	end := make(chan bool)
	if en_http {
		go serveHTTP(srv, bind)
	}
	if en_https {
		go serveHTTPS(srv, binds, cert, key)
	}
	<-end
}
func serveHTTP(srv *gin.Engine, bind string) {
	err := http.ListenAndServe(bind, srv)
	if err != nil {
		panic(err)
	}
}
func serveHTTPS(srv *gin.Engine, bind string, cert string, key string) {
	err := http.ListenAndServeTLS(bind, cert, key, srv)
	if err != nil {
		panic(err)
	}
}

func main() {
	cfe = cache.New(5*time.Minute, 30*time.Second) // File-Exists Cache

	// Use config
	flag.String("config", "", "Parse Config File")
	var script = flag.String("script", "", "Parse Lua Script as initialization")

	var host = flag.String("host", "", "IP of Host to bind the Webserver on")
	var port = flag.Int("port", 8080, "Port to run Webserver on (HTTP)")
	var ports = flag.Int("ports", 8443, "Port to run Webserver on (HTTPS)")
	var cert = flag.String("cert", "", "Certificate File for HTTPS")
	var key = flag.String("key", "", "Key File for HTTPS")
	var en_http = flag.Bool("http", true, "Listen HTTP")
	var en_https = flag.Bool("https", false, "Listen HTTPS")

	jobs = flag.Int("states", runtime.NumCPU(), "Number of Preinitialized Lua States")
	var workers = flag.Int("workers", runtime.NumCPU(), "Number of Worker threads.")
	var webroot = flag.String("root", ".", "Path to Web Root")

	// Middleware options
	useRecovery := flag.Bool("recovery", false, "Recover from Panics")
	useLogger := flag.Bool("logger", true, "Log Requests and Cache information")
	useGzip := flag.Bool("gzip", false, "Use GZIP")

	flag.Parse()

	if *en_https {
		if *key == "" || *cert == "" {
			panic("Need to have a Key and a Cert defined.")
		}
	}

	runtime.GOMAXPROCS(*workers)

	root, _ := filepath.Abs(*webroot)
	filesystem = initPhysFS(root)
	defer physfs.Deinit()
	go scheduler.Run()        // Run the scheduler.
	go middleware.Preloader() // Run the Preloader.
	middleware.Init(*jobs)    // Run init sequence.
	if *script == "" {
		srv := new_server()
		if *useLogger {
			doLog = true
			srv.Use(gin.Logger())
		}
		if *useRecovery {
			srv.Use(gin.Recovery())
		}
		if *useGzip {
			srv.Use(gzip.Gzip(gzip.DefaultCompression))
		}
		bootstrap(srv, "", cfe)
		serve(srv, *en_http, *en_https, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
	} else {
		srv, err := luaconf.Configure(*script, cfe, *webroot)
		if err != nil {
			panic(err)
		}
		serve(srv, *en_http, *en_https, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
	}
}
<|endoftext|>