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

package drive

import (
	"crypto/md5"
	"fmt"
	"io"
	"os"
	"time"

	"github.com/odeke-em/drive/config"
	drive "github.com/odeke-em/google-api-go-client/drive/v2"
)

const (
	OpNone = iota
	OpAdd
	OpDelete
	OpMod
	OpModConflict
)

const (
	DifferNone = 1 << iota
	DifferDirType
	DifferMd5Checksum
	DifferModTime
	DifferSize
)

const (
	DriveFolderMimeType = "application/vnd.google-apps.folder"
)

// Arbitrary value. TODO: Get better definition of BigFileSize.
var BigFileSize = int64(1024 * 1024 * 400)

var opPrecedence = map[int]int{
	OpNone:        0,
	OpDelete:      1,
	OpAdd:         2,
	OpMod:         3,
	OpModConflict: 4,
}

type File struct {
	BlobAt      string
	ExportLinks map[string]string
	Id          string
	IsDir       bool
	Md5Checksum string
	MimeType    string
	ModTime     time.Time
	Name        string
	Size        int64
	Etag        string
	Shared      bool
	// UserPermission contains the permissions for the authenticated user on this file
	UserPermission *drive.Permission
	// CacheChecksum when set avoids recomputation of checksums
	CacheChecksum bool
	// Monotonically increasing version number for the file
	Version int64
	// The onwers of this file.
	OwnerNames []string
	// Permissions contains the overall permissions for this file
	Permissions []*drive.Permission
}

func NewRemoteFile(f *drive.File) *File {
	mtime, _ := time.Parse("2006-01-02T15:04:05.000Z", f.ModifiedDate)
	mtime = mtime.Round(time.Second)
	return &File{
		BlobAt:      f.DownloadUrl,
		Etag:        f.Etag,
		ExportLinks: f.ExportLinks,
		Id:          f.Id,
		IsDir:       f.MimeType == DriveFolderMimeType,
		Md5Checksum: f.Md5Checksum,
		MimeType:    f.MimeType,
		ModTime:     mtime,
		// We must convert each title to match that on the FS.
		Name:           urlToPath(f.Title, true),
		Size:           f.FileSize,
		Shared:         f.Shared,
		UserPermission: f.UserPermission,
		Version:        f.Version,
		OwnerNames:     f.OwnerNames,
		Permissions:    f.Permissions,
	}
}

func NewLocalFile(absPath string, f os.FileInfo) *File {
	return &File{
		Id:      "",
		Name:    f.Name(),
		ModTime: f.ModTime().Round(time.Second),
		IsDir:   f.IsDir(),
		Size:    f.Size(),
		BlobAt:  absPath,
		// TODO: Read the CacheChecksum toggle dynamically if set
		// by the requester ie if the file is rapidly changing.
		CacheChecksum: true,
	}
}

func fauxLocalFile(relToRootPath string) *File {
	return &File{
		Id:    "",
		Name:  relToRootPath,
		IsDir: false,
		Size:  0,
	}
}

type Change struct {
	Dest           *File
	Parent         string
	Path           string
	Src            *File
	Force          bool
	NoClobber      bool
	IgnoreConflict bool
	IgnoreChecksum bool
}

type ByPrecedence []*Change

func (cl ByPrecedence) Less(i, j int) bool {
	if cl[i] == nil {
		return false
	}
	if cl[j] == nil {
		return true
	}

	rank1, rank2 := opPrecedence[cl[i].Op()], opPrecedence[cl[j].Op()]
	return rank1 < rank2
}

func (cl ByPrecedence) Len() int {
	return len(cl)
}

func (cl ByPrecedence) Swap(i, j int) {
	cl[i], cl[j] = cl[j], cl[i]
}

func (self *File) sameDirType(other *File) bool {
	return other != nil && self.IsDir == other.IsDir
}

func opToString(op int) (string, string) {
	switch op {
	case OpAdd:
		return "\033[32m+\033[0m", "Addition"
	case OpDelete:
		return "\033[31m-\033[0m", "Deletion"
	case OpMod:
		return "\033[33mM\033[0m", "Modification"
	case OpModConflict:
		return "\033[35mX\033[0m", "Clashing modification"
	default:
		return "", ""
	}
}

func (f *File) largeFile() bool {
	return f.Size > BigFileSize
}

func (c *Change) Symbol() string {
	symbol, _ := opToString(c.Op())
	return symbol
}

func md5Checksum(f *File) string {
	if f == nil || f.IsDir {
		return ""
	}
	if f.Md5Checksum != "" {
		return f.Md5Checksum
	}

	if f.largeFile() { // Just warn the user in case of impatience.
		// TODO: Only turn on warnings if verbosity is set.
		fmt.Printf("\033[91mmd5Checksum\033[00m: `%s` (%v)\nmight take time to checksum.\n",
			f.Name, prettyBytes(f.Size))
	}
	fh, err := os.Open(f.BlobAt)

	if err != nil {
		return ""
	}
	defer fh.Close()

	h := md5.New()
	_, err = io.Copy(h, fh)
	if err != nil {
		return ""
	}
	checksum := fmt.Sprintf("%x", h.Sum(nil))
	if f.CacheChecksum {
		// fmt.Println("CACHING CHECKSUM", checksum, f.Name)
		f.Md5Checksum = checksum
	}
	return checksum
}

func checksumDiffers(mask int) bool {
	return (mask & DifferMd5Checksum) != 0
}

func dirTypeDiffers(mask int) bool {
	return (mask & DifferDirType) != 0
}

func modTimeDiffers(mask int) bool {
	return (mask & DifferModTime) != 0
}

func sizeDiffers(mask int) bool {
	return (mask & DifferSize) != 0
}

func fileDifferences(src, dest *File, ignoreChecksum bool) int {
	if src == nil || dest == nil {
		return DifferMd5Checksum | DifferSize | DifferModTime | DifferDirType
	}

	difference := DifferNone
	if src.Size != dest.Size {
		difference |= DifferSize
	}
	if !src.ModTime.Equal(dest.ModTime) {
		difference |= DifferModTime
	}
	if src.IsDir != dest.IsDir {
		difference |= DifferDirType
	}

	if !ignoreChecksum {
		// Only compute the checksum if the size differs
		if sizeDiffers(difference) || md5Checksum(src) != md5Checksum(dest) {
			difference |= DifferMd5Checksum
		}
	}
	return difference
}

func sameFileTillChecksum(src, dest *File, ignoreChecksum bool) bool {
	return fileDifferences(src, dest, ignoreChecksum) == DifferNone
}

func (c *Change) op() int {
	if c.Src == nil && c.Dest == nil {
		return OpNone
	}
	if c.Src != nil && c.Dest == nil {
		return OpAdd
	}
	if c.Src == nil && c.Dest != nil {
		return OpDelete
	}
	if c.Src.IsDir != c.Dest.IsDir {
		return OpMod
	}
	if c.Src.IsDir {
		return OpNone
	}

	mask := fileDifferences(c.Src, c.Dest, c.IgnoreChecksum)

	if sizeDiffers(mask) || checksumDiffers(mask) {
		if c.IgnoreConflict {
			return OpMod
		}
		return OpModConflict
	}
	if modTimeDiffers(mask) {
		return OpMod
	}

	return OpNone
}

func (c *Change) Op() int {
	op := c.op()
	if c.Force {
		if op == OpModConflict {
			return OpMod
		}
		return OpAdd
	}
	if op != OpAdd && c.NoClobber {
		return OpNone
	}
	return op
}

func (f *File) ToIndex() *config.Index {
	return &config.Index{
		FileId:      f.Id,
		Etag:        f.Etag,
		Md5Checksum: f.Md5Checksum,
		MimeType:    f.MimeType,
		ModTime:     f.ModTime.Unix(),
		Version:     f.Version,
	}
}
<commit_msg>faux file: use time.Now() as modTime<commit_after>// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package drive

import (
	"crypto/md5"
	"fmt"
	"io"
	"os"
	"time"

	"github.com/odeke-em/drive/config"
	drive "github.com/odeke-em/google-api-go-client/drive/v2"
)

const (
	OpNone = iota
	OpAdd
	OpDelete
	OpMod
	OpModConflict
)

const (
	DifferNone = 1 << iota
	DifferDirType
	DifferMd5Checksum
	DifferModTime
	DifferSize
)

const (
	DriveFolderMimeType = "application/vnd.google-apps.folder"
)

// Arbitrary value. TODO: Get better definition of BigFileSize.
var BigFileSize = int64(1024 * 1024 * 400)

var opPrecedence = map[int]int{
	OpNone:        0,
	OpDelete:      1,
	OpAdd:         2,
	OpMod:         3,
	OpModConflict: 4,
}

type File struct {
	BlobAt      string
	ExportLinks map[string]string
	Id          string
	IsDir       bool
	Md5Checksum string
	MimeType    string
	ModTime     time.Time
	Name        string
	Size        int64
	Etag        string
	Shared      bool
	// UserPermission contains the permissions for the authenticated user on this file
	UserPermission *drive.Permission
	// CacheChecksum when set avoids recomputation of checksums
	CacheChecksum bool
	// Monotonically increasing version number for the file
	Version int64
	// The onwers of this file.
	OwnerNames []string
	// Permissions contains the overall permissions for this file
	Permissions []*drive.Permission
}

func NewRemoteFile(f *drive.File) *File {
	mtime, _ := time.Parse("2006-01-02T15:04:05.000Z", f.ModifiedDate)
	mtime = mtime.Round(time.Second)
	return &File{
		BlobAt:      f.DownloadUrl,
		Etag:        f.Etag,
		ExportLinks: f.ExportLinks,
		Id:          f.Id,
		IsDir:       f.MimeType == DriveFolderMimeType,
		Md5Checksum: f.Md5Checksum,
		MimeType:    f.MimeType,
		ModTime:     mtime,
		// We must convert each title to match that on the FS.
		Name:           urlToPath(f.Title, true),
		Size:           f.FileSize,
		Shared:         f.Shared,
		UserPermission: f.UserPermission,
		Version:        f.Version,
		OwnerNames:     f.OwnerNames,
		Permissions:    f.Permissions,
	}
}

func NewLocalFile(absPath string, f os.FileInfo) *File {
	return &File{
		Id:      "",
		Name:    f.Name(),
		ModTime: f.ModTime().Round(time.Second),
		IsDir:   f.IsDir(),
		Size:    f.Size(),
		BlobAt:  absPath,
		// TODO: Read the CacheChecksum toggle dynamically if set
		// by the requester ie if the file is rapidly changing.
		CacheChecksum: true,
	}
}

func fauxLocalFile(relToRootPath string) *File {
	return &File{
		Id:      "",
		IsDir:   false,
		ModTime: time.Now(),
		Name:    relToRootPath,
		Size:    0,
	}
}

type Change struct {
	Dest           *File
	Parent         string
	Path           string
	Src            *File
	Force          bool
	NoClobber      bool
	IgnoreConflict bool
	IgnoreChecksum bool
}

type ByPrecedence []*Change

func (cl ByPrecedence) Less(i, j int) bool {
	if cl[i] == nil {
		return false
	}
	if cl[j] == nil {
		return true
	}

	rank1, rank2 := opPrecedence[cl[i].Op()], opPrecedence[cl[j].Op()]
	return rank1 < rank2
}

func (cl ByPrecedence) Len() int {
	return len(cl)
}

func (cl ByPrecedence) Swap(i, j int) {
	cl[i], cl[j] = cl[j], cl[i]
}

func (self *File) sameDirType(other *File) bool {
	return other != nil && self.IsDir == other.IsDir
}

func opToString(op int) (string, string) {
	switch op {
	case OpAdd:
		return "\033[32m+\033[0m", "Addition"
	case OpDelete:
		return "\033[31m-\033[0m", "Deletion"
	case OpMod:
		return "\033[33mM\033[0m", "Modification"
	case OpModConflict:
		return "\033[35mX\033[0m", "Clashing modification"
	default:
		return "", ""
	}
}

func (f *File) largeFile() bool {
	return f.Size > BigFileSize
}

func (c *Change) Symbol() string {
	symbol, _ := opToString(c.Op())
	return symbol
}

func md5Checksum(f *File) string {
	if f == nil || f.IsDir {
		return ""
	}
	if f.Md5Checksum != "" {
		return f.Md5Checksum
	}

	if f.largeFile() { // Just warn the user in case of impatience.
		// TODO: Only turn on warnings if verbosity is set.
		fmt.Printf("\033[91mmd5Checksum\033[00m: `%s` (%v)\nmight take time to checksum.\n",
			f.Name, prettyBytes(f.Size))
	}
	fh, err := os.Open(f.BlobAt)

	if err != nil {
		return ""
	}
	defer fh.Close()

	h := md5.New()
	_, err = io.Copy(h, fh)
	if err != nil {
		return ""
	}
	checksum := fmt.Sprintf("%x", h.Sum(nil))
	if f.CacheChecksum {
		// fmt.Println("CACHING CHECKSUM", checksum, f.Name)
		f.Md5Checksum = checksum
	}
	return checksum
}

func checksumDiffers(mask int) bool {
	return (mask & DifferMd5Checksum) != 0
}

func dirTypeDiffers(mask int) bool {
	return (mask & DifferDirType) != 0
}

func modTimeDiffers(mask int) bool {
	return (mask & DifferModTime) != 0
}

func sizeDiffers(mask int) bool {
	return (mask & DifferSize) != 0
}

func fileDifferences(src, dest *File, ignoreChecksum bool) int {
	if src == nil || dest == nil {
		return DifferMd5Checksum | DifferSize | DifferModTime | DifferDirType
	}

	difference := DifferNone
	if src.Size != dest.Size {
		difference |= DifferSize
	}
	if !src.ModTime.Equal(dest.ModTime) {
		difference |= DifferModTime
	}
	if src.IsDir != dest.IsDir {
		difference |= DifferDirType
	}

	if !ignoreChecksum {
		// Only compute the checksum if the size differs
		if sizeDiffers(difference) || md5Checksum(src) != md5Checksum(dest) {
			difference |= DifferMd5Checksum
		}
	}
	return difference
}

func sameFileTillChecksum(src, dest *File, ignoreChecksum bool) bool {
	return fileDifferences(src, dest, ignoreChecksum) == DifferNone
}

func (c *Change) op() int {
	if c.Src == nil && c.Dest == nil {
		return OpNone
	}
	if c.Src != nil && c.Dest == nil {
		return OpAdd
	}
	if c.Src == nil && c.Dest != nil {
		return OpDelete
	}
	if c.Src.IsDir != c.Dest.IsDir {
		return OpMod
	}
	if c.Src.IsDir {
		return OpNone
	}

	mask := fileDifferences(c.Src, c.Dest, c.IgnoreChecksum)

	if sizeDiffers(mask) || checksumDiffers(mask) {
		if c.IgnoreConflict {
			return OpMod
		}
		return OpModConflict
	}
	if modTimeDiffers(mask) {
		return OpMod
	}

	return OpNone
}

func (c *Change) Op() int {
	op := c.op()
	if c.Force {
		if op == OpModConflict {
			return OpMod
		}
		return OpAdd
	}
	if op != OpAdd && c.NoClobber {
		return OpNone
	}
	return op
}

func (f *File) ToIndex() *config.Index {
	return &config.Index{
		FileId:      f.Id,
		Etag:        f.Etag,
		Md5Checksum: f.Md5Checksum,
		MimeType:    f.MimeType,
		ModTime:     f.ModTime.Unix(),
		Version:     f.Version,
	}
}
<|endoftext|>


<commit_before>package main
/*
import (
	"contract"
	"router"
)

const (
	CONTRACT_FILE = "./contract.json"
)

func main() {
	c := contract.NewContract(CONTRACT_FILE)
	r := router.NewRouter(c)
	r.RegistAndRun()
}
*/
// Prepare:
// 1. Router: read and regist URL from contract file
// 2. Get Data: read from contract file and validate the json data
// 3. Post Data: read from contract file and validate the json data
<commit_msg>rm old IntermediateDuck<commit_after><|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/datastream/nsq/nsq"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"
)

var (
	conf_file   = flag.String("conf", "config.json", "config file")
	nsq_address = flag.String("nsq_address", "127.0.0.1:4150", "nsq")
)

func main() {
	flag.Parse()
	w := nsq.NewWriter()
	err := w.ConnectToNSQ(*nsq_address)
	if err != nil {
		log.Fatal("fail to connect nsq", err)
	}
	setting, err := ReadConfig(*conf_file)
	if err != nil {
		log.Fatal("fail to read config", err)
	}
	exitchan := make(chan int)
	offset := read_stat(setting)
	for k, v := range setting {
		go read_log(v, offset[k], k, w, exitchan)
	}
	termchan := make(chan os.Signal, 1)
	signal.Notify(termchan, syscall.SIGINT, syscall.SIGTERM)
	<-termchan
	close(exitchan)
}

func read_stat(setting map[string]string) map[string]int64 {
	stat := make(map[string]int64)
	for k, _ := range setting {
		stat_file, err := os.Open(k)
		if err != nil {
			stat[k] = 0
			continue
		}
		s, err := ioutil.ReadAll(stat_file)
		if err != nil {
			stat[k] = 0
			continue
		}
		i, err := strconv.ParseInt(string(s), 10, 64)
		stat[k] = i
	}
	return stat
}

func sync_stat(stat string, value int64) {
	fd, err := os.Create(stat)
	if err != nil {
		log.Println("fail to create ", stat, err)
	}
	defer fd.Close()
	fd.WriteString(fmt.Sprintf("%d", value))
}

func ReadConfig(file string) (map[string]string, error) {
	var setting map[string]string
	config_file, err := os.Open(file)
	config, err := ioutil.ReadAll(config_file)
	if err != nil {
		return nil, err
	}
	defer config_file.Close()
	if err := json.Unmarshal(config, &setting); err != nil {
		return nil, err
	}
	return setting, nil
}

func read_log(file string, offset int64, topic string, w *nsq.Writer, exitchan chan int) {
	fd, err := os.Open(file)
	if err != nil {
		log.Println(err)
		return
	}
	defer fd.Close()
	reader := bufio.NewReader(fd)
	tick := time.Tick(time.Second * 10)
	for {
		line, err := reader.ReadString('\n')
		if err == io.EOF {
			size0, er := fd.Seek(0, 1)
			if er != nil {
				log.Println(er)
				return
			}
			fd, err = os.Open(file)
			if err != nil {
				log.Println(err)
				return
			}
			_, err = fd.Seek(size0, 0)
			if err != nil {
				fd.Seek(0, 0)
			}
			reader = bufio.NewReader(fd)
			continue
		}
		if err != nil {
			log.Println(err)
			return
		}
		cmd := nsq.Publish(topic, []byte(line))
		_, _, err = w.Write(cmd)
		if err != nil {
			log.Println("NSQ writer", err)
			w.ConnectToNSQ(*nsq_address)
		}
		select {
		case <-tick:
			size, _ := fd.Seek(0, 1)
			sync_stat(topic, size)
		case <-exitchan:
			size, _ := fd.Seek(0, 1)
			sync_stat(topic, size)
			return
		}
	}
}
<commit_msg>add debuginfo<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/datastream/nsq/nsq"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"
)

var (
	conf_file   = flag.String("conf", "config.json", "config file")
	nsq_address = flag.String("nsq_address", "127.0.0.1:4150", "nsq")
)

func main() {
	flag.Parse()
	w := nsq.NewWriter()
	err := w.ConnectToNSQ(*nsq_address)
	if err != nil {
		log.Fatal("fail to connect nsq", err)
	}
	setting, err := ReadConfig(*conf_file)
	if err != nil {
		log.Fatal("fail to read config", err)
	}
	exitchan := make(chan int)
	offset := read_stat(setting)
	for k, v := range setting {
		go read_log(v, offset[k], k, w, exitchan)
	}
	termchan := make(chan os.Signal, 1)
	signal.Notify(termchan, syscall.SIGINT, syscall.SIGTERM)
	<-termchan
	close(exitchan)
}

func read_stat(setting map[string]string) map[string]int64 {
	stat := make(map[string]int64)
	for k, _ := range setting {
		stat_file, err := os.Open(k)
		if err != nil {
			stat[k] = 0
			continue
		}
		s, err := ioutil.ReadAll(stat_file)
		if err != nil {
			stat[k] = 0
			continue
		}
		i, err := strconv.ParseInt(string(s), 10, 64)
		stat[k] = i
	}
	return stat
}

func sync_stat(stat string, value int64) {
	fd, err := os.Create(stat)
	if err != nil {
		log.Println("fail to create ", stat, err)
	}
	defer fd.Close()
	fd.WriteString(fmt.Sprintf("%d", value))
}

func ReadConfig(file string) (map[string]string, error) {
	var setting map[string]string
	config_file, err := os.Open(file)
	config, err := ioutil.ReadAll(config_file)
	if err != nil {
		return nil, err
	}
	defer config_file.Close()
	if err := json.Unmarshal(config, &setting); err != nil {
		return nil, err
	}
	return setting, nil
}

func read_log(file string, offset int64, topic string, w *nsq.Writer, exitchan chan int) {
	fd, err := os.Open(file)
	if err != nil {
		log.Println(err)
		return
	}
	defer fd.Close()
	reader := bufio.NewReader(fd)
	tick := time.Tick(time.Second * 10)
	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			log.Println("retry")
			line, err = reader.ReadString('\n')
		}
		if err == io.EOF {
			log.Println("READ EOF")
			size0, err := fd.Seek(0, 1)
			if err != nil {
				log.Println(er)
				return
			}
			fd, err = os.Open(file)
			if err != nil {
				log.Println("open failed", err)
				return
			}
			_, err = fd.Seek(size0, 0)
			if err != nil {
				log.Println("reopen", file)
				fd.Seek(0, 0)
			}
			reader = bufio.NewReader(fd)
			continue
		}
		if err != nil {
			log.Println(err)
			return
		}
		cmd := nsq.Publish(topic, []byte(line))
		_, _, err = w.Write(cmd)
		if err != nil {
			log.Println("NSQ writer", err)
			w.ConnectToNSQ(*nsq_address)
		}
		select {
		case <-tick:
			size, _ := fd.Seek(0, 1)
			sync_stat(topic, size)
		case <-exitchan:
			size, _ := fd.Seek(0, 1)
			sync_stat(topic, size)
			return
		default:
		}
	}
}
<|endoftext|>


<commit_before>/*
Acmepipe executes a command
and replaces the current Acme window's body with the output.
It writes the window's body to the command's stdin
and writes messages from its stderr to the window's errors file.

Usage:

	acmepipe [<option>...] <command> [<argument>...]

The options are:

	-all
		replace the body all at once
	-mark
		mark each changed region as a separate undo step

Acmepipe draws inspiration from
github.com/9fans/acme/acmego,
github.com/eaburns/Fmt, and
github.com/rog-go/cmd/apipe.
*/
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"syscall"

	"9fans.net/go/acme"
)

var (
	this = os.Args[0]
	win  *acme.Win
	all  = flag.Bool("all", false, "replace the body all at once")
	mark = flag.Bool("mark", false, "mark each changed region as a separate undo step")
)

type thing struct {
	oldStart, oldEnd int
	op               string
	newStart, newEnd int
}

func usage() {
	usage := `usage: %s [<option>...] <command> [<argument>...]
Execute <command>, replace acme/$winid/body with its output,
and write diagnostic messages to acme/$winid/errors.
`
	fmt.Fprintf(os.Stderr, usage, this)
	flag.PrintDefaults()
	os.Exit(2)
}

func main() {
	log.SetFlags(0)
	log.SetPrefix(fmt.Sprintf("%s: ", this))
	flag.Usage = usage
	flag.Parse()
	if err := cli(flag.Args()); err != nil {
		switch e := err.(type) {
		case *exec.ExitError: // command failed
			status := e.Sys().(syscall.WaitStatus).ExitStatus()
			os.Exit(status)
		default:
			log.Fatalln(err)
		}
	}
}

func cli(args []string) error {
	if err := open(); err != nil {
		return err
	}
	defer win.CloseFiles()
	body, err := win.ReadAll("body")
	if err != nil {
		return err
	}
	stdout, stderr, exit := run(args)
	if err := writeErrors(stderr); err != nil {
		return err
	}
	if exit != nil {
		return exit
	}
	if *all {
		return writeOutput(body, stdout)
	}
	return patchOutput(body, stdout)
}

func open() error {
	winid := os.Getenv("winid")
	id, err := strconv.Atoi(winid)
	if err != nil {
		return err
	}
	win, err = acme.Open(id, nil)
	return err
}

func run(args []string) ([]byte, []byte, error) {
	if len(args) == 0 {
		stdin, err := ioutil.ReadAll(os.Stdin)
		return stdin, nil, err
	}
	var stdout, stderr bytes.Buffer
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	return stdout.Bytes(), stderr.Bytes(), err
}

func writeErrors(buf []byte) error {
	if len(buf) == 0 {
		return nil // no errors
	}
	_, err := win.Write("errors", buf)
	return err
}

func writeOutput(old, new []byte) error {
	if len(new) == 0 {
		return nil // no output
	}
	if bytes.Equal(new, old) {
		return nil // no change
	}
	_, _, err := win.ReadAddr() // make sure the address file is open
	if err != nil {
		return err
	}
	if err := win.Ctl("addr=dot"); err != nil {
		return err
	}
	q0, q1, err := win.ReadAddr()
	if err != nil {
		return err
	}
	if err := win.Addr(","); err != nil {
		return err
	}
	if _, err := win.Write("data", new); err != nil {
		return err
	}
	if err := win.Addr("#%d,#%d", q0, q1); err != nil {
		return err
	}
	return win.Ctl("dot=addr\nshow")
}

func patchOutput(old, new []byte) error {
	if len(new) == 0 {
		return nil // no output
	}
	newFile, err := tempfile(new)
	if err != nil {
		return err
	}
	oldFile, err := tempfile(old)
	if err != nil {
		return err
	}
	var stdout, stderr bytes.Buffer
	cmd := exec.Command("9", "diff", oldFile.Name(), newFile.Name())
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil && err.(*exec.ExitError) == nil { // ignore exit status
		return err
	}
	if err := writeErrors(stderr.Bytes()); err != nil {
		return err
	}
	return apply(stdout.Bytes(), new)
}

func tempfile(buf []byte) (*os.File, error) {
	file, err := ioutil.TempFile("", this)
	if err != nil {
		return nil, err
	}
	_, err = file.Write(buf)
	return file, err
}

func apply(diff, buf []byte) error {
	if len(diff) == 0 {
		return nil // no diff
	}
	if !*mark {
		if err := win.Ctl("mark\nnomark"); err != nil {
			return err
		}
	}
	lines := strings.Split(string(diff), "\n")
	for i := len(lines) - 1; i >= 0; i-- {
		line := lines[i]
		if len(line) == 0 {
			continue
		}
		if line[0] == '<' || line[0] == '-' || line[0] == '>' {
			continue
		}
		j := strings.IndexAny(line, "acd")
		if j < 0 {
			log.Println("cannot parse diff: %q", line)
			continue
		}
		n1, n2 := span(line[:j])
		n3, n4 := span(line[j+1:])
		if n1 == 0 || n3 == 0 {
			log.Println("cannot parse diff: %q", line)
			continue
		}
		switch line[j] {
		case 'a': // add
			if err := win.Addr("%d+#0", n1); err != nil {
				return err
			}
			if _, err := win.Write("data", region(buf, n3, n4)); err != nil {
				return err
			}
		case 'c': // change
			if err := win.Addr("%d,%d", n1, n2); err != nil {
				return err
			}
			if _, err := win.Write("data", region(buf, n3, n4)); err != nil {
				return err
			}
		case 'd': // delete
			if err := win.Addr("%d,%d", n1, n2); err != nil {
				return err
			}
			if _, err := win.Write("data", nil); err != nil {
				return err
			}
		}
	}
	return nil
}

func applyRegion(line string, buf []byte) error {
	if len(line) == 0 {
		return nil // no change
	}
	if line[0] == '<' || line[0] == '-' || line[0] == '>' {
		return nil // details
	}
	i := strings.IndexAny(line, "acd")
	if i < 0 {
		log.Println("cannot parse diff: %q", line)
		return nil
	}
	n1, n2 := span(line[:i])
	n3, n4 := span(line[i+1:])
	if n1 == 0 || n3 == 0 {
		log.Println("cannot parse diff: %q", line)
		return nil
	}
	switch line[i] {
	case 'a': // add
		if err := write(region(buf, n3, n4), "%d+#0", n1); err != nil {
			return err
		}
	case 'c': // change
		if err := write(region(buf, n3, n4), "%d,%d", n1, n2); err != nil {
			return err
		}
	case 'd': // delete
		if err := write(nil, "%d,%d", n1, n2); err != nil {
			return err
		}
	}
	return nil
}

func span(text string) (int, int) {
	i := strings.IndexByte(text, ',')
	if i < 0 {
		n, err := strconv.Atoi(text)
		if err != nil {
			return 0, 0
		}
		return n, n
	}
	x, err1 := strconv.Atoi(text[:i])
	y, err2 := strconv.Atoi(text[i+1:])
	if err1 != nil || err2 != nil {
		return 0, 0
	}
	return x, y
}

func region(text []byte, start, end int) []byte {
	start-- // line numbers are 1-based
	i := 0
	for ; i < len(text) && start > 0; i++ {
		if text[i] == '\n' {
			start--
			end--
		}
	}
	startByte := i
	for ; i < len(text) && end > 0; i++ {
		if text[i] == '\n' {
			end--
		}
	}
	endByte := i
	return text[startByte:endByte]
}

func write(buf []byte, format string, args ...interface{}) error {
	if err := win.Addr(format, args...); err != nil {
		return err
	}
	_, err := win.Write("data", buf)
	return err
}
<commit_msg>acmepipe: Use extracted function applyRegion<commit_after>/*
Acmepipe executes a command
and replaces the current Acme window's body with the output.
It writes the window's body to the command's stdin
and writes messages from its stderr to the window's errors file.

Usage:

	acmepipe [<option>...] <command> [<argument>...]

The options are:

	-all
		replace the body all at once
	-mark
		mark each changed region as a separate undo step

Acmepipe draws inspiration from
github.com/9fans/acme/acmego,
github.com/eaburns/Fmt, and
github.com/rog-go/cmd/apipe.
*/
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"syscall"

	"9fans.net/go/acme"
)

var (
	this = os.Args[0]
	win  *acme.Win
	all  = flag.Bool("all", false, "replace the body all at once")
	mark = flag.Bool("mark", false, "mark each changed region as a separate undo step")
)

type thing struct {
	oldStart, oldEnd int
	op               string
	newStart, newEnd int
}

func usage() {
	usage := `usage: %s [<option>...] <command> [<argument>...]
Execute <command>, replace acme/$winid/body with its output,
and write diagnostic messages to acme/$winid/errors.
`
	fmt.Fprintf(os.Stderr, usage, this)
	flag.PrintDefaults()
	os.Exit(2)
}

func main() {
	log.SetFlags(0)
	log.SetPrefix(fmt.Sprintf("%s: ", this))
	flag.Usage = usage
	flag.Parse()
	if err := cli(flag.Args()); err != nil {
		switch e := err.(type) {
		case *exec.ExitError: // command failed
			status := e.Sys().(syscall.WaitStatus).ExitStatus()
			os.Exit(status)
		default:
			log.Fatalln(err)
		}
	}
}

func cli(args []string) error {
	if err := open(); err != nil {
		return err
	}
	defer win.CloseFiles()
	body, err := win.ReadAll("body")
	if err != nil {
		return err
	}
	stdout, stderr, exit := run(args)
	if err := writeErrors(stderr); err != nil {
		return err
	}
	if exit != nil {
		return exit
	}
	if *all {
		return writeOutput(body, stdout)
	}
	return patchOutput(body, stdout)
}

func open() error {
	winid := os.Getenv("winid")
	id, err := strconv.Atoi(winid)
	if err != nil {
		return err
	}
	win, err = acme.Open(id, nil)
	return err
}

func run(args []string) ([]byte, []byte, error) {
	if len(args) == 0 {
		stdin, err := ioutil.ReadAll(os.Stdin)
		return stdin, nil, err
	}
	var stdout, stderr bytes.Buffer
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	return stdout.Bytes(), stderr.Bytes(), err
}

func writeErrors(buf []byte) error {
	if len(buf) == 0 {
		return nil // no errors
	}
	_, err := win.Write("errors", buf)
	return err
}

func writeOutput(old, new []byte) error {
	if len(new) == 0 {
		return nil // no output
	}
	if bytes.Equal(new, old) {
		return nil // no change
	}
	_, _, err := win.ReadAddr() // make sure the address file is open
	if err != nil {
		return err
	}
	if err := win.Ctl("addr=dot"); err != nil {
		return err
	}
	q0, q1, err := win.ReadAddr()
	if err != nil {
		return err
	}
	if err := win.Addr(","); err != nil {
		return err
	}
	if _, err := win.Write("data", new); err != nil {
		return err
	}
	if err := win.Addr("#%d,#%d", q0, q1); err != nil {
		return err
	}
	return win.Ctl("dot=addr\nshow")
}

func patchOutput(old, new []byte) error {
	if len(new) == 0 {
		return nil // no output
	}
	newFile, err := tempfile(new)
	if err != nil {
		return err
	}
	oldFile, err := tempfile(old)
	if err != nil {
		return err
	}
	var stdout, stderr bytes.Buffer
	cmd := exec.Command("9", "diff", oldFile.Name(), newFile.Name())
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil && err.(*exec.ExitError) == nil { // ignore exit status
		return err
	}
	if err := writeErrors(stderr.Bytes()); err != nil {
		return err
	}
	return apply(stdout.Bytes(), new)
}

func tempfile(buf []byte) (*os.File, error) {
	file, err := ioutil.TempFile("", this)
	if err != nil {
		return nil, err
	}
	_, err = file.Write(buf)
	return file, err
}

func apply(diff, buf []byte) error {
	if len(diff) == 0 {
		return nil // no diff
	}
	if !*mark {
		if err := win.Ctl("mark\nnomark"); err != nil {
			return err
		}
	}
	lines := strings.Split(string(diff), "\n")
	for i := len(lines) - 1; i >= 0; i-- {
		line := lines[i]
		if err := applyRegion(line, buf); err != nil {
			return err
		}
	}
	return nil
}

func applyRegion(line string, buf []byte) error {
	if len(line) == 0 {
		return nil // no change
	}
	if line[0] == '<' || line[0] == '-' || line[0] == '>' {
		return nil // details
	}
	i := strings.IndexAny(line, "acd")
	if i < 0 {
		log.Println("cannot parse diff: %q", line)
		return nil
	}
	n1, n2 := span(line[:i])
	n3, n4 := span(line[i+1:])
	if n1 == 0 || n3 == 0 {
		log.Println("cannot parse diff: %q", line)
		return nil
	}
	switch line[i] {
	case 'a': // add
		if err := write(region(buf, n3, n4), "%d+#0", n1); err != nil {
			return err
		}
	case 'c': // change
		if err := write(region(buf, n3, n4), "%d,%d", n1, n2); err != nil {
			return err
		}
	case 'd': // delete
		if err := write(nil, "%d,%d", n1, n2); err != nil {
			return err
		}
	}
	return nil
}

func span(text string) (int, int) {
	i := strings.IndexByte(text, ',')
	if i < 0 {
		n, err := strconv.Atoi(text)
		if err != nil {
			return 0, 0
		}
		return n, n
	}
	x, err1 := strconv.Atoi(text[:i])
	y, err2 := strconv.Atoi(text[i+1:])
	if err1 != nil || err2 != nil {
		return 0, 0
	}
	return x, y
}

func region(text []byte, start, end int) []byte {
	start-- // line numbers are 1-based
	i := 0
	for ; i < len(text) && start > 0; i++ {
		if text[i] == '\n' {
			start--
			end--
		}
	}
	startByte := i
	for ; i < len(text) && end > 0; i++ {
		if text[i] == '\n' {
			end--
		}
	}
	endByte := i
	return text[startByte:endByte]
}

func write(buf []byte, format string, args ...interface{}) error {
	if err := win.Addr(format, args...); err != nil {
		return err
	}
	_, err := win.Write("data", buf)
	return err
}
<|endoftext|>


<commit_before>/*
Apt Action

Install packages and their dependencies to the target rootfs with 'apt'.

Yaml syntax:
 - action: apt
   recommends: bool
   unauthenticated: bool
   packages:
     - package1
     - package2

Mandatory properties:

- packages -- list of packages to install

Optional properties:

- recommends -- boolean indicating if suggested packages will be installed
- unauthenticated -- boolean indicating if unauthenticated packages can be installed
*/
package actions

import (
	"github.com/go-debos/debos"
)

type AptAction struct {
	debos.BaseAction `yaml:",inline"`
	Recommends       bool
	Unauthenticated  bool
	Packages         []string
}

func (apt *AptAction) Run(context *debos.DebosContext) error {
	apt.LogStart()
	aptOptions := []string{"apt-get", "-y"}

	if !apt.Recommends {
		aptOptions = append(aptOptions, "--no-install-recommends")
	}

	if apt.Unauthenticated {
		aptOptions = append(aptOptions, "--allow-unauthenticated")
	}

	aptOptions = append(aptOptions, "install")
	aptOptions = append(aptOptions, apt.Packages...)

	c := debos.NewChrootCommandForContext(*context)
	c.AddEnv("DEBIAN_FRONTEND=noninteractive")

	err := c.Run("apt", "apt-get", "update")
	if err != nil {
		return err
	}
	err = c.Run("apt", aptOptions...)
	if err != nil {
		return err
	}
	err = c.Run("apt", "apt-get", "clean")
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>actions: Add missing newline in apt documentation<commit_after>/*
Apt Action

Install packages and their dependencies to the target rootfs with 'apt'.

Yaml syntax:
 - action: apt
   recommends: bool
   unauthenticated: bool
   packages:
     - package1
     - package2

Mandatory properties:

- packages -- list of packages to install

Optional properties:

- recommends -- boolean indicating if suggested packages will be installed

- unauthenticated -- boolean indicating if unauthenticated packages can be installed
*/
package actions

import (
	"github.com/go-debos/debos"
)

type AptAction struct {
	debos.BaseAction `yaml:",inline"`
	Recommends       bool
	Unauthenticated  bool
	Packages         []string
}

func (apt *AptAction) Run(context *debos.DebosContext) error {
	apt.LogStart()
	aptOptions := []string{"apt-get", "-y"}

	if !apt.Recommends {
		aptOptions = append(aptOptions, "--no-install-recommends")
	}

	if apt.Unauthenticated {
		aptOptions = append(aptOptions, "--allow-unauthenticated")
	}

	aptOptions = append(aptOptions, "install")
	aptOptions = append(aptOptions, apt.Packages...)

	c := debos.NewChrootCommandForContext(*context)
	c.AddEnv("DEBIAN_FRONTEND=noninteractive")

	err := c.Run("apt", "apt-get", "update")
	if err != nil {
		return err
	}
	err = c.Run("apt", aptOptions...)
	if err != nil {
		return err
	}
	err = c.Run("apt", "apt-get", "clean")
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
    "github.com/Jeffail/gabs"
)

func GetConfig(path string) *gabs.Container {
    json, err := gabs.ParseJSONFile(path)

    if err != nil {
        panic(err)
    }

    return json
}

<commit_msg>Add cleverbot<commit_after>package main

import (
    "github.com/Jeffail/gabs"
    "github.com/ugjka/cleverbot-go"
)

var (
    cleverbotSession *cleverbot.Session
)

func GetConfig(path string) *gabs.Container {
    json, err := gabs.ParseJSONFile(path)

    if err != nil {
        panic(err)
    }

    return json
}

func SendToCleverbot(message string) string {
    if cleverbotSession == nil {
        cleverbotSession = cleverbot.New()
    }

    response, err := cleverbotSession.Ask(message)
    if err != nil {
        return "Error :frowning:\n```\n" + err.Error() + "\n```"
    }

    return response
}

<|endoftext|>


<commit_before>package docker

import (
	"bufio"
	"context"
	"encoding/json"
	"github.com/ViBiOh/dashboard/auth"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/gorilla/websocket"
	"log"
	"net/http"
	"regexp"
)

const ignoredByteLogSize = 8
const tailSize = `100`

var logWebsocketRequest = regexp.MustCompile(`containers/([^/]+)/logs`)
var statsWebsocketRequest = regexp.MustCompile(`containers/([^/]+)/stats`)
var eventsWebsocketRequest = regexp.MustCompile(`events`)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return hostCheck.MatchString(r.Host)
	},
}

func upgradeAndAuth(w http.ResponseWriter, r *http.Request) (*websocket.Conn, *auth.User, error) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		defer ws.Close()
		return nil, nil, err
	}

	_, basicAuth, err := ws.ReadMessage()
	if err != nil {
		defer ws.Close()
		return nil, nil, err
	}

	user, err := auth.IsAuthenticatedByAuth(string(basicAuth))
	if err != nil {
		ws.WriteMessage(websocket.TextMessage, []byte(err.Error()))
		defer ws.Close()
		return nil, nil, err
	}

	return ws, user, nil
}

func logsContainerWebsocketHandler(w http.ResponseWriter, r *http.Request, containerID []byte) {
	ws, _, err := upgradeAndAuth(w, r)
	if err != nil {
		log.Print(err)
		return
	}
	defer ws.Close()

	context := context.Background()
	defer context.Done()

	logs, err := docker.ContainerLogs(context, string(containerID), types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: tailSize})
	if err != nil {
		log.Print(err)
		return
	}
	defer logs.Close()

	done := make(chan struct{})

	go func() {
		scanner := bufio.NewScanner(logs)
		for scanner.Scan() {
			select {
			case <-done:
				return

			default:
				logLine := scanner.Bytes()
				if len(logLine) > ignoredByteLogSize {
					if err = ws.WriteMessage(websocket.TextMessage, logLine[ignoredByteLogSize:]); err != nil {
						log.Printf(`Error while writing to logs socket: %v`, err)
						close(done)
						return
					}
				}
			}
		}
	}()

	for {
		select {
		case <-done:
			return

		default:
			if _, _, err := ws.NextReader(); err != nil {
				log.Printf(`Error while reading from logs socket: %v`, err)
				close(done)
				return
			}
		}
	}
}

func eventsWebsocketHandler(w http.ResponseWriter, r *http.Request) {
	ws, user, err := upgradeAndAuth(w, r)
	if err != nil {
		return
	}
	defer ws.Close()

	filtersArgs := filters.NewArgs()
	if labelFilters(&filtersArgs, user, nil) != nil {
		log.Printf(`Error while defining label filters: %v`, err)
		return
	}
	if eventFilters(&filtersArgs) != nil {
		log.Printf(`Error while defining event filters: %v`, err)
		return
	}

	context := context.Background()
	defer context.Done()
	messages, errors := docker.Events(context, types.EventsOptions{Filters: filtersArgs})

	done := make(chan struct{})

	go func() {
		for {
			select {
			case <-done:
				return

			case message := <-messages:
				messageJSON, err := json.Marshal(message)
				if err != nil {
					log.Printf(`Error while marshalling event: %v`, err)
					close(done)
					return
				}

				if err = ws.WriteMessage(websocket.TextMessage, messageJSON); err != nil {
					log.Printf(`Error while writing to events socket: %v`, err)
					close(done)
					return
				}
				break

			case err := <-errors:
				log.Printf(`Error while reading events: %v`, err)
				close(done)
				return
			}
		}
	}()

	for {
		select {
		case <-done:
			return
		default:
			if _, _, err := ws.NextReader(); err != nil {
				log.Printf(`Error while reading from events socket: %v`, err)
				close(done)
				return
			}
		}
	}
}

func statsWebsocketHandler(w http.ResponseWriter, r *http.Request, containerID []byte) {
	ws, _, err := upgradeAndAuth(w, r)
	if err != nil {
		log.Print(err)
		return
	}
	defer ws.Close()

	context := context.Background()
	defer context.Done()

	stats, err := docker.ContainerStats(context, string(containerID), true)
	if err != nil {
		log.Print(err)
		return
	}
	defer stats.Body.Close()

	done := make(chan struct{})

	go func() {
		scanner := bufio.NewScanner(stats.Body)
		for scanner.Scan() {
			select {
			case <-done:
				return

			default:
				if err = ws.WriteMessage(websocket.TextMessage, scanner.Bytes()); err != nil {
					log.Printf(`Error while writing to stats socket: %v`, err)
					close(done)
					return
				}
			}
		}
	}()

	for {
		select {
		case <-done:
			return

		default:
			if _, _, err := ws.NextReader(); err != nil {
				log.Printf(`Error while reading from stats socket: %v`, err)
				close(done)
				return
			}
		}
	}
}

// WebsocketHandler for Docker Websocket request. Should be use with net/http
type WebsocketHandler struct {
}

func (handler WebsocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	urlPath := []byte(r.URL.Path)

	if logWebsocketRequest.Match(urlPath) {
		logsContainerWebsocketHandler(w, r, logWebsocketRequest.FindSubmatch(urlPath)[1])
	} else if eventsWebsocketRequest.Match((urlPath)) {
		eventsWebsocketHandler(w, r)
	} else if statsWebsocketRequest.Match(urlPath) {
		statsWebsocketHandler(w, r, statsWebsocketRequest.FindSubmatch(urlPath)[1])
	}
}
<commit_msg>Update websocket.go<commit_after>package docker

import (
	"bufio"
	"context"
	"encoding/json"
	"github.com/ViBiOh/dashboard/auth"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/gorilla/websocket"
	"log"
	"net/http"
	"regexp"
)

const ignoredByteLogSize = 8
const tailSize = `100`

var logWebsocketRequest = regexp.MustCompile(`containers/([^/]+)/logs`)
var statsWebsocketRequest = regexp.MustCompile(`containers/([^/]+)/stats`)
var eventsWebsocketRequest = regexp.MustCompile(`events`)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return hostCheck.MatchString(r.Host)
	},
}

func upgradeAndAuth(w http.ResponseWriter, r *http.Request) (*websocket.Conn, *auth.User, error) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		defer ws.Close()
		return nil, nil, err
	}

	_, basicAuth, err := ws.ReadMessage()
	if err != nil {
		defer ws.Close()
		return nil, nil, err
	}

	user, err := auth.IsAuthenticatedByAuth(string(basicAuth))
	if err != nil {
		ws.WriteMessage(websocket.TextMessage, []byte(err.Error()))

		defer ws.Close()
		return nil, nil, err
	}

	return ws, user, nil
}

func logsContainerWebsocketHandler(w http.ResponseWriter, r *http.Request, containerID []byte) {
	ws, _, err := upgradeAndAuth(w, r)
	if err != nil {
		log.Print(err)
		return
	}
	defer ws.Close()

	context := context.Background()
	defer context.Done()

	logs, err := docker.ContainerLogs(context, string(containerID), types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: tailSize})
	if err != nil {
		log.Print(err)
		return
	}
	defer logs.Close()

	done := make(chan struct{})

	go func() {
		scanner := bufio.NewScanner(logs)
		for scanner.Scan() {
			select {
			case <-done:
				return

			default:
				logLine := scanner.Bytes()
				if len(logLine) > ignoredByteLogSize {
					if err = ws.WriteMessage(websocket.TextMessage, logLine[ignoredByteLogSize:]); err != nil {
						log.Printf(`Error while writing to logs socket: %v`, err)
						close(done)
						return
					}
				}
			}
		}
	}()

	for {
		select {
		case <-done:
			return

		default:
			if messageType, content, err := ws.NextReader(); err != nil {
				log.Printf(`Error while reading from logs socket: %v | %v | %v`, err, messageType, content)
				close(done)
				return
			}
		}
	}
}

func eventsWebsocketHandler(w http.ResponseWriter, r *http.Request) {
	ws, user, err := upgradeAndAuth(w, r)
	if err != nil {
		return
	}
	defer ws.Close()

	filtersArgs := filters.NewArgs()
	if labelFilters(&filtersArgs, user, nil) != nil {
		log.Printf(`Error while defining label filters: %v`, err)
		return
	}
	if eventFilters(&filtersArgs) != nil {
		log.Printf(`Error while defining event filters: %v`, err)
		return
	}

	context := context.Background()
	defer context.Done()
	messages, errors := docker.Events(context, types.EventsOptions{Filters: filtersArgs})

	done := make(chan struct{})

	go func() {
		for {
			select {
			case <-done:
				return

			case message := <-messages:
				messageJSON, err := json.Marshal(message)
				if err != nil {
					log.Printf(`Error while marshalling event: %v`, err)
					close(done)
					return
				}

				if err = ws.WriteMessage(websocket.TextMessage, messageJSON); err != nil {
					log.Printf(`Error while writing to events socket: %v`, err)
					close(done)
					return
				}
				break

			case err := <-errors:
				log.Printf(`Error while reading events: %v`, err)
				close(done)
				return
			}
		}
	}()

	for {
		select {
		case <-done:
			return
		default:
			if _, _, err := ws.NextReader(); err != nil {
				log.Printf(`Error while reading from events socket: %v`, err)
				close(done)
				return
			}
		}
	}
}

func statsWebsocketHandler(w http.ResponseWriter, r *http.Request, containerID []byte) {
	ws, _, err := upgradeAndAuth(w, r)
	if err != nil {
		log.Print(err)
		return
	}
	defer ws.Close()

	context := context.Background()
	defer context.Done()

	stats, err := docker.ContainerStats(context, string(containerID), true)
	if err != nil {
		log.Print(err)
		return
	}
	defer stats.Body.Close()

	done := make(chan struct{})

	go func() {
		scanner := bufio.NewScanner(stats.Body)
		for scanner.Scan() {
			select {
			case <-done:
				return

			default:
				if err = ws.WriteMessage(websocket.TextMessage, scanner.Bytes()); err != nil {
					log.Printf(`Error while writing to stats socket: %v`, err)
					close(done)
					return
				}
			}
		}
	}()

	for {
		select {
		case <-done:
			return

		default:
			if _, _, err := ws.NextReader(); err != nil {
				log.Printf(`Error while reading from stats socket: %v`, err)
				close(done)
				return
			}
		}
	}
}

// WebsocketHandler for Docker Websocket request. Should be use with net/http
type WebsocketHandler struct {
}

func (handler WebsocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	urlPath := []byte(r.URL.Path)

	if logWebsocketRequest.Match(urlPath) {
		logsContainerWebsocketHandler(w, r, logWebsocketRequest.FindSubmatch(urlPath)[1])
	} else if eventsWebsocketRequest.Match((urlPath)) {
		eventsWebsocketHandler(w, r)
	} else if statsWebsocketRequest.Match(urlPath) {
		statsWebsocketHandler(w, r, statsWebsocketRequest.FindSubmatch(urlPath)[1])
	}
}
<|endoftext|>


<commit_before>package agent

import (
	"encoding/json"
	"strings"
	"sync"

	"github.com/coreos/go-etcd/etcd"
)

const KeyPrefix = "/discover"

type EtcdBackend struct {
	Client *etcd.Client
}

func servicePath(name, addr string) string {
	if addr == "" {
		return KeyPrefix + "/services/" + name
	}
	return KeyPrefix + "/services/" + name + "/" + addr
}

func (b *EtcdBackend) Subscribe(name string) (UpdateStream, error) {
	stream := &etcdStream{ch: make(chan *ServiceUpdate), stop: make(chan bool)}
	watch := b.getStateChanges(name, stream.stop)
	response, _ := b.getCurrentState(name)
	go func() {
		if response != nil {
			for _, n := range response.Node.Nodes {
				if update := b.responseToUpdate(response, &n); update != nil {
					stream.ch <- update
				}
			}
		}
		stream.ch <- &ServiceUpdate{}
		for resp := range watch {
			if update := b.responseToUpdate(resp, resp.Node); update != nil {
				stream.ch <- update
			}
		}
	}()
	return stream, nil
}

type etcdStream struct {
	ch       chan *ServiceUpdate
	stop     chan bool
	stopOnce sync.Once
}

func (s *etcdStream) Chan() chan *ServiceUpdate { return s.ch }

func (s *etcdStream) Close() { s.stopOnce.Do(func() { close(s.stop) }) }

func (b *EtcdBackend) responseToUpdate(resp *etcd.Response, node *etcd.Node) *ServiceUpdate {
	// expected key structure: /PREFIX/services/NAME/ADDR
	splitKey := strings.SplitN(node.Key, "/", 5)
	if len(splitKey) < 5 {
		return nil
	}
	serviceName := splitKey[3]
	serviceAddr := splitKey[4]
	if "get" == resp.Action || "set" == resp.Action && (resp.PrevNode == nil || node.Value != resp.PrevNode.Value) {
		// GET is because getCurrentState returns responses of Action GET.
		// some SETs are heartbeats, so we ignore SETs where value didn't change.
		var serviceAttrs map[string]string
		err := json.Unmarshal([]byte(node.Value), &serviceAttrs)
		if err != nil {
			return nil
		}
		return &ServiceUpdate{
			Name:    serviceName,
			Addr:    serviceAddr,
			Online:  true,
			Attrs:   serviceAttrs,
			Created: uint(node.CreatedIndex),
		}
	} else if "delete" == resp.Action || "expire" == resp.Action {
		return &ServiceUpdate{
			Name: serviceName,
			Addr: serviceAddr,
		}
	} else {
		return nil
	}
}

func (b *EtcdBackend) getCurrentState(name string) (*etcd.Response, error) {
	return b.Client.Get(servicePath(name, ""), false, true)
}

func (b *EtcdBackend) getStateChanges(name string, stop chan bool) chan *etcd.Response {
	watch := make(chan *etcd.Response)
	go b.Client.Watch(servicePath(name, ""), 0, true, watch, stop)
	return watch
}

func (b *EtcdBackend) Register(name, addr string, attrs map[string]string) error {
	attrsJSON, err := json.Marshal(attrs)
	if err != nil {
		return err
	}
	attrsString := string(attrsJSON)
	path := servicePath(name, addr)
	ttl := uint64(HeartbeatIntervalSecs + MissedHearbeatTTL)

	_, err = b.Client.Update(path, attrsString, ttl)
	if e, ok := err.(*etcd.EtcdError); ok && e.ErrorCode == 100 {
		// This is a workaround for etcd issue #407: https://github.com/coreos/etcd/issues/407
		// If we just do a Set and don't try to Update first, createdIndex will get incremented
		// on each heartbeat, breaking leader election.
		_, err = b.Client.Set(path, attrsString, ttl)
	}
	return err
}

func (b *EtcdBackend) Unregister(name, addr string) error {
	_, err := b.Client.Delete(servicePath(name, addr), false)
	return err
}
<commit_msg>Handle key update events<commit_after>package agent

import (
	"encoding/json"
	"strings"
	"sync"

	"github.com/coreos/go-etcd/etcd"
)

const KeyPrefix = "/discover"

type EtcdBackend struct {
	Client *etcd.Client
}

func servicePath(name, addr string) string {
	if addr == "" {
		return KeyPrefix + "/services/" + name
	}
	return KeyPrefix + "/services/" + name + "/" + addr
}

func (b *EtcdBackend) Subscribe(name string) (UpdateStream, error) {
	stream := &etcdStream{ch: make(chan *ServiceUpdate), stop: make(chan bool)}
	watch := b.getStateChanges(name, stream.stop)
	response, _ := b.getCurrentState(name)
	go func() {
		if response != nil {
			for _, n := range response.Node.Nodes {
				if update := b.responseToUpdate(response, &n); update != nil {
					stream.ch <- update
				}
			}
		}
		stream.ch <- &ServiceUpdate{}
		for resp := range watch {
			if update := b.responseToUpdate(resp, resp.Node); update != nil {
				stream.ch <- update
			}
		}
	}()
	return stream, nil
}

type etcdStream struct {
	ch       chan *ServiceUpdate
	stop     chan bool
	stopOnce sync.Once
}

func (s *etcdStream) Chan() chan *ServiceUpdate { return s.ch }

func (s *etcdStream) Close() { s.stopOnce.Do(func() { close(s.stop) }) }

func (b *EtcdBackend) responseToUpdate(resp *etcd.Response, node *etcd.Node) *ServiceUpdate {
	// expected key structure: /PREFIX/services/NAME/ADDR
	splitKey := strings.SplitN(node.Key, "/", 5)
	if len(splitKey) < 5 {
		return nil
	}
	serviceName := splitKey[3]
	serviceAddr := splitKey[4]
	if "get" == resp.Action || ("set" == resp.Action || "update" == resp.Action) && (resp.PrevNode == nil || node.Value != resp.PrevNode.Value) {
		// GET is because getCurrentState returns responses of Action GET.
		// some SETs are heartbeats, so we ignore SETs where value didn't change.
		var serviceAttrs map[string]string
		err := json.Unmarshal([]byte(node.Value), &serviceAttrs)
		if err != nil {
			return nil
		}
		return &ServiceUpdate{
			Name:    serviceName,
			Addr:    serviceAddr,
			Online:  true,
			Attrs:   serviceAttrs,
			Created: uint(node.CreatedIndex),
		}
	} else if "delete" == resp.Action || "expire" == resp.Action {
		return &ServiceUpdate{
			Name: serviceName,
			Addr: serviceAddr,
		}
	} else {
		return nil
	}
}

func (b *EtcdBackend) getCurrentState(name string) (*etcd.Response, error) {
	return b.Client.Get(servicePath(name, ""), false, true)
}

func (b *EtcdBackend) getStateChanges(name string, stop chan bool) chan *etcd.Response {
	watch := make(chan *etcd.Response)
	go b.Client.Watch(servicePath(name, ""), 0, true, watch, stop)
	return watch
}

func (b *EtcdBackend) Register(name, addr string, attrs map[string]string) error {
	attrsJSON, err := json.Marshal(attrs)
	if err != nil {
		return err
	}
	attrsString := string(attrsJSON)
	path := servicePath(name, addr)
	ttl := uint64(HeartbeatIntervalSecs + MissedHearbeatTTL)

	_, err = b.Client.Update(path, attrsString, ttl)
	if e, ok := err.(*etcd.EtcdError); ok && e.ErrorCode == 100 {
		// This is a workaround for etcd issue #407: https://github.com/coreos/etcd/issues/407
		// If we just do a Set and don't try to Update first, createdIndex will get incremented
		// on each heartbeat, breaking leader election.
		_, err = b.Client.Set(path, attrsString, ttl)
	}
	return err
}

func (b *EtcdBackend) Unregister(name, addr string) error {
	_, err := b.Client.Delete(servicePath(name, addr), false)
	return err
}
<|endoftext|>


<commit_before>package config

import "github.com/akaspin/soil/agent/resource"

// Agent configuration
type AgentConfig struct {
	Meta   map[string]string
	System struct {
		PodExec string `json:"pod_exec" hcl:"pod_exec"`
	}
	Resource []resource.Config
}
<commit_msg>- agent/config<commit_after><|endoftext|>


<commit_before>// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package mysql

import (
	"database/sql/driver"
	"fmt"
	"reflect"
	"strconv"
)

type mysqlStmt struct {
	mc         *mysqlConn
	id         uint32
	paramCount int
	columns    []mysqlField // cached from the first query
}

func (stmt *mysqlStmt) Close() error {
	if stmt.mc == nil || stmt.mc.netConn == nil {
		errLog.Print(ErrInvalidConn)
		return driver.ErrBadConn
	}

	err := stmt.mc.writeCommandPacketUint32(comStmtClose, stmt.id)
	stmt.mc = nil
	return err
}

func (stmt *mysqlStmt) NumInput() int {
	return stmt.paramCount
}

func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
	return converter{}
}

func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
	if stmt.mc.netConn == nil {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	// Send command
	err := stmt.writeExecutePacket(args)
	if err != nil {
		return nil, err
	}

	mc := stmt.mc

	mc.affectedRows = 0
	mc.insertId = 0

	// Read Result
	resLen, err := mc.readResultSetHeaderPacket()
	if err == nil {
		if resLen > 0 {
			// Columns
			err = mc.readUntilEOF()
			if err != nil {
				return nil, err
			}

			// Rows
			err = mc.readUntilEOF()
		}
		if err == nil {
			return &mysqlResult{
				affectedRows: int64(mc.affectedRows),
				insertId:     int64(mc.insertId),
			}, nil
		}
	}

	return nil, err
}

func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
	if stmt.mc.netConn == nil {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	// Send command
	err := stmt.writeExecutePacket(args)
	if err != nil {
		return nil, err
	}

	mc := stmt.mc

	// Read Result
	resLen, err := mc.readResultSetHeaderPacket()
	if err != nil {
		return nil, err
	}

	rows := new(binaryRows)

	if resLen > 0 {
		rows.mc = mc
		// Columns
		// If not cached, read them and cache them
		if stmt.columns == nil {
			rows.columns, err = mc.readColumns(resLen)
			stmt.columns = rows.columns
		} else {
			rows.columns = stmt.columns
			err = mc.readUntilEOF()
		}
	}

	return rows, err
}

type converter struct{}

func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
	if driver.IsValue(v) {
		return v, nil
	}

	rv := reflect.ValueOf(v)
	switch rv.Kind() {
	case reflect.Ptr:
		// indirect pointers
		if rv.IsNil() {
			return nil, nil
		}
		return c.ConvertValue(rv.Elem().Interface())
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return rv.Int(), nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
		return int64(rv.Uint()), nil
	case reflect.Uint64:
		u64 := rv.Uint()
		if u64 >= 1<<63 {
			return strconv.FormatUint(u64, 10), nil
		}
		return int64(u64), nil
	case reflect.Float32, reflect.Float64:
		return rv.Float(), nil
	}
	return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
}
<commit_msg>statment: Remove invalid connection error on multiple Close calls (#512)<commit_after>// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package mysql

import (
	"database/sql/driver"
	"fmt"
	"reflect"
	"strconv"
)

type mysqlStmt struct {
	mc         *mysqlConn
	id         uint32
	paramCount int
	columns    []mysqlField // cached from the first query
}

func (stmt *mysqlStmt) Close() error {
	if stmt.mc == nil || stmt.mc.netConn == nil {
		// driver.Stmt.Close can be called more than once, thus this function
		// has to be idempotent.
		// See also Issue #450 and golang/go#16019.
		//errLog.Print(ErrInvalidConn)
		return driver.ErrBadConn
	}

	err := stmt.mc.writeCommandPacketUint32(comStmtClose, stmt.id)
	stmt.mc = nil
	return err
}

func (stmt *mysqlStmt) NumInput() int {
	return stmt.paramCount
}

func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
	return converter{}
}

func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
	if stmt.mc.netConn == nil {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	// Send command
	err := stmt.writeExecutePacket(args)
	if err != nil {
		return nil, err
	}

	mc := stmt.mc

	mc.affectedRows = 0
	mc.insertId = 0

	// Read Result
	resLen, err := mc.readResultSetHeaderPacket()
	if err == nil {
		if resLen > 0 {
			// Columns
			err = mc.readUntilEOF()
			if err != nil {
				return nil, err
			}

			// Rows
			err = mc.readUntilEOF()
		}
		if err == nil {
			return &mysqlResult{
				affectedRows: int64(mc.affectedRows),
				insertId:     int64(mc.insertId),
			}, nil
		}
	}

	return nil, err
}

func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
	if stmt.mc.netConn == nil {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	// Send command
	err := stmt.writeExecutePacket(args)
	if err != nil {
		return nil, err
	}

	mc := stmt.mc

	// Read Result
	resLen, err := mc.readResultSetHeaderPacket()
	if err != nil {
		return nil, err
	}

	rows := new(binaryRows)

	if resLen > 0 {
		rows.mc = mc
		// Columns
		// If not cached, read them and cache them
		if stmt.columns == nil {
			rows.columns, err = mc.readColumns(resLen)
			stmt.columns = rows.columns
		} else {
			rows.columns = stmt.columns
			err = mc.readUntilEOF()
		}
	}

	return rows, err
}

type converter struct{}

func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
	if driver.IsValue(v) {
		return v, nil
	}

	rv := reflect.ValueOf(v)
	switch rv.Kind() {
	case reflect.Ptr:
		// indirect pointers
		if rv.IsNil() {
			return nil, nil
		}
		return c.ConvertValue(rv.Elem().Interface())
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return rv.Int(), nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
		return int64(rv.Uint()), nil
	case reflect.Uint64:
		u64 := rv.Uint()
		if u64 >= 1<<63 {
			return strconv.FormatUint(u64, 10), nil
		}
		return int64(u64), nil
	case reflect.Float32, reflect.Float64:
		return rv.Float(), nil
	}
	return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
}
<|endoftext|>


<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package api

// facadeVersions lists the best version of facades that we know about. This
// will be used to pick out a default version for communication, given the list
// of known versions that the API server tells us it is capable of supporting.
// This map should be updated whenever the API server exposes a new version (so
// that the client will use it whenever it is available).
// New facades should start at 1.
// Facades that existed before versioning start at 0.
var facadeVersions = map[string]int{
	"Action":               0,
	"Agent":                1,
	"AllWatcher":           0,
	"Annotations":          1,
	"Backups":              0,
	"Charms":               1,
	"CharmRevisionUpdater": 0,
	"Client":               0,
	"Deployer":             0,
	"DiskFormatter":        1,
	"DiskManager":          1,
	"Environment":          0,
	"EnvironmentManager":   1,
	"Firewaller":           1,
	"HighAvailability":     1,
	"ImageManager":         1,
	"KeyManager":           0,
	"KeyUpdater":           0,
	"LeadershipService":    1,
	"Logger":               0,
	"Machiner":             0,
	"MetricsManager":       0,
	"Networker":            0,
	"NotifyWatcher":        0,
	"Pinger":               0,
	"Provisioner":          0,
	"Reboot":               1,
	"RelationUnitsWatcher": 0,
	"Rsyslog":              0,
	"Service":              1,
	"Storage":              1,
	"StringsWatcher":       0,
	"Upgrader":             0,
	"Uniter":               1,
	"UserManager":          0,
}

// bestVersion tries to find the newest version in the version list that we can
// use.
func bestVersion(desiredVersion int, versions []int) int {
	best := 0
	for _, version := range versions {
		if version <= desiredVersion && version > best {
			best = version
		}
	}
	return best
}
<commit_msg>Missed correct value in conflict reolsution. Thnx for the tests!<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package api

// facadeVersions lists the best version of facades that we know about. This
// will be used to pick out a default version for communication, given the list
// of known versions that the API server tells us it is capable of supporting.
// This map should be updated whenever the API server exposes a new version (so
// that the client will use it whenever it is available).
// New facades should start at 1.
// Facades that existed before versioning start at 0.
var facadeVersions = map[string]int{
	"Action":               0,
	"Agent":                1,
	"AllWatcher":           0,
	"Annotations":          1,
	"Backups":              0,
	"Charms":               1,
	"CharmRevisionUpdater": 0,
	"Client":               0,
	"Deployer":             0,
	"DiskFormatter":        1,
	"DiskManager":          1,
	"Environment":          0,
	"EnvironmentManager":   1,
	"Firewaller":           1,
	"HighAvailability":     1,
	"ImageManager":         1,
	"KeyManager":           0,
	"KeyUpdater":           0,
	"LeadershipService":    1,
	"Logger":               0,
	"Machiner":             0,
	"MetricsManager":       0,
	"Networker":            0,
	"NotifyWatcher":        0,
	"Pinger":               0,
	"Provisioner":          0,
	"Reboot":               1,
	"RelationUnitsWatcher": 0,
	"Rsyslog":              0,
	"Service":              1,
	"Storage":              1,
	"StringsWatcher":       0,
	"Upgrader":             0,
	"Uniter":               2,
	"UserManager":          0,
}

// bestVersion tries to find the newest version in the version list that we can
// use.
func bestVersion(desiredVersion int, versions []int) int {
	best := 0
	for _, version := range versions {
		if version <= desiredVersion && version > best {
			best = version
		}
	}
	return best
}
<|endoftext|>


<commit_before>package dmgo

import (
	"fmt"
	"os"
)

type cpuState struct {
	pc                     uint16
	sp                     uint16
	a, f, b, c, d, e, h, l byte
	mem                    mem

	lcd lcd
	apu apu

	inHaltMode bool
	inStopMode bool

	interruptMasterEnable bool

	vBlankInterruptEnabled  bool
	lcdStatInterruptEnabled bool
	timerInterruptEnabled   bool
	serialInterruptEnabled  bool
	joypadInterruptEnabled  bool
	dummyEnableBits         [3]bool

	vBlankIRQ  bool
	lcdStatIRQ bool
	timerIRQ   bool
	serialIRQ  bool
	joypadIRQ  bool

	serialTransferData            byte
	serialTransferStartFlag       bool
	serialTransferClockIsInternal bool

	timerOn           bool
	timerModuloReg    byte
	timerCounterReg   byte
	timerFreqSelector byte
	timerDivCycles    uint16 // div reg is top 8 bits of this

	joypad Joypad

	steps  uint
	cycles uint
}

// NOTE: timer is more complicated than this.
// See TCAGBD
func (cs *cpuState) runTimerCycle() {

	cs.timerDivCycles++

	if !cs.timerOn {
		return
	}

	cycleCount := map[byte]uint{
		0: 1024,
		1: 16,
		2: 64,
		3: 256,
	}[cs.timerFreqSelector]
	if cs.cycles&(cycleCount-1) == 0 {
		cs.timerCounterReg++
		if cs.timerCounterReg == 0 {
			cs.timerCounterReg = cs.timerModuloReg
			cs.timerIRQ = true
		}
	}
}

func (cs *cpuState) readTimerControlReg() byte {
	return boolBit(cs.timerOn, 2) | cs.timerFreqSelector
}
func (cs *cpuState) writeTimerControlReg(val byte) {
	cs.timerOn = val&0x04 != 0
	cs.timerFreqSelector = val & 0x03
}

func (cs *cpuState) readSerialControlReg() byte {
	return boolBit(cs.serialTransferStartFlag, 7) | boolBit(cs.serialTransferClockIsInternal, 0)
}
func (cs *cpuState) writeSerialControlReg(val byte) {
	cs.serialTransferStartFlag = val&0x80 != 0
	cs.serialTransferClockIsInternal = val&0x01 != 0
}

// Joypad represents the buttons on a gameboy
type Joypad struct {
	Sel      bool
	Start    bool
	Up       bool
	Down     bool
	Left     bool
	Right    bool
	A        bool
	B        bool
	readMask byte
}

func (jp *Joypad) writeJoypadReg(val byte) {
	jp.readMask = (val >> 4) & 0x03
}
func (jp *Joypad) readJoypadReg() byte {
	val := 0xc0 | (jp.readMask << 4) | 0x0f
	if jp.readMask&0x01 == 0 {
		val &^= boolBit(jp.Down, 3)
		val &^= boolBit(jp.Up, 2)
		val &^= boolBit(jp.Left, 1)
		val &^= boolBit(jp.Right, 0)
	}
	if jp.readMask&0x02 == 0 {
		val &^= boolBit(jp.Start, 3)
		val &^= boolBit(jp.Sel, 2)
		val &^= boolBit(jp.B, 1)
		val &^= boolBit(jp.A, 0)
	}
	return val
}

func (cs *cpuState) updateJoypad(newJP Joypad) {
	lastVal := cs.joypad.readJoypadReg() & 0x0f
	if cs.joypad.readMask&0x01 == 0 {
		cs.joypad.Down = newJP.Down
		cs.joypad.Up = newJP.Up
		cs.joypad.Left = newJP.Left
		cs.joypad.Right = newJP.Right
	}
	if cs.joypad.readMask&0x10 == 0 {
		cs.joypad.Start = newJP.Start
		cs.joypad.Sel = newJP.Sel
		cs.joypad.B = newJP.B
		cs.joypad.A = newJP.A
	}
	newVal := cs.joypad.readJoypadReg() & 0x0f
	// this is correct behavior. it only triggers irq
	// if it goes from no-buttons-pressed to any-pressed.
	if lastVal == 0x0f && newVal < lastVal {
		cs.joypadIRQ = true
	}
}

// TODO: handle HALT hardware bug (see TCAGBD)
func (cs *cpuState) handleInterrupts() bool {

	var intFlag *bool
	var intAddr uint16
	if cs.vBlankInterruptEnabled && cs.vBlankIRQ {
		intFlag, intAddr = &cs.vBlankIRQ, 0x0040
	} else if cs.lcdStatInterruptEnabled && cs.lcdStatIRQ {
		intFlag, intAddr = &cs.lcdStatIRQ, 0x0048
	} else if cs.timerInterruptEnabled && cs.timerIRQ {
		intFlag, intAddr = &cs.timerIRQ, 0x0050
	} else if cs.serialInterruptEnabled && cs.serialIRQ {
		intFlag, intAddr = &cs.serialIRQ, 0x0058
	} else if cs.joypadInterruptEnabled && cs.joypadIRQ {
		intFlag, intAddr = &cs.joypadIRQ, 0x0060
	}

	if intFlag != nil {
		if cs.interruptMasterEnable {
			cs.interruptMasterEnable = false
			*intFlag = false
			cs.pushOp16(20, 0, cs.pc)
			cs.pc = intAddr
		}
		return true
	}
	return false
}

func (cs *cpuState) writeInterruptEnableReg(val byte) {
	boolsFromByte(val,
		&cs.dummyEnableBits[2],
		&cs.dummyEnableBits[1],
		&cs.dummyEnableBits[0],
		&cs.joypadInterruptEnabled,
		&cs.serialInterruptEnabled,
		&cs.timerInterruptEnabled,
		&cs.lcdStatInterruptEnabled,
		&cs.vBlankInterruptEnabled,
	)
}
func (cs *cpuState) readInterruptEnableReg() byte {
	return byteFromBools(
		cs.dummyEnableBits[2],
		cs.dummyEnableBits[1],
		cs.dummyEnableBits[0],
		cs.joypadInterruptEnabled,
		cs.serialInterruptEnabled,
		cs.timerInterruptEnabled,
		cs.lcdStatInterruptEnabled,
		cs.vBlankInterruptEnabled,
	)
}

func (cs *cpuState) writeInterruptFlagReg(val byte) {
	boolsFromByte(val,
		nil, nil, nil,
		&cs.joypadIRQ,
		&cs.serialIRQ,
		&cs.timerIRQ,
		&cs.lcdStatIRQ,
		&cs.vBlankIRQ,
	)
}
func (cs *cpuState) readInterruptFlagReg() byte {
	return byteFromBools(
		true, true, true,
		cs.joypadIRQ,
		cs.serialIRQ,
		cs.timerIRQ,
		cs.lcdStatIRQ,
		cs.vBlankIRQ,
	)
}

func (cs *cpuState) getZeroFlag() bool      { return cs.f&0x80 > 0 }
func (cs *cpuState) getAddSubFlag() bool    { return cs.f&0x40 > 0 }
func (cs *cpuState) getHalfCarryFlag() bool { return cs.f&0x20 > 0 }
func (cs *cpuState) getCarryFlag() bool     { return cs.f&0x10 > 0 }

func (cs *cpuState) setFlags(flags uint16) {

	setZero, clearZero := flags&0x1000 != 0, flags&0xf000 == 0
	setAddSub, clearAddSub := flags&0x100 != 0, flags&0xf00 == 0
	setHalfCarry, clearHalfCarry := flags&0x10 != 0, flags&0xf0 == 0
	setCarry, clearCarry := flags&0x1 != 0, flags&0xf == 0

	if setZero {
		cs.f |= 0x80
	} else if clearZero {
		cs.f &^= 0x80
	}
	if setAddSub {
		cs.f |= 0x40
	} else if clearAddSub {
		cs.f &^= 0x40
	}
	if setHalfCarry {
		cs.f |= 0x20
	} else if clearHalfCarry {
		cs.f &^= 0x20
	}
	if setCarry {
		cs.f |= 0x10
	} else if clearCarry {
		cs.f &^= 0x10
	}
}

func (cs *cpuState) getAF() uint16 { return (uint16(cs.a) << 8) | uint16(cs.f) }
func (cs *cpuState) getBC() uint16 { return (uint16(cs.b) << 8) | uint16(cs.c) }
func (cs *cpuState) getDE() uint16 { return (uint16(cs.d) << 8) | uint16(cs.e) }
func (cs *cpuState) getHL() uint16 { return (uint16(cs.h) << 8) | uint16(cs.l) }

func (cs *cpuState) setAF(val uint16) { cs.a, cs.f = byte(val>>8), byte(val) }
func (cs *cpuState) setBC(val uint16) { cs.b, cs.c = byte(val>>8), byte(val) }
func (cs *cpuState) setDE(val uint16) { cs.d, cs.e = byte(val>>8), byte(val) }
func (cs *cpuState) setHL(val uint16) { cs.h, cs.l = byte(val>>8), byte(val) }

func (cs *cpuState) setSP(val uint16) { cs.sp = val }
func (cs *cpuState) setPC(val uint16) { cs.pc = val }

const entryPoint = 0x100

func newState(cart []byte) *cpuState {
	cartInfo := ParseCartInfo(cart)
	if cartInfo.cgbOnly() {
		fatalErr("CGB-only not supported yet")
	}
	mem := mem{
		cart:    cart,
		cartRAM: make([]byte, cartInfo.GetRAMSize()),
		mbc:     makeMBC(cartInfo),
	}
	state := cpuState{pc: entryPoint, mem: mem}
	state.mem.mbc.Init(&state.mem)
	state.lcd.init()
	state.apu.init()
	return &state
}

// much TODO
func (cs *cpuState) runCycles(ncycles uint) {
	for i := uint(0); i < ncycles; i++ {
		cs.cycles++
		cs.runTimerCycle()
	}
	cs.lcd.runCycles(cs, ncycles)
}

// Emulator exposes the publi1 facing fns for an emulation session
type Emulator interface {
	Framebuffer() []byte
	FlipRequested() bool
	FrameWaitRequested() bool
	UpdateInput(input Input)
	Step()
}

// NewEmulator creates an emulation session
func NewEmulator(cart []byte) Emulator {
	return newState(cart)
}

// Input covers all outside info sent to the Emulator
// TODO: add dt?
type Input struct {
	Joypad Joypad
}

func (cs *cpuState) UpdateInput(input Input) {
	cs.updateJoypad(input.Joypad)
}

// Framebuffer returns the current state of the lcd screen
func (cs *cpuState) Framebuffer() []byte {
	return cs.lcd.framebuffer
}

// FlipRequested indicates if a draw request is pending
// and clears it before returning
func (cs *cpuState) FlipRequested() bool {
	val := cs.lcd.flipRequested
	cs.lcd.flipRequested = false
	return val
}

// FrameWaitRequested indicates, separatate from an actual
// draw event, whether or not there should be a wait until
// when the frame would have been drawn
func (cs *cpuState) FrameWaitRequested() bool {
	val := cs.lcd.frameWaitRequested
	cs.lcd.frameWaitRequested = false
	return val
}

// Step steps the emulator one instruction
func (cs *cpuState) Step() {

	ieAndIfFlagMatch := cs.handleInterrupts()
	if cs.inHaltMode {
		if ieAndIfFlagMatch {
			cs.runCycles(4)
			cs.inHaltMode = false
		} else {
			cs.runCycles(4)
			return
		}
	}

	// TODO: correct behavior, e.g. check for
	// button press only. but for now lets
	// treat it like halt
	if ieAndIfFlagMatch && cs.inStopMode {
		cs.runCycles(4)
		cs.inHaltMode = false
	}
	if cs.inStopMode {
		cs.runCycles(4)
	}

	cs.stepOpcode()
}

func fatalErr(v ...interface{}) {
	fmt.Println(v...)
	os.Exit(1)
}
<commit_msg>Set regs to correct initial values for DMG<commit_after>package dmgo

import (
	"fmt"
	"os"
)

type cpuState struct {
	pc                     uint16
	sp                     uint16
	a, f, b, c, d, e, h, l byte
	mem                    mem

	lcd lcd
	apu apu

	inHaltMode bool
	inStopMode bool

	interruptMasterEnable bool

	vBlankInterruptEnabled  bool
	lcdStatInterruptEnabled bool
	timerInterruptEnabled   bool
	serialInterruptEnabled  bool
	joypadInterruptEnabled  bool
	dummyEnableBits         [3]bool

	vBlankIRQ  bool
	lcdStatIRQ bool
	timerIRQ   bool
	serialIRQ  bool
	joypadIRQ  bool

	serialTransferData            byte
	serialTransferStartFlag       bool
	serialTransferClockIsInternal bool

	timerOn           bool
	timerModuloReg    byte
	timerCounterReg   byte
	timerFreqSelector byte
	timerDivCycles    uint16 // div reg is top 8 bits of this

	joypad Joypad

	steps  uint
	cycles uint
}

// NOTE: timer is more complicated than this.
// See TCAGBD
func (cs *cpuState) runTimerCycle() {

	cs.timerDivCycles++

	if !cs.timerOn {
		return
	}

	cycleCount := map[byte]uint{
		0: 1024,
		1: 16,
		2: 64,
		3: 256,
	}[cs.timerFreqSelector]
	if cs.cycles&(cycleCount-1) == 0 {
		cs.timerCounterReg++
		if cs.timerCounterReg == 0 {
			cs.timerCounterReg = cs.timerModuloReg
			cs.timerIRQ = true
		}
	}
}

func (cs *cpuState) readTimerControlReg() byte {
	return boolBit(cs.timerOn, 2) | cs.timerFreqSelector
}
func (cs *cpuState) writeTimerControlReg(val byte) {
	cs.timerOn = val&0x04 != 0
	cs.timerFreqSelector = val & 0x03
}

func (cs *cpuState) readSerialControlReg() byte {
	return boolBit(cs.serialTransferStartFlag, 7) | boolBit(cs.serialTransferClockIsInternal, 0)
}
func (cs *cpuState) writeSerialControlReg(val byte) {
	cs.serialTransferStartFlag = val&0x80 != 0
	cs.serialTransferClockIsInternal = val&0x01 != 0
}

// Joypad represents the buttons on a gameboy
type Joypad struct {
	Sel      bool
	Start    bool
	Up       bool
	Down     bool
	Left     bool
	Right    bool
	A        bool
	B        bool
	readMask byte
}

func (jp *Joypad) writeJoypadReg(val byte) {
	jp.readMask = (val >> 4) & 0x03
}
func (jp *Joypad) readJoypadReg() byte {
	val := 0xc0 | (jp.readMask << 4) | 0x0f
	if jp.readMask&0x01 == 0 {
		val &^= boolBit(jp.Down, 3)
		val &^= boolBit(jp.Up, 2)
		val &^= boolBit(jp.Left, 1)
		val &^= boolBit(jp.Right, 0)
	}
	if jp.readMask&0x02 == 0 {
		val &^= boolBit(jp.Start, 3)
		val &^= boolBit(jp.Sel, 2)
		val &^= boolBit(jp.B, 1)
		val &^= boolBit(jp.A, 0)
	}
	return val
}

func (cs *cpuState) updateJoypad(newJP Joypad) {
	lastVal := cs.joypad.readJoypadReg() & 0x0f
	if cs.joypad.readMask&0x01 == 0 {
		cs.joypad.Down = newJP.Down
		cs.joypad.Up = newJP.Up
		cs.joypad.Left = newJP.Left
		cs.joypad.Right = newJP.Right
	}
	if cs.joypad.readMask&0x10 == 0 {
		cs.joypad.Start = newJP.Start
		cs.joypad.Sel = newJP.Sel
		cs.joypad.B = newJP.B
		cs.joypad.A = newJP.A
	}
	newVal := cs.joypad.readJoypadReg() & 0x0f
	// this is correct behavior. it only triggers irq
	// if it goes from no-buttons-pressed to any-pressed.
	if lastVal == 0x0f && newVal < lastVal {
		cs.joypadIRQ = true
	}
}

// TODO: handle HALT hardware bug (see TCAGBD)
func (cs *cpuState) handleInterrupts() bool {

	var intFlag *bool
	var intAddr uint16
	if cs.vBlankInterruptEnabled && cs.vBlankIRQ {
		intFlag, intAddr = &cs.vBlankIRQ, 0x0040
	} else if cs.lcdStatInterruptEnabled && cs.lcdStatIRQ {
		intFlag, intAddr = &cs.lcdStatIRQ, 0x0048
	} else if cs.timerInterruptEnabled && cs.timerIRQ {
		intFlag, intAddr = &cs.timerIRQ, 0x0050
	} else if cs.serialInterruptEnabled && cs.serialIRQ {
		intFlag, intAddr = &cs.serialIRQ, 0x0058
	} else if cs.joypadInterruptEnabled && cs.joypadIRQ {
		intFlag, intAddr = &cs.joypadIRQ, 0x0060
	}

	if intFlag != nil {
		if cs.interruptMasterEnable {
			cs.interruptMasterEnable = false
			*intFlag = false
			cs.pushOp16(20, 0, cs.pc)
			cs.pc = intAddr
		}
		return true
	}
	return false
}

func (cs *cpuState) writeInterruptEnableReg(val byte) {
	boolsFromByte(val,
		&cs.dummyEnableBits[2],
		&cs.dummyEnableBits[1],
		&cs.dummyEnableBits[0],
		&cs.joypadInterruptEnabled,
		&cs.serialInterruptEnabled,
		&cs.timerInterruptEnabled,
		&cs.lcdStatInterruptEnabled,
		&cs.vBlankInterruptEnabled,
	)
}
func (cs *cpuState) readInterruptEnableReg() byte {
	return byteFromBools(
		cs.dummyEnableBits[2],
		cs.dummyEnableBits[1],
		cs.dummyEnableBits[0],
		cs.joypadInterruptEnabled,
		cs.serialInterruptEnabled,
		cs.timerInterruptEnabled,
		cs.lcdStatInterruptEnabled,
		cs.vBlankInterruptEnabled,
	)
}

func (cs *cpuState) writeInterruptFlagReg(val byte) {
	boolsFromByte(val,
		nil, nil, nil,
		&cs.joypadIRQ,
		&cs.serialIRQ,
		&cs.timerIRQ,
		&cs.lcdStatIRQ,
		&cs.vBlankIRQ,
	)
}
func (cs *cpuState) readInterruptFlagReg() byte {
	return byteFromBools(
		true, true, true,
		cs.joypadIRQ,
		cs.serialIRQ,
		cs.timerIRQ,
		cs.lcdStatIRQ,
		cs.vBlankIRQ,
	)
}

func (cs *cpuState) getZeroFlag() bool      { return cs.f&0x80 > 0 }
func (cs *cpuState) getAddSubFlag() bool    { return cs.f&0x40 > 0 }
func (cs *cpuState) getHalfCarryFlag() bool { return cs.f&0x20 > 0 }
func (cs *cpuState) getCarryFlag() bool     { return cs.f&0x10 > 0 }

func (cs *cpuState) setFlags(flags uint16) {

	setZero, clearZero := flags&0x1000 != 0, flags&0xf000 == 0
	setAddSub, clearAddSub := flags&0x100 != 0, flags&0xf00 == 0
	setHalfCarry, clearHalfCarry := flags&0x10 != 0, flags&0xf0 == 0
	setCarry, clearCarry := flags&0x1 != 0, flags&0xf == 0

	if setZero {
		cs.f |= 0x80
	} else if clearZero {
		cs.f &^= 0x80
	}
	if setAddSub {
		cs.f |= 0x40
	} else if clearAddSub {
		cs.f &^= 0x40
	}
	if setHalfCarry {
		cs.f |= 0x20
	} else if clearHalfCarry {
		cs.f &^= 0x20
	}
	if setCarry {
		cs.f |= 0x10
	} else if clearCarry {
		cs.f &^= 0x10
	}
}

func (cs *cpuState) getAF() uint16 { return (uint16(cs.a) << 8) | uint16(cs.f) }
func (cs *cpuState) getBC() uint16 { return (uint16(cs.b) << 8) | uint16(cs.c) }
func (cs *cpuState) getDE() uint16 { return (uint16(cs.d) << 8) | uint16(cs.e) }
func (cs *cpuState) getHL() uint16 { return (uint16(cs.h) << 8) | uint16(cs.l) }

func (cs *cpuState) setAF(val uint16) { cs.a, cs.f = byte(val>>8), byte(val) }
func (cs *cpuState) setBC(val uint16) { cs.b, cs.c = byte(val>>8), byte(val) }
func (cs *cpuState) setDE(val uint16) { cs.d, cs.e = byte(val>>8), byte(val) }
func (cs *cpuState) setHL(val uint16) { cs.h, cs.l = byte(val>>8), byte(val) }

func (cs *cpuState) setSP(val uint16) { cs.sp = val }
func (cs *cpuState) setPC(val uint16) { cs.pc = val }

func newState(cart []byte) *cpuState {
	cartInfo := ParseCartInfo(cart)
	if cartInfo.cgbOnly() {
		fatalErr("CGB-only not supported yet")
	}
	mem := mem{
		cart:    cart,
		cartRAM: make([]byte, cartInfo.GetRAMSize()),
		mbc:     makeMBC(cartInfo),
	}
	state := cpuState{mem: mem}
	state.mem.mbc.Init(&state.mem)
	state.initRegisters()
	state.lcd.init()
	state.apu.init()
	return &state
}

func (cs *cpuState) initRegisters() {
	// NOTE: these are DMG values,
	// others are different, see
	// TCAGBD
	cs.setAF(0x01b0)
	cs.setBC(0x0013)
	cs.setDE(0x00d8)
	cs.setHL(0x014d)
	cs.setSP(0xfffe)
	cs.setPC(0x0100)
}

// much TODO
func (cs *cpuState) runCycles(ncycles uint) {
	for i := uint(0); i < ncycles; i++ {
		cs.cycles++
		cs.runTimerCycle()
	}
	cs.lcd.runCycles(cs, ncycles)
}

// Emulator exposes the publi1 facing fns for an emulation session
type Emulator interface {
	Framebuffer() []byte
	FlipRequested() bool
	FrameWaitRequested() bool
	UpdateInput(input Input)
	Step()
}

// NewEmulator creates an emulation session
func NewEmulator(cart []byte) Emulator {
	return newState(cart)
}

// Input covers all outside info sent to the Emulator
// TODO: add dt?
type Input struct {
	Joypad Joypad
}

func (cs *cpuState) UpdateInput(input Input) {
	cs.updateJoypad(input.Joypad)
}

// Framebuffer returns the current state of the lcd screen
func (cs *cpuState) Framebuffer() []byte {
	return cs.lcd.framebuffer
}

// FlipRequested indicates if a draw request is pending
// and clears it before returning
func (cs *cpuState) FlipRequested() bool {
	val := cs.lcd.flipRequested
	cs.lcd.flipRequested = false
	return val
}

// FrameWaitRequested indicates, separatate from an actual
// draw event, whether or not there should be a wait until
// when the frame would have been drawn
func (cs *cpuState) FrameWaitRequested() bool {
	val := cs.lcd.frameWaitRequested
	cs.lcd.frameWaitRequested = false
	return val
}

// Step steps the emulator one instruction
func (cs *cpuState) Step() {

	ieAndIfFlagMatch := cs.handleInterrupts()
	if cs.inHaltMode {
		if ieAndIfFlagMatch {
			cs.runCycles(4)
			cs.inHaltMode = false
		} else {
			cs.runCycles(4)
			return
		}
	}

	// TODO: correct behavior, e.g. check for
	// button press only. but for now lets
	// treat it like halt
	if ieAndIfFlagMatch && cs.inStopMode {
		cs.runCycles(4)
		cs.inHaltMode = false
	}
	if cs.inStopMode {
		cs.runCycles(4)
	}

	cs.stepOpcode()
}

func fatalErr(v ...interface{}) {
	fmt.Println(v...)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package asyncLog

// 日志优先级
type Priority int

const (
	LevelAll Priority = iota
	LevelDebug
	LevelInfo
	LevelWarn
	LevelError
	LevelFatal
	LevelOff
)

var (
	// 日志等级
	levelTitle = map[Priority]string{
		LevelDebug: "[DEBUG]",
		LevelInfo:  "[INFO]",
		LevelWarn:  "[WARN]",
		LevelError: "[ERROR]",
		LevelFatal: "[FATAL]",
	}
)

// NewLevelLog 写入等级日志
// 级别高于logLevel才会被写入
func NewLevelLog(filename string, logLevel Priority) *LogFile {
	lf := NewLogFile(filename)
	lf.level = logLevel

	return lf
}

func (lf *LogFile) Debug(msg string) error {
	return lf.writeLevelMsg(msg, LevelDebug)
}

func (lf *LogFile) Info(msg string) error {
	return lf.writeLevelMsg(msg, LevelInfo)
}

func (lf *LogFile) Warn(msg string) error {
	return lf.writeLevelMsg(msg, LevelWarn)
}

func (lf *LogFile) Error(msg string) error {
	return lf.writeLevelMsg(msg, LevelError)
}

func (lf *LogFile) Fatal(msg string) error {
	return lf.writeLevelMsg(msg, LevelFatal)
}

func (lf *LogFile) writeLevelMsg(msg string, level Priority) error {
	if level >= lf.level {
		return lf.Write(levelTitle[level] + msg)
	}

	return nil
}
<commit_msg>优化日志格式<commit_after>package asyncLog

// 日志优先级
type Priority int

const (
	LevelAll Priority = iota
	LevelDebug
	LevelInfo
	LevelWarn
	LevelError
	LevelFatal
	LevelOff
)

var (
	// 日志等级
	levelTitle = map[Priority]string{
		LevelDebug: "[DEBUG]",
		LevelInfo:  "[INFO]",
		LevelWarn:  "[WARN]",
		LevelError: "[ERROR]",
		LevelFatal: "[FATAL]",
	}
)

// NewLevelLog 写入等级日志
// 级别高于logLevel才会被写入
func NewLevelLog(filename string, logLevel Priority) *LogFile {
	lf := NewLogFile(filename)
	lf.level = logLevel

	return lf
}

func (lf *LogFile) Debug(msg string) error {
	return lf.writeLevelMsg(msg, LevelDebug)
}

func (lf *LogFile) Info(msg string) error {
	return lf.writeLevelMsg(msg, LevelInfo)
}

func (lf *LogFile) Warn(msg string) error {
	return lf.writeLevelMsg(msg, LevelWarn)
}

func (lf *LogFile) Error(msg string) error {
	return lf.writeLevelMsg(msg, LevelError)
}

func (lf *LogFile) Fatal(msg string) error {
	return lf.writeLevelMsg(msg, LevelFatal)
}

func (lf *LogFile) writeLevelMsg(msg string, level Priority) error {
	if level >= lf.level {
		return lf.Write(levelTitle[level] + " " + msg)
	}

	return nil
}
<|endoftext|>