<commit_before>// Copyright 2014 The Go 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 runtime

import "unsafe"

// NOTE: Func does not expose the actual unexported fields, because we return *Func
// values to users, and we want to keep them from being able to overwrite the data
// with (say) *f = Func{}.
// All code operating on a *Func must call raw to get the *_func instead.

// A Func represents a Go function in the running binary.
type Func struct {
	opaque struct{} // unexported field to disallow conversions
}

func (f *Func) raw() *_func {
	return (*_func)(unsafe.Pointer(f))
}

// funcdata.h
const (
	_PCDATA_StackMapIndex       = 0
	_FUNCDATA_ArgsPointerMaps   = 0
	_FUNCDATA_LocalsPointerMaps = 1
	_FUNCDATA_DeadValueMaps     = 2
	_ArgsSizeUnknown            = -0x80000000
)

// moduledata records information about the layout of the executable
// image. It is written by the linker. Any changes here must be
// matched changes to the code in cmd/internal/ld/symtab.go:symtab.
// moduledata is stored in read-only memory; none of the pointers here
// are visible to the garbage collector.
type moduledata struct {
	pclntable    []byte
	ftab         []functab
	filetab      []uint32
	findfunctab  uintptr
	minpc, maxpc uintptr

	text, etext           uintptr
	noptrdata, enoptrdata uintptr
	data, edata           uintptr
	bss, ebss             uintptr
	noptrbss, enoptrbss   uintptr
	end, gcdata, gcbss    uintptr

	typelinks []*_type

	modulename   string
	modulehashes []modulehash

	gcdatamask, gcbssmask bitvector

	next *moduledata
}

// For each shared library a module links against, the linker creates an entry in the
// moduledata.modulehashes slice containing the name of the module, the abi hash seen
// at link time and a pointer to the runtime abi hash. These are checked in
// moduledataverify1 below.
type modulehash struct {
	modulename   string
	linktimehash string
	runtimehash  *string
}

var firstmoduledata moduledata  // linker symbol
var lastmoduledatap *moduledata // linker symbol

type functab struct {
	entry   uintptr
	funcoff uintptr
}

const minfunc = 16                 // minimum function size
const pcbucketsize = 256 * minfunc // size of bucket in the pc->func lookup table

// findfunctab is an array of these structures.
// Each bucket represents 4096 bytes of the text segment.
// Each subbucket represents 256 bytes of the text segment.
// To find a function given a pc, locate the bucket and subbucket for
// that pc.  Add together the idx and subbucket value to obtain a
// function index.  Then scan the functab array starting at that
// index to find the target function.
// This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead.
type findfuncbucket struct {
	idx        uint32
	subbuckets [16]byte
}

func moduledataverify() {
	for datap := &firstmoduledata; datap != nil; datap = datap.next {
		moduledataverify1(datap)
	}
}

const debugPcln = false

func moduledataverify1(datap *moduledata) {
	// See golang.org/s/go12symtab for header: 0xfffffffb,
	// two zero bytes, a byte giving the PC quantum,
	// and a byte giving the pointer width in bytes.
	pcln := *(**[8]byte)(unsafe.Pointer(&datap.pclntable))
	pcln32 := *(**[2]uint32)(unsafe.Pointer(&datap.pclntable))
	if pcln32[0] != 0xfffffffb || pcln[4] != 0 || pcln[5] != 0 || pcln[6] != _PCQuantum || pcln[7] != ptrSize {
		println("runtime: function symbol table header:", hex(pcln32[0]), hex(pcln[4]), hex(pcln[5]), hex(pcln[6]), hex(pcln[7]))
		throw("invalid function symbol table\n")
	}

	// ftab is lookup table for function by program counter.
	nftab := len(datap.ftab) - 1
	for i := 0; i < nftab; i++ {
		// NOTE: ftab[nftab].entry is legal; it is the address beyond the final function.
		if datap.ftab[i].entry > datap.ftab[i+1].entry {
			f1 := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i].funcoff]))
			f2 := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i+1].funcoff]))
			f2name := "end"
			if i+1 < nftab {
				f2name = funcname(f2)
			}
			println("function symbol table not sorted by program counter:", hex(datap.ftab[i].entry), funcname(f1), ">", hex(datap.ftab[i+1].entry), f2name)
			for j := 0; j <= i; j++ {
				print("\t", hex(datap.ftab[j].entry), " ", funcname((*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[j].funcoff]))), "\n")
			}
			throw("invalid runtime symbol table")
		}

		if debugPcln || nftab-i < 5 {
			f := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i].funcoff]))
			pcvalue(f, f.pcfile, datap.ftab[i+1].entry-1, true)
			pcvalue(f, f.pcln, datap.ftab[i+1].entry-1, true)
			pcvalue(f, f.pcsp, datap.ftab[i+1].entry-1, true)
		}
	}

	if datap.minpc != datap.ftab[0].entry ||
		datap.maxpc != datap.ftab[nftab].entry {
		throw("minpc or maxpc invalid")
	}

	for _, modulehash := range datap.modulehashes {
		if modulehash.linktimehash != *modulehash.runtimehash {
			println("abi mismatch detected between", datap.modulename, "and", modulehash.modulename)
			throw("abi mismatch")
		}
	}
}

// FuncForPC returns a *Func describing the function that contains the
// given program counter address, or else nil.
func FuncForPC(pc uintptr) *Func {
	return (*Func)(unsafe.Pointer(findfunc(pc)))
}

// Name returns the name of the function.
func (f *Func) Name() string {
	return funcname(f.raw())
}

// Entry returns the entry address of the function.
func (f *Func) Entry() uintptr {
	return f.raw().entry
}

// FileLine returns the file name and line number of the
// source code corresponding to the program counter pc.
// The result will not be accurate if pc is not a program
// counter within f.
func (f *Func) FileLine(pc uintptr) (file string, line int) {
	// Pass strict=false here, because anyone can call this function,
	// and they might just be wrong about targetpc belonging to f.
	file, line32 := funcline1(f.raw(), pc, false)
	return file, int(line32)
}

func findmoduledatap(pc uintptr) *moduledata {
	for datap := &firstmoduledata; datap != nil; datap = datap.next {
		if datap.minpc <= pc && pc <= datap.maxpc {
			return datap
		}
	}
	return nil
}

func findfunc(pc uintptr) *_func {
	datap := findmoduledatap(pc)
	if datap == nil {
		return nil
	}
	const nsub = uintptr(len(findfuncbucket{}.subbuckets))

	x := pc - datap.minpc
	b := x / pcbucketsize
	i := x % pcbucketsize / (pcbucketsize / nsub)

	ffb := (*findfuncbucket)(add(unsafe.Pointer(datap.findfunctab), b*unsafe.Sizeof(findfuncbucket{})))
	idx := ffb.idx + uint32(ffb.subbuckets[i])
	if pc < datap.ftab[idx].entry {
		throw("findfunc: bad findfunctab entry")
	}

	// linear search to find func with pc >= entry.
	for datap.ftab[idx+1].entry <= pc {
		idx++
	}
	return (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[idx].funcoff]))
}

func pcvalue(f *_func, off int32, targetpc uintptr, strict bool) int32 {
	if off == 0 {
		return -1
	}
	datap := findmoduledatap(f.entry) // inefficient
	p := datap.pclntable[off:]
	pc := f.entry
	val := int32(-1)
	for {
		var ok bool
		p, ok = step(p, &pc, &val, pc == f.entry)
		if !ok {
			break
		}
		if targetpc < pc {
			return val
		}
	}

	// If there was a table, it should have covered all program counters.
	// If not, something is wrong.
	if panicking != 0 || !strict {
		return -1
	}

	print("runtime: invalid pc-encoded table f=", funcname(f), " pc=", hex(pc), " targetpc=", hex(targetpc), " tab=", p, "\n")

	p = datap.pclntable[off:]
	pc = f.entry
	val = -1
	for {
		var ok bool
		p, ok = step(p, &pc, &val, pc == f.entry)
		if !ok {
			break
		}
		print("\tvalue=", val, " until pc=", hex(pc), "\n")
	}

	throw("invalid runtime symbol table")
	return -1
}

func cfuncname(f *_func) *byte {
	if f == nil || f.nameoff == 0 {
		return nil
	}
	datap := findmoduledatap(f.entry) // inefficient
	return (*byte)(unsafe.Pointer(&datap.pclntable[f.nameoff]))
}

func funcname(f *_func) string {
	return gostringnocopy(cfuncname(f))
}

func funcline1(f *_func, targetpc uintptr, strict bool) (file string, line int32) {
	datap := findmoduledatap(f.entry) // inefficient
	fileno := int(pcvalue(f, f.pcfile, targetpc, strict))
	line = pcvalue(f, f.pcln, targetpc, strict)
	if fileno == -1 || line == -1 || fileno >= len(datap.filetab) {
		// print("looking for ", hex(targetpc), " in ", funcname(f), " got file=", fileno, " line=", lineno, "\n")
		return "?", 0
	}
	file = gostringnocopy(&datap.pclntable[datap.filetab[fileno]])
	return
}

func funcline(f *_func, targetpc uintptr) (file string, line int32) {
	return funcline1(f, targetpc, true)
}

func funcspdelta(f *_func, targetpc uintptr) int32 {
	x := pcvalue(f, f.pcsp, targetpc, true)
	if x&(ptrSize-1) != 0 {
		print("invalid spdelta ", funcname(f), " ", hex(f.entry), " ", hex(targetpc), " ", hex(f.pcsp), " ", x, "\n")
	}
	return x
}

func pcdatavalue(f *_func, table int32, targetpc uintptr) int32 {
	if table < 0 || table >= f.npcdata {
		return -1
	}
	off := *(*int32)(add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(table)*4))
	return pcvalue(f, off, targetpc, true)
}

func funcdata(f *_func, i int32) unsafe.Pointer {
	if i < 0 || i >= f.nfuncdata {
		return nil
	}
	p := add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(f.npcdata)*4)
	if ptrSize == 8 && uintptr(p)&4 != 0 {
		if uintptr(unsafe.Pointer(f))&4 != 0 {
			println("runtime: misaligned func", f)
		}
		p = add(p, 4)
	}
	return *(*unsafe.Pointer)(add(p, uintptr(i)*ptrSize))
}

// step advances to the next pc, value pair in the encoded table.
func step(p []byte, pc *uintptr, val *int32, first bool) (newp []byte, ok bool) {
	p, uvdelta := readvarint(p)
	if uvdelta == 0 && !first {
		return nil, false
	}
	if uvdelta&1 != 0 {
		uvdelta = ^(uvdelta >> 1)
	} else {
		uvdelta >>= 1
	}
	vdelta := int32(uvdelta)
	p, pcdelta := readvarint(p)
	*pc += uintptr(pcdelta * _PCQuantum)
	*val += vdelta
	return p, true
}

// readvarint reads a varint from p.
func readvarint(p []byte) (newp []byte, val uint32) {
	var v, shift uint32
	for {
		b := p[0]
		p = p[1:]
		v |= (uint32(b) & 0x7F) << shift
		if b&0x80 == 0 {
			break
		}
		shift += 7
	}
	return p, v
}

type stackmap struct {
	n        int32   // number of bitmaps
	nbit     int32   // number of bits in each bitmap
	bytedata [1]byte // bitmaps, each starting on a 32-bit boundary
}

//go:nowritebarrier
func stackmapdata(stkmap *stackmap, n int32) bitvector {
	if n < 0 || n >= stkmap.n {
		throw("stackmapdata: index out of range")
	}
	return bitvector{stkmap.nbit, (*byte)(add(unsafe.Pointer(&stkmap.bytedata), uintptr(n*((stkmap.nbit+31)/32*4))))}
}
<commit_msg>runtime: fix broken arm builds<commit_after>// Copyright 2014 The Go 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 runtime

import "unsafe"

// NOTE: Func does not expose the actual unexported fields, because we return *Func
// values to users, and we want to keep them from being able to overwrite the data
// with (say) *f = Func{}.
// All code operating on a *Func must call raw to get the *_func instead.

// A Func represents a Go function in the running binary.
type Func struct {
	opaque struct{} // unexported field to disallow conversions
}

func (f *Func) raw() *_func {
	return (*_func)(unsafe.Pointer(f))
}

// funcdata.h
const (
	_PCDATA_StackMapIndex       = 0
	_FUNCDATA_ArgsPointerMaps   = 0
	_FUNCDATA_LocalsPointerMaps = 1
	_FUNCDATA_DeadValueMaps     = 2
	_ArgsSizeUnknown            = -0x80000000
)

// moduledata records information about the layout of the executable
// image. It is written by the linker. Any changes here must be
// matched changes to the code in cmd/internal/ld/symtab.go:symtab.
// moduledata is stored in read-only memory; none of the pointers here
// are visible to the garbage collector.
type moduledata struct {
	pclntable    []byte
	ftab         []functab
	filetab      []uint32
	findfunctab  uintptr
	minpc, maxpc uintptr

	text, etext           uintptr
	noptrdata, enoptrdata uintptr
	data, edata           uintptr
	bss, ebss             uintptr
	noptrbss, enoptrbss   uintptr
	end, gcdata, gcbss    uintptr

	typelinks []*_type

	modulename   string
	modulehashes []modulehash

	gcdatamask, gcbssmask bitvector

	next *moduledata
}

// For each shared library a module links against, the linker creates an entry in the
// moduledata.modulehashes slice containing the name of the module, the abi hash seen
// at link time and a pointer to the runtime abi hash. These are checked in
// moduledataverify1 below.
type modulehash struct {
	modulename   string
	linktimehash string
	runtimehash  *string
}

var firstmoduledata moduledata  // linker symbol
var lastmoduledatap *moduledata // linker symbol

type functab struct {
	entry   uintptr
	funcoff uintptr
}

const minfunc = 16                 // minimum function size
const pcbucketsize = 256 * minfunc // size of bucket in the pc->func lookup table

// findfunctab is an array of these structures.
// Each bucket represents 4096 bytes of the text segment.
// Each subbucket represents 256 bytes of the text segment.
// To find a function given a pc, locate the bucket and subbucket for
// that pc.  Add together the idx and subbucket value to obtain a
// function index.  Then scan the functab array starting at that
// index to find the target function.
// This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead.
type findfuncbucket struct {
	idx        uint32
	subbuckets [16]byte
}

func moduledataverify() {
	for datap := &firstmoduledata; datap != nil; datap = datap.next {
		moduledataverify1(datap)
	}
}

const debugPcln = false

func moduledataverify1(datap *moduledata) {
	// See golang.org/s/go12symtab for header: 0xfffffffb,
	// two zero bytes, a byte giving the PC quantum,
	// and a byte giving the pointer width in bytes.
	pcln := *(**[8]byte)(unsafe.Pointer(&datap.pclntable))
	pcln32 := *(**[2]uint32)(unsafe.Pointer(&datap.pclntable))
	if pcln32[0] != 0xfffffffb || pcln[4] != 0 || pcln[5] != 0 || pcln[6] != _PCQuantum || pcln[7] != ptrSize {
		println("runtime: function symbol table header:", hex(pcln32[0]), hex(pcln[4]), hex(pcln[5]), hex(pcln[6]), hex(pcln[7]))
		throw("invalid function symbol table\n")
	}

	// ftab is lookup table for function by program counter.
	nftab := len(datap.ftab) - 1
	for i := 0; i < nftab; i++ {
		// NOTE: ftab[nftab].entry is legal; it is the address beyond the final function.
		if datap.ftab[i].entry > datap.ftab[i+1].entry {
			f1 := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i].funcoff]))
			f2 := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i+1].funcoff]))
			f2name := "end"
			if i+1 < nftab {
				f2name = funcname(f2)
			}
			println("function symbol table not sorted by program counter:", hex(datap.ftab[i].entry), funcname(f1), ">", hex(datap.ftab[i+1].entry), f2name)
			for j := 0; j <= i; j++ {
				print("\t", hex(datap.ftab[j].entry), " ", funcname((*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[j].funcoff]))), "\n")
			}
			throw("invalid runtime symbol table")
		}

		if debugPcln || nftab-i < 5 {
			// Check a PC near but not at the very end.
			// The very end might be just padding that is not covered by the tables.
			// No architecture rounds function entries to more than 16 bytes,
			// but if one came along we'd need to subtract more here.
			end := datap.ftab[i+1].entry - 16
			if end < datap.ftab[i].entry {
				end = datap.ftab[i].entry
			}
			f := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i].funcoff]))
			pcvalue(f, f.pcfile, end, true)
			pcvalue(f, f.pcln, end, true)
			pcvalue(f, f.pcsp, end, true)
		}
	}

	if datap.minpc != datap.ftab[0].entry ||
		datap.maxpc != datap.ftab[nftab].entry {
		throw("minpc or maxpc invalid")
	}

	for _, modulehash := range datap.modulehashes {
		if modulehash.linktimehash != *modulehash.runtimehash {
			println("abi mismatch detected between", datap.modulename, "and", modulehash.modulename)
			throw("abi mismatch")
		}
	}
}

// FuncForPC returns a *Func describing the function that contains the
// given program counter address, or else nil.
func FuncForPC(pc uintptr) *Func {
	return (*Func)(unsafe.Pointer(findfunc(pc)))
}

// Name returns the name of the function.
func (f *Func) Name() string {
	return funcname(f.raw())
}

// Entry returns the entry address of the function.
func (f *Func) Entry() uintptr {
	return f.raw().entry
}

// FileLine returns the file name and line number of the
// source code corresponding to the program counter pc.
// The result will not be accurate if pc is not a program
// counter within f.
func (f *Func) FileLine(pc uintptr) (file string, line int) {
	// Pass strict=false here, because anyone can call this function,
	// and they might just be wrong about targetpc belonging to f.
	file, line32 := funcline1(f.raw(), pc, false)
	return file, int(line32)
}

func findmoduledatap(pc uintptr) *moduledata {
	for datap := &firstmoduledata; datap != nil; datap = datap.next {
		if datap.minpc <= pc && pc <= datap.maxpc {
			return datap
		}
	}
	return nil
}

func findfunc(pc uintptr) *_func {
	datap := findmoduledatap(pc)
	if datap == nil {
		return nil
	}
	const nsub = uintptr(len(findfuncbucket{}.subbuckets))

	x := pc - datap.minpc
	b := x / pcbucketsize
	i := x % pcbucketsize / (pcbucketsize / nsub)

	ffb := (*findfuncbucket)(add(unsafe.Pointer(datap.findfunctab), b*unsafe.Sizeof(findfuncbucket{})))
	idx := ffb.idx + uint32(ffb.subbuckets[i])
	if pc < datap.ftab[idx].entry {
		throw("findfunc: bad findfunctab entry")
	}

	// linear search to find func with pc >= entry.
	for datap.ftab[idx+1].entry <= pc {
		idx++
	}
	return (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[idx].funcoff]))
}

func pcvalue(f *_func, off int32, targetpc uintptr, strict bool) int32 {
	if off == 0 {
		return -1
	}
	datap := findmoduledatap(f.entry) // inefficient
	p := datap.pclntable[off:]
	pc := f.entry
	val := int32(-1)
	for {
		var ok bool
		p, ok = step(p, &pc, &val, pc == f.entry)
		if !ok {
			break
		}
		if targetpc < pc {
			return val
		}
	}

	// If there was a table, it should have covered all program counters.
	// If not, something is wrong.
	if panicking != 0 || !strict {
		return -1
	}

	print("runtime: invalid pc-encoded table f=", funcname(f), " pc=", hex(pc), " targetpc=", hex(targetpc), " tab=", p, "\n")

	p = datap.pclntable[off:]
	pc = f.entry
	val = -1
	for {
		var ok bool
		p, ok = step(p, &pc, &val, pc == f.entry)
		if !ok {
			break
		}
		print("\tvalue=", val, " until pc=", hex(pc), "\n")
	}

	throw("invalid runtime symbol table")
	return -1
}

func cfuncname(f *_func) *byte {
	if f == nil || f.nameoff == 0 {
		return nil
	}
	datap := findmoduledatap(f.entry) // inefficient
	return (*byte)(unsafe.Pointer(&datap.pclntable[f.nameoff]))
}

func funcname(f *_func) string {
	return gostringnocopy(cfuncname(f))
}

func funcline1(f *_func, targetpc uintptr, strict bool) (file string, line int32) {
	datap := findmoduledatap(f.entry) // inefficient
	fileno := int(pcvalue(f, f.pcfile, targetpc, strict))
	line = pcvalue(f, f.pcln, targetpc, strict)
	if fileno == -1 || line == -1 || fileno >= len(datap.filetab) {
		// print("looking for ", hex(targetpc), " in ", funcname(f), " got file=", fileno, " line=", lineno, "\n")
		return "?", 0
	}
	file = gostringnocopy(&datap.pclntable[datap.filetab[fileno]])
	return
}

func funcline(f *_func, targetpc uintptr) (file string, line int32) {
	return funcline1(f, targetpc, true)
}

func funcspdelta(f *_func, targetpc uintptr) int32 {
	x := pcvalue(f, f.pcsp, targetpc, true)
	if x&(ptrSize-1) != 0 {
		print("invalid spdelta ", funcname(f), " ", hex(f.entry), " ", hex(targetpc), " ", hex(f.pcsp), " ", x, "\n")
	}
	return x
}

func pcdatavalue(f *_func, table int32, targetpc uintptr) int32 {
	if table < 0 || table >= f.npcdata {
		return -1
	}
	off := *(*int32)(add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(table)*4))
	return pcvalue(f, off, targetpc, true)
}

func funcdata(f *_func, i int32) unsafe.Pointer {
	if i < 0 || i >= f.nfuncdata {
		return nil
	}
	p := add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(f.npcdata)*4)
	if ptrSize == 8 && uintptr(p)&4 != 0 {
		if uintptr(unsafe.Pointer(f))&4 != 0 {
			println("runtime: misaligned func", f)
		}
		p = add(p, 4)
	}
	return *(*unsafe.Pointer)(add(p, uintptr(i)*ptrSize))
}

// step advances to the next pc, value pair in the encoded table.
func step(p []byte, pc *uintptr, val *int32, first bool) (newp []byte, ok bool) {
	p, uvdelta := readvarint(p)
	if uvdelta == 0 && !first {
		return nil, false
	}
	if uvdelta&1 != 0 {
		uvdelta = ^(uvdelta >> 1)
	} else {
		uvdelta >>= 1
	}
	vdelta := int32(uvdelta)
	p, pcdelta := readvarint(p)
	*pc += uintptr(pcdelta * _PCQuantum)
	*val += vdelta
	return p, true
}

// readvarint reads a varint from p.
func readvarint(p []byte) (newp []byte, val uint32) {
	var v, shift uint32
	for {
		b := p[0]
		p = p[1:]
		v |= (uint32(b) & 0x7F) << shift
		if b&0x80 == 0 {
			break
		}
		shift += 7
	}
	return p, v
}

type stackmap struct {
	n        int32   // number of bitmaps
	nbit     int32   // number of bits in each bitmap
	bytedata [1]byte // bitmaps, each starting on a 32-bit boundary
}

//go:nowritebarrier
func stackmapdata(stkmap *stackmap, n int32) bitvector {
	if n < 0 || n >= stkmap.n {
		throw("stackmapdata: index out of range")
	}
	return bitvector{stkmap.nbit, (*byte)(add(unsafe.Pointer(&stkmap.bytedata), uintptr(n*((stkmap.nbit+31)/32*4))))}
}
<|endoftext|>


<commit_before>package gelf

import (
	"encoding/json"
	"errors"
	"log"
	"net"
	"os"
	"time"

	"github.com/gliderlabs/logspout/router"
)

var hostname string

func init() {
	hostname, _ = os.Hostname()
	router.AdapterFactories.Register(NewGelfAdapter, "gelf")
}

// GelfAdapter is an adapter that streams UDP JSON to Graylog
type GelfAdapter struct {
	conn  net.Conn
	route *router.Route
}

// NewGelfAdapter creates a GelfAdapter with UDP as the default transport.
func NewGelfAdapter(route *router.Route) (router.LogAdapter, error) {
	transport, found := router.AdapterTransports.Lookup(route.AdapterTransport("udp"))
	if !found {
		return nil, errors.New("unable to find adapter: " + route.Adapter)
	}

	conn, err := transport.Dial(route.Address, route.Options)
	if err != nil {
		return nil, err
	}

	return &GelfAdapter{
		route: route,
		conn:  conn,
	}, nil
}

// Stream implements the router.LogAdapter interface.
func (a *GelfAdapter) Stream(logstream chan *router.Message) {
	for m := range logstream {

		msg := GelfMessage{
			Version:        "1.1",
			Host:           hostname,
			ShortMessage:   m.Data,
			Timestamp:      float64(m.Time.UnixNano()) / float64(time.Second),
			ContainerId:    m.Container.ID,
			ContainerImage: m.Container.Config.Image,
		}
		js, err := json.Marshal(msg)
		if err != nil {
			log.Println("Graylog:", err)
			continue
		}
		_, err = a.conn.Write(js)
		if err != nil {
			log.Println("Graylog:", err)
			continue
		}
	}
}

type GelfMessage struct {
	Version      string  `json:"version"`
	Host         string  `json:"host"`
	ShortMessage string  `json:"short_message"`
	FullMessage  string  `json:"full_message,omitempty"`
	Timestamp    float64 `json:"timestamp,omitempty"`
	Level        int     `json:"level,omitempty"`

	ContainerId    string `json:"_docker.container,omitempty"`
	ContainerImage string `json:"_docker.image,omitempty"`
}
<commit_msg>Added ContainerName<commit_after>package gelf

import (
	"encoding/json"
	"errors"
	"log"
	"net"
	"os"
	"time"

	"github.com/gliderlabs/logspout/router"
)

var hostname string

func init() {
	hostname, _ = os.Hostname()
	router.AdapterFactories.Register(NewGelfAdapter, "gelf")
}

// GelfAdapter is an adapter that streams UDP JSON to Graylog
type GelfAdapter struct {
	conn  net.Conn
	route *router.Route
}

// NewGelfAdapter creates a GelfAdapter with UDP as the default transport.
func NewGelfAdapter(route *router.Route) (router.LogAdapter, error) {
	transport, found := router.AdapterTransports.Lookup(route.AdapterTransport("udp"))
	if !found {
		return nil, errors.New("unable to find adapter: " + route.Adapter)
	}

	conn, err := transport.Dial(route.Address, route.Options)
	if err != nil {
		return nil, err
	}

	return &GelfAdapter{
		route: route,
		conn:  conn,
	}, nil
}

// Stream implements the router.LogAdapter interface.
func (a *GelfAdapter) Stream(logstream chan *router.Message) {
	for m := range logstream {

		msg := GelfMessage{
			Version:        "1.1",
			Host:           hostname,
			ShortMessage:   m.Data,
			Timestamp:      float64(m.Time.UnixNano()) / float64(time.Second),
			ContainerId:    m.Container.ID,
			ContainerImage: m.Container.Config.Image,
			ContainerName:  m.Container.Name,
		}
		js, err := json.Marshal(msg)
		if err != nil {
			log.Println("Graylog:", err)
			continue
		}
		_, err = a.conn.Write(js)
		if err != nil {
			log.Println("Graylog:", err)
			continue
		}
	}
}

type GelfMessage struct {
	Version      string  `json:"version"`
	Host         string  `json:"host"`
	ShortMessage string  `json:"short_message"`
	FullMessage  string  `json:"full_message,omitempty"`
	Timestamp    float64 `json:"timestamp,omitempty"`
	Level        int     `json:"level,omitempty"`

	ContainerId    string `json:"_docker.container,omitempty"`
	ContainerImage string `json:"_docker.image,omitempty"`
	ContainerName  string `json:"_docker.name,omitempty"`
}
<|endoftext|>


<commit_before>package support

import (
	"fmt"
	"net/http"
	"net/url"
)

var baseURL string

// Session is a Telegram bot session
type Session struct {
	Token         string
	Port          string
	Endpoint      string
	URL           string
	Configuration ConfigFile
}

// NewSession returns a new Telegram bot session
func NewSession() (Session, error) {
	conf, err := CheckConfigFile()
	if err != nil {
		return Session{}, err
	}

	var s Session
	s.Token = conf.BotToken
	s.Port = conf.Port
	s.Endpoint = conf.Endpoint
	s.URL = conf.URL
	s.Configuration = conf

	baseURL = "https://api.telegram.org/bot" + s.Token + "/"

	// setup webhook already
	err = s.SetupWebHook()
	if err != nil {
		return Session{}, err
	}

	return s, nil
}

// SetupWebHook setup the Telegram webhook for the running server
func (s *Session) SetupWebHook() error {
	// TODO: handle webhook errors
	_, err := http.PostForm(baseURL+"setWebhook", url.Values{"url": {s.URL + ":" + s.Port + "/" + s.Endpoint}})
	if err != nil {
		return err
	}

	return nil
}

// PrintBotInformations prints some informations about the Bot the session represents
func (s *Session) PrintBotInformations() error {
	data, err := http.Get(baseURL + "getMe")
	if err != nil {
		return err
	}
	var u BotInfo
	err = u.DecodeJSON(data.Body)
	if err != nil {
		return err
	}

	fmt.Println("Bot handle: @" + u.Result.Username)
	fmt.Println("Bot name: " + u.Result.FirstName)

	return err
}
<commit_msg>first revision of what will be used to reply to user selecting and sending inline urls<commit_after>package support

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

var baseURL string

// Session is a Telegram bot session
type Session struct {
	Token         string
	Port          string
	Endpoint      string
	URL           string
	Configuration ConfigFile
}

// NewSession returns a new Telegram bot session
func NewSession() (Session, error) {
	conf, err := CheckConfigFile()
	if err != nil {
		return Session{}, err
	}

	var s Session
	s.Token = conf.BotToken
	s.Port = conf.Port
	s.Endpoint = conf.Endpoint
	s.URL = conf.URL
	s.Configuration = conf

	baseURL = "https://api.telegram.org/bot" + s.Token + "/"

	// setup webhook already
	err = s.SetupWebHook()
	if err != nil {
		return Session{}, err
	}

	return s, nil
}

// SetupWebHook setup the Telegram webhook for the running server
func (s *Session) SetupWebHook() error {
	// TODO: handle webhook errors
	_, err := http.PostForm(baseURL+"setWebhook", url.Values{"url": {s.URL + ":" + s.Port + "/" + s.Endpoint}})
	if err != nil {
		return err
	}

	return nil
}

// PrintBotInformations prints some informations about the Bot the session represents
func (s *Session) PrintBotInformations() error {
	data, err := http.Get(baseURL + "getMe")
	if err != nil {
		return err
	}
	var u BotInfo
	err = u.DecodeJSON(data.Body)
	if err != nil {
		return err
	}

	fmt.Println("Bot handle: @" + u.Result.Username)
	fmt.Println("Bot name: " + u.Result.FirstName)

	return err
}

// ReplyToInlineQuery replies to the inline query contained into the TelegramObject we're referencing
func (s *Session) ReplyToInlineQuery(t TelegramObject) error {
	article := []InlineQueryResultArticle{NewResultArticle(t.InlineQuery.ID, t.InlineQuery.Query, false)}

	enc, err := json.Marshal(article)

	if err != nil {
		return err
	}

	v := url.Values{}
	v.Add("inline_query_id", t.InlineQuery.ID)
	v.Add("results", string(enc))

	_, err = http.PostForm(baseURL+"answerInlineQuery", v)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package surveys

import (
	"encoding/json"
	"log"
	"net/http"
	"regexp"

	"github.com/AreaHQ/jsonhal"
	"github.com/ONSdigital/go-launch-a-survey/settings"
)

// LauncherSchema is a representation of a schema in the Launcher
type LauncherSchema struct {
	Name     string
	EqID     string
	FormType string
	URL      string
}

// RegisterResponse is the response from the eq-survey-register request
type RegisterResponse struct {
	jsonhal.Hal
}

// Schemas is a list of Schema
type Schemas []Schema

// Schema is an available schema
type Schema struct {
	jsonhal.Hal
	Name string `json:"name"`
}

var eqIDFormTypeRegex = regexp.MustCompile(`^(?P<eq_id>[a-z0-9]+)_(?P<form_type>\w+)`)

func extractEqIDFormType(schema string) (EqID, formType string) {
	match := eqIDFormTypeRegex.FindStringSubmatch(schema)
	if match != nil {
		EqID = match[1]
		formType = match[2]
	}
	return
}

// LauncherSchemaFromFilename creates a LauncherSchema record from a schema filename
func LauncherSchemaFromFilename(filename string) LauncherSchema {
	EqID, formType := extractEqIDFormType(filename)
	return LauncherSchema{
		Name:     filename,
		EqID:     EqID,
		FormType: formType,
	}
}

// GetAvailableSchemas Gets the list of static schemas an joins them with any schemas from the eq-survey-register if defined
func GetAvailableSchemas() []LauncherSchema {
	schemaList := []LauncherSchema{
		LauncherSchemaFromFilename("0_star_wars.json"),
		LauncherSchemaFromFilename("1_0005.json"),
		LauncherSchemaFromFilename("1_0102.json"),
		LauncherSchemaFromFilename("1_0112.json"),
		LauncherSchemaFromFilename("1_0203.json"),
		LauncherSchemaFromFilename("1_0205.json"),
		LauncherSchemaFromFilename("1_0213.json"),
		LauncherSchemaFromFilename("1_0215.json"),
		LauncherSchemaFromFilename("2_0001.json"),
		LauncherSchemaFromFilename("census_communal.json"),
		LauncherSchemaFromFilename("census_household.json"),
		LauncherSchemaFromFilename("census_individual.json"),
		LauncherSchemaFromFilename("e_commerce.json"),
		LauncherSchemaFromFilename("lms_1.json"),
		LauncherSchemaFromFilename("mbs_0106.json"),
		LauncherSchemaFromFilename("mbs_0111.json"),
		LauncherSchemaFromFilename("mbs_0117.json"),
		LauncherSchemaFromFilename("mbs_0123.json"),
		LauncherSchemaFromFilename("mbs_0158.json"),
		LauncherSchemaFromFilename("mbs_0161.json"),
		LauncherSchemaFromFilename("mbs_0167.json"),
		LauncherSchemaFromFilename("mbs_0173.json"),
		LauncherSchemaFromFilename("mbs_0201.json"),
		LauncherSchemaFromFilename("mbs_0202.json"),
		LauncherSchemaFromFilename("mbs_0203.json"),
		LauncherSchemaFromFilename("mbs_0204.json"),
		LauncherSchemaFromFilename("mbs_0205.json"),
		LauncherSchemaFromFilename("mbs_0216.json"),
		LauncherSchemaFromFilename("mbs_0251.json"),
		LauncherSchemaFromFilename("mbs_0253.json"),
		LauncherSchemaFromFilename("mbs_0255.json"),
		LauncherSchemaFromFilename("mbs_0817.json"),
		LauncherSchemaFromFilename("mbs_0823.json"),
		LauncherSchemaFromFilename("mbs_0867.json"),
		LauncherSchemaFromFilename("mbs_0873.json"),
		LauncherSchemaFromFilename("multiple_answers.json"),
		LauncherSchemaFromFilename("rsi_transformation.json"),
		LauncherSchemaFromFilename("test_big_list_naughty_strings.json"),
		LauncherSchemaFromFilename("test_checkbox.json"),
		LauncherSchemaFromFilename("test_checkbox_mutually_exclusive.json"),
		LauncherSchemaFromFilename("test_conditional_dates.json"),
		LauncherSchemaFromFilename("test_conditional_routing.json"),
		LauncherSchemaFromFilename("test_confirmation_question.json"),
		LauncherSchemaFromFilename("test_currency.json"),
		LauncherSchemaFromFilename("test_date_validation_combined.json"),
		LauncherSchemaFromFilename("test_date_validation_mm_yyyy_combined.json"),
		LauncherSchemaFromFilename("test_date_validation_range.json"),
		LauncherSchemaFromFilename("test_date_validation_single.json"),
		LauncherSchemaFromFilename("test_dates.json"),
		LauncherSchemaFromFilename("test_default.json"),
		LauncherSchemaFromFilename("test_dependencies_calculation.json"),
		LauncherSchemaFromFilename("test_dependencies_max_value.json"),
		LauncherSchemaFromFilename("test_dependencies_min_value.json"),
		LauncherSchemaFromFilename("test_difference_in_years.json"),
		LauncherSchemaFromFilename("test_difference_in_years_month_year.json"),
		LauncherSchemaFromFilename("test_difference_in_years_month_year_range.json"),
		LauncherSchemaFromFilename("test_difference_in_years_range.json"),
		LauncherSchemaFromFilename("test_different_question_titles.json"),
		LauncherSchemaFromFilename("test_dropdown_mandatory.json"),
		LauncherSchemaFromFilename("test_dropdown_mandatory_with_overridden_error.json"),
		LauncherSchemaFromFilename("test_dropdown_optional.json"),
		LauncherSchemaFromFilename("test_error_messages.json"),
		LauncherSchemaFromFilename("test_final_confirmation.json"),
		LauncherSchemaFromFilename("test_household_question.json"),
		LauncherSchemaFromFilename("test_interstitial_page.json"),
		LauncherSchemaFromFilename("test_introduction.json"),
		LauncherSchemaFromFilename("test_language.json"),
		LauncherSchemaFromFilename("test_language_cy.json"),
		LauncherSchemaFromFilename("test_markup.json"),
		LauncherSchemaFromFilename("test_metadata_routing.json"),
		LauncherSchemaFromFilename("test_multiple_piping.json"),
		LauncherSchemaFromFilename("test_navigation.json"),
		LauncherSchemaFromFilename("test_navigation_completeness.json"),
		LauncherSchemaFromFilename("test_navigation_confirmation.json"),
		LauncherSchemaFromFilename("test_navigation_routing.json"),
		LauncherSchemaFromFilename("test_numbers.json"),
		LauncherSchemaFromFilename("test_percentage.json"),
		LauncherSchemaFromFilename("test_question_guidance.json"),
		LauncherSchemaFromFilename("test_radio_checkbox_descriptions.json"),
		LauncherSchemaFromFilename("test_radio_checkbox_titles.json"),
		LauncherSchemaFromFilename("test_radio_mandatory.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_mandatory_other.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_mandatory_other_overridden_error.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_optional_other.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_overridden_error.json"),
		LauncherSchemaFromFilename("test_radio_optional.json"),
		LauncherSchemaFromFilename("test_radio_optional_with_mandatory_other.json"),
		LauncherSchemaFromFilename("test_radio_optional_with_mandatory_other_overridden_error.json"),
		LauncherSchemaFromFilename("test_radio_optional_with_optional_other.json"),
		LauncherSchemaFromFilename("test_relationship_household.json"),
		LauncherSchemaFromFilename("test_repeating_and_conditional_routing.json"),
		LauncherSchemaFromFilename("test_repeating_household.json"),
		LauncherSchemaFromFilename("test_routing_date_equals.json"),
		LauncherSchemaFromFilename("test_routing_date_greater_than.json"),
		LauncherSchemaFromFilename("test_routing_date_less_than.json"),
		LauncherSchemaFromFilename("test_routing_date_not_equals.json"),
		LauncherSchemaFromFilename("test_routing_group.json"),
		LauncherSchemaFromFilename("test_routing_number_equals.json"),
		LauncherSchemaFromFilename("test_routing_number_greater_than.json"),
		LauncherSchemaFromFilename("test_routing_number_greater_than_or_equal.json"),
		LauncherSchemaFromFilename("test_routing_number_less_than.json"),
		LauncherSchemaFromFilename("test_routing_number_less_than_or_equal.json"),
		LauncherSchemaFromFilename("test_routing_number_not_equals.json"),
		LauncherSchemaFromFilename("test_routing_on_multiple_select.json"),
		LauncherSchemaFromFilename("test_skip_condition.json"),
		LauncherSchemaFromFilename("test_skip_condition_block.json"),
		LauncherSchemaFromFilename("test_skip_condition_group.json"),
		LauncherSchemaFromFilename("test_summary.json"),
		LauncherSchemaFromFilename("test_section_summary.json"),
		LauncherSchemaFromFilename("test_sum_equal_validation_against_total.json"),
		LauncherSchemaFromFilename("test_sum_equal_or_less_validation_against_total.json"),
		LauncherSchemaFromFilename("test_sum_less_validation_against_total.json"),
		LauncherSchemaFromFilename("test_sum_multi_validation_against_total.json"),
		LauncherSchemaFromFilename("test_view_submitted_response.json"),
		LauncherSchemaFromFilename("test_textarea.json"),
		LauncherSchemaFromFilename("test_textfield.json"),
		LauncherSchemaFromFilename("test_timeout.json"),
		LauncherSchemaFromFilename("test_titles_within_repeating_blocks.json"),
		LauncherSchemaFromFilename("test_total_breakdown.json"),
		LauncherSchemaFromFilename("test_unit_patterns.json"),
	}

	return append(schemaList, getAvailableSchemasFromRegister()...)
}

func getAvailableSchemasFromRegister() []LauncherSchema {

	schemaList := []LauncherSchema{}

	if settings.Get("SURVEY_REGISTER_URL") != "" {
		req, err := http.NewRequest("GET", settings.Get("SURVEY_REGISTER_URL"), nil)
		if err != nil {
			log.Fatal("NewRequest: ", err)
			return []LauncherSchema{}
		}
		client := &http.Client{}

		resp, err := client.Do(req)
		if err != nil {
			log.Fatal("Do: ", err)
			return []LauncherSchema{}
		}

		defer resp.Body.Close()

		var registerResponse RegisterResponse

		if err := json.NewDecoder(resp.Body).Decode(&registerResponse); err != nil {
			log.Println(err)
		}

		var schemas Schemas

		schemasJSON, _ := json.Marshal(registerResponse.Embedded["schemas"])

		if err := json.Unmarshal(schemasJSON, &schemas); err != nil {
			log.Println(err)
		}

		for _, schema := range schemas {
			url := schema.Links["self"]
			EqID, formType := extractEqIDFormType(schema.Name)
			schemaList = append(schemaList, LauncherSchema{
				Name:     schema.Name,
				URL:      url.Href,
				EqID:     EqID,
				FormType: formType,
			})
		}
	}

	return schemaList
}

// FindSurveyByName Finds the schema in the list of available schemas
func FindSurveyByName(name string) LauncherSchema {
	for _, survey := range GetAvailableSchemas() {
		if survey.Name == name {
			return survey
		}
	}
	panic("Survey not found")
}
<commit_msg>Changed survey names used in titles<commit_after>package surveys

import (
	"encoding/json"
	"log"
	"net/http"
	"regexp"

	"github.com/AreaHQ/jsonhal"
	"github.com/ONSdigital/go-launch-a-survey/settings"
)

// LauncherSchema is a representation of a schema in the Launcher
type LauncherSchema struct {
	Name     string
	EqID     string
	FormType string
	URL      string
}

// RegisterResponse is the response from the eq-survey-register request
type RegisterResponse struct {
	jsonhal.Hal
}

// Schemas is a list of Schema
type Schemas []Schema

// Schema is an available schema
type Schema struct {
	jsonhal.Hal
	Name string `json:"name"`
}

var eqIDFormTypeRegex = regexp.MustCompile(`^(?P<eq_id>[a-z0-9]+)_(?P<form_type>\w+)`)

func extractEqIDFormType(schema string) (EqID, formType string) {
	match := eqIDFormTypeRegex.FindStringSubmatch(schema)
	if match != nil {
		EqID = match[1]
		formType = match[2]
	}
	return
}

// LauncherSchemaFromFilename creates a LauncherSchema record from a schema filename
func LauncherSchemaFromFilename(filename string) LauncherSchema {
	EqID, formType := extractEqIDFormType(filename)
	return LauncherSchema{
		Name:     filename,
		EqID:     EqID,
		FormType: formType,
	}
}

// GetAvailableSchemas Gets the list of static schemas an joins them with any schemas from the eq-survey-register if defined
func GetAvailableSchemas() []LauncherSchema {
	schemaList := []LauncherSchema{
		LauncherSchemaFromFilename("0_star_wars.json"),
		LauncherSchemaFromFilename("1_0005.json"),
		LauncherSchemaFromFilename("1_0102.json"),
		LauncherSchemaFromFilename("1_0112.json"),
		LauncherSchemaFromFilename("1_0203.json"),
		LauncherSchemaFromFilename("1_0205.json"),
		LauncherSchemaFromFilename("1_0213.json"),
		LauncherSchemaFromFilename("1_0215.json"),
		LauncherSchemaFromFilename("2_0001.json"),
		LauncherSchemaFromFilename("census_communal.json"),
		LauncherSchemaFromFilename("census_household.json"),
		LauncherSchemaFromFilename("census_individual.json"),
		LauncherSchemaFromFilename("e_commerce.json"),
		LauncherSchemaFromFilename("lms_1.json"),
		LauncherSchemaFromFilename("mbs_0106.json"),
		LauncherSchemaFromFilename("mbs_0111.json"),
		LauncherSchemaFromFilename("mbs_0117.json"),
		LauncherSchemaFromFilename("mbs_0123.json"),
		LauncherSchemaFromFilename("mbs_0158.json"),
		LauncherSchemaFromFilename("mbs_0161.json"),
		LauncherSchemaFromFilename("mbs_0167.json"),
		LauncherSchemaFromFilename("mbs_0173.json"),
		LauncherSchemaFromFilename("mbs_0201.json"),
		LauncherSchemaFromFilename("mbs_0202.json"),
		LauncherSchemaFromFilename("mbs_0203.json"),
		LauncherSchemaFromFilename("mbs_0204.json"),
		LauncherSchemaFromFilename("mbs_0205.json"),
		LauncherSchemaFromFilename("mbs_0216.json"),
		LauncherSchemaFromFilename("mbs_0251.json"),
		LauncherSchemaFromFilename("mbs_0253.json"),
		LauncherSchemaFromFilename("mbs_0255.json"),
		LauncherSchemaFromFilename("mbs_0817.json"),
		LauncherSchemaFromFilename("mbs_0823.json"),
		LauncherSchemaFromFilename("mbs_0867.json"),
		LauncherSchemaFromFilename("mbs_0873.json"),
		LauncherSchemaFromFilename("multiple_answers.json"),
		LauncherSchemaFromFilename("rsi_transformation.json"),
		LauncherSchemaFromFilename("test_big_list_naughty_strings.json"),
		LauncherSchemaFromFilename("test_checkbox.json"),
		LauncherSchemaFromFilename("test_checkbox_mutually_exclusive.json"),
		LauncherSchemaFromFilename("test_conditional_dates.json"),
		LauncherSchemaFromFilename("test_conditional_routing.json"),
		LauncherSchemaFromFilename("test_confirmation_question.json"),
		LauncherSchemaFromFilename("test_currency.json"),
		LauncherSchemaFromFilename("test_date_validation_combined.json"),
		LauncherSchemaFromFilename("test_date_validation_mm_yyyy_combined.json"),
		LauncherSchemaFromFilename("test_date_validation_range.json"),
		LauncherSchemaFromFilename("test_date_validation_single.json"),
		LauncherSchemaFromFilename("test_dates.json"),
		LauncherSchemaFromFilename("test_default.json"),
		LauncherSchemaFromFilename("test_dependencies_calculation.json"),
		LauncherSchemaFromFilename("test_dependencies_max_value.json"),
		LauncherSchemaFromFilename("test_dependencies_min_value.json"),
		LauncherSchemaFromFilename("test_difference_in_years.json"),
		LauncherSchemaFromFilename("test_difference_in_years_month_year.json"),
		LauncherSchemaFromFilename("test_difference_in_years_month_year_range.json"),
		LauncherSchemaFromFilename("test_difference_in_years_range.json"),
		LauncherSchemaFromFilename("test_dropdown_mandatory.json"),
		LauncherSchemaFromFilename("test_dropdown_mandatory_with_overridden_error.json"),
		LauncherSchemaFromFilename("test_dropdown_optional.json"),
		LauncherSchemaFromFilename("test_error_messages.json"),
		LauncherSchemaFromFilename("test_final_confirmation.json"),
		LauncherSchemaFromFilename("test_household_question.json"),
		LauncherSchemaFromFilename("test_interstitial_page.json"),
		LauncherSchemaFromFilename("test_introduction.json"),
		LauncherSchemaFromFilename("test_language.json"),
		LauncherSchemaFromFilename("test_language_cy.json"),
		LauncherSchemaFromFilename("test_markup.json"),
		LauncherSchemaFromFilename("test_metadata_routing.json"),
		LauncherSchemaFromFilename("test_multiple_piping.json"),
		LauncherSchemaFromFilename("test_navigation.json"),
		LauncherSchemaFromFilename("test_navigation_completeness.json"),
		LauncherSchemaFromFilename("test_navigation_confirmation.json"),
		LauncherSchemaFromFilename("test_navigation_routing.json"),
		LauncherSchemaFromFilename("test_numbers.json"),
		LauncherSchemaFromFilename("test_percentage.json"),
		LauncherSchemaFromFilename("test_question_guidance.json"),
		LauncherSchemaFromFilename("test_radio_checkbox_descriptions.json"),
		LauncherSchemaFromFilename("test_radio_mandatory.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_mandatory_other.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_mandatory_other_overridden_error.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_optional_other.json"),
		LauncherSchemaFromFilename("test_radio_mandatory_with_overridden_error.json"),
		LauncherSchemaFromFilename("test_radio_optional.json"),
		LauncherSchemaFromFilename("test_radio_optional_with_mandatory_other.json"),
		LauncherSchemaFromFilename("test_radio_optional_with_mandatory_other_overridden_error.json"),
		LauncherSchemaFromFilename("test_radio_optional_with_optional_other.json"),
		LauncherSchemaFromFilename("test_relationship_household.json"),
		LauncherSchemaFromFilename("test_repeating_and_conditional_routing.json"),
		LauncherSchemaFromFilename("test_repeating_household.json"),
		LauncherSchemaFromFilename("test_routing_date_equals.json"),
		LauncherSchemaFromFilename("test_routing_date_greater_than.json"),
		LauncherSchemaFromFilename("test_routing_date_less_than.json"),
		LauncherSchemaFromFilename("test_routing_date_not_equals.json"),
		LauncherSchemaFromFilename("test_routing_group.json"),
		LauncherSchemaFromFilename("test_routing_number_equals.json"),
		LauncherSchemaFromFilename("test_routing_number_greater_than.json"),
		LauncherSchemaFromFilename("test_routing_number_greater_than_or_equal.json"),
		LauncherSchemaFromFilename("test_routing_number_less_than.json"),
		LauncherSchemaFromFilename("test_routing_number_less_than_or_equal.json"),
		LauncherSchemaFromFilename("test_routing_number_not_equals.json"),
		LauncherSchemaFromFilename("test_routing_on_multiple_select.json"),
		LauncherSchemaFromFilename("test_skip_condition.json"),
		LauncherSchemaFromFilename("test_skip_condition_block.json"),
		LauncherSchemaFromFilename("test_skip_condition_group.json"),
		LauncherSchemaFromFilename("test_summary.json"),
		LauncherSchemaFromFilename("test_section_summary.json"),
		LauncherSchemaFromFilename("test_sum_equal_validation_against_total.json"),
		LauncherSchemaFromFilename("test_sum_equal_or_less_validation_against_total.json"),
		LauncherSchemaFromFilename("test_sum_less_validation_against_total.json"),
		LauncherSchemaFromFilename("test_sum_multi_validation_against_total.json"),
		LauncherSchemaFromFilename("test_titles.json"),
		LauncherSchemaFromFilename("test_titles_radio_and_checkbox.json"),
		LauncherSchemaFromFilename("test_titles_within_repeating_blocks"),
		LauncherSchemaFromFilename("test_view_submitted_response.json"),
		LauncherSchemaFromFilename("test_textarea.json"),
		LauncherSchemaFromFilename("test_textfield.json"),
		LauncherSchemaFromFilename("test_timeout.json"),
		LauncherSchemaFromFilename("test_total_breakdown.json"),
		LauncherSchemaFromFilename("test_unit_patterns.json"),
	}

	return append(schemaList, getAvailableSchemasFromRegister()...)
}

func getAvailableSchemasFromRegister() []LauncherSchema {

	schemaList := []LauncherSchema{}

	if settings.Get("SURVEY_REGISTER_URL") != "" {
		req, err := http.NewRequest("GET", settings.Get("SURVEY_REGISTER_URL"), nil)
		if err != nil {
			log.Fatal("NewRequest: ", err)
			return []LauncherSchema{}
		}
		client := &http.Client{}

		resp, err := client.Do(req)
		if err != nil {
			log.Fatal("Do: ", err)
			return []LauncherSchema{}
		}

		defer resp.Body.Close()

		var registerResponse RegisterResponse

		if err := json.NewDecoder(resp.Body).Decode(&registerResponse); err != nil {
			log.Println(err)
		}

		var schemas Schemas

		schemasJSON, _ := json.Marshal(registerResponse.Embedded["schemas"])

		if err := json.Unmarshal(schemasJSON, &schemas); err != nil {
			log.Println(err)
		}

		for _, schema := range schemas {
			url := schema.Links["self"]
			EqID, formType := extractEqIDFormType(schema.Name)
			schemaList = append(schemaList, LauncherSchema{
				Name:     schema.Name,
				URL:      url.Href,
				EqID:     EqID,
				FormType: formType,
			})
		}
	}

	return schemaList
}

// FindSurveyByName Finds the schema in the list of available schemas
func FindSurveyByName(name string) LauncherSchema {
	for _, survey := range GetAvailableSchemas() {
		if survey.Name == name {
			return survey
		}
	}
	panic("Survey not found")
}
<|endoftext|>


<commit_before>// +build go1.8

package httpstat

import (
	"context"
	"crypto/tls"
	"net/http/httptrace"
	"time"
)

// End sets the time when reading response is done.
// This must be called after reading response body.
func (r *Result) End(t time.Time) {
	r.trasferDone = t
	r.t5 = t // for Formatter

	// This means result is empty (it does nothing).
	// Skip setting value(contentTransfer and total will be zero).
	if r.dnsStart.IsZero() {
		return
	}

	r.contentTransfer = r.trasferDone.Sub(r.transferStart)
	r.total = r.trasferDone.Sub(r.dnsStart)
}

// ContentTransfer returns the duration of content transfer time.
// It is from first response byte to the given time. The time must
// be time after read body (go-httpstat can not detect that time).
func (r *Result) ContentTransfer(t time.Time) time.Duration {
	return t.Sub(r.serverDone)
}

// Total returns the duration of total http request.
// It is from dns lookup start time to the given time. The
// time must be time after read body (go-httpstat can not detect that time).
func (r *Result) Total(t time.Time) time.Duration {
	return t.Sub(r.dnsStart)
}

func withClientTrace(ctx context.Context, r *Result) context.Context {
	return httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{
		DNSStart: func(i httptrace.DNSStartInfo) {
			r.dnsStart = time.Now()
		},

		DNSDone: func(i httptrace.DNSDoneInfo) {
			r.dnsDone = time.Now()

			r.DNSLookup = r.dnsDone.Sub(r.dnsStart)
			r.NameLookup = r.dnsDone.Sub(r.dnsStart)
		},

		ConnectStart: func(_, _ string) {
			r.tcpStart = time.Now()

			// When connecting to IP (When no DNS lookup)
			if r.dnsStart.IsZero() {
				r.dnsStart = r.tcpStart
				r.dnsDone = r.tcpStart
			}
		},

		ConnectDone: func(network, addr string, err error) {
			r.tcpDone = time.Now()

			r.TCPConnection = r.tcpDone.Sub(r.tcpStart)
			r.Connect = r.tcpDone.Sub(r.dnsStart)
		},

		TLSHandshakeStart: func() {
			r.isTLS = true
			r.tlsStart = time.Now()
		},

		TLSHandshakeDone: func(_ tls.ConnectionState, _ error) {
			r.tlsDone = time.Now()

			r.TLSHandshake = r.tlsDone.Sub(r.tlsStart)
			r.Pretransfer = r.tlsDone.Sub(r.dnsStart)
		},

		GotConn: func(i httptrace.GotConnInfo) {
			// Handle when keep alive is used and connection is reused.
			// DNSStart(Done) and ConnectStart(Done) is skipped
			if i.Reused {
				r.isReused = true
			}
		},

		WroteRequest: func(info httptrace.WroteRequestInfo) {
			r.serverStart = time.Now()

			// When client doesn't use DialContext or using old (before go1.7) `net`
			// pakcage, DNS/TCP/TLS hook is not called.
			if r.dnsStart.IsZero() && r.tcpStart.IsZero() {
				now := r.serverStart

				r.dnsStart = now
				r.dnsDone = now
				r.tcpStart = now
				r.tcpDone = now
			}

			// When connection is re-used, DNS/TCP/TLS hook is not called.
			if r.isReused {
				now := r.serverStart

				r.dnsStart = now
				r.dnsDone = now
				r.tcpStart = now
				r.tcpDone = now
				r.tlsStart = now
				r.tlsDone = now
			}

			if r.isTLS {
				return
			}

			r.TLSHandshake = r.tcpDone.Sub(r.tcpDone)
			r.Pretransfer = r.Connect
		},

		GotFirstResponseByte: func() {
			r.serverDone = time.Now()

			r.ServerProcessing = r.serverDone.Sub(r.serverStart)
			r.StartTransfer = r.serverDone.Sub(r.dnsStart)

			r.transferStart = r.serverDone
		},
	})
}
<commit_msg>adjustment<commit_after>// +build go1.8

package httpstat

import (
	"context"
	"crypto/tls"
	"net/http/httptrace"
	"time"
)

// End sets the time when reading response is done.
// This must be called after reading response body.
func (r *Result) End(t time.Time) {
	r.trasferDone = t

	// This means result is empty (it does nothing).
	// Skip setting value(contentTransfer and total will be zero).
	if r.dnsStart.IsZero() {
		return
	}

	r.contentTransfer = r.trasferDone.Sub(r.transferStart)
	r.total = r.trasferDone.Sub(r.dnsStart)
}

// ContentTransfer returns the duration of content transfer time.
// It is from first response byte to the given time. The time must
// be time after read body (go-httpstat can not detect that time).
func (r *Result) ContentTransfer(t time.Time) time.Duration {
	return t.Sub(r.serverDone)
}

// Total returns the duration of total http request.
// It is from dns lookup start time to the given time. The
// time must be time after read body (go-httpstat can not detect that time).
func (r *Result) Total(t time.Time) time.Duration {
	return t.Sub(r.dnsStart)
}

func withClientTrace(ctx context.Context, r *Result) context.Context {
	return httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{
		DNSStart: func(i httptrace.DNSStartInfo) {
			r.dnsStart = time.Now()
		},

		DNSDone: func(i httptrace.DNSDoneInfo) {
			r.dnsDone = time.Now()

			r.DNSLookup = r.dnsDone.Sub(r.dnsStart)
			r.NameLookup = r.dnsDone.Sub(r.dnsStart)
		},

		ConnectStart: func(_, _ string) {
			r.tcpStart = time.Now()

			// When connecting to IP (When no DNS lookup)
			if r.dnsStart.IsZero() {
				r.dnsStart = r.tcpStart
				r.dnsDone = r.tcpStart
			}
		},

		ConnectDone: func(network, addr string, err error) {
			r.tcpDone = time.Now()

			r.TCPConnection = r.tcpDone.Sub(r.tcpStart)
			r.Connect = r.tcpDone.Sub(r.dnsStart)
		},

		TLSHandshakeStart: func() {
			r.isTLS = true
			r.tlsStart = time.Now()
		},

		TLSHandshakeDone: func(_ tls.ConnectionState, _ error) {
			r.tlsDone = time.Now()

			r.TLSHandshake = r.tlsDone.Sub(r.tlsStart)
			r.Pretransfer = r.tlsDone.Sub(r.dnsStart)
		},

		GotConn: func(i httptrace.GotConnInfo) {
			// Handle when keep alive is used and connection is reused.
			// DNSStart(Done) and ConnectStart(Done) is skipped
			if i.Reused {
				r.isReused = true
			}
		},

		WroteRequest: func(info httptrace.WroteRequestInfo) {
			r.serverStart = time.Now()

			// When client doesn't use DialContext or using old (before go1.7) `net`
			// pakcage, DNS/TCP/TLS hook is not called.
			if r.dnsStart.IsZero() && r.tcpStart.IsZero() {
				now := r.serverStart

				r.dnsStart = now
				r.dnsDone = now
				r.tcpStart = now
				r.tcpDone = now
			}

			// When connection is re-used, DNS/TCP/TLS hook is not called.
			if r.isReused {
				now := r.serverStart

				r.dnsStart = now
				r.dnsDone = now
				r.tcpStart = now
				r.tcpDone = now
				r.tlsStart = now
				r.tlsDone = now
			}

			if r.isTLS {
				return
			}

			r.TLSHandshake = r.tcpDone.Sub(r.tcpDone)
			r.Pretransfer = r.Connect
		},

		GotFirstResponseByte: func() {
			r.serverDone = time.Now()

			r.ServerProcessing = r.serverDone.Sub(r.serverStart)
			r.StartTransfer = r.serverDone.Sub(r.dnsStart)

			r.transferStart = r.serverDone
		},
	})
}
<|endoftext|>


<commit_before>// gogh project gogh.go
package gogh

import (
	//"fmt"
	"image"
	"image/color"
)

var (
	GrayArray = make([]color.Color, 256)
)

func NewMat(src image.Image) *Mat {
	dst := ImageToRGBA(src)
	return &Mat{dst}
}

type Mat struct {
	src *image.RGBA
}

type Pixel struct {
	src   *image.RGBA
	X     int
	Y     int
	color color.Color
}

func (src *Mat) At(x, y int) *Pixel {
	return &Pixel{src.src, x, y, src.src.At(x, y)}
}

func (src *Pixel) RGBA() (int, int, int, int) {
	r, g, b, a := src.color.RGBA()
	return int(r >> 8), int(g >> 8), int(b >> 8), int(a >> 8)
}

func (src *Mat) Save(path string) {
	Save(path, src.src)
}

func (src *Mat) Clone() *Mat {
	return clone(src)
}

func (src *Pixel) Gray() int {
	gray, _, _, _ := src.color.RGBA()
	return int(gray >> 8)
}

func (src *Pixel) Set(r, g, b int) {
	src.src.Set(src.X, src.Y, color.Color(color.RGBA{uint8(r), uint8(g), uint8(b), uint8(255)}))
}

func (src *Mat) Bounds() image.Rectangle {
	return src.src.Bounds()
}

func (src *Mat) Pixels() []uint8 {
	return src.src.Pix
}
<commit_msg>Rename<commit_after>// gogh project gogh.go
package gogh

import (
	//"fmt"
	"image"
	"image/color"
)

var (
	GrayArray = make([]color.Color, 256)
)

func NewMat(src image.Image) *Img {
	dst := ImageToRGBA(src)
	return &Img{dst}
}

type Img struct {
	src *image.RGBA
}

type Pixel struct {
	src   *image.RGBA
	X     int
	Y     int
	color color.Color
}

func (src *Img) At(x, y int) *Pixel {
	return &Pixel{src.src, x, y, src.src.At(x, y)}
}

func (src *Pixel) RGBA() (int, int, int, int) {
	r, g, b, a := src.color.RGBA()
	return int(r >> 8), int(g >> 8), int(b >> 8), int(a >> 8)
}

func (src *Img) Save(path string) {
	Save(path, src.src)
}

func (src *Img) Clone() *Img {
	return clone(src)
}

func (src *Pixel) Gray() int {
	gray, _, _, _ := src.color.RGBA()
	return int(gray >> 8)
}

func (src *Pixel) Set(r, g, b int) {
	src.src.Set(src.X, src.Y, color.Color(color.RGBA{uint8(r), uint8(g), uint8(b), uint8(255)}))
}

func (src *Img) Bounds() image.Rectangle {
	return src.src.Bounds()
}

func (src *Img) Pixels() []uint8 {
	return src.src.Pix
}
<|endoftext|>


<commit_before>// Gone Time Tracker -or- Where has my time gone?
package main

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
	"github.com/mewkiz/pkg/goutil"
)

const (
	port = "127.0.0.1:8001"
	dump = "gone.gob"
	logf = "gone.log"
)

var (
	goneDir string
	tracks  Tracker
	tmpl    *template.Template
	zzz     bool
	m       sync.Mutex
	logger  *log.Logger
)

func init() {
	var err error
	goneDir, err = goutil.SrcDir("github.com/dim13/gone")
	if err != nil {
		log.Fatal("init: ", err)
	}
	tmpl = template.Must(template.ParseFiles(filepath.Join(goneDir, "index.html")))
}

type Tracker map[Window]*Track

type Track struct {
	Seen  time.Time
	Spent time.Duration
}

type Window struct {
	Class string
	Name  string
}

type Xorg struct {
	X           *xgb.Conn
	root        xproto.Window
	activeAtom  *xproto.InternAtomReply
	netNameAtom *xproto.InternAtomReply
	nameAtom    *xproto.InternAtomReply
	classAtom   *xproto.InternAtomReply
}

func (t Track) String() string {
	return fmt.Sprintf("%s %s", t.Seen.Format("2006/01/02 15:04:05"), t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (x Xorg) atom(aname string) *xproto.InternAtomReply {
	a, err := xproto.InternAtom(x.X, true, uint16(len(aname)), aname).Reply()
	if err != nil {
		log.Fatal("atom: ", err)
	}
	return a
}

func (x Xorg) property(w xproto.Window, a *xproto.InternAtomReply) (*xproto.GetPropertyReply, error) {
	return xproto.GetProperty(x.X, false, w, a.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
}

func (x Xorg) active() xproto.Window {
	p, err := x.property(x.root, x.activeAtom)
	if err != nil {
		return x.root
	}
	return xproto.Window(xgb.Get32(p.Value))
}

func (x Xorg) name(w xproto.Window) (string, error) {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		return "", err
	}
	if string(name.Value) == "" {
		name, err = x.property(w, x.nameAtom)
		if err != nil {
			return "", err
		}
		if string(name.Value) == "" {
			return "", errors.New("empty value")
		}
	}
	return string(name.Value), nil
}

func (x Xorg) class(w xproto.Window) (string, error) {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		return "", err
	}
	zero := []byte{0}
	s := bytes.Split(bytes.TrimSuffix(class.Value, zero), zero)
	if l := len(s); l > 0 && len(s[l-1]) != 0 {
		return string(s[l-1]), nil
	}
	return "", errors.New("empty class")
}

func (x Xorg) window() (Window, bool) {
	id := x.active()
	/* skip invalid window id */
	if id == 0 {
		return Window{}, false
	}
	class, err := x.class(id)
	if err != nil {
		return Window{}, false
	}
	name, err := x.name(id)
	if err != nil {
		return Window{}, false
	}
	x.spy(id)
	return Window{
		Class: class,
		Name:  name,
	}, true
}

func (x Xorg) spy(w xproto.Window) {
	xproto.ChangeWindowAttributes(x.X, w, xproto.CwEventMask,
		[]uint32{xproto.EventMaskPropertyChange})
}

func (x Xorg) update(t Tracker) (current *Track) {
	if win, ok := x.window(); ok {
		m.Lock()
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Seen = time.Now()
		current = t[win]
		m.Unlock()
	}
	return
}

func connect() Xorg {
	var x Xorg
	var err error

	display := os.Getenv("DISPLAY")
	if display == "" {
		display = ":0"
	}
	x.X, err = xgb.NewConnDisplay(display)
	if err != nil {
		log.Fatal("xgb: ", err)
	}

	err = screensaver.Init(x.X)
	if err != nil {
		log.Fatal("screensaver: ", err)
	}

	setup := xproto.Setup(x.X)
	x.root = setup.DefaultScreen(x.X).Root

	drw := xproto.Drawable(x.root)
	screensaver.SelectInput(x.X, drw, screensaver.EventNotifyMask)

	x.activeAtom = x.atom("_NET_ACTIVE_WINDOW")
	x.netNameAtom = x.atom("_NET_WM_NAME")
	x.nameAtom = x.atom("WM_NAME")
	x.classAtom = x.atom("WM_CLASS")

	x.spy(x.root)

	return x
}

func (t Tracker) collect() {
	x := connect()
	defer x.X.Close()

	current := x.update(t)
	for {
		ev, everr := x.X.WaitForEvent()
		if everr != nil {
			log.Println("wait for event:", everr)
			continue
		}
		switch event := ev.(type) {
		case xproto.PropertyNotifyEvent:
			if current != nil {
				m.Lock()
				current.Spent += time.Since(current.Seen)
				m.Unlock()
			}
			current = x.update(t)
		case screensaver.NotifyEvent:
			switch event.State {
			case screensaver.StateOn:
				log.Println("away from keyboard")
				current = nil
				zzz = true
			default:
				log.Println("back to keyboard")
				zzz = false
			}
		}
	}
}

func (t Tracker) cleanup(d time.Duration) {
	m.Lock()
	for k, v := range t {
		if time.Since(v.Seen) > d {
			logger.Println(v, k)
			delete(t, k)
		}
	}
	m.Unlock()
}

func (t Tracker) reset() {
	m.Lock()
	for k, v := range t {
		logger.Println(v, k)
		delete(t, k)
	}
	m.Unlock()
}

func load(fname string) Tracker {
	t := make(Tracker)
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return t
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	m.Lock()
	err = dec.Decode(&t)
	m.Unlock()
	if err != nil {
		log.Println(err)
	}
	return t
}

func (t Tracker) store(fname string) {
	tmp := fname+".tmp"
	dump, err := os.Create(tmp)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	m.Lock()
	err = enc.Encode(t)
	m.Unlock()
	if err != nil {
		log.Println(err)
		os.Remove(tmp)
		return
	}
	os.Rename(tmp, fname)
}

type Index struct {
	Title   string
	Records Records
	Classes Records
	Total   Duration
	Zzz     bool
}

type Records []Record
type Duration time.Duration

type Record struct {
	Class string
	Name  string
	Spent Duration
	Seen  time.Time
	Odd   bool `json:"-"`
}

func (r Records) Len() int           { return len(r) }
func (r Records) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
func (r Records) Less(i, j int) bool { return r[i].Spent < r[j].Spent }

func (d Duration) String() string {
	h := int(time.Duration(d).Hours())
	m := int(time.Duration(d).Minutes()) % 60
	s := int(time.Duration(d).Seconds()) % 60
	var ret string
	if h > 0 {
		ret += fmt.Sprintf("%dh", h)
	}
	if m > 0 {
		ret += fmt.Sprintf("%dm", m)
	}
	return ret + fmt.Sprintf("%ds", s)
}

func (d Duration) Seconds() int {
	return int(time.Duration(d).Seconds())
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	var idx Index
	idx.Title = "Gone Time Tracker"
	idx.Zzz = zzz
	class := r.URL.Path[1:]

	classtotal := make(map[string]time.Duration)

	m.Lock()
	for k, v := range tracks {
		classtotal[k.Class] += v.Spent
		idx.Total += Duration(v.Spent)
		if class != "" && class != k.Class {
			continue
		}
		idx.Records = append(idx.Records, Record{
			Class: k.Class,
			Name:  k.Name,
			Spent: Duration(v.Spent)})
	}
	m.Unlock()
	for k, v := range classtotal {
		idx.Classes = append(idx.Classes, Record{Class: k, Spent: Duration(v)})
	}
	sort.Sort(sort.Reverse(idx.Classes))
	sort.Sort(sort.Reverse(idx.Records))
	for j := range idx.Records {
		idx.Records[j].Odd = j%2 == 0
	}
	err := tmpl.Execute(w, idx)
	if err != nil {
		log.Println(err)
	}
}

func dumpHandler(w http.ResponseWriter, r *http.Request) {
	var rec Records

	m.Lock()
	for k, v := range tracks {
		rec = append(rec, Record{
			Class: k.Class,
			Name:  k.Name,
			Spent: Duration(v.Spent),
			Seen:  v.Seen})
	}
	m.Unlock()

	data, err := json.MarshalIndent(rec, "", "\t")
	if err != nil {
		log.Println("dump:", err)
	}
	w.Write(data)
}

func resetHandler(w http.ResponseWriter, r *http.Request) {
	tracks.reset()
	http.Redirect(w, r, "/", http.StatusFound)
}

func main() {
	logfile, err := os.OpenFile(filepath.Join(goneDir, logf), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer logfile.Close()
	logger = log.New(logfile, "", log.LstdFlags)

	dumpPath := filepath.Join(goneDir, dump)
	tracks = load(dumpPath)

	go tracks.collect()
	go func() {
		for {
			tracks.cleanup(8 * time.Hour)
			tracks.store(dumpPath)
			time.Sleep(time.Minute)
		}
	}()
	log.Println("listen on", port)
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/gone.json", dumpHandler)
	http.HandleFunc("/reset", resetHandler)
	err = http.ListenAndServe(port, nil)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>remove duplicated code: reset() -> cleanup(0)<commit_after>// Gone Time Tracker -or- Where has my time gone?
package main

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
	"github.com/mewkiz/pkg/goutil"
)

const (
	port = "127.0.0.1:8001"
	dump = "gone.gob"
	logf = "gone.log"
)

var (
	goneDir string
	tracks  Tracker
	tmpl    *template.Template
	zzz     bool
	m       sync.Mutex
	logger  *log.Logger
)

func init() {
	var err error
	goneDir, err = goutil.SrcDir("github.com/dim13/gone")
	if err != nil {
		log.Fatal("init: ", err)
	}
	tmpl = template.Must(template.ParseFiles(filepath.Join(goneDir, "index.html")))
}

type Tracker map[Window]*Track

type Track struct {
	Seen  time.Time
	Spent time.Duration
}

type Window struct {
	Class string
	Name  string
}

type Xorg struct {
	X           *xgb.Conn
	root        xproto.Window
	activeAtom  *xproto.InternAtomReply
	netNameAtom *xproto.InternAtomReply
	nameAtom    *xproto.InternAtomReply
	classAtom   *xproto.InternAtomReply
}

func (t Track) String() string {
	return fmt.Sprintf("%s %s", t.Seen.Format("2006/01/02 15:04:05"), t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (x Xorg) atom(aname string) *xproto.InternAtomReply {
	a, err := xproto.InternAtom(x.X, true, uint16(len(aname)), aname).Reply()
	if err != nil {
		log.Fatal("atom: ", err)
	}
	return a
}

func (x Xorg) property(w xproto.Window, a *xproto.InternAtomReply) (*xproto.GetPropertyReply, error) {
	return xproto.GetProperty(x.X, false, w, a.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
}

func (x Xorg) active() xproto.Window {
	p, err := x.property(x.root, x.activeAtom)
	if err != nil {
		return x.root
	}
	return xproto.Window(xgb.Get32(p.Value))
}

func (x Xorg) name(w xproto.Window) (string, error) {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		return "", err
	}
	if string(name.Value) == "" {
		name, err = x.property(w, x.nameAtom)
		if err != nil {
			return "", err
		}
		if string(name.Value) == "" {
			return "", errors.New("empty value")
		}
	}
	return string(name.Value), nil
}

func (x Xorg) class(w xproto.Window) (string, error) {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		return "", err
	}
	zero := []byte{0}
	s := bytes.Split(bytes.TrimSuffix(class.Value, zero), zero)
	if l := len(s); l > 0 && len(s[l-1]) != 0 {
		return string(s[l-1]), nil
	}
	return "", errors.New("empty class")
}

func (x Xorg) window() (Window, bool) {
	id := x.active()
	/* skip invalid window id */
	if id == 0 {
		return Window{}, false
	}
	class, err := x.class(id)
	if err != nil {
		return Window{}, false
	}
	name, err := x.name(id)
	if err != nil {
		return Window{}, false
	}
	x.spy(id)
	return Window{
		Class: class,
		Name:  name,
	}, true
}

func (x Xorg) spy(w xproto.Window) {
	xproto.ChangeWindowAttributes(x.X, w, xproto.CwEventMask,
		[]uint32{xproto.EventMaskPropertyChange})
}

func (x Xorg) update(t Tracker) (current *Track) {
	if win, ok := x.window(); ok {
		m.Lock()
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Seen = time.Now()
		current = t[win]
		m.Unlock()
	}
	return
}

func connect() Xorg {
	var x Xorg
	var err error

	display := os.Getenv("DISPLAY")
	if display == "" {
		display = ":0"
	}
	x.X, err = xgb.NewConnDisplay(display)
	if err != nil {
		log.Fatal("xgb: ", err)
	}

	err = screensaver.Init(x.X)
	if err != nil {
		log.Fatal("screensaver: ", err)
	}

	setup := xproto.Setup(x.X)
	x.root = setup.DefaultScreen(x.X).Root

	drw := xproto.Drawable(x.root)
	screensaver.SelectInput(x.X, drw, screensaver.EventNotifyMask)

	x.activeAtom = x.atom("_NET_ACTIVE_WINDOW")
	x.netNameAtom = x.atom("_NET_WM_NAME")
	x.nameAtom = x.atom("WM_NAME")
	x.classAtom = x.atom("WM_CLASS")

	x.spy(x.root)

	return x
}

func (t Tracker) collect() {
	x := connect()
	defer x.X.Close()

	current := x.update(t)
	for {
		ev, everr := x.X.WaitForEvent()
		if everr != nil {
			log.Println("wait for event:", everr)
			continue
		}
		switch event := ev.(type) {
		case xproto.PropertyNotifyEvent:
			if current != nil {
				m.Lock()
				current.Spent += time.Since(current.Seen)
				m.Unlock()
			}
			current = x.update(t)
		case screensaver.NotifyEvent:
			switch event.State {
			case screensaver.StateOn:
				log.Println("away from keyboard")
				current = nil
				zzz = true
			default:
				log.Println("back to keyboard")
				zzz = false
			}
		}
	}
}

func (t Tracker) cleanup(d time.Duration) {
	m.Lock()
	for k, v := range t {
		if time.Since(v.Seen) > d {
			logger.Println(v, k)
			delete(t, k)
		}
	}
	m.Unlock()
}

func load(fname string) Tracker {
	t := make(Tracker)
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return t
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	m.Lock()
	err = dec.Decode(&t)
	m.Unlock()
	if err != nil {
		log.Println(err)
	}
	return t
}

func (t Tracker) store(fname string) {
	tmp := fname+".tmp"
	dump, err := os.Create(tmp)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	m.Lock()
	err = enc.Encode(t)
	m.Unlock()
	if err != nil {
		log.Println(err)
		os.Remove(tmp)
		return
	}
	os.Rename(tmp, fname)
}

type Index struct {
	Title   string
	Records Records
	Classes Records
	Total   Duration
	Zzz     bool
}

type Records []Record
type Duration time.Duration

type Record struct {
	Class string
	Name  string
	Spent Duration
	Seen  time.Time
	Odd   bool `json:"-"`
}

func (r Records) Len() int           { return len(r) }
func (r Records) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
func (r Records) Less(i, j int) bool { return r[i].Spent < r[j].Spent }

func (d Duration) String() string {
	h := int(time.Duration(d).Hours())
	m := int(time.Duration(d).Minutes()) % 60
	s := int(time.Duration(d).Seconds()) % 60
	var ret string
	if h > 0 {
		ret += fmt.Sprintf("%dh", h)
	}
	if m > 0 {
		ret += fmt.Sprintf("%dm", m)
	}
	return ret + fmt.Sprintf("%ds", s)
}

func (d Duration) Seconds() int {
	return int(time.Duration(d).Seconds())
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	var idx Index
	idx.Title = "Gone Time Tracker"
	idx.Zzz = zzz
	class := r.URL.Path[1:]

	classtotal := make(map[string]time.Duration)

	m.Lock()
	for k, v := range tracks {
		classtotal[k.Class] += v.Spent
		idx.Total += Duration(v.Spent)
		if class != "" && class != k.Class {
			continue
		}
		idx.Records = append(idx.Records, Record{
			Class: k.Class,
			Name:  k.Name,
			Spent: Duration(v.Spent)})
	}
	m.Unlock()
	for k, v := range classtotal {
		idx.Classes = append(idx.Classes, Record{Class: k, Spent: Duration(v)})
	}
	sort.Sort(sort.Reverse(idx.Classes))
	sort.Sort(sort.Reverse(idx.Records))
	for j := range idx.Records {
		idx.Records[j].Odd = j%2 == 0
	}
	err := tmpl.Execute(w, idx)
	if err != nil {
		log.Println(err)
	}
}

func dumpHandler(w http.ResponseWriter, r *http.Request) {
	var rec Records

	m.Lock()
	for k, v := range tracks {
		rec = append(rec, Record{
			Class: k.Class,
			Name:  k.Name,
			Spent: Duration(v.Spent),
			Seen:  v.Seen})
	}
	m.Unlock()

	data, err := json.MarshalIndent(rec, "", "\t")
	if err != nil {
		log.Println("dump:", err)
	}
	w.Write(data)
}

func resetHandler(w http.ResponseWriter, r *http.Request) {
	tracks.cleanup(0)
	http.Redirect(w, r, "/", http.StatusFound)
}

func main() {
	logfile, err := os.OpenFile(filepath.Join(goneDir, logf), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer logfile.Close()
	logger = log.New(logfile, "", log.LstdFlags)

	dumpPath := filepath.Join(goneDir, dump)
	tracks = load(dumpPath)

	go tracks.collect()
	go func() {
		for {
			tracks.cleanup(8 * time.Hour)
			tracks.store(dumpPath)
			time.Sleep(time.Minute)
		}
	}()
	log.Println("listen on", port)
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/gone.json", dumpHandler)
	http.HandleFunc("/reset", resetHandler)
	err = http.ListenAndServe(port, nil)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 Matt Jibson <matt.jibson@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package goon

import (
	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"bytes"
	"encoding/gob"
	"net/http"
	"reflect"
)

// Goon holds the app engine context and request memory cache.
type Goon struct {
	context       appengine.Context
	cache         map[string]*Entity
	inTransaction bool
	toSet         map[string]*Entity
	toDelete      []string
}

func memkey(k *datastore.Key) string {
	return k.String()
}

func NewGoon(r *http.Request) *Goon {
	return FromContext(appengine.NewContext(r))
}

func FromContext(c appengine.Context) *Goon {
	return &Goon{
		context: c,
		cache:   make(map[string]*Entity),
	}
}

// RunInTransaction runs f in a transaction. It calls f with a transaction
// context tg that f should use for all App Engine operations. Neither cache nor
// memcache are used or set during a transaction.
//
// Otherwise similar to appengine/datastore.RunInTransaction:
// https://developers.google.com/appengine/docs/go/datastore/reference#RunInTransaction
func (g *Goon) RunInTransaction(f func(tg *Goon) error, opts *datastore.TransactionOptions) error {
	var ng *Goon
	err := datastore.RunInTransaction(g.context, func(tc appengine.Context) error {
		ng = &Goon{
			context:       tc,
			inTransaction: true,
			toSet:         make(map[string]*Entity),
		}
		return f(ng)
	}, opts)

	if err == nil {
		for k, v := range ng.toSet {
			g.cache[k] = v
		}

		for _, k := range ng.toDelete {
			delete(g.cache, k)
		}
	}

	return err
}

// Put stores Entity e.
// If e has an incomplete key, it is updated.
func (g *Goon) Put(e *Entity) error {
	return g.PutMulti([]*Entity{e})
}

// PutMany is a wrapper around PutMulti.
func (g *Goon) PutMany(es ...*Entity) error {
	return g.PutMulti(es)
}

// PutMulti stores a sequence of Entities.
// Any entity with an incomplete key will be updated.
func (g *Goon) PutMulti(es []*Entity) error {
	var err error

	var memkeys []string
	keys := make([]*datastore.Key, len(es))
	src := make([]interface{}, len(es))

	for i, e := range es {
		if !e.Key.Incomplete() {
			memkeys = append(memkeys, e.memkey())
		}

		keys[i] = e.Key
		src[i] = e.Src
	}

	memcache.DeleteMulti(g.context, memkeys)

	keys, err = datastore.PutMulti(g.context, keys, src)

	if err != nil {
		return err
	}

	for i, e := range es {
		es[i].setKey(keys[i])

		if g.inTransaction {
			g.toSet[e.memkey()] = e
		}
	}

	if !g.inTransaction {
		g.putMemoryMulti(es)
	}

	return nil
}

func (g *Goon) putMemoryMulti(es []*Entity) {
	for _, e := range es {
		g.putMemory(e)
	}
}

func (g *Goon) putMemory(e *Entity) {
	g.cache[e.memkey()] = e
}

func (g *Goon) putMemcache(es []*Entity) error {
	items := make([]*memcache.Item, len(es))

	for i, e := range es {
		gob, err := e.gob()
		if err != nil {
			return err
		}

		items[i] = &memcache.Item{
			Key:   e.memkey(),
			Value: gob,
		}
	}

	err := memcache.SetMulti(g.context, items)

	if err != nil {
		return err
	}

	g.putMemoryMulti(es)
	return nil
}

// Kind returns the Kind name of src, which must be a struct.
// If src has a field named _goon with a tag "kind", that is used.
// Otherwise, reflection is used to determine the type name of src.
// In the case of an error, the empty string is returned.
//
// For example, to overwrite the default "Group" kind name:
//   type Group struct {
//     _goon interface{} `kind:"something_else"`
//     Name  string
//   }
func Kind (src interface{}) string {
	v := reflect.ValueOf(src)
	v = reflect.Indirect(v)
	t := v.Type()
	k := t.Kind()

	if k == reflect.Struct {
		if f, present := t.FieldByName("_goon"); present {
			name := f.Tag.Get("kind")
			if name != "" {
				return name
			}
		}

		return t.Name()
	}
	return ""
}

// GetById fetches an entity of kind src by id.
// Refer to appengine/datastore.NewKey regarding key specification.
func (g *Goon) GetById(src interface{}, stringID string, intID int64, parent *datastore.Key) (*Entity, error) {
	key := datastore.NewKey(g.context, Kind(src), stringID, intID, parent)
	return g.Get(src, key)
}

// Get fetches an entity of kind src by key.
func (g *Goon) Get(src interface{}, key *datastore.Key) (*Entity, error) {
	e := NewEntity(key, src)
	es := []*Entity{e}
	err := g.GetMulti(es)
	if err != nil {
		return nil, err
	}
	return es[0], nil
}

// Get fetches a sequency of Entities, whose keys must already be valid.
// Entities with no correspending key have their NotFound field set to true.
func (g *Goon) GetMulti(es []*Entity) error {
	var dskeys []*datastore.Key
	var dst []interface{}
	var dixs []int

	if !g.inTransaction {
		var memkeys []string
		var mixs []int

		for i, e := range es {
			m := e.memkey()
			if s, present := g.cache[m]; present {
				es[i] = s
			} else {
				memkeys = append(memkeys, m)
				mixs = append(mixs, i)
			}
		}

		memvalues, err := memcache.GetMulti(g.context, memkeys)
		if err != nil {
			return err
		}

		for i, m := range memkeys {
			e := es[mixs[i]]
			if s, present := memvalues[m]; present {
				err := fromGob(e, s.Value)
				if err != nil {
					return err
				}

				g.putMemory(e)
			} else {
				dskeys = append(dskeys, e.Key)
				dst = append(dst, e.Src)
				dixs = append(dixs, mixs[i])
			}
		}
	} else {
		dskeys = make([]*datastore.Key, len(es))
		dst = make([]interface{}, len(es))
		dixs = make([]int, len(es))

		for i, e := range es {
			dskeys[i] = e.Key
			dst[i] = e.Src
			dixs[i] = i
		}
	}

	var merr appengine.MultiError
	err := datastore.GetMulti(g.context, dskeys, dst)
	if err != nil {
		switch err.(type) {
		case appengine.MultiError:
			merr = err.(appengine.MultiError)
		default:
			return err
		}
	}
	var mes []*Entity

	for i, idx := range dixs {
		e := es[idx]
		if merr != nil && merr[i] != nil {
			e.NotFound = true
		}
		mes = append(mes, e)
	}

	if len(mes) > 0 && !g.inTransaction {
		err = g.putMemcache(mes)
		if err != nil {
			return err
		}
	}

	multiErr, any := make(appengine.MultiError, len(es)), false
	for i, e := range es {
		if e.NotFound {
			multiErr[i] = datastore.ErrNoSuchEntity
			any = true
		}
	}

	if any {
		return multiErr
	}

	return nil
}

func fromGob(e *Entity, b []byte) error {
	var buf bytes.Buffer
	_, _ = buf.Write(b)
	gob.Register(e.Src)
	dec := gob.NewDecoder(&buf)
	t := Entity{}
	err := dec.Decode(&t)
	if err != nil {
		return err
	}

	e.NotFound = t.NotFound
	ev := reflect.Indirect(reflect.ValueOf(e.Src))

	v := reflect.Indirect(reflect.ValueOf(t.Src))
	for i := 0; i < v.NumField(); i++ {
		f := v.Field(i)
		if f.CanSet() {
			ev.Field(i).Set(f)
		}
	}

	return nil
}

// Delete deletes the entity for the given key.
func (g *Goon) Delete(key *datastore.Key) error {
	keys := []*datastore.Key{key}
	return g.DeleteMulti(keys)
}

// DeleteMulti is a batch version of Delete.
func (g *Goon) DeleteMulti(keys []*datastore.Key) error {
	memkeys := make([]string, len(keys))
	for i, k := range keys {
		mk := memkey(k)
		memkeys[i] = mk

		if g.inTransaction {
			g.toDelete = append(g.toDelete, mk)
		} else {
			delete(g.cache, mk)
		}
	}

	memcache.DeleteMulti(g.context, memkeys)

	return datastore.DeleteMulti(g.context, keys)
}
<commit_msg>Not found is not an error, just set the flag<commit_after>/*
 * Copyright (c) 2012 Matt Jibson <matt.jibson@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package goon

import (
	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"bytes"
	"encoding/gob"
	"net/http"
	"reflect"
)

// Goon holds the app engine context and request memory cache.
type Goon struct {
	context       appengine.Context
	cache         map[string]*Entity
	inTransaction bool
	toSet         map[string]*Entity
	toDelete      []string
}

func memkey(k *datastore.Key) string {
	return k.String()
}

func NewGoon(r *http.Request) *Goon {
	return FromContext(appengine.NewContext(r))
}

func FromContext(c appengine.Context) *Goon {
	return &Goon{
		context: c,
		cache:   make(map[string]*Entity),
	}
}

// RunInTransaction runs f in a transaction. It calls f with a transaction
// context tg that f should use for all App Engine operations. Neither cache nor
// memcache are used or set during a transaction.
//
// Otherwise similar to appengine/datastore.RunInTransaction:
// https://developers.google.com/appengine/docs/go/datastore/reference#RunInTransaction
func (g *Goon) RunInTransaction(f func(tg *Goon) error, opts *datastore.TransactionOptions) error {
	var ng *Goon
	err := datastore.RunInTransaction(g.context, func(tc appengine.Context) error {
		ng = &Goon{
			context:       tc,
			inTransaction: true,
			toSet:         make(map[string]*Entity),
		}
		return f(ng)
	}, opts)

	if err == nil {
		for k, v := range ng.toSet {
			g.cache[k] = v
		}

		for _, k := range ng.toDelete {
			delete(g.cache, k)
		}
	}

	return err
}

// Put stores Entity e.
// If e has an incomplete key, it is updated.
func (g *Goon) Put(e *Entity) error {
	return g.PutMulti([]*Entity{e})
}

// PutMany is a wrapper around PutMulti.
func (g *Goon) PutMany(es ...*Entity) error {
	return g.PutMulti(es)
}

// PutMulti stores a sequence of Entities.
// Any entity with an incomplete key will be updated.
func (g *Goon) PutMulti(es []*Entity) error {
	var err error

	var memkeys []string
	keys := make([]*datastore.Key, len(es))
	src := make([]interface{}, len(es))

	for i, e := range es {
		if !e.Key.Incomplete() {
			memkeys = append(memkeys, e.memkey())
		}

		keys[i] = e.Key
		src[i] = e.Src
	}

	memcache.DeleteMulti(g.context, memkeys)

	keys, err = datastore.PutMulti(g.context, keys, src)

	if err != nil {
		return err
	}

	for i, e := range es {
		es[i].setKey(keys[i])

		if g.inTransaction {
			g.toSet[e.memkey()] = e
		}
	}

	if !g.inTransaction {
		g.putMemoryMulti(es)
	}

	return nil
}

func (g *Goon) putMemoryMulti(es []*Entity) {
	for _, e := range es {
		g.putMemory(e)
	}
}

func (g *Goon) putMemory(e *Entity) {
	g.cache[e.memkey()] = e
}

func (g *Goon) putMemcache(es []*Entity) error {
	items := make([]*memcache.Item, len(es))

	for i, e := range es {
		gob, err := e.gob()
		if err != nil {
			return err
		}

		items[i] = &memcache.Item{
			Key:   e.memkey(),
			Value: gob,
		}
	}

	err := memcache.SetMulti(g.context, items)

	if err != nil {
		return err
	}

	g.putMemoryMulti(es)
	return nil
}

// Kind returns the Kind name of src, which must be a struct.
// If src has a field named _goon with a tag "kind", that is used.
// Otherwise, reflection is used to determine the type name of src.
// In the case of an error, the empty string is returned.
//
// For example, to overwrite the default "Group" kind name:
//   type Group struct {
//     _goon interface{} `kind:"something_else"`
//     Name  string
//   }
func Kind (src interface{}) string {
	v := reflect.ValueOf(src)
	v = reflect.Indirect(v)
	t := v.Type()
	k := t.Kind()

	if k == reflect.Struct {
		if f, present := t.FieldByName("_goon"); present {
			name := f.Tag.Get("kind")
			if name != "" {
				return name
			}
		}

		return t.Name()
	}
	return ""
}

// GetById fetches an entity of kind src by id.
// Refer to appengine/datastore.NewKey regarding key specification.
func (g *Goon) GetById(src interface{}, stringID string, intID int64, parent *datastore.Key) (*Entity, error) {
	key := datastore.NewKey(g.context, Kind(src), stringID, intID, parent)
	return g.Get(src, key)
}

// Get fetches an entity of kind src by key.
func (g *Goon) Get(src interface{}, key *datastore.Key) (*Entity, error) {
	e := NewEntity(key, src)
	es := []*Entity{e}
	err := g.GetMulti(es)
	if err != nil {
		return nil, err
	}
	return es[0], nil
}

// Get fetches a sequency of Entities, whose keys must already be valid.
// Entities with no correspending key have their NotFound field set to true.
func (g *Goon) GetMulti(es []*Entity) error {
	var dskeys []*datastore.Key
	var dst []interface{}
	var dixs []int

	if !g.inTransaction {
		var memkeys []string
		var mixs []int

		for i, e := range es {
			m := e.memkey()
			if s, present := g.cache[m]; present {
				es[i] = s
			} else {
				memkeys = append(memkeys, m)
				mixs = append(mixs, i)
			}
		}

		memvalues, err := memcache.GetMulti(g.context, memkeys)
		if err != nil {
			return err
		}

		for i, m := range memkeys {
			e := es[mixs[i]]
			if s, present := memvalues[m]; present {
				err := fromGob(e, s.Value)
				if err != nil {
					return err
				}

				g.putMemory(e)
			} else {
				dskeys = append(dskeys, e.Key)
				dst = append(dst, e.Src)
				dixs = append(dixs, mixs[i])
			}
		}
	} else {
		dskeys = make([]*datastore.Key, len(es))
		dst = make([]interface{}, len(es))
		dixs = make([]int, len(es))

		for i, e := range es {
			dskeys[i] = e.Key
			dst[i] = e.Src
			dixs[i] = i
		}
	}

	var merr appengine.MultiError
	err := datastore.GetMulti(g.context, dskeys, dst)
	if err != nil {
		switch err.(type) {
		case appengine.MultiError:
			merr = err.(appengine.MultiError)
		default:
			return err
		}
	}
	var mes []*Entity

	multiErr, any := make(appengine.MultiError, len(es)), false
	for i, idx := range dixs {
		e := es[idx]
		if merr != nil {
			if merr[i] == datastore.ErrNoSuchEntity {
				e.NotFound = true
			} else {
				multiErr[i] = merr[i]
				any = true
			}
		}
		mes = append(mes, e)
	}

	if len(mes) > 0 && !g.inTransaction {
		err = g.putMemcache(mes)
		if err != nil {
			return err
		}
	}

	if any {
		return multiErr
	}

	return nil
}

func fromGob(e *Entity, b []byte) error {
	var buf bytes.Buffer
	_, _ = buf.Write(b)
	gob.Register(e.Src)
	dec := gob.NewDecoder(&buf)
	t := Entity{}
	err := dec.Decode(&t)
	if err != nil {
		return err
	}

	e.NotFound = t.NotFound
	ev := reflect.Indirect(reflect.ValueOf(e.Src))

	v := reflect.Indirect(reflect.ValueOf(t.Src))
	for i := 0; i < v.NumField(); i++ {
		f := v.Field(i)
		if f.CanSet() {
			ev.Field(i).Set(f)
		}
	}

	return nil
}

// Delete deletes the entity for the given key.
func (g *Goon) Delete(key *datastore.Key) error {
	keys := []*datastore.Key{key}
	return g.DeleteMulti(keys)
}

// DeleteMulti is a batch version of Delete.
func (g *Goon) DeleteMulti(keys []*datastore.Key) error {
	memkeys := make([]string, len(keys))
	for i, k := range keys {
		mk := memkey(k)
		memkeys[i] = mk

		if g.inTransaction {
			g.toDelete = append(g.toDelete, mk)
		} else {
			delete(g.cache, mk)
		}
	}

	memcache.DeleteMulti(g.context, memkeys)

	return datastore.DeleteMulti(g.context, keys)
}
<|endoftext|>


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

// Program gops is a tool to list currently running Go processes.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"

	"hello/gops/agent/signal"
	"hello/gops/internal/objfile"

	ps "github.com/keybase/go-ps"
)

const helpText = `Usage: gops is a tool to list and diagnose Go processes.

  gops  Lists all Go processes currently running.

Advanced tools, requires gops agent:
       gops stack -p pid    Prints the stack trace of the process.
       gops gc -p pid       Runs the garbage collector.
       gops gcstats -p pid  Prints the GC stats of the process.
       gops help            Prints this help message.
`

var (
	pid     = flag.Int("p", -1, "")
	stack   = flag.Bool("stack", false, "")
	gc      = flag.Bool("gc", false, "")
	gcstats = flag.Bool("gcstats", false, "")
	help    = flag.Bool("help", false, "")
)

func main() {
	flag.Usage = usage
	flag.Parse()

	if len(os.Args) < 2 {
		list()
		return
	}

	if *pid == -1 {
		usage()
	}
	if *help {
		usage()
	}

	if *stack {
		out, err := cmd(signal.Stack)
		exit(err)
		fmt.Println(out)
	}

	if *gc {
		_, err := cmd(signal.GC)
		exit(err)
	}

	if *gcstats {
		out, err := cmd(signal.GCStats)
		exit(err)
		fmt.Printf(out)
	}

}

func cmd(c byte) (string, error) {
	sock := fmt.Sprintf("/tmp/gops%d.sock", *pid)
	conn, err := net.Dial("unix", sock)
	if err != nil {
		return "", err
	}
	if _, err := conn.Write([]byte{c}); err != nil {
		return "", err
	}
	all, err := ioutil.ReadAll(conn)
	if err != nil {
		return "", err
	}
	return string(all), nil
}

func list() {
	pss, err := ps.Processes()
	if err != nil {
		log.Fatal(err)
	}
	var undetermined int
	for _, pr := range pss {
		name, err := pr.Path()
		if err != nil {
			undetermined++
			continue
		}
		ok, err := isGo(name)
		if err != nil {
			// TODO(jbd): worth to report the number?
			continue
		}
		if ok {
			fmt.Printf("%d\t%v\t(%v)\n", pr.Pid(), pr.Executable(), name)
		}
	}
	if undetermined > 0 {
		fmt.Printf("\n%d processes left undetermined\n", undetermined)
	}
}

func isGo(filename string) (ok bool, err error) {
	obj, err := objfile.Open(filename)
	if err != nil {
		return false, err
	}
	defer obj.Close()

	symbols, err := obj.Symbols()
	if err != nil {
		return false, err
	}

	// TODO(jbd): find a faster way to determine Go programs
	// looping over the symbols is a joke.
	for _, s := range symbols {
		if s.Name == "runtime.buildVersion" {
			return true, nil
		}
	}
	return false, nil
}

func usage() {
	fmt.Fprintf(os.Stderr, "%v\n", helpText)
	os.Exit(1)
}

func exit(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
}
<commit_msg>fix help text<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Program gops is a tool to list currently running Go processes.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"

	"hello/gops/agent/signal"
	"hello/gops/internal/objfile"

	ps "github.com/keybase/go-ps"
)

const helpText = `Usage: gops is a tool to list and diagnose Go processes.
    gops              Lists all Go processes currently running.
    gops [options...] See the section below.

Options: (All requires the agent and the -p=<pid> flag.)
    -stack    Prints the stack trace.
    -gc       Runs the garbage collector and blocks until successful.
    -gcstats  Prints the garbage collection stats.
`

var (
	pid     = flag.Int("p", -1, "")
	stack   = flag.Bool("stack", false, "")
	gc      = flag.Bool("gc", false, "")
	gcstats = flag.Bool("gcstats", false, "")
	help    = flag.Bool("help", false, "")
)

func main() {
	flag.Usage = usage
	flag.Parse()

	if len(os.Args) < 2 {
		list()
		return
	}

	if *pid == -1 {
		usage()
	}
	if *help {
		usage()
	}

	if *stack {
		out, err := cmd(signal.Stack)
		exit(err)
		fmt.Println(out)
	}

	if *gc {
		_, err := cmd(signal.GC)
		exit(err)
	}

	if *gcstats {
		out, err := cmd(signal.GCStats)
		exit(err)
		fmt.Printf(out)
	}

}

func cmd(c byte) (string, error) {
	sock := fmt.Sprintf("/tmp/gops%d.sock", *pid)
	conn, err := net.Dial("unix", sock)
	if err != nil {
		return "", err
	}
	if _, err := conn.Write([]byte{c}); err != nil {
		return "", err
	}
	all, err := ioutil.ReadAll(conn)
	if err != nil {
		return "", err
	}
	return string(all), nil
}

func list() {
	pss, err := ps.Processes()
	if err != nil {
		log.Fatal(err)
	}
	var undetermined int
	for _, pr := range pss {
		name, err := pr.Path()
		if err != nil {
			undetermined++
			continue
		}
		ok, err := isGo(name)
		if err != nil {
			// TODO(jbd): worth to report the number?
			continue
		}
		if ok {
			fmt.Printf("%d\t%v\t(%v)\n", pr.Pid(), pr.Executable(), name)
		}
	}
	if undetermined > 0 {
		fmt.Printf("\n%d processes left undetermined\n", undetermined)
	}
}

func isGo(filename string) (ok bool, err error) {
	obj, err := objfile.Open(filename)
	if err != nil {
		return false, err
	}
	defer obj.Close()

	symbols, err := obj.Symbols()
	if err != nil {
		return false, err
	}

	// TODO(jbd): find a faster way to determine Go programs
	// looping over the symbols is a joke.
	for _, s := range symbols {
		if s.Name == "runtime.buildVersion" {
			return true, nil
		}
	}
	return false, nil
}

func usage() {
	fmt.Fprintf(os.Stderr, "%v\n", helpText)
	os.Exit(1)
}

func exit(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
}
<|endoftext|>