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

package win

import (
	"syscall"
	"unsafe"
)

// PDH error codes, which can be returned by all Pdh* functions.
const (
	PDH_CSTATUS_VALID_DATA   = 0x00000000 // the returned data is valid.
	PDH_CSTATUS_INVALID_DATA = 0xC0000BBA // The counter was successfully found, but the data returned is not valid.
	PDH_CSTATUS_NEW_DATA     = 0x00000001 // the return data value is valid and different from the last sample.
	PDH_CSTATUS_NO_MACHINE   = 0x800007D0
	PDH_INVALID_ARGUMENT     = 0xC0000BBD // required argument is missing or incorrect.
	PDH_INVALID_DATA         = 0xC0000BC6 // specified counter does not contain valid data or a successful status code.
)

// Formatting options for GetFormattedCounterValue().
const (
	PDH_FMT_RAW          = 0x00010
	PDH_FMT_ANSI         = 0x00020
	PDH_FMT_UNICODE      = 0x00040
	PDH_FMT_LONG         = 0x00100
	PDH_FMT_DOUBLE       = 0x00200
	PDH_FMT_LARGE        = 0x00400
	PDH_FMT_NOSCALE      = 0x01000 // can be OR-ed: Do not apply the counter's default scaling factor.
	PDH_FMT_1000         = 0x02000 // can be OR-ed: multiply the actual value by 1,000.
	PDH_FMT_NODATA       = 0x04000 // can be OR-ed: unknown what this is for, MSDN says nothing.
	PDH_FMT_NOCAP100     = 0x08000 // can be OR-ed: do not cap values > 100.
	PERF_DETAIL_COSTLY   = 0x10000
	PERF_DETAIL_STANDARD = 0x0FFFF
)

type (
	PDH_HQUERY   HANDLE // query handle
	PDH_HCOUNTER HANDLE // counter handle
)

type PDH_FMT_COUNTERVALUE struct {
	CStatus         uint32
	LongValue       int32
	DoubleValue     float64
	LargeValue      int64
	AnsiStringValue uintptr //
	WideStringValue uintptr
}

// PdhBrowseCounters configuration struct. Untested.
type PDH_BROWSE_DLG_CONFIG_W struct {
	BIncludeInstanceIndex    uint32
	BSingleCounterPerAdd     uint32
	BSingleCounterPerDialog  uint32
	BLocalCountersOnly       uint32
	BWildCardInstance        uint32
	BHideDetailBox           uint32
	BInitializePath          uint32
	BDisableMachineSelection uint32
	BIncludeCostlyObjects    uint32
	BShowObjectBrowser       uint32
	BReserved                uint32
	HwndOwner                HWND
	SzDataSource             uintptr
	SzReturnPathBuffer       uintptr
	CchReturnPathLength      uint32
	PCallBack                int // CounterPathCallBack
	DwCallBackArg            uintptr
	CallBackStatus           uint32 // PDH error status code
	DwDefaultDetailLevel     uint32
	SzDialogBoxCaption       uintptr // pointer to a string
}

var (
	// Library
	libpdhDll *syscall.DLL

	// Functions
	pdh_AddCounterW              *syscall.Proc
	pdh_AddEnglishCounterW       *syscall.Proc
	pdh_BrowseCounters           *syscall.Proc
	pdh_CloseQuery               *syscall.Proc
	pdh_CollectQueryData         *syscall.Proc
	pdh_GetFormattedCounterValue *syscall.Proc
	pdh_OpenQuery                *syscall.Proc
	pdh_ValidatePath             *syscall.Proc
)

func init() {
	// Library
	libpdhDll = syscall.MustLoadDLL("pdh.dll")

	// Functions
	pdh_AddCounterW = libpdhDll.MustFindProc("PdhAddCounterW")
	pdh_AddEnglishCounterW = libpdhDll.MustFindProc("PdhAddEnglishCounterW")
	pdh_BrowseCounters = libpdhDll.MustFindProc("PdhBrowseCountersW")
	pdh_CloseQuery = libpdhDll.MustFindProc("PdhCloseQuery")
	pdh_CollectQueryData = libpdhDll.MustFindProc("PdhCollectQueryData")
	pdh_GetFormattedCounterValue = libpdhDll.MustFindProc("PdhGetFormattedCounterValue")
	pdh_OpenQuery = libpdhDll.MustFindProc("PdhOpenQuery")
	pdh_ValidatePath = libpdhDll.MustFindProc("PdhValidatePathW")
}

// Adds the specified counter to the query. This is the NON-ENGLISH version. Preferrably, use the
// function PdhAddEnglishCounterW instead. hQuery is the query handle, which has been fetched by PdhOpenQuery.
// szFullCounterPath is a full, internationalized counter path (this will differ per Windows language version).
// dwUserData is a 'user-defined value', which becomes part of the counter information. To retrieve this value
// later, call PdhGetCounterInfo() and access dwQueryUserData of the PDH_COUNTER_INFO structure.
//
// Examples of szFullCounterPath (in an English version of Windows):
//
//	\\Processor(_Total)\\% Idle Time
//	\\Processor(_Total)\\% Processor Time
//	\\LogicalDisk(C:)\% Free Space
//
// To view all available counters on a system, try the PdhBrowseCounters() function.
func PdhAddCounterW(hQuery PDH_HQUERY, szFullCounterPath string, dwUserData uintptr, phCounter *PDH_HCOUNTER) uint32 {
	ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath)
	ret, _, _ := pdh_AddCounterW.Call(uintptr(hQuery),
		uintptr(unsafe.Pointer(ptxt)),
		dwUserData,
		uintptr(unsafe.Pointer(phCounter)))

	return uint32(ret)
}

// Adds the specified language-neutral counter to the query. See the PdhAddCounterW function.
func PdhAddEnglishCounterW(hQuery PDH_HQUERY, szFullCounterPath string, dwUserData uintptr, phCounter *PDH_HCOUNTER) uint32 {
	ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath)
	ret, _, _ := pdh_AddEnglishCounterW.Call(uintptr(hQuery),
		uintptr(unsafe.Pointer(ptxt)),
		dwUserData,
		uintptr(unsafe.Pointer(phCounter)))

	return uint32(ret)
}

// Creates a new query that is used to manage the collection of performance data.
// szDataSource is a null terminated string that specifies the name of the log file from which to
// retrieve the performance data. If 0, performance data is collected from a real-time data source.
// dwUserData is a user-defined value to associate with this query. To retrieve the user data later,
// call PdhGetCounterInfo and access dwQueryUserData of the PDH_COUNTER_INFO structure. phQuery is
// the handle to the query, and must be used in subsequent calls. This function returns a PDH_
// constant error code, or 0 if the call succeeded.
func PdhOpenQuery(szDataSource uintptr, dwUserData uintptr, phQuery *PDH_HQUERY) uint32 {
	ret, _, _ := pdh_OpenQuery.Call(szDataSource,
		dwUserData,
		uintptr(unsafe.Pointer(phQuery)))

	return uint32(ret)
}

// Closes all counters contained in the specified query, closes all handles related to the query,
// and frees all memory associated with the query.
func PdhCloseQuery(hQuery PDH_HQUERY) uint32 {
	ret, _, _ := pdh_CloseQuery.Call(uintptr(hQuery))
	return uint32(ret)
}

// Collects the current raw data value for all counters in the specified query and updates the status
// code of each counter. With some counters, this function needs to be repeatedly called before the value
// of the counter can be extracted with PdhGetFormattedCounterValue(). For example, the following code
// requires at least two calls:
//
// 	var handle win.PDH_HQUERY
// 	var counterHandle win.PDH_HCOUNTER
// 	ret := win.PdhOpenQuery(0, 0, &handle)
//	ret = win.PdhAddEnglishCounterW(handle, "\\Processor(_Total)\\% Idle Time", 0, &counterHandle)
//	var derp win.PDH_FMT_COUNTERVALUE
//
//	ret = win.PdhCollectQueryData(handle)
//	fmt.Printf("Collect return code is %x\n", ret) // return code will be PDH_CSTATUS_INVALID_DATA
//	ret = win.PdhGetFormattedCounterValue(counterHandle, win.PDH_FMT_DOUBLE, 0, &derp)
//
//	ret = win.PdhCollectQueryData(handle)
//	fmt.Printf("Collect return code is %x\n", ret) // return code will be ERROR_SUCCESS
//	ret = win.PdhGetFormattedCounterValue(counterHandle, win.PDH_FMT_DOUBLE, 0, &derp)
//
// The PdhCollectQueryData will return an error in the first call because it needs two values for
// displaying the correct data for the processor idle time. The second call will have a 0 return code.
func PdhCollectQueryData(hQuery PDH_HQUERY) uint32 {
	ret, _, _ := pdh_CollectQueryData.Call(uintptr(hQuery))
	return uint32(ret)
}

// Formats a counter hCounter according the dwFormat. The result is set in the PDH_FMT_COUNTERVALUE struct, pValue.
func PdhGetFormattedCounterValue(hCounter PDH_HCOUNTER, dwFormat uint32, lpdwType uintptr, pValue *PDH_FMT_COUNTERVALUE) uint32 {
	ret, _, _ := pdh_GetFormattedCounterValue.Call(uintptr(hCounter),
		uintptr(dwFormat),
		uintptr(unsafe.Pointer(lpdwType)),
		uintptr(unsafe.Pointer(pValue)))

	return uint32(ret)
}

// Displays a Browse Counters dialog box that the user can use to select one or more counters
// that they want to add to the query. This function returns a PDH error code, or 0 of the call succeeded.
// This call is pretty much untested. On my machine, it displays a dialog box perfectly with specifying
// a practical 'unfilled' struct, but that's all I've tested.
func PdhBrowseCounters(pBrowseDlgData *PDH_BROWSE_DLG_CONFIG_W) uint32 {
	ret, _, _ := pdh_BrowseCounters.Call(uintptr(unsafe.Pointer(pBrowseDlgData)))

	return uint32(ret)
}
<commit_msg>Added PdhValidatePathW.<commit_after>// Copyright 2010 The win Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package win

import (
	"syscall"
	"unsafe"
)

// PDH error codes, which can be returned by all Pdh* functions.
const (
	PDH_CSTATUS_VALID_DATA      = 0x00000000 // The returned data is valid.
	PDH_CSTATUS_INVALID_DATA    = 0xC0000BBA // The counter was successfully found, but the data returned is not valid.
	PDH_CSTATUS_NEW_DATA        = 0x00000001 // The return data value is valid and different from the last sample.
	PDH_CSTATUS_NO_MACHINE      = 0x800007D0 // Unable to connect to the specified computer, or the computer is offline.
	PDH_CSTATUS_BAD_COUNTERNAME = 0xC0000BC0 // Unable to parse the counter path. Check the format and syntax of the specified path.
	PDH_INVALID_ARGUMENT        = 0xC0000BBD // Required argument is missing or incorrect.
	PDH_INVALID_DATA            = 0xC0000BC6 // specified counter does not contain valid data or a successful status code.
)

// Formatting options for GetFormattedCounterValue().
const (
	PDH_FMT_RAW          = 0x00010
	PDH_FMT_ANSI         = 0x00020
	PDH_FMT_UNICODE      = 0x00040
	PDH_FMT_LONG         = 0x00100
	PDH_FMT_DOUBLE       = 0x00200
	PDH_FMT_LARGE        = 0x00400
	PDH_FMT_NOSCALE      = 0x01000 // can be OR-ed: Do not apply the counter's default scaling factor.
	PDH_FMT_1000         = 0x02000 // can be OR-ed: multiply the actual value by 1,000.
	PDH_FMT_NODATA       = 0x04000 // can be OR-ed: unknown what this is for, MSDN says nothing.
	PDH_FMT_NOCAP100     = 0x08000 // can be OR-ed: do not cap values > 100.
	PERF_DETAIL_COSTLY   = 0x10000
	PERF_DETAIL_STANDARD = 0x0FFFF
)

type (
	PDH_HQUERY   HANDLE // query handle
	PDH_HCOUNTER HANDLE // counter handle
)

type PDH_FMT_COUNTERVALUE struct {
	CStatus         uint32
	LongValue       int32
	DoubleValue     float64
	LargeValue      int64
	AnsiStringValue uintptr //
	WideStringValue uintptr
}

// PdhBrowseCounters configuration struct. Untested.
type PDH_BROWSE_DLG_CONFIG_W struct {
	BIncludeInstanceIndex    uint32
	BSingleCounterPerAdd     uint32
	BSingleCounterPerDialog  uint32
	BLocalCountersOnly       uint32
	BWildCardInstance        uint32
	BHideDetailBox           uint32
	BInitializePath          uint32
	BDisableMachineSelection uint32
	BIncludeCostlyObjects    uint32
	BShowObjectBrowser       uint32
	BReserved                uint32
	HwndOwner                HWND
	SzDataSource             uintptr
	SzReturnPathBuffer       uintptr
	CchReturnPathLength      uint32
	PCallBack                int // CounterPathCallBack
	DwCallBackArg            uintptr
	CallBackStatus           uint32 // PDH error status code
	DwDefaultDetailLevel     uint32
	SzDialogBoxCaption       uintptr // pointer to a string
}

var (
	// Library
	libpdhDll *syscall.DLL

	// Functions
	pdh_AddCounterW              *syscall.Proc
	pdh_AddEnglishCounterW       *syscall.Proc
	pdh_BrowseCounters           *syscall.Proc
	pdh_CloseQuery               *syscall.Proc
	pdh_CollectQueryData         *syscall.Proc
	pdh_GetFormattedCounterValue *syscall.Proc
	pdh_OpenQuery                *syscall.Proc
	pdh_ValidatePath             *syscall.Proc
)

func init() {
	// Library
	libpdhDll = syscall.MustLoadDLL("pdh.dll")

	// Functions
	pdh_AddCounterW = libpdhDll.MustFindProc("PdhAddCounterW")
	pdh_AddEnglishCounterW = libpdhDll.MustFindProc("PdhAddEnglishCounterW")
	pdh_BrowseCounters = libpdhDll.MustFindProc("PdhBrowseCountersW")
	pdh_CloseQuery = libpdhDll.MustFindProc("PdhCloseQuery")
	pdh_CollectQueryData = libpdhDll.MustFindProc("PdhCollectQueryData")
	pdh_GetFormattedCounterValue = libpdhDll.MustFindProc("PdhGetFormattedCounterValue")
	pdh_OpenQuery = libpdhDll.MustFindProc("PdhOpenQuery")
	pdh_ValidatePath = libpdhDll.MustFindProc("PdhValidatePathW")
}

// Adds the specified counter to the query. This is the NON-ENGLISH version. Preferably, use the
// function PdhAddEnglishCounterW instead. hQuery is the query handle, which has been fetched by PdhOpenQuery.
// szFullCounterPath is a full, internationalized counter path (this will differ per Windows language version).
// dwUserData is a 'user-defined value', which becomes part of the counter information. To retrieve this value
// later, call PdhGetCounterInfo() and access dwQueryUserData of the PDH_COUNTER_INFO structure.
//
// Examples of szFullCounterPath (in an English version of Windows):
//
//	\\Processor(_Total)\\% Idle Time
//	\\Processor(_Total)\\% Processor Time
//	\\LogicalDisk(C:)\% Free Space
//
// To view all available counters on a system, try the PdhBrowseCounters() function.
func PdhAddCounterW(hQuery PDH_HQUERY, szFullCounterPath string, dwUserData uintptr, phCounter *PDH_HCOUNTER) uint32 {
	ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath)
	ret, _, _ := pdh_AddCounterW.Call(uintptr(hQuery),
		uintptr(unsafe.Pointer(ptxt)),
		dwUserData,
		uintptr(unsafe.Pointer(phCounter)))

	return uint32(ret)
}

// Adds the specified language-neutral counter to the query. See the PdhAddCounterW function.
func PdhAddEnglishCounterW(hQuery PDH_HQUERY, szFullCounterPath string, dwUserData uintptr, phCounter *PDH_HCOUNTER) uint32 {
	ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath)
	ret, _, _ := pdh_AddEnglishCounterW.Call(uintptr(hQuery),
		uintptr(unsafe.Pointer(ptxt)),
		dwUserData,
		uintptr(unsafe.Pointer(phCounter)))

	return uint32(ret)
}

// Creates a new query that is used to manage the collection of performance data.
// szDataSource is a null terminated string that specifies the name of the log file from which to
// retrieve the performance data. If 0, performance data is collected from a real-time data source.
// dwUserData is a user-defined value to associate with this query. To retrieve the user data later,
// call PdhGetCounterInfo and access dwQueryUserData of the PDH_COUNTER_INFO structure. phQuery is
// the handle to the query, and must be used in subsequent calls. This function returns a PDH_
// constant error code, or 0 if the call succeeded.
func PdhOpenQuery(szDataSource uintptr, dwUserData uintptr, phQuery *PDH_HQUERY) uint32 {
	ret, _, _ := pdh_OpenQuery.Call(szDataSource,
		dwUserData,
		uintptr(unsafe.Pointer(phQuery)))

	return uint32(ret)
}

// Closes all counters contained in the specified query, closes all handles related to the query,
// and frees all memory associated with the query.
func PdhCloseQuery(hQuery PDH_HQUERY) uint32 {
	ret, _, _ := pdh_CloseQuery.Call(uintptr(hQuery))
	return uint32(ret)
}

// Collects the current raw data value for all counters in the specified query and updates the status
// code of each counter. With some counters, this function needs to be repeatedly called before the value
// of the counter can be extracted with PdhGetFormattedCounterValue(). For example, the following code
// requires at least two calls:
//
// 	var handle win.PDH_HQUERY
// 	var counterHandle win.PDH_HCOUNTER
// 	ret := win.PdhOpenQuery(0, 0, &handle)
//	ret = win.PdhAddEnglishCounterW(handle, "\\Processor(_Total)\\% Idle Time", 0, &counterHandle)
//	var derp win.PDH_FMT_COUNTERVALUE
//
//	ret = win.PdhCollectQueryData(handle)
//	fmt.Printf("Collect return code is %x\n", ret) // return code will be PDH_CSTATUS_INVALID_DATA
//	ret = win.PdhGetFormattedCounterValue(counterHandle, win.PDH_FMT_DOUBLE, 0, &derp)
//
//	ret = win.PdhCollectQueryData(handle)
//	fmt.Printf("Collect return code is %x\n", ret) // return code will be ERROR_SUCCESS
//	ret = win.PdhGetFormattedCounterValue(counterHandle, win.PDH_FMT_DOUBLE, 0, &derp)
//
// The PdhCollectQueryData will return an error in the first call because it needs two values for
// displaying the correct data for the processor idle time. The second call will have a 0 return code.
func PdhCollectQueryData(hQuery PDH_HQUERY) uint32 {
	ret, _, _ := pdh_CollectQueryData.Call(uintptr(hQuery))
	return uint32(ret)
}

// Formats a counter hCounter according the dwFormat. The result is set in the PDH_FMT_COUNTERVALUE struct, pValue.
func PdhGetFormattedCounterValue(hCounter PDH_HCOUNTER, dwFormat uint32, lpdwType uintptr, pValue *PDH_FMT_COUNTERVALUE) uint32 {
	ret, _, _ := pdh_GetFormattedCounterValue.Call(uintptr(hCounter),
		uintptr(dwFormat),
		uintptr(unsafe.Pointer(lpdwType)),
		uintptr(unsafe.Pointer(pValue)))

	return uint32(ret)
}

// Displays a Browse Counters dialog box that the user can use to select one or more counters
// that they want to add to the query. This function returns a PDH error code, or 0 of the call succeeded.
// This call is pretty much untested. On my machine, it displays a dialog box perfectly with specifying
// a practical 'unfilled' struct, but that's all I've tested.
func PdhBrowseCounters(pBrowseDlgData *PDH_BROWSE_DLG_CONFIG_W) uint32 {
	ret, _, _ := pdh_BrowseCounters.Call(uintptr(unsafe.Pointer(pBrowseDlgData)))

	return uint32(ret)
}

// Validates a path. Will return ERROR_SUCCESS when ok, or PDH_CSTATUS_BAD_COUNTERNAME when the path is
// erroneous.
func PdhValidatePathW(path string) uint32 {
	ptxt, _ := syscall.UTF16PtrFromString(path)
	ret, _, _ := pdh_ValidatePath.Call(uintptr(unsafe.Pointer(ptxt)))

	return uint32(ret)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"strings"
	"syscall"
	"time"
)

/*

# the plan

qst: run things quickly

qst - detects the current project type and runs it
qst hello.rb - runs `ruby hello.rb`
qst hello.go - compiles & runs hello.go

*/

var mappings = map[string]func(string) string{
	".go": func(name string) string {
		outpath := strings.TrimSuffix(name, path.Ext(name))
		if !path.IsAbs(outpath) {
			outpath = fmt.Sprintf("./%s", outpath)
		}
		return fmt.Sprintf("go build -o %s %s && %s", outpath, name, outpath)
	},
	".rb": func(name string) string {
		return fmt.Sprintf("ruby %s", name)
	},
	".py": func(name string) string {
		return fmt.Sprintf("python %s", name)
	},
}

var delay = flag.Duration("delay", 1*time.Second, "time to wait until restart")
var autoRestart = flag.Bool("autorestart", true, "restart after command exists")
var command = flag.String("command", "", "command to run ({file} will be substituted)")

func main() {
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s <file>\n", os.Args[0])
		flag.PrintDefaults()
	}

	flag.Parse()
	args := flag.Args()

	if len(args) < 1 {
		flag.Usage()
		os.Exit(1)
	}

	file := args[0]
	if !isFile(file) {
		fmt.Fprintf(os.Stderr, "Error: %s is not a file.\n", file)
		os.Exit(1)
	}

	var cmd string
	if command != nil && strings.TrimSpace(*command) != "" {
		cmd = strings.Replace(*command, "{file}", file, -1)
	} else {
		ext := path.Ext(file)
		fn, found := mappings[ext]
		if !found {
			log.Fatalf("error: no mapping found for `%s'", file)
		}
		cmd = fn(file)
	}
	log.Printf("command to run: `%s'", cmd)

	runner := MakeRunner(cmd, *autoRestart)
	go runCmd(file, runner)

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)
	s := <-c
	log.Printf("got signal: %s, exiting...", s)
	runner.Stop()
}

func runCmd(file string, runner *Runner) {
	runner.Start()
	lastMtime := time.Now()
	for {
		info, err := os.Stat(file)
		if os.IsNotExist(err) {
			log.Fatalf("`%s' disappeared, exiting")
		}

		mtime := info.ModTime()
		if mtime.After(lastMtime) {
			log.Printf("`%s' changed, trying to restart", file)
			runner.Restart()
		}

		lastMtime = mtime
		time.Sleep(1 * time.Second)
	}
}

type Runner struct {
	cmd        *exec.Cmd
	shellCmd   string
	started    bool
	restart    bool
	restarting bool
}

func MakeRunner(shellCmd string, restart bool) *Runner {
	return &Runner{nil, shellCmd, false, restart, false}
}

func (r *Runner) Start() error {
	if r.started {
		return errors.New("already started, use Restart()")
	}

	r.started = true
	go func() {
		for {
			log.Printf("running %s", r.shellCmd)
			r.cmd = exec.Command("sh", "-c", r.shellCmd)
			r.cmd.Stderr = os.Stderr
			r.cmd.Stdout = os.Stdout
			r.cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
			err := r.cmd.Run()
			var result interface{}
			if err != nil {
				result = err
			} else {
				result = r.cmd.ProcessState
			}
			log.Printf("%s finished: %s", r.shellCmd, result)

			time.Sleep(*delay)
			if !r.restarting && !r.restart {
				r.started = false
				break
			}

			r.restarting = false
		}
	}()

	return nil
}

func (r *Runner) Kill() error {
	pgid, err := syscall.Getpgid(r.cmd.Process.Pid)
	if err == nil {
		syscall.Kill(-pgid, syscall.SIGTERM)
	}
	return err
}

func (r *Runner) Restart() error {
	r.restarting = true
	if r.started {
		return r.Kill()
	} else {
		return r.Start()
	}
}

func (r *Runner) Stop() {
	r.restart = false
	r.Kill()
}

func isFile(file string) bool {
	info, err := os.Stat(file)
	return err == nil && !info.IsDir()
}
<commit_msg>try to clarify autorestart vs restarting.<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"strings"
	"syscall"
	"time"
)

/*

# the plan

qst: run things quickly

qst - detects the current project type and runs it
qst hello.rb - runs `ruby hello.rb`
qst hello.go - compiles & runs hello.go

*/

var mappings = map[string]func(string) string{
	".go": func(name string) string {
		outpath := strings.TrimSuffix(name, path.Ext(name))
		if !path.IsAbs(outpath) {
			outpath = fmt.Sprintf("./%s", outpath)
		}
		return fmt.Sprintf("go build -o %s %s && %s", outpath, name, outpath)
	},
	".rb": func(name string) string {
		return fmt.Sprintf("ruby %s", name)
	},
	".py": func(name string) string {
		return fmt.Sprintf("python %s", name)
	},
}

var delay = flag.Duration("delay", 1*time.Second, "time to wait until restart")
var autoRestart = flag.Bool("autorestart", true, "automatically restart after command exists")
var command = flag.String("command", "", "command to run ({file} will be substituted)")

func main() {
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s <file>\n", os.Args[0])
		flag.PrintDefaults()
	}

	flag.Parse()
	args := flag.Args()

	if len(args) < 1 {
		flag.Usage()
		os.Exit(1)
	}

	file := args[0]
	if !isFile(file) {
		fmt.Fprintf(os.Stderr, "Error: %s is not a file.\n", file)
		os.Exit(1)
	}

	var cmd string
	if command != nil && strings.TrimSpace(*command) != "" {
		cmd = strings.Replace(*command, "{file}", file, -1)
	} else {
		ext := path.Ext(file)
		fn, found := mappings[ext]
		if !found {
			log.Fatalf("error: no mapping found for `%s'", file)
		}
		cmd = fn(file)
	}
	log.Printf("command to run: `%s'", cmd)

	runner := MakeRunner(cmd, *autoRestart)
	go runCmd(file, runner)

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)
	s := <-c
	log.Printf("got signal: %s, exiting...", s)
	runner.Stop()
}

func runCmd(file string, runner *Runner) {
	runner.Start()
	lastMtime := time.Now()
	for {
		info, err := os.Stat(file)
		if os.IsNotExist(err) {
			log.Fatalf("`%s' disappeared, exiting")
		}

		mtime := info.ModTime()
		if mtime.After(lastMtime) {
			log.Printf("`%s' changed, trying to restart", file)
			runner.Restart()
		}

		lastMtime = mtime
		time.Sleep(1 * time.Second)
	}
}

type Runner struct {
	cmd         *exec.Cmd
	shellCmd    string
	started     bool
	autoRestart bool
	restarting  bool
}

func MakeRunner(shellCmd string, autoRestart bool) *Runner {
	return &Runner{nil, shellCmd, false, autoRestart, false}
}

func (r *Runner) Start() error {
	if r.started {
		return errors.New("already started, use Restart()")
	}

	r.started = true
	go func() {
		for {
			log.Printf("running %s", r.shellCmd)
			r.cmd = exec.Command("sh", "-c", r.shellCmd)
			r.cmd.Stderr = os.Stderr
			r.cmd.Stdout = os.Stdout
			r.cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
			err := r.cmd.Run()
			var result interface{}
			if err != nil {
				result = err
			} else {
				result = r.cmd.ProcessState
			}
			log.Printf("%s finished: %s", r.shellCmd, result)

			time.Sleep(*delay)
			if !r.restarting && !r.autoRestart {
				r.started = false
				break
			}

			r.restarting = false
		}
	}()

	return nil
}

func (r *Runner) Kill() error {
	pgid, err := syscall.Getpgid(r.cmd.Process.Pid)
	if err == nil {
		syscall.Kill(-pgid, syscall.SIGTERM)
	}
	return err
}

func (r *Runner) Restart() error {
	r.restarting = true
	if r.started {
		return r.Kill()
	} else {
		return r.Start()
	}
}

func (r *Runner) Stop() {
	r.autoRestart = false
	r.Kill()
}

func isFile(file string) bool {
	info, err := os.Stat(file)
	return err == nil && !info.IsDir()
}
<|endoftext|>


<commit_before>package asn1

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"strconv"
)

// ASN.1 class tags.
const (
	classUniversal       = 0x00
	classApplication     = 0x01
	classContextSpecific = 0x02
	classPrivate         = 0x03
)

// ASN.1 universal tag numbers.
const (
	tagEoc             = 0x00
	tagBoolean         = 0x01
	tagInteger         = 0x02
	tagBitString       = 0x03
	tagOctetString     = 0x04
	tagNull            = 0x05
	tagOid             = 0x06
	tagSequence        = 0x10
	tagSet             = 0x11
	tagPrintableString = 0x13
	tagT61String       = 0x14
	tagIA5String       = 0x16
	tagUtcTime         = 0x17
)

// Internal consts
const (
	intBits  = strconv.IntSize
	intBytes = intBits / 8
)

type rawValue struct {
	Class       uint
	Tag         uint
	Constructed bool
	Indefinite  bool
	Content     []byte
}

func (raw *rawValue) encode() ([]byte, error) {

	if raw == nil {
		return []byte{}, nil
	}

	buf, err := encodeIdentifier(raw)
	if err != nil {
		return nil, err
	}

	// Add length information and raw data
	if !raw.Indefinite {
		length := uint(len(raw.Content))
		buf = append(buf, encodeLength(length)...)
		buf = append(buf, raw.Content...)
	} else {
		// Indefinite length uses 0x80, data..., 0x00, 0x00
		if !raw.Constructed {
			return nil, fmt.Errorf("Indefinite length is only allowed to constructed types")
		}
		buf = append(buf, 0x80)
		buf = append(buf, raw.Content...)
		buf = append(buf, 0x00, 0x00)
	}

	return buf, nil
}

func encodeIdentifier(node *rawValue) ([]byte, error) {

	if node.Class > 0x03 {
		return nil, fmt.Errorf("Invalid class value: %d", node.Class)
	}

	identifier := []byte{0x00}

	// Class (bits 7 and 6) + primitive/constructed (1 bit) + tag (5 bits)
	identifier[0] += byte((node.Class & 0x03) << 6)

	// Primitive/constructed (bit 5)
	if node.Constructed {
		identifier[0] += byte(1 << 5)
	}

	// Tag (bits 4 to 0)
	if node.Tag <= 30 {
		identifier[0] += byte(0x1f & node.Tag)
	} else {
		identifier[0] += 0x1f
		identifier = append(identifier, encodeMultiByteTag(node.Tag)...)
	}
	return identifier, nil
}

func encodeMultiByteTag(tag uint) []byte {

	// A tag is encoded in a big endian sequence of octets each one holding a 7 bit value.
	// The most significant bit of each octet must be 1, with the exception of the last octet that must be zero.
	// Exemple:  1xxxxxxx 1xxxxxxx ... 0xxxxxxx

	// An int32 needs 5 octets and an int64 needs 10:
	bufLen := (intBits-1)/7 + 1
	buf := make([]byte, bufLen)

	for i := range buf {
		shift := uint(7 * (len(buf) - i - 1))
		mask := uint(0x7f << shift)
		buf[i] = byte((tag & mask) >> shift)
		// Only the last byte is not marked with 0x80
		if i != len(buf)-1 {
			buf[i] |= 0x80
		}
	}
	// Discard leading zero values
	return removeLeadingBytes(buf, 0x80)
}

func encodeLength(length uint) []byte {

	// The first bit indicates if length is encoded in a single byte
	if length < 0x80 {
		return []byte{byte(length)}
	}

	// Multi byte length follow the rules:
	// - First byte: 0x80 + N (number of following bytes where length is encoded).
	// - N bytes

	// A byte slice length is an int. So we just need at most 4 bytes
	buf := make([]byte, intBytes)
	for i := range buf {
		shift := uint((intBytes - i - 1) * 8)
		mask := uint(0xff << shift)
		buf[i] = byte((mask & length) >> shift)
	}

	// Ignore leading zeros
	buf = removeLeadingBytes(buf, 0x00)

	// Add leading byte with the number of following bytes
	buf = append([]byte{0x80 + byte(len(buf))}, buf...)
	return buf
}

func removeLeadingBytes(buf []byte, target byte) []byte {
	start := 0
	for start < len(buf)-1 && buf[start] == target {
		start++
	}
	return buf[start:]
}

func decodeRawValue(reader io.Reader) (*rawValue, error) {

	class, tag, constructed, err := decodeIdentifier(reader)
	if err != nil {
		return nil, err
	}

	length, indefinite, err := decodeLength(reader)
	if err != nil {
		return nil, err
	}
	if indefinite && !constructed {
		return nil, fmt.Errorf("Primitive node with indefinite length.")
	}

	// Indefinite form
	var content []byte
	if !indefinite {
		content = make([]byte, length)
		_, err = io.ReadFull(reader, content)
		if err != nil {
			return nil, err
		}
	} else {
		buffer := bytes.NewBuffer([]byte{})
		childrenReader := io.TeeReader(reader, buffer)
		err := readEoc(childrenReader)
		if err != nil {
			return nil, err
		}
		// At this point, buffer also contains the EoC bytes
		content = buffer.Bytes()
		content = content[:len(content)-2]
	}

	raw := rawValue{class, tag, constructed, indefinite, content}
	return &raw, nil
}

func readEoc(reader io.Reader) error {

	for {
		class, tag, constructed, err := decodeIdentifier(reader)
		if err != nil {
			return err
		}

		length, indefinite, err := decodeLength(reader)
		if err != nil {
			return err
		}

		if indefinite && !constructed {
			return fmt.Errorf("Primitive node with indefinite length.")
		}

		if class == 0 && tag == 0 && indefinite == false && length == 0 {
			break
		}

		if indefinite {
			err = readEoc(reader)
		} else {
			err = skipBytes(reader, int64(length))
		}
		if err != nil {
			return err
		}
	}
	return nil
}

func decodeMultiByteTag(reader io.Reader) (uint, error) {
	// Tag is encoded in one or more following bytes
	tag := uint(0)
	for {
		// Read a byte
		b, err := readByte(reader)
		if err != nil {
			return 0, err
		}
		// if we need to shift out non zeros bits, so the tag is too big for an uint
		msb := uint64(0xfe) << (intBits - 8) // 7 most significant bits
		if uint64(tag)&msb != 0 {
			return 0, fmt.Errorf("Multi byte tag too big")
		}
		// Shift the previous value and add the new 7 bits
		tag = (tag << 7) | uint(b&0x7f)
		// The last byte is indicated by the most significant bit equals to zero
		if b&0x80 == 0 {
			break
		}
	}
	return tag, nil
}

func decodeIdentifier(reader io.Reader) (class uint, tag uint, constructed bool, err error) {

	b, err := readByte(reader)
	if err != nil {
		return
	}

	// Read class and constructed flag
	class = uint((b & 0xc0) >> 6)
	if b&0x20 != 0 {
		constructed = true
	}

	// Read the tag number
	tag = uint(b & 0x1f)
	if tag == 0x1f {
		// Tag is encoded in one or more following bytes
		tag, err = decodeMultiByteTag(reader)
		if err != nil {
			return
		}
	}
	return
}

func decodeLength(reader io.Reader) (length uint, indefinite bool, err error) {

	// Read length
	b, err := readByte(reader)
	if err != nil {
		return
	}

	// Short form
	if b&0x80 == 0 {
		length = uint(b)
		return
	}

	// Indefinite form
	if b == 0x80 {
		indefinite = true
		return
	}

	// Long form
	if b == 0xff {
		err = fmt.Errorf("Invalid number of length octets: %x", b)
		return
	}
	octets := make([]byte, int(b&0x7f))
	_, err = io.ReadFull(reader, octets)
	if err != nil {
		return
	}
	for _, b = range octets {
		msb := uint64(0xff) << (intBits - 8)
		if uint64(length)&msb != 0 {
			err = fmt.Errorf("Multi byte length too big")
			return
		}
		length = (length << 8) | uint(b)
	}

	return
}

func readByte(reader io.Reader) (byte, error) {
	buf := []byte{0x00}
	_, err := io.ReadFull(reader, buf)
	return buf[0], err
}

func skipBytes(reader io.Reader, count int64) error {
	_, err := io.CopyN(ioutil.Discard, reader, count)
	return err
}
<commit_msg>Fix typo<commit_after>package asn1

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"strconv"
)

// ASN.1 class tags.
const (
	classUniversal       = 0x00
	classApplication     = 0x01
	classContextSpecific = 0x02
	classPrivate         = 0x03
)

// ASN.1 universal tag numbers.
const (
	tagEoc             = 0x00
	tagBoolean         = 0x01
	tagInteger         = 0x02
	tagBitString       = 0x03
	tagOctetString     = 0x04
	tagNull            = 0x05
	tagOid             = 0x06
	tagSequence        = 0x10
	tagSet             = 0x11
	tagPrintableString = 0x13
	tagT61String       = 0x14
	tagIA5String       = 0x16
	tagUtcTime         = 0x17
)

// Internal consts
const (
	intBits  = strconv.IntSize
	intBytes = intBits / 8
)

type rawValue struct {
	Class       uint
	Tag         uint
	Constructed bool
	Indefinite  bool
	Content     []byte
}

func (raw *rawValue) encode() ([]byte, error) {

	if raw == nil {
		return []byte{}, nil
	}

	buf, err := encodeIdentifier(raw)
	if err != nil {
		return nil, err
	}

	// Add length information and raw data
	if !raw.Indefinite {
		length := uint(len(raw.Content))
		buf = append(buf, encodeLength(length)...)
		buf = append(buf, raw.Content...)
	} else {
		// Indefinite length uses 0x80, data..., 0x00, 0x00
		if !raw.Constructed {
			return nil, fmt.Errorf("Indefinite length is only allowed to constructed types")
		}
		buf = append(buf, 0x80)
		buf = append(buf, raw.Content...)
		buf = append(buf, 0x00, 0x00)
	}

	return buf, nil
}

func encodeIdentifier(node *rawValue) ([]byte, error) {

	if node.Class > 0x03 {
		return nil, fmt.Errorf("Invalid class value: %d", node.Class)
	}

	identifier := []byte{0x00}

	// Class (bits 7 and 6) + primitive/constructed (1 bit) + tag (5 bits)
	identifier[0] += byte((node.Class & 0x03) << 6)

	// Primitive/constructed (bit 5)
	if node.Constructed {
		identifier[0] += byte(1 << 5)
	}

	// Tag (bits 4 to 0)
	if node.Tag <= 30 {
		identifier[0] += byte(0x1f & node.Tag)
	} else {
		identifier[0] += 0x1f
		identifier = append(identifier, encodeMultiByteTag(node.Tag)...)
	}
	return identifier, nil
}

func encodeMultiByteTag(tag uint) []byte {

	// A tag is encoded in a big endian sequence of octets each one holding a 7 bit value.
	// The most significant bit of each octet must be 1, with the exception of the last octet that must be zero.
	// Example:  1xxxxxxx 1xxxxxxx ... 0xxxxxxx

	// An int32 needs 5 octets and an int64 needs 10:
	bufLen := (intBits-1)/7 + 1
	buf := make([]byte, bufLen)

	for i := range buf {
		shift := uint(7 * (len(buf) - i - 1))
		mask := uint(0x7f << shift)
		buf[i] = byte((tag & mask) >> shift)
		// Only the last byte is not marked with 0x80
		if i != len(buf)-1 {
			buf[i] |= 0x80
		}
	}
	// Discard leading zero values
	return removeLeadingBytes(buf, 0x80)
}

func encodeLength(length uint) []byte {

	// The first bit indicates if length is encoded in a single byte
	if length < 0x80 {
		return []byte{byte(length)}
	}

	// Multi byte length follow the rules:
	// - First byte: 0x80 + N (number of following bytes where length is encoded).
	// - N bytes

	// A byte slice length is an int. So we just need at most 4 bytes
	buf := make([]byte, intBytes)
	for i := range buf {
		shift := uint((intBytes - i - 1) * 8)
		mask := uint(0xff << shift)
		buf[i] = byte((mask & length) >> shift)
	}

	// Ignore leading zeros
	buf = removeLeadingBytes(buf, 0x00)

	// Add leading byte with the number of following bytes
	buf = append([]byte{0x80 + byte(len(buf))}, buf...)
	return buf
}

func removeLeadingBytes(buf []byte, target byte) []byte {
	start := 0
	for start < len(buf)-1 && buf[start] == target {
		start++
	}
	return buf[start:]
}

func decodeRawValue(reader io.Reader) (*rawValue, error) {

	class, tag, constructed, err := decodeIdentifier(reader)
	if err != nil {
		return nil, err
	}

	length, indefinite, err := decodeLength(reader)
	if err != nil {
		return nil, err
	}
	if indefinite && !constructed {
		return nil, fmt.Errorf("Primitive node with indefinite length.")
	}

	// Indefinite form
	var content []byte
	if !indefinite {
		content = make([]byte, length)
		_, err = io.ReadFull(reader, content)
		if err != nil {
			return nil, err
		}
	} else {
		buffer := bytes.NewBuffer([]byte{})
		childrenReader := io.TeeReader(reader, buffer)
		err := readEoc(childrenReader)
		if err != nil {
			return nil, err
		}
		// At this point, buffer also contains the EoC bytes
		content = buffer.Bytes()
		content = content[:len(content)-2]
	}

	raw := rawValue{class, tag, constructed, indefinite, content}
	return &raw, nil
}

func readEoc(reader io.Reader) error {

	for {
		class, tag, constructed, err := decodeIdentifier(reader)
		if err != nil {
			return err
		}

		length, indefinite, err := decodeLength(reader)
		if err != nil {
			return err
		}

		if indefinite && !constructed {
			return fmt.Errorf("Primitive node with indefinite length.")
		}

		if class == 0 && tag == 0 && indefinite == false && length == 0 {
			break
		}

		if indefinite {
			err = readEoc(reader)
		} else {
			err = skipBytes(reader, int64(length))
		}
		if err != nil {
			return err
		}
	}
	return nil
}

func decodeMultiByteTag(reader io.Reader) (uint, error) {
	// Tag is encoded in one or more following bytes
	tag := uint(0)
	for {
		// Read a byte
		b, err := readByte(reader)
		if err != nil {
			return 0, err
		}
		// if we need to shift out non zeros bits, so the tag is too big for an uint
		msb := uint64(0xfe) << (intBits - 8) // 7 most significant bits
		if uint64(tag)&msb != 0 {
			return 0, fmt.Errorf("Multi byte tag too big")
		}
		// Shift the previous value and add the new 7 bits
		tag = (tag << 7) | uint(b&0x7f)
		// The last byte is indicated by the most significant bit equals to zero
		if b&0x80 == 0 {
			break
		}
	}
	return tag, nil
}

func decodeIdentifier(reader io.Reader) (class uint, tag uint, constructed bool, err error) {

	b, err := readByte(reader)
	if err != nil {
		return
	}

	// Read class and constructed flag
	class = uint((b & 0xc0) >> 6)
	if b&0x20 != 0 {
		constructed = true
	}

	// Read the tag number
	tag = uint(b & 0x1f)
	if tag == 0x1f {
		// Tag is encoded in one or more following bytes
		tag, err = decodeMultiByteTag(reader)
		if err != nil {
			return
		}
	}
	return
}

func decodeLength(reader io.Reader) (length uint, indefinite bool, err error) {

	// Read length
	b, err := readByte(reader)
	if err != nil {
		return
	}

	// Short form
	if b&0x80 == 0 {
		length = uint(b)
		return
	}

	// Indefinite form
	if b == 0x80 {
		indefinite = true
		return
	}

	// Long form
	if b == 0xff {
		err = fmt.Errorf("Invalid number of length octets: %x", b)
		return
	}
	octets := make([]byte, int(b&0x7f))
	_, err = io.ReadFull(reader, octets)
	if err != nil {
		return
	}
	for _, b = range octets {
		msb := uint64(0xff) << (intBits - 8)
		if uint64(length)&msb != 0 {
			err = fmt.Errorf("Multi byte length too big")
			return
		}
		length = (length << 8) | uint(b)
	}

	return
}

func readByte(reader io.Reader) (byte, error) {
	buf := []byte{0x00}
	_, err := io.ReadFull(reader, buf)
	return buf[0], err
}

func skipBytes(reader io.Reader, count int64) error {
	_, err := io.CopyN(ioutil.Discard, reader, count)
	return err
}
<|endoftext|>


<commit_before>// Package red implements server speaking redis serialization protocol.
package red

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"net"
	"os"
	"runtime/debug"
	"strings"
	"sync"
	"time"

	"github.com/artyom/resp"
)

// HandlerFunc is a type of command processing function. It should return value
// that's passed to resp.Encode function, so it's expected such function would
// return values of resp package types.
type HandlerFunc func(req Request) (interface{}, error)

// Request holds information about single redis command.
type Request struct {
	Name string   // lowercase command itself (first word)
	Args []string // command arguments
}

// NewServer returns initialized server.
func NewServer() *Server {
	return &Server{log: noopLogger{}}
}

// WithLogger configures server to use provided Logger.
func (s *Server) WithLogger(l Logger) {
	if l != nil {
		s.log = l
	}
}

// WithUnsafeTx disables command processing serialization that guarantees
// redis-like transaction safety.
func (s *Server) WithUnsafeTx() { s.unsafe = true }

// Handle registers handler for command with given name (case-insensitive)
func (s *Server) Handle(name string, h HandlerFunc) {
	if name == "" {
		panic("Handle called with empty name")
	}
	if h == nil {
		panic("Handle called with nil HandlerFunc")
	}
	if s.handlers == nil {
		s.handlers = make(map[string]HandlerFunc)
	}
	s.handlers[strings.ToLower(name)] = h
}

// Server implements server speaking RESP (REdis Serialization Protocol). Server
// automatically handles MULTI, EXEC, DISCARD commands for transactions, QUIT for
// client-initiated disconnect, other commands are expected to be implemented
// separately and registered with Handle method.
type Server struct {
	log      Logger
	handlers map[string]HandlerFunc
	mu       sync.Mutex // used to serialize transactions
	unsafe   bool       // whether to skip transaction serialization
}

func (s *Server) txLock() {
	if s.unsafe {
		return
	}
	s.mu.Lock()
}
func (s *Server) txUnlock() {
	if s.unsafe {
		return
	}
	s.mu.Unlock()
}

// HandleConn processes single client connection, automatically handling
// following commands MULTI/EXEC/DISCARD (transactions), QUIT (client disconnect). It
// calls user-provided handlers for registered commands.
func (s *Server) HandleConn(conn io.ReadWriteCloser) error {
	defer conn.Close()
	rd := bufio.NewReader(conn)
	var tx []Request
	var inTx bool  // if we're inside transaction
	var errTx bool // true if transaction seen error and should be discarded
	var err error
	for {
		if err != nil {
			return err
		}
		req, err := resp.DecodeRequest(rd)
		switch err {
		case nil:
		case resp.ErrInvalidRequest:
			err = resp.Encode(conn, resp.Error("ERR unknown command"))
			continue
		default:
			return err
		}
		cmd := strings.ToLower(req[0])
		switch cmd {
		case "quit":
			return nil
		case "discard":
			if len(req) != 1 {
				if inTx {
					errTx = true
				}
				err = resp.Encode(conn, errWrongArgs(cmd))
				continue
			}
			if !inTx {
				err = resp.Encode(conn, resp.Error("ERR DISCARD without MULTI"))
				continue
			}
			inTx, errTx, tx = false, false, tx[:0]
			err = resp.Encode(conn, resp.OK)
			continue
		case "multi":
			if len(req) != 1 {
				if inTx {
					errTx = true
				}
				err = resp.Encode(conn, errWrongArgs(cmd))
				continue
			}
			if inTx {
				errTx = true
				err = resp.Encode(conn, resp.Error("ERR MULTI calls can not be nested"))
				continue
			}
			inTx, errTx = true, false
			err = resp.Encode(conn, resp.OK)
			continue
		case "exec":
			if len(req) != 1 {
				if inTx {
					errTx = true
				}
				err = resp.Encode(conn, errWrongArgs(cmd))
				continue
			}
			if !inTx {
				err = resp.Encode(conn, resp.Error("ERR EXEC without MULTI"))
				continue
			}
			if errTx {
				inTx, errTx = false, false
				tx = tx[:0]
				err = resp.Encode(conn, resp.Error("EXECABORT Transaction discarded because of previous errors."))
				continue
			}
		default:
			h, ok := s.handlers[cmd]
			if !ok {
				if inTx {
					errTx = true
				}
				err = resp.Encode(conn, errNoCmd(cmd))
				continue
			}
			if inTx {
				if !errTx {
					tx = append(tx, Request{Name: cmd, Args: req[1:]})
				}
				err = resp.Encode(conn, resp.SimpleString("QUEUED"))
				continue
			}
			s.txLock()
			err = resp.Encode(conn, runHandler(h, Request{Name: cmd, Args: req[1:]}))
			s.txUnlock()
			continue
		}

		txReplies := make(resp.Array, 0, len(tx))
		s.txLock()
		for _, r := range tx {
			h, ok := s.handlers[r.Name]
			if !ok {
				txReplies = append(txReplies, errNoCmd(r.Name))
				continue
			}
			txReplies = append(txReplies, runHandler(h, r))
		}
		s.txUnlock()
		inTx, errTx = false, false
		tx = tx[:0]
		err = resp.Encode(conn, txReplies)
	}
}

// ListenAndServe listens on TCP network address addr and then calls Serve to
// handle requests on incoming connections.
func (s *Server) ListenAndServe(addr string) error {
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return s.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

// Serve accepts incoming connections on the Listener l, creating a new service
// goroutine for each.
func (s *Server) Serve(l net.Listener) error {
	defer l.Close()
	for {
		conn, err := l.Accept()
		if err != nil {
			return err
		}
		go func(c net.Conn) {
			switch err := s.HandleConn(c); err {
			case nil, io.EOF:
			default:
				if s.log != nil {
					s.log.Println(err)
				}
			}
		}(conn)
	}
}

// Logger is a set of methods used to log information. *log.Logger implements
// this interface.
type Logger interface {
	Print(v ...interface{})
	Printf(format string, v ...interface{})
	Println(v ...interface{})
}

type noopLogger struct{}

func (noopLogger) Print(v ...interface{})                 {}
func (noopLogger) Printf(format string, v ...interface{}) {}
func (noopLogger) Println(v ...interface{})               {}

func errNoCmd(name string) resp.Error { return resp.Error("ERR unknown command '" + name + "'") }

// ErrWrongArgs are expected to be returned by HandlerFunc implementations when
// number of arguments are wrong. This error is automatically passed to client
// with command name annotated.
var ErrWrongArgs = errors.New("wrong number of arguments")

func errWrongArgs(name string) resp.Error {
	return resp.Error("ERR wrong number of arguments for '" + name + "' command")
}

// runHandler calls HandlerFunc h with given Request r and returns single value
// that is expected to be passed to resp.Encode.
//
// If h returns a non-nil error or panics, then runHandler returns resp.Error
// type, otherwise it's an interface{} returned by h.
func runHandler(h HandlerFunc, r Request) interface{} {
	var panicked bool
	v, err := func() (interface{}, error) {
		defer func() {
			if x := recover(); x != nil {
				panicked = true
				fmt.Fprintln(os.Stderr, "panic recovered:", x)
				debug.PrintStack()
			}
		}()
		return h(r)
	}()
	if panicked {
		return resp.Error("ERR internal server error (panic)")
	}
	if err != nil {
		if err == ErrWrongArgs {
			return errWrongArgs(r.Name)
		}
		text := err.Error()
		if strings.ContainsAny(text, "\r\n") {
			text = strings.Replace(text, "\r", " ", -1)
			text = strings.Replace(text, "\n", " ", -1)
		}
		return resp.Error("ERR " + text)
	}
	return v
}

// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}
<commit_msg>Return OK response on QUIT command<commit_after>// Package red implements server speaking redis serialization protocol.
package red

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"net"
	"os"
	"runtime/debug"
	"strings"
	"sync"
	"time"

	"github.com/artyom/resp"
)

// HandlerFunc is a type of command processing function. It should return value
// that's passed to resp.Encode function, so it's expected such function would
// return values of resp package types.
type HandlerFunc func(req Request) (interface{}, error)

// Request holds information about single redis command.
type Request struct {
	Name string   // lowercase command itself (first word)
	Args []string // command arguments
}

// NewServer returns initialized server.
func NewServer() *Server {
	return &Server{log: noopLogger{}}
}

// WithLogger configures server to use provided Logger.
func (s *Server) WithLogger(l Logger) {
	if l != nil {
		s.log = l
	}
}

// WithUnsafeTx disables command processing serialization that guarantees
// redis-like transaction safety.
func (s *Server) WithUnsafeTx() { s.unsafe = true }

// Handle registers handler for command with given name (case-insensitive)
func (s *Server) Handle(name string, h HandlerFunc) {
	if name == "" {
		panic("Handle called with empty name")
	}
	if h == nil {
		panic("Handle called with nil HandlerFunc")
	}
	if s.handlers == nil {
		s.handlers = make(map[string]HandlerFunc)
	}
	s.handlers[strings.ToLower(name)] = h
}

// Server implements server speaking RESP (REdis Serialization Protocol). Server
// automatically handles MULTI, EXEC, DISCARD commands for transactions, QUIT for
// client-initiated disconnect, other commands are expected to be implemented
// separately and registered with Handle method.
type Server struct {
	log      Logger
	handlers map[string]HandlerFunc
	mu       sync.Mutex // used to serialize transactions
	unsafe   bool       // whether to skip transaction serialization
}

func (s *Server) txLock() {
	if s.unsafe {
		return
	}
	s.mu.Lock()
}
func (s *Server) txUnlock() {
	if s.unsafe {
		return
	}
	s.mu.Unlock()
}

// HandleConn processes single client connection, automatically handling
// following commands MULTI/EXEC/DISCARD (transactions), QUIT (client disconnect). It
// calls user-provided handlers for registered commands.
func (s *Server) HandleConn(conn io.ReadWriteCloser) error {
	defer conn.Close()
	rd := bufio.NewReader(conn)
	var tx []Request
	var inTx bool  // if we're inside transaction
	var errTx bool // true if transaction seen error and should be discarded
	var err error
	for {
		if err != nil {
			return err
		}
		req, err := resp.DecodeRequest(rd)
		switch err {
		case nil:
		case resp.ErrInvalidRequest:
			err = resp.Encode(conn, resp.Error("ERR unknown command"))
			continue
		default:
			return err
		}
		cmd := strings.ToLower(req[0])
		switch cmd {
		case "quit":
			return resp.Encode(conn, resp.OK)
		case "discard":
			if len(req) != 1 {
				if inTx {
					errTx = true
				}
				err = resp.Encode(conn, errWrongArgs(cmd))
				continue
			}
			if !inTx {
				err = resp.Encode(conn, resp.Error("ERR DISCARD without MULTI"))
				continue
			}
			inTx, errTx, tx = false, false, tx[:0]
			err = resp.Encode(conn, resp.OK)
			continue
		case "multi":
			if len(req) != 1 {
				if inTx {
					errTx = true
				}
				err = resp.Encode(conn, errWrongArgs(cmd))
				continue
			}
			if inTx {
				errTx = true
				err = resp.Encode(conn, resp.Error("ERR MULTI calls can not be nested"))
				continue
			}
			inTx, errTx = true, false
			err = resp.Encode(conn, resp.OK)
			continue
		case "exec":
			if len(req) != 1 {
				if inTx {
					errTx = true
				}
				err = resp.Encode(conn, errWrongArgs(cmd))
				continue
			}
			if !inTx {
				err = resp.Encode(conn, resp.Error("ERR EXEC without MULTI"))
				continue
			}
			if errTx {
				inTx, errTx = false, false
				tx = tx[:0]
				err = resp.Encode(conn, resp.Error("EXECABORT Transaction discarded because of previous errors."))
				continue
			}
		default:
			h, ok := s.handlers[cmd]
			if !ok {
				if inTx {
					errTx = true
				}
				err = resp.Encode(conn, errNoCmd(cmd))
				continue
			}
			if inTx {
				if !errTx {
					tx = append(tx, Request{Name: cmd, Args: req[1:]})
				}
				err = resp.Encode(conn, resp.SimpleString("QUEUED"))
				continue
			}
			s.txLock()
			err = resp.Encode(conn, runHandler(h, Request{Name: cmd, Args: req[1:]}))
			s.txUnlock()
			continue
		}

		txReplies := make(resp.Array, 0, len(tx))
		s.txLock()
		for _, r := range tx {
			h, ok := s.handlers[r.Name]
			if !ok {
				txReplies = append(txReplies, errNoCmd(r.Name))
				continue
			}
			txReplies = append(txReplies, runHandler(h, r))
		}
		s.txUnlock()
		inTx, errTx = false, false
		tx = tx[:0]
		err = resp.Encode(conn, txReplies)
	}
}

// ListenAndServe listens on TCP network address addr and then calls Serve to
// handle requests on incoming connections.
func (s *Server) ListenAndServe(addr string) error {
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return s.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

// Serve accepts incoming connections on the Listener l, creating a new service
// goroutine for each.
func (s *Server) Serve(l net.Listener) error {
	defer l.Close()
	for {
		conn, err := l.Accept()
		if err != nil {
			return err
		}
		go func(c net.Conn) {
			switch err := s.HandleConn(c); err {
			case nil, io.EOF:
			default:
				if s.log != nil {
					s.log.Println(err)
				}
			}
		}(conn)
	}
}

// Logger is a set of methods used to log information. *log.Logger implements
// this interface.
type Logger interface {
	Print(v ...interface{})
	Printf(format string, v ...interface{})
	Println(v ...interface{})
}

type noopLogger struct{}

func (noopLogger) Print(v ...interface{})                 {}
func (noopLogger) Printf(format string, v ...interface{}) {}
func (noopLogger) Println(v ...interface{})               {}

func errNoCmd(name string) resp.Error { return resp.Error("ERR unknown command '" + name + "'") }

// ErrWrongArgs are expected to be returned by HandlerFunc implementations when
// number of arguments are wrong. This error is automatically passed to client
// with command name annotated.
var ErrWrongArgs = errors.New("wrong number of arguments")

func errWrongArgs(name string) resp.Error {
	return resp.Error("ERR wrong number of arguments for '" + name + "' command")
}

// runHandler calls HandlerFunc h with given Request r and returns single value
// that is expected to be passed to resp.Encode.
//
// If h returns a non-nil error or panics, then runHandler returns resp.Error
// type, otherwise it's an interface{} returned by h.
func runHandler(h HandlerFunc, r Request) interface{} {
	var panicked bool
	v, err := func() (interface{}, error) {
		defer func() {
			if x := recover(); x != nil {
				panicked = true
				fmt.Fprintln(os.Stderr, "panic recovered:", x)
				debug.PrintStack()
			}
		}()
		return h(r)
	}()
	if panicked {
		return resp.Error("ERR internal server error (panic)")
	}
	if err != nil {
		if err == ErrWrongArgs {
			return errWrongArgs(r.Name)
		}
		text := err.Error()
		if strings.ContainsAny(text, "\r\n") {
			text = strings.Replace(text, "\r", " ", -1)
			text = strings.Replace(text, "\n", " ", -1)
		}
		return resp.Error("ERR " + text)
	}
	return v
}

// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}
<|endoftext|>


<commit_before>// Copyright 2017 ibelie, Chen Jie, Joungtao. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package rpc

import (
	"fmt"
	"log"
	"os"
	"path"
	"reflect"
	"sort"
	"strings"

	"go/ast"
	"go/build"
	"go/doc"
	"go/parser"
	"go/token"

	"github.com/ibelie/tygo"
)

var (
	SRC_PATH = path.Join(os.Getenv("GOPATH"), "src")
	PKG_PATH = reflect.TypeOf(Depend{}).PkgPath()
)

type Depend struct {
	Path     string
	Services []string
}

type Component struct {
	Name     string
	Path     string
	Methods  []string
	Protocol *tygo.Object
	Service  *doc.Type
}

type Entity struct {
	Name       string
	Components []*Component
}

func Extract(dir string) (pkgname string, depends []*Depend) {
	buildPackage, err := build.Import(dir, "", build.ImportComment)
	if err != nil {
		log.Fatalf("[RPC][Entity] Cannot import package:\n>>>> %v", err)
		return
	}
	fs := token.NewFileSet()
	for _, filename := range buildPackage.GoFiles {
		file, err := parser.ParseFile(fs, path.Join(buildPackage.Dir, filename), nil, parser.ParseComments)
		if err != nil {
			log.Fatalf("[RPC][Entity] Cannot parse file:\n>>>> %v", err)
		}
		pkgname = file.Name.Name
	file_loop:
		for _, d := range file.Decls {
			decl, ok := d.(*ast.GenDecl)
			if !ok || decl.Tok != token.IMPORT {
				continue
			}
			for _, s := range decl.Specs {
				spec, ok := s.(*ast.ImportSpec)
				if !ok || strings.Trim(spec.Path.Value, "\"") != PKG_PATH {
					continue
				}
				if strings.TrimSpace(decl.Doc.Text()) != "" {
					depends = merge(depends, parse(decl.Doc.Text())...)
				}
				break file_loop
			}
		}
	}
	return
}

func parse(code string) (depends []*Depend) {
	code = strings.Split(code, "depends on:")[1]
	for _, line := range strings.Split(code, "\n") {
		tokens := strings.Split(line, "from")
		if len(tokens) != 2 {
			continue
		}
		depends = merge(depends, &Depend{
			Services: []string{strings.TrimSpace(tokens[0])},
			Path:     strings.TrimSpace(tokens[1]),
		})
	}
	return
}

func merge(a []*Depend, b ...*Depend) (c []*Depend) {
	var sorted []string
	m := make(map[string]map[string]bool)
	for _, ab := range [][]*Depend{a, b} {
		for _, x := range ab {
			if _, ok := m[x.Path]; !ok {
				m[x.Path] = make(map[string]bool)
				sorted = append(sorted, x.Path)
			}
			for _, y := range x.Services {
				m[x.Path][y] = true
			}
		}
	}
	sort.Strings(sorted)
	for _, p := range sorted {
		var s []string
		for x, _ := range m[p] {
			s = append(s, x)
		}
		sort.Strings(s)
		c = append(c, &Depend{Path: p, Services: s})
	}
	return
}

func update(a map[string]string, b map[string]string) map[string]string {
	if b == nil {
		return a
	} else if a == nil {
		a = make(map[string]string)
	}
	for k, v := range b {
		a[k] = v
	}
	return a
}

func isComponent(t tygo.Type) (*tygo.Object, bool) {
	object, ok := t.(*tygo.Object)
	return object, ok && object.Parent.Name == "Entity"
}

func hasMethod(object *doc.Type, method *tygo.Method) bool {
	for _, m := range object.Methods {
		if m.Name == method.Name {
			return true
		}
	}
	return false
}

func (c *Component) isService() bool {
	if hasMethod(c.Service, &tygo.Method{Name: "onCreate"}) {
		return true
	} else if hasMethod(c.Service, &tygo.Method{Name: "onDestroy"}) {
		return true
	}
	for _, m := range c.Protocol.Methods {
		if hasMethod(c.Service, m) {
			return true
		}
	}
	return false
}

func packageDoc(path string) *doc.Package {
	p, err := build.Import(path, "", build.ImportComment)
	if err != nil {
		return nil
	}
	fs := token.NewFileSet()
	include := func(info os.FileInfo) bool {
		for _, name := range p.GoFiles {
			if name == info.Name() {
				return true
			}
		}
		return false
	}

	if pkgs, err := parser.ParseDir(fs, p.Dir, include, parser.ParseComments); err != nil || len(pkgs) != 1 {
		return nil
	} else {
		return doc.New(pkgs[p.Name], p.ImportPath, doc.AllDecls)
	}
}

func resolveTypes(typesMap map[string]tygo.Type, typ tygo.Type) {
	switch t := typ.(type) {
	case *tygo.InstanceType:
		if _, exist := typesMap[t.Name]; !exist && (t.Name != "Entity" || t.PkgName != "" || t.PkgPath != "") {
			for _, typ := range tygo.Extract(t.PkgPath, ReplaceEntity) {
				switch t := typ.(type) {
				case *tygo.Object:
					typesMap[t.Name] = t
					resolveTypes(typesMap, t)
				case *tygo.Enum:
					typesMap[t.Name] = t
					resolveTypes(typesMap, t)
				}
			}
			if _, exist := typesMap[t.Name]; !exist {
				log.Fatalf("[RPC][Entity] Cannot resolve type: %v", t)
			}
		}
	case *tygo.Object:
		if t.HasParent() {
			resolveTypes(typesMap, t.Parent)
		}
		for _, f := range t.Fields {
			resolveTypes(typesMap, f)
		}
		for _, m := range t.Methods {
			for _, p := range m.Params {
				resolveTypes(typesMap, p)
			}
			for _, r := range m.Results {
				resolveTypes(typesMap, r)
			}
		}
	case *tygo.ListType:
		resolveTypes(typesMap, t.E)
	case *tygo.DictType:
		resolveTypes(typesMap, t.K)
		resolveTypes(typesMap, t.V)
	case *tygo.VariantType:
		for _, ts := range t.Ts {
			resolveTypes(typesMap, ts)
		}
	}
}

func resolveEntities(entities []*Entity) (types []tygo.Type) {
	fs := token.NewFileSet()
	pkgMap := make(map[string][]tygo.Type)
	docMap := make(map[string]map[string]*doc.Type)
	typesMap := make(map[string]tygo.Type)
	for _, e := range entities {
		for _, c := range e.Components {
			ts, ok := pkgMap[c.Path]
			if !ok {
				if p, err := build.Import(c.Path, "", build.ImportComment); err != nil {
					if strings.Contains(err.Error(), fmt.Sprintf("cannot find package %q in any of:\n", c.Path)) {
						log.Printf("[RPC][Entity] Ignore client component %q from %q", c.Name, c.Path)
					} else {
						log.Fatalf("[RPC][Entity] Import component %q from %q:\n>>>> %v", c.Name, c.Path, err)
					}
					continue
				} else {
					include := func(info os.FileInfo) bool {
						for _, name := range p.GoFiles {
							if name == info.Name() {
								return true
							}
						}
						return false
					}
					if pkgs, err := parser.ParseDir(fs, p.Dir, include, parser.ParseComments); err == nil && len(pkgs) == 1 {
						docTypes := make(map[string]*doc.Type)
						for _, t := range doc.New(pkgs[p.Name], p.ImportPath, doc.AllDecls).Types {
							docTypes[t.Name] = t
						}
						docMap[c.Path] = docTypes
					}
					ts = tygo.Extract(c.Path, ReplaceEntity)
					pkgMap[c.Path] = ts
				}
			}
			c.Service = docMap[c.Path][c.Name]
			for _, typ := range ts {
				switch t := typ.(type) {
				case *tygo.Object:
					if t.Name == c.Name {
						c.Protocol = t
					}
					typesMap[t.Name] = t
				case *tygo.Enum:
					typesMap[t.Name] = t
				}
			}
		}
	}

	var typesSorted []string
	for n, _ := range typesMap {
		typesSorted = append(typesSorted, n)
	}
	for _, n := range typesSorted {
		resolveTypes(typesMap, typesMap[n])
	}
	typesSorted = nil
	for n, _ := range typesMap {
		typesSorted = append(typesSorted, n)
	}
	sort.Strings(typesSorted)
	for _, n := range typesSorted {
		types = append(types, typesMap[n])
	}

	return
}
<commit_msg>commit rpc<commit_after>// Copyright 2017 ibelie, Chen Jie, Joungtao. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package rpc

import (
	"fmt"
	"log"
	"os"
	"path"
	"reflect"
	"sort"
	"strings"

	"go/ast"
	"go/build"
	"go/doc"
	"go/parser"
	"go/token"

	"github.com/ibelie/tygo"
)

var (
	SRC_PATH = path.Join(os.Getenv("GOPATH"), "src")
	PKG_PATH = reflect.TypeOf(Depend{}).PkgPath()
)

type Depend struct {
	Path     string
	Services []string
}

type Component struct {
	Name     string
	Path     string
	Methods  []string
	Protocol *tygo.Object
	Service  *doc.Type
}

type Behavior struct {
	Name       string
	Methods    []string
	Components []*Component
}

type Entity struct {
	Name       string
	Behavior   []*Behavior
	Components []*Component
}

func Extract(dir string) (pkgname string, depends []*Depend) {
	buildPackage, err := build.Import(dir, "", build.ImportComment)
	if err != nil {
		log.Fatalf("[RPC][Entity] Cannot import package:\n>>>> %v", err)
		return
	}
	fs := token.NewFileSet()
	for _, filename := range buildPackage.GoFiles {
		file, err := parser.ParseFile(fs, path.Join(buildPackage.Dir, filename), nil, parser.ParseComments)
		if err != nil {
			log.Fatalf("[RPC][Entity] Cannot parse file:\n>>>> %v", err)
		}
		pkgname = file.Name.Name
	file_loop:
		for _, d := range file.Decls {
			decl, ok := d.(*ast.GenDecl)
			if !ok || decl.Tok != token.IMPORT {
				continue
			}
			for _, s := range decl.Specs {
				spec, ok := s.(*ast.ImportSpec)
				if !ok || strings.Trim(spec.Path.Value, "\"") != PKG_PATH {
					continue
				}
				if strings.TrimSpace(decl.Doc.Text()) != "" {
					depends = merge(depends, parse(decl.Doc.Text())...)
				}
				break file_loop
			}
		}
	}
	return
}

func parse(code string) (depends []*Depend) {
	code = strings.Split(code, "depends on:")[1]
	for _, line := range strings.Split(code, "\n") {
		tokens := strings.Split(line, "from")
		if len(tokens) != 2 {
			continue
		}
		depends = merge(depends, &Depend{
			Services: []string{strings.TrimSpace(tokens[0])},
			Path:     strings.TrimSpace(tokens[1]),
		})
	}
	return
}

func merge(a []*Depend, b ...*Depend) (c []*Depend) {
	var sorted []string
	m := make(map[string]map[string]bool)
	for _, ab := range [][]*Depend{a, b} {
		for _, x := range ab {
			if _, ok := m[x.Path]; !ok {
				m[x.Path] = make(map[string]bool)
				sorted = append(sorted, x.Path)
			}
			for _, y := range x.Services {
				m[x.Path][y] = true
			}
		}
	}
	sort.Strings(sorted)
	for _, p := range sorted {
		var s []string
		for x, _ := range m[p] {
			s = append(s, x)
		}
		sort.Strings(s)
		c = append(c, &Depend{Path: p, Services: s})
	}
	return
}

func update(a map[string]string, b map[string]string) map[string]string {
	if b == nil {
		return a
	} else if a == nil {
		a = make(map[string]string)
	}
	for k, v := range b {
		a[k] = v
	}
	return a
}

func isComponent(t tygo.Type) (*tygo.Object, bool) {
	object, ok := t.(*tygo.Object)
	return object, ok && object.Parent.Name == "Entity"
}

func hasMethod(object *doc.Type, method *tygo.Method) bool {
	for _, m := range object.Methods {
		if m.Name == method.Name {
			return true
		}
	}
	return false
}

func (c *Component) isService() bool {
	if hasMethod(c.Service, &tygo.Method{Name: "onCreate"}) {
		return true
	} else if hasMethod(c.Service, &tygo.Method{Name: "onDestroy"}) {
		return true
	}
	for _, m := range c.Protocol.Methods {
		if hasMethod(c.Service, m) {
			return true
		}
	}
	return false
}

func packageDoc(path string) *doc.Package {
	p, err := build.Import(path, "", build.ImportComment)
	if err != nil {
		return nil
	}
	fs := token.NewFileSet()
	include := func(info os.FileInfo) bool {
		for _, name := range p.GoFiles {
			if name == info.Name() {
				return true
			}
		}
		return false
	}

	if pkgs, err := parser.ParseDir(fs, p.Dir, include, parser.ParseComments); err != nil || len(pkgs) != 1 {
		return nil
	} else {
		return doc.New(pkgs[p.Name], p.ImportPath, doc.AllDecls)
	}
}

func resolveTypes(typesMap map[string]tygo.Type, typ tygo.Type) {
	switch t := typ.(type) {
	case *tygo.InstanceType:
		if _, exist := typesMap[t.Name]; !exist && (t.Name != "Entity" || t.PkgName != "" || t.PkgPath != "") {
			for _, typ := range tygo.Extract(t.PkgPath, ReplaceEntity) {
				switch t := typ.(type) {
				case *tygo.Object:
					typesMap[t.Name] = t
					resolveTypes(typesMap, t)
				case *tygo.Enum:
					typesMap[t.Name] = t
					resolveTypes(typesMap, t)
				}
			}
			if _, exist := typesMap[t.Name]; !exist {
				log.Fatalf("[RPC][Entity] Cannot resolve type: %v", t)
			}
		}
	case *tygo.Object:
		if t.HasParent() {
			resolveTypes(typesMap, t.Parent)
		}
		for _, f := range t.Fields {
			resolveTypes(typesMap, f)
		}
		for _, m := range t.Methods {
			for _, p := range m.Params {
				resolveTypes(typesMap, p)
			}
			for _, r := range m.Results {
				resolveTypes(typesMap, r)
			}
		}
	case *tygo.ListType:
		resolveTypes(typesMap, t.E)
	case *tygo.DictType:
		resolveTypes(typesMap, t.K)
		resolveTypes(typesMap, t.V)
	case *tygo.VariantType:
		for _, ts := range t.Ts {
			resolveTypes(typesMap, ts)
		}
	}
}

func resolveEntities(entities []*Entity) (types []tygo.Type) {
	fs := token.NewFileSet()
	pkgMap := make(map[string][]tygo.Type)
	docMap := make(map[string]map[string]*doc.Type)
	typesMap := make(map[string]tygo.Type)
	for _, e := range entities {
		for _, c := range e.Components {
			ts, ok := pkgMap[c.Path]
			if !ok {
				if p, err := build.Import(c.Path, "", build.ImportComment); err != nil {
					if strings.Contains(err.Error(), fmt.Sprintf("cannot find package %q in any of:\n", c.Path)) {
						log.Printf("[RPC][Entity] Ignore client component %q from %q", c.Name, c.Path)
					} else {
						log.Fatalf("[RPC][Entity] Import component %q from %q:\n>>>> %v", c.Name, c.Path, err)
					}
					continue
				} else {
					include := func(info os.FileInfo) bool {
						for _, name := range p.GoFiles {
							if name == info.Name() {
								return true
							}
						}
						return false
					}
					if pkgs, err := parser.ParseDir(fs, p.Dir, include, parser.ParseComments); err == nil && len(pkgs) == 1 {
						docTypes := make(map[string]*doc.Type)
						for _, t := range doc.New(pkgs[p.Name], p.ImportPath, doc.AllDecls).Types {
							docTypes[t.Name] = t
						}
						docMap[c.Path] = docTypes
					}
					ts = tygo.Extract(c.Path, ReplaceEntity)
					pkgMap[c.Path] = ts
				}
			}
			c.Service = docMap[c.Path][c.Name]
			for _, typ := range ts {
				switch t := typ.(type) {
				case *tygo.Object:
					if t.Name == c.Name {
						c.Protocol = t
					}
					typesMap[t.Name] = t
				case *tygo.Enum:
					typesMap[t.Name] = t
				}
			}
		}
	}

	var typesSorted []string
	for n, _ := range typesMap {
		typesSorted = append(typesSorted, n)
	}
	for _, n := range typesSorted {
		resolveTypes(typesMap, typesMap[n])
	}
	typesSorted = nil
	for n, _ := range typesMap {
		typesSorted = append(typesSorted, n)
	}
	sort.Strings(typesSorted)
	for _, n := range typesSorted {
		types = append(types, typesMap[n])
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"io"
	"os"
	"os/exec"
	"path"
	"regexp"

	"github.com/armon/circbuf"
)

const (
	StateStopped = iota
	StateLoading
	StateRunning
	StateShuttingDown
)

const BufferSize = 1024 * 512

var (
	saveCmd = []byte{'\r', '\n', 's', 'a', 'v', 'e', 'a', 'l', 'l', ' ', '\r', '\n'}
	stopCmd = []byte{'\r', '\n', 's', 't', 'o', 'p', ' ', '\r', '\n'}
)

// 2015-09-27 15:33:41 [INFO] [Minecraft-Server] Done (3.959s)! For help, type "help" or "?"
var doneRegex = regexp.MustCompile("^[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9}{2} \\[Info\\] \\[Minecraft-Server\\] Done ")

type Controller struct {
	c       *Config
	running map[int]running
}

func (c *Controller) Start(sID int) error {
	c.c.mu.Lock()
	defer c.c.mu.Unlock()
	s, ok := c.c.Servers[sID]
	if !ok {
		return ErrNoServer
	}
	if s.state != StateStopped {
		return ErrServerRunning
	}
	if s.Map == -1 {
		return ErrNoMap
	}
	m, ok := c.c.Maps[s.Map]
	if !ok {
		return ErrNoMap // Shouldn't happen, different error?
	}
	ps, err := os.Open(path.Join(c.c.ServersDir, s.Path, "properties.server"))
	if err != nil {
		return err
	}
	defer ps.Close()
	pm, err := os.Open(path.Join(c.c.MapsDir, m.Path, "properties.map"))
	if err != nil {
		return err
	}
	defer pm.Close()
	f, err := os.Create(path.Joing(c.c.ServersDir, s.Path, "server.properties"))
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = io.Copy(f, ps)
	if err != nil {
		return err
	}
	_, err = io.Copy(f, pm)
	if err != nil {
		return err
	}

	s.state = StateLoading
	c.c.Servers[sID] = s
	go c.run(s)
	return nil
}

func (c *Controller) Stop(sID int) error {
	c.c.mu.RLock()
	defer c.c.mu.RUnlock()
	r, ok := c.running[sID]
	if !ok {
		return ErrServerNotRunning
	}
	close(r.shutdown)
	delete(c.running, sID)
	return nil
}

// runs in its own goroutine
func (c *Controller) run(s Server) {
	r := running{
		shutdown: make(chan struct{}),
	}
	c.c.mu.Lock()
	c.running[s.ID] = r
	c.c.mu.Unlock()
	cmd := exec.Command(path.Join(s.Path, "server.jar"), s.Args...)
	cmd.Dir = s.Path
	/*r.cb = circbuf.NewBuffer(BufferSize)
	cmd.Stdout = r.cb
	wp, _ := cmd.StdoutPipe()
	r.w.io.MultiWriter(r.cb, wp)
	*/
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
}

type running struct {
	shutdown chan struct{}
	cb       *circbuf.Buffer
	stdin    io.Writer
	w        io.Writer
}

func (r *running) Write(p []byte) (int, error) {
	/*if r.id < 0 {
		return 0, ErrServerNotRunning
	}*/
	return r.w.Write(p)
}

// Errors
var (
	ErrServerRunning    = errors.New("server already running")
	ErrServerNotRunning = errors.New("server not running")
)
<commit_msg>Added shutdown detection<commit_after>package main

import (
	"errors"
	"io"
	"os"
	"os/exec"
	"path"
	"regexp"

	"github.com/armon/circbuf"
)

const (
	StateStopped = iota
	StateLoading
	StateRunning
	StateShuttingDown
)

const BufferSize = 1024 * 512

var (
	saveCmd = []byte{'\r', '\n', 's', 'a', 'v', 'e', 'a', 'l', 'l', ' ', '\r', '\n'}
	stopCmd = []byte{'\r', '\n', 's', 't', 'o', 'p', ' ', '\r', '\n'}
)

// 2015-09-27 15:33:41 [INFO] [Minecraft-Server] Done (3.959s)! For help, type "help" or "?"
var doneRegex = regexp.MustCompile("^[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9}{2} \\[Info\\] \\[Minecraft-Server\\] Done ")

type Controller struct {
	c       *Config
	running map[int]running
}

func (c *Controller) Start(sID int) error {
	c.c.mu.Lock()
	defer c.c.mu.Unlock()
	s, ok := c.c.Servers[sID]
	if !ok {
		return ErrNoServer
	}
	if s.state != StateStopped {
		return ErrServerRunning
	}
	if s.Map == -1 {
		return ErrNoMap
	}
	m, ok := c.c.Maps[s.Map]
	if !ok {
		return ErrNoMap // Shouldn't happen, different error?
	}
	ps, err := os.Open(path.Join(c.c.ServersDir, s.Path, "properties.server"))
	if err != nil {
		return err
	}
	defer ps.Close()
	pm, err := os.Open(path.Join(c.c.MapsDir, m.Path, "properties.map"))
	if err != nil {
		return err
	}
	defer pm.Close()
	f, err := os.Create(path.Join(c.c.ServersDir, s.Path, "server.properties"))
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = io.Copy(f, ps)
	if err != nil {
		return err
	}
	_, err = io.Copy(f, pm)
	if err != nil {
		return err
	}

	s.state = StateLoading
	c.c.Servers[sID] = s
	sc := make(chan struct{})
	c.running[s.ID] = running{shutdown: sc}
	go c.run(s, sc)
	return nil
}

func (c *Controller) Stop(sID int) error {
	c.c.mu.Lock()
	defer c.c.mu.Unlock()
	r, ok := c.running[sID]
	if !ok {
		return ErrServerNotRunning
	}
	close(r.shutdown)
	delete(c.running, sID)
	return nil
}

// runs in its own goroutine
func (c *Controller) run(s Server, shutdown chan struct{}) {
	cmd := exec.Command(path.Join(s.Path, "server.jar"), s.Args...)
	cmd.Dir = s.Path
	/*r.cb = circbuf.NewBuffer(BufferSize)
	cmd.Stdout = r.cb
	wp, _ := cmd.StdoutPipe()
	r.w.io.MultiWriter(r.cb, wp)
	*/
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	died := make(chan struct{})
	go func() {
		select {
		case <-shutdown:
			// write stopCmd to stdin
			s.state = StateShuttingDown
			c.c.mu.Lock()
			c.c.Servers[s.ID] = s
			c.c.mu.Unlock()
		case <-died:
			c.c.mu.Lock()
			delete(c.running, s.ID)
			c.c.mu.Unlock()
		}
	}()

	cmd.Start()

	s.state = StateRunning
	c.c.mu.Lock()
	c.c.Servers[s.ID] = s
	c.c.mu.Unlock()
	cmd.Wait()
	shutdown = nil

	close(died)

	s.state = StateStopped
	c.c.mu.Lock()
	c.c.Servers[s.ID] = s
	c.c.mu.Unlock()
}

type running struct {
	shutdown chan struct{}
	cb       *circbuf.Buffer
	stdin    io.Writer
	w        io.Writer
}

func (r *running) Write(p []byte) (int, error) {
	/*if r.id < 0 {
		return 0, ErrServerNotRunning
	}*/
	return r.w.Write(p)
}

// Errors
var (
	ErrServerRunning    = errors.New("server already running")
	ErrServerNotRunning = errors.New("server not running")
)
<|endoftext|>


<commit_before>package checkstyle

import (
	"bytes"
	"encoding/json"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"strconv"
	"strings"
)

type ProblemType string

const (
	FileLine     ProblemType = "file_line"
	FunctionLine ProblemType = "func_line"
	ParamsNum    ProblemType = "params_num"
	ResultsNum   ProblemType = "results_num"
	Formated     ProblemType = "formated"
	PackageName  ProblemType = "pkg_name"
)

type Problem struct {
	Position    *token.Position
	Description string
	SourceLine  string
	Type        ProblemType
}

type Checker interface {
	Check(fileName string, src []byte) ([]Problem, error)
	IsFatal(p *Problem) bool
}

type checker struct {
	FunctionComment bool     `json:"func_comment"`
	FileLine        int      `json:"file_line"`
	FunctionLine    int      `json:"func_line"`
	MaxIndent       int      `json:"max_indent"`
	Formated        bool     `json:"formated"`
	Fatal           []string `json:"fatal"`
	ParamsNum       int      `json:"params_num"`
	ResultsNum      int      `json:"results_num"`
	PackageName     bool     `json:"pkg_name"`
}

func New(config []byte) (Checker, error) {
	var _checker checker
	err := json.Unmarshal(config, &_checker)
	if err != nil {
		return nil, err
	}
	return &_checker, nil
}

func (c *checker) Check(fileName string, src []byte) (ps []Problem, err error) {
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, fileName, src, parser.ParseComments)
	if err != nil {
		return nil, err
	}
	return (&file{fileName, src, c, f, fset, []Problem{}}).check(), nil
}

func (c *checker) IsFatal(p *Problem) bool {
	for _, v := range c.Fatal {
		if v == string(p.Type) {
			return true
		}
	}
	return false
}

type file struct {
	fileName string
	src      []byte

	config *checker

	ast  *ast.File
	fset *token.FileSet

	problems []Problem
}

func (f *file) isTest() bool {
	return strings.HasSuffix(f.fileName, "_test.go")
}

func (f *file) check() (ps []Problem) {
	f.checkFormat()
	f.checkFileLine()
	f.checkFileContent()
	return f.problems
}

func (f *file) checkFormat() {
	if !f.config.Formated {
		return
	}
	src, err := format.Source(f.src)
	if err != nil {
		panic(f.fileName + err.Error())
	}
	if bytes.Compare(src, f.src) != 0 {
		desc := "source is not formated"
		pos := f.fset.Position(f.ast.Pos())
		problem := Problem{Description: desc, Position: &pos, Type: Formated}
		f.problems = append(f.problems, problem)
	}
}

func (f *file) checkFileLine() {
	if f.isTest() {
		return
	}

	lineLimit := f.config.FileLine
	if lineLimit == 0 {
		return
	}

	f.fset.Iterate(func(_file *token.File) bool {
		lineCount := _file.LineCount()
		if lineCount > lineLimit {
			desc := strconv.Itoa(lineCount) + " lines more than " + strconv.Itoa(lineLimit)
			pos := f.fset.Position(f.ast.End())
			problem := Problem{Description: desc, Position: &pos, Type: FileLine}
			f.problems = append(f.problems, problem)
		}
		return true
	})
}

func genFuncLineProblem(name string, lineCount, lineLimit int, start token.Position) Problem {
	desc := "func " + name + "() body lines num " + strconv.Itoa(lineCount) +
		" more than " + strconv.Itoa(lineLimit)
	return Problem{Description: desc, Position: &start, Type: FunctionLine}
}

func genParamsNumProblem(name string, paramsNum, limit int, start token.Position) Problem {
	desc := "func " + name + "() params num " + strconv.Itoa(paramsNum) +
		"  more than " + strconv.Itoa(limit)
	return Problem{Description: desc, Position: &start, Type: ParamsNum}
}

func genResultsNumProblem(name string, resultsNum, limit int, start token.Position) Problem {
	desc := "func " + name + "() results num " + strconv.Itoa(resultsNum) +
		"  more than " + strconv.Itoa(limit)
	return Problem{Description: desc, Position: &start, Type: ResultsNum}
}

func (f *file) checkFileContent() {
	if f.isTest() {
		return
	}

	if f.config.PackageName {
		//ref "http://golang.org/doc/effective_go.html#package-names"
		pkgName := f.ast.Name.Name
		var desc string
		if strings.Contains(pkgName, "_") {
			suggestName := strings.Replace(pkgName, "_", "/", -1)
			desc = "don't use an underscore in package name," + pkgName + " should be " + suggestName
		} else if strings.ToLower(pkgName) != pkgName {
			desc = "don't use upper case in package name, " + pkgName + " should be single word"
		}
		if desc != "" {
			start := f.fset.Position(f.ast.Name.Pos())
			problem := Problem{Description: desc, Position: &start, Type: PackageName}
			f.problems = append(f.problems, problem)
		}
	}

	lineLimit := f.config.FunctionLine
	paramsNumLimit := f.config.ParamsNum
	resultsNumLimit := f.config.ResultsNum
	for _, v := range f.ast.Decls {
		switch v2 := v.(type) {
		case *ast.FuncDecl:
			start := f.fset.Position(v2.Pos())
			if lineLimit > 0 {
				startLine := start.Line
				endLine := f.fset.Position(v2.End()).Line
				lineCount := endLine - startLine
				if lineCount > lineLimit {
					problem := genFuncLineProblem(v2.Name.Name, lineCount, lineLimit, start)
					f.problems = append(f.problems, problem)
				}
			}

			_type := v2.Type
			params := _type.Params
			if paramsNumLimit != 0 && params.NumFields() > paramsNumLimit {
				problem := genParamsNumProblem(v2.Name.Name, params.NumFields(), paramsNumLimit, start)
				f.problems = append(f.problems, problem)
			}

			results := _type.Results
			if resultsNumLimit != 0 && results.NumFields() > resultsNumLimit {
				problem := genResultsNumProblem(v2.Name.Name, results.NumFields(), resultsNumLimit, start)
				f.problems = append(f.problems, problem)
			}
		}
	}
}
<commit_msg>modify description<commit_after>package checkstyle

import (
	"bytes"
	"encoding/json"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"strconv"
	"strings"
)

type ProblemType string

const (
	FileLine     ProblemType = "file_line"
	FunctionLine ProblemType = "func_line"
	ParamsNum    ProblemType = "params_num"
	ResultsNum   ProblemType = "results_num"
	Formated     ProblemType = "formated"
	PackageName  ProblemType = "pkg_name"
)

type Problem struct {
	Position    *token.Position
	Description string
	SourceLine  string
	Type        ProblemType
}

type Checker interface {
	Check(fileName string, src []byte) ([]Problem, error)
	IsFatal(p *Problem) bool
}

type checker struct {
	FunctionComment bool     `json:"func_comment"`
	FileLine        int      `json:"file_line"`
	FunctionLine    int      `json:"func_line"`
	MaxIndent       int      `json:"max_indent"`
	Formated        bool     `json:"formated"`
	Fatal           []string `json:"fatal"`
	ParamsNum       int      `json:"params_num"`
	ResultsNum      int      `json:"results_num"`
	PackageName     bool     `json:"pkg_name"`
}

func New(config []byte) (Checker, error) {
	var _checker checker
	err := json.Unmarshal(config, &_checker)
	if err != nil {
		return nil, err
	}
	return &_checker, nil
}

func (c *checker) Check(fileName string, src []byte) (ps []Problem, err error) {
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, fileName, src, parser.ParseComments)
	if err != nil {
		return nil, err
	}
	return (&file{fileName, src, c, f, fset, []Problem{}}).check(), nil
}

func (c *checker) IsFatal(p *Problem) bool {
	for _, v := range c.Fatal {
		if v == string(p.Type) {
			return true
		}
	}
	return false
}

type file struct {
	fileName string
	src      []byte

	config *checker

	ast  *ast.File
	fset *token.FileSet

	problems []Problem
}

func (f *file) isTest() bool {
	return strings.HasSuffix(f.fileName, "_test.go")
}

func (f *file) check() (ps []Problem) {
	f.checkFormat()
	f.checkFileLine()
	f.checkFileContent()
	return f.problems
}

func (f *file) checkFormat() {
	if !f.config.Formated {
		return
	}
	src, err := format.Source(f.src)
	if err != nil {
		panic(f.fileName + err.Error())
	}
	if bytes.Compare(src, f.src) != 0 {
		desc := "source is not formated"
		pos := f.fset.Position(f.ast.Pos())
		problem := Problem{Description: desc, Position: &pos, Type: Formated}
		f.problems = append(f.problems, problem)
	}
}

func (f *file) checkFileLine() {
	if f.isTest() {
		return
	}

	lineLimit := f.config.FileLine
	if lineLimit == 0 {
		return
	}

	f.fset.Iterate(func(_file *token.File) bool {
		lineCount := _file.LineCount()
		if lineCount > lineLimit {
			desc := strconv.Itoa(lineCount) + " lines more than " + strconv.Itoa(lineLimit)
			pos := f.fset.Position(f.ast.End())
			problem := Problem{Description: desc, Position: &pos, Type: FileLine}
			f.problems = append(f.problems, problem)
		}
		return true
	})
}

func genFuncLineProblem(name string, lineCount, lineLimit int, start token.Position) Problem {
	desc := "func " + name + "() body lines num " + strconv.Itoa(lineCount) +
		" more than " + strconv.Itoa(lineLimit)
	return Problem{Description: desc, Position: &start, Type: FunctionLine}
}

func genParamsNumProblem(name string, paramsNum, limit int, start token.Position) Problem {
	desc := "func " + name + "() params num " + strconv.Itoa(paramsNum) +
		"  more than " + strconv.Itoa(limit)
	return Problem{Description: desc, Position: &start, Type: ParamsNum}
}

func genResultsNumProblem(name string, resultsNum, limit int, start token.Position) Problem {
	desc := "func " + name + "() results num " + strconv.Itoa(resultsNum) +
		"  more than " + strconv.Itoa(limit)
	return Problem{Description: desc, Position: &start, Type: ResultsNum}
}

func (f *file) checkFileContent() {
	if f.isTest() {
		return
	}

	if f.config.PackageName {
		//ref "http://golang.org/doc/effective_go.html#package-names"
		pkgName := f.ast.Name.Name
		var desc string
		if strings.Contains(pkgName, "_") {
			suggestName := strings.Replace(pkgName, "_", "/", -1)
			desc = "don't use an underscore in package name, " + pkgName + " should be " + suggestName
		} else if strings.ToLower(pkgName) != pkgName {
			desc = "don't use capital letters in package name: " + pkgName
		}
		if desc != "" {
			start := f.fset.Position(f.ast.Name.Pos())
			problem := Problem{Description: desc, Position: &start, Type: PackageName}
			f.problems = append(f.problems, problem)
		}
	}

	lineLimit := f.config.FunctionLine
	paramsNumLimit := f.config.ParamsNum
	resultsNumLimit := f.config.ResultsNum
	for _, v := range f.ast.Decls {
		switch v2 := v.(type) {
		case *ast.FuncDecl:
			start := f.fset.Position(v2.Pos())
			if lineLimit > 0 {
				startLine := start.Line
				endLine := f.fset.Position(v2.End()).Line
				lineCount := endLine - startLine
				if lineCount > lineLimit {
					problem := genFuncLineProblem(v2.Name.Name, lineCount, lineLimit, start)
					f.problems = append(f.problems, problem)
				}
			}

			_type := v2.Type
			params := _type.Params
			if paramsNumLimit != 0 && params.NumFields() > paramsNumLimit {
				problem := genParamsNumProblem(v2.Name.Name, params.NumFields(), paramsNumLimit, start)
				f.problems = append(f.problems, problem)
			}

			results := _type.Results
			if resultsNumLimit != 0 && results.NumFields() > resultsNumLimit {
				problem := genResultsNumProblem(v2.Name.Name, results.NumFields(), resultsNumLimit, start)
				f.problems = append(f.problems, problem)
			}
		}
	}
}
<|endoftext|>