<commit_before>package glisp

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
)

type PreHook func(*Glisp, string, []Sexp)
type PostHook func(*Glisp, string, Sexp)

type Glisp struct {
	datastack   *Stack
	scopestack  *Stack
	addrstack   *Stack
	stackstack  *Stack
	symtable    map[string]int
	revsymtable map[int]string
	builtins    map[int]SexpFunction
	macros      map[int]SexpFunction
	curfunc     SexpFunction
	mainfunc    SexpFunction
	pc          int
	nextsymbol  int
	before      []PreHook
	after       []PostHook
}

const CallStackSize = 25
const ScopeStackSize = 50
const DataStackSize = 100
const StackStackSize = 5

func NewGlisp() *Glisp {
	env := new(Glisp)
	env.datastack = NewStack(DataStackSize)
	env.scopestack = NewStack(ScopeStackSize)
	env.scopestack.PushScope()
	env.stackstack = NewStack(StackStackSize)
	env.addrstack = NewStack(CallStackSize)
	env.builtins = make(map[int]SexpFunction)
	env.macros = make(map[int]SexpFunction)
	env.symtable = make(map[string]int)
	env.revsymtable = make(map[int]string)
	env.nextsymbol = 1
	env.before = []PreHook{}
	env.after = []PostHook{}

	for key, function := range BuiltinFunctions {
		sym := env.MakeSymbol(key)
		env.builtins[sym.number] = MakeUserFunction(key, function)
		env.AddFunction(key, function)
	}

	env.mainfunc = MakeFunction("__main", 0, false, make([]Instruction, 0))
	env.curfunc = env.mainfunc
	env.pc = 0
	return env
}

func (env *Glisp) Clone() *Glisp {
	dupenv := new(Glisp)

	dupenv.datastack = env.datastack.Clone()
	dupenv.stackstack = env.stackstack.Clone()
	dupenv.scopestack = env.scopestack.Clone()
	dupenv.addrstack = env.addrstack.Clone()

	dupenv.builtins = env.builtins
	dupenv.macros = env.macros
	dupenv.symtable = env.symtable
	dupenv.revsymtable = env.revsymtable
	dupenv.nextsymbol = env.nextsymbol
	dupenv.before = env.before
	dupenv.after = env.after

	dupenv.scopestack.Push(env.scopestack.elements[0])

	dupenv.mainfunc = MakeFunction("__main", 0, false, make([]Instruction, 0))
	dupenv.curfunc = dupenv.mainfunc
	dupenv.pc = 0
	return dupenv
}

func (env *Glisp) Duplicate() *Glisp {
	dupenv := new(Glisp)
	dupenv.datastack = NewStack(DataStackSize)
	dupenv.scopestack = NewStack(ScopeStackSize)
	dupenv.stackstack = NewStack(StackStackSize)
	dupenv.addrstack = NewStack(CallStackSize)
	dupenv.builtins = env.builtins
	dupenv.macros = env.macros
	dupenv.symtable = env.symtable
	dupenv.revsymtable = env.revsymtable
	dupenv.nextsymbol = env.nextsymbol
	dupenv.before = env.before
	dupenv.after = env.after

	dupenv.scopestack.Push(env.scopestack.elements[0])

	dupenv.mainfunc = MakeFunction("__main", 0, false, make([]Instruction, 0))
	dupenv.curfunc = dupenv.mainfunc
	dupenv.pc = 0
	return dupenv
}

func (env *Glisp) MakeSymbol(name string) SexpSymbol {
	symnum, ok := env.symtable[name]
	if ok {
		return SexpSymbol{name, symnum}
	}
	symbol := SexpSymbol{name, env.nextsymbol}
	env.symtable[name] = symbol.number
	env.revsymtable[symbol.number] = name
	env.nextsymbol++
	return symbol
}

func (env *Glisp) GenSymbol(prefix string) SexpSymbol {
	symname := prefix + strconv.Itoa(env.nextsymbol)
	return env.MakeSymbol(symname)
}

func (env *Glisp) CurrentFunctionSize() int {
	if env.curfunc.user {
		return 0
	}
	return len(env.curfunc.fun)
}

func (env *Glisp) wrangleOptargs(fnargs, nargs int) error {
	if nargs < fnargs {
		return errors.New(
			fmt.Sprintf("Expected >%d arguments, got %d",
				fnargs, nargs))
	}
	if nargs > fnargs {
		optargs, err := env.datastack.PopExpressions(nargs - fnargs)
		if err != nil {
			return err
		}
		env.datastack.PushExpr(MakeList(optargs))
	} else {
		env.datastack.PushExpr(SexpNull)
	}
	return nil
}

func (env *Glisp) CallFunction(function SexpFunction, nargs int) error {
	for _, prehook := range env.before {
		expressions, err := env.datastack.GetExpressions(nargs)
		if err != nil {
			return err
		}
		prehook(env, function.name, expressions)
	}

	if function.varargs {
		err := env.wrangleOptargs(function.nargs, nargs)
		if err != nil {
			return err
		}
	} else if nargs != function.nargs {
		return errors.New(
			fmt.Sprintf("%s expected %d arguments, got %d",
				function.name, function.nargs, nargs))
	}

	if env.scopestack.IsEmpty() {
		panic("where's the global scope?")
	}
	globalScope := env.scopestack.elements[0]
	env.stackstack.Push(env.scopestack)
	env.scopestack = NewStack(ScopeStackSize)
	env.scopestack.Push(globalScope)

	if function.closeScope != nil {
		function.closeScope.PushAllTo(env.scopestack)
	}

	env.addrstack.PushAddr(env.curfunc, env.pc+1)
	env.scopestack.PushScope()
	env.curfunc = function
	env.pc = 0

	return nil
}

func (env *Glisp) ReturnFromFunction() error {
	for _, posthook := range env.after {
		retval, err := env.datastack.GetExpr(0)
		if err != nil {
			return err
		}
		posthook(env, env.curfunc.name, retval)
	}

	var err error
	env.curfunc, env.pc, err = env.addrstack.PopAddr()
	if err != nil {
		return err
	}
	scopestack, err := env.stackstack.Pop()
	if err != nil {
		return err
	}
	env.scopestack = scopestack.(*Stack)

	return nil
}

func (env *Glisp) CallUserFunction(
	function SexpFunction, name string, nargs int) error {

	for _, prehook := range env.before {
		expressions, err := env.datastack.GetExpressions(nargs)
		if err != nil {
			return err
		}
		prehook(env, function.name, expressions)
	}

	args, err := env.datastack.PopExpressions(nargs)
	if err != nil {
		return errors.New(
			fmt.Sprintf("Error calling %s: %v", name, err))
	}

	env.addrstack.PushAddr(env.curfunc, env.pc+1)
	env.curfunc = function
	env.pc = -1

	res, err := function.userfun(env, name, args)
	if err != nil {
		return errors.New(
			fmt.Sprintf("Error calling %s: %v", name, err))
	}
	env.datastack.PushExpr(res)

	for _, posthook := range env.after {
		posthook(env, name, res)
	}

	env.curfunc, env.pc, _ = env.addrstack.PopAddr()
	return nil
}

// SourceExpressions, this should be called from a user func context
func (env *Glisp) SourceExpressions(expressions []Sexp) error {
	gen := NewGenerator(env)
	if !env.ReachedEnd() {
		gen.AddInstruction(PopInstr(0))
	}
	err := gen.GenerateBegin(expressions)
	if err != nil {
		return err
	}

	curfunc := env.curfunc
	curpc := env.pc

	env.curfunc = MakeFunction("__source", 0, false, gen.instructions)
	env.pc = 0

	env.datastack.PushExpr(SexpNull)

	if _, err = env.Run(); err != nil {
		return err
	}

	env.datastack.PopExpr()

	env.pc = curpc
	env.curfunc = curfunc

	return nil
}

func (env *Glisp) SourceStream(stream io.RuneReader) error {
	lexer := NewLexerFromStream(stream)

	expressions, err := ParseTokens(env, lexer)
	if err != nil {
		return errors.New(fmt.Sprintf(
			"Error on line %d: %v\n", lexer.Linenum(), err))
	}

	return env.SourceExpressions(expressions)
}

func (env *Glisp) SourceFile(file *os.File) error {
	return env.SourceStream(bufio.NewReader(file))
}

func (env *Glisp) LoadExpressions(expressions []Sexp) error {
	gen := NewGenerator(env)
	if !env.ReachedEnd() {
		gen.AddInstruction(PopInstr(0))
	}
	err := gen.GenerateBegin(expressions)
	if err != nil {
		return err
	}

	env.mainfunc.fun = append(env.mainfunc.fun, gen.instructions...)
	env.curfunc = env.mainfunc

	return nil
}

func (env *Glisp) ParseFile(file string) ([]Sexp, error) {
	in, err := os.Open(file)
	if err != nil {
		return nil, err
	}

	lexer := NewLexerFromStream(bufio.NewReader(in))

	var exp []Sexp

	exp, err = ParseTokens(env, lexer)
	if err != nil {
		return nil, fmt.Errorf("Error on line %d: %v\n", lexer.Linenum(), err)
	}

	in.Close()

	return exp, nil
}

func (env *Glisp) LoadStream(stream io.RuneReader) error {
	lexer := NewLexerFromStream(stream)

	expressions, err := ParseTokens(env, lexer)
	if err != nil {
		return fmt.Errorf("Error on line %d: %v\n", lexer.Linenum(), err)
	}

	return env.LoadExpressions(expressions)
}

func (env *Glisp) EvalString(str string) (Sexp, error) {
	err := env.LoadString(str)
	if err != nil {
		return SexpNull, err
	}

	return env.Run()
}

func (env *Glisp) LoadFile(file *os.File) error {
	return env.LoadStream(bufio.NewReader(file))
}

func (env *Glisp) LoadString(str string) error {
	return env.LoadStream(bytes.NewBuffer([]byte(str)))
}

func (env *Glisp) AddFunction(name string, function GlispUserFunction) {
	env.AddGlobal(name, MakeUserFunction(name, function))
}

func (env *Glisp) AddGlobal(name string, obj Sexp) {
	sym := env.MakeSymbol(name)
	env.scopestack.elements[0].(Scope)[sym.number] = obj
}

func (env *Glisp) AddMacro(name string, function GlispUserFunction) {
	sym := env.MakeSymbol(name)
	env.macros[sym.number] = MakeUserFunction(name, function)
}

func (env *Glisp) ImportEval() {
	env.AddFunction("eval", EvalFunction)
}

func (env *Glisp) DumpFunctionByName(name string) error {
	obj, found := env.FindObject(name)
	if !found {
		return errors.New(fmt.Sprintf("%q not found", name))
	}

	var fun GlispFunction
	switch t := obj.(type) {
	case SexpFunction:
		if !t.user {
			fun = t.fun
		} else {
			return errors.New("not a glisp function")
		}
	default:
		return errors.New("not a function")
	}
	DumpFunction(fun)
	return nil
}

func DumpFunction(fun GlispFunction) {
	for _, instr := range fun {
		fmt.Println("\t" + instr.InstrString())
	}
}

func (env *Glisp) DumpEnvironment() {
	fmt.Println("Instructions:")
	if !env.curfunc.user {
		DumpFunction(env.curfunc.fun)
	}
	fmt.Println("Stack:")
	env.datastack.PrintStack()
	fmt.Printf("PC: %d\n", env.pc)
}

func (env *Glisp) ReachedEnd() bool {
	return env.pc == env.CurrentFunctionSize()
}

func (env *Glisp) GetStackTrace(err error) string {
	str := fmt.Sprintf("error in %s:%d: %v\n",
		env.curfunc.name, env.pc, err)
	for !env.addrstack.IsEmpty() {
		fun, pos, _ := env.addrstack.PopAddr()
		str += fmt.Sprintf("in %s:%d\n", fun.name, pos)
	}
	return str
}

func (env *Glisp) Clear() {
	env.datastack.tos = -1
	env.scopestack.tos = 0
	env.addrstack.tos = -1
	env.mainfunc = MakeFunction("__main", 0, false, make([]Instruction, 0))
	env.curfunc = env.mainfunc
	env.pc = 0
}

func (env *Glisp) FindObject(name string) (Sexp, bool) {
	sym := env.MakeSymbol(name)
	obj, err := env.scopestack.LookupSymbol(sym)
	if err != nil {
		return SexpNull, false
	}
	return obj, true
}

func (env *Glisp) Apply(fun SexpFunction, args []Sexp) (Sexp, error) {
	if fun.user {
		return fun.userfun(env, fun.name, args)
	}

	env.pc = -2
	for _, expr := range args {
		env.datastack.PushExpr(expr)
	}

	//log.Print("Apply Calling ", fun, " with ", len(args))
	err := env.CallFunction(fun, len(args))
	if err != nil {
		return SexpNull, err
	}

	return env.Run()
}

func (env *Glisp) Run() (Sexp, error) {
	for env.pc != -1 && !env.ReachedEnd() {
		instr := env.curfunc.fun[env.pc]
		err := instr.Execute(env)
		if err != nil {
			return SexpNull, err
		}
	}

	return env.datastack.PopExpr()
}

func (env *Glisp) AddPreHook(fun PreHook) {
	env.before = append(env.before, fun)
}

func (env *Glisp) AddPostHook(fun PostHook) {
	env.after = append(env.after, fun)
}
<commit_msg>refactor a bit so there is less duplicate code on parsing files<commit_after>package glisp

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
)

type PreHook func(*Glisp, string, []Sexp)
type PostHook func(*Glisp, string, Sexp)

type Glisp struct {
	datastack   *Stack
	scopestack  *Stack
	addrstack   *Stack
	stackstack  *Stack
	symtable    map[string]int
	revsymtable map[int]string
	builtins    map[int]SexpFunction
	macros      map[int]SexpFunction
	curfunc     SexpFunction
	mainfunc    SexpFunction
	pc          int
	nextsymbol  int
	before      []PreHook
	after       []PostHook
}

const CallStackSize = 25
const ScopeStackSize = 50
const DataStackSize = 100
const StackStackSize = 5

func NewGlisp() *Glisp {
	env := new(Glisp)
	env.datastack = NewStack(DataStackSize)
	env.scopestack = NewStack(ScopeStackSize)
	env.scopestack.PushScope()
	env.stackstack = NewStack(StackStackSize)
	env.addrstack = NewStack(CallStackSize)
	env.builtins = make(map[int]SexpFunction)
	env.macros = make(map[int]SexpFunction)
	env.symtable = make(map[string]int)
	env.revsymtable = make(map[int]string)
	env.nextsymbol = 1
	env.before = []PreHook{}
	env.after = []PostHook{}

	for key, function := range BuiltinFunctions {
		sym := env.MakeSymbol(key)
		env.builtins[sym.number] = MakeUserFunction(key, function)
		env.AddFunction(key, function)
	}

	env.mainfunc = MakeFunction("__main", 0, false, make([]Instruction, 0))
	env.curfunc = env.mainfunc
	env.pc = 0
	return env
}

func (env *Glisp) Clone() *Glisp {
	dupenv := new(Glisp)

	dupenv.datastack = env.datastack.Clone()
	dupenv.stackstack = env.stackstack.Clone()
	dupenv.scopestack = env.scopestack.Clone()
	dupenv.addrstack = env.addrstack.Clone()

	dupenv.builtins = env.builtins
	dupenv.macros = env.macros
	dupenv.symtable = env.symtable
	dupenv.revsymtable = env.revsymtable
	dupenv.nextsymbol = env.nextsymbol
	dupenv.before = env.before
	dupenv.after = env.after

	dupenv.scopestack.Push(env.scopestack.elements[0])

	dupenv.mainfunc = MakeFunction("__main", 0, false, make([]Instruction, 0))
	dupenv.curfunc = dupenv.mainfunc
	dupenv.pc = 0
	return dupenv
}

func (env *Glisp) Duplicate() *Glisp {
	dupenv := new(Glisp)
	dupenv.datastack = NewStack(DataStackSize)
	dupenv.scopestack = NewStack(ScopeStackSize)
	dupenv.stackstack = NewStack(StackStackSize)
	dupenv.addrstack = NewStack(CallStackSize)
	dupenv.builtins = env.builtins
	dupenv.macros = env.macros
	dupenv.symtable = env.symtable
	dupenv.revsymtable = env.revsymtable
	dupenv.nextsymbol = env.nextsymbol
	dupenv.before = env.before
	dupenv.after = env.after

	dupenv.scopestack.Push(env.scopestack.elements[0])

	dupenv.mainfunc = MakeFunction("__main", 0, false, make([]Instruction, 0))
	dupenv.curfunc = dupenv.mainfunc
	dupenv.pc = 0
	return dupenv
}

func (env *Glisp) MakeSymbol(name string) SexpSymbol {
	symnum, ok := env.symtable[name]
	if ok {
		return SexpSymbol{name, symnum}
	}
	symbol := SexpSymbol{name, env.nextsymbol}
	env.symtable[name] = symbol.number
	env.revsymtable[symbol.number] = name
	env.nextsymbol++
	return symbol
}

func (env *Glisp) GenSymbol(prefix string) SexpSymbol {
	symname := prefix + strconv.Itoa(env.nextsymbol)
	return env.MakeSymbol(symname)
}

func (env *Glisp) CurrentFunctionSize() int {
	if env.curfunc.user {
		return 0
	}
	return len(env.curfunc.fun)
}

func (env *Glisp) wrangleOptargs(fnargs, nargs int) error {
	if nargs < fnargs {
		return errors.New(
			fmt.Sprintf("Expected >%d arguments, got %d",
				fnargs, nargs))
	}
	if nargs > fnargs {
		optargs, err := env.datastack.PopExpressions(nargs - fnargs)
		if err != nil {
			return err
		}
		env.datastack.PushExpr(MakeList(optargs))
	} else {
		env.datastack.PushExpr(SexpNull)
	}
	return nil
}

func (env *Glisp) CallFunction(function SexpFunction, nargs int) error {
	for _, prehook := range env.before {
		expressions, err := env.datastack.GetExpressions(nargs)
		if err != nil {
			return err
		}
		prehook(env, function.name, expressions)
	}

	if function.varargs {
		err := env.wrangleOptargs(function.nargs, nargs)
		if err != nil {
			return err
		}
	} else if nargs != function.nargs {
		return errors.New(
			fmt.Sprintf("%s expected %d arguments, got %d",
				function.name, function.nargs, nargs))
	}

	if env.scopestack.IsEmpty() {
		panic("where's the global scope?")
	}
	globalScope := env.scopestack.elements[0]
	env.stackstack.Push(env.scopestack)
	env.scopestack = NewStack(ScopeStackSize)
	env.scopestack.Push(globalScope)

	if function.closeScope != nil {
		function.closeScope.PushAllTo(env.scopestack)
	}

	env.addrstack.PushAddr(env.curfunc, env.pc+1)
	env.scopestack.PushScope()
	env.curfunc = function
	env.pc = 0

	return nil
}

func (env *Glisp) ReturnFromFunction() error {
	for _, posthook := range env.after {
		retval, err := env.datastack.GetExpr(0)
		if err != nil {
			return err
		}
		posthook(env, env.curfunc.name, retval)
	}

	var err error
	env.curfunc, env.pc, err = env.addrstack.PopAddr()
	if err != nil {
		return err
	}
	scopestack, err := env.stackstack.Pop()
	if err != nil {
		return err
	}
	env.scopestack = scopestack.(*Stack)

	return nil
}

func (env *Glisp) CallUserFunction(
	function SexpFunction, name string, nargs int) error {

	for _, prehook := range env.before {
		expressions, err := env.datastack.GetExpressions(nargs)
		if err != nil {
			return err
		}
		prehook(env, function.name, expressions)
	}

	args, err := env.datastack.PopExpressions(nargs)
	if err != nil {
		return errors.New(
			fmt.Sprintf("Error calling %s: %v", name, err))
	}

	env.addrstack.PushAddr(env.curfunc, env.pc+1)
	env.curfunc = function
	env.pc = -1

	res, err := function.userfun(env, name, args)
	if err != nil {
		return errors.New(
			fmt.Sprintf("Error calling %s: %v", name, err))
	}
	env.datastack.PushExpr(res)

	for _, posthook := range env.after {
		posthook(env, name, res)
	}

	env.curfunc, env.pc, _ = env.addrstack.PopAddr()
	return nil
}

func (env *Glisp) ParseStream(in io.Reader) ([]Sexp, error) {
	lexer := NewLexerFromStream(bufio.NewReader(in))

	var err error
	var exp []Sexp

	exp, err = ParseTokens(env, lexer)
	if err != nil {
		return nil, fmt.Errorf("Error on line %d: %v\n", lexer.Linenum(), err)
	}

	return exp, nil
}

// ParseFile, used in the generator at read time to dynamiclly add more defs from other files
func (env *Glisp) ParseFile(file string) ([]Sexp, error) {
	in, err := os.Open(file)
	if err != nil {
		return nil, err
	}

	var exp []Sexp

	exp, err = env.ParseStream(in)

	in.Close()

	return exp, err
}

func (env *Glisp) SourceExpressions(expressions []Sexp) error {
	gen := NewGenerator(env)
	if !env.ReachedEnd() {
		gen.AddInstruction(PopInstr(0))
	}
	err := gen.GenerateBegin(expressions)
	if err != nil {
		return err
	}

	curfunc := env.curfunc
	curpc := env.pc

	env.curfunc = MakeFunction("__source", 0, false, gen.instructions)
	env.pc = 0

	env.datastack.PushExpr(SexpNull)

	if _, err = env.Run(); err != nil {
		return err
	}

	env.datastack.PopExpr()

	env.pc = curpc
	env.curfunc = curfunc

	return nil
}

// SourceStream, load this in via a __source dynamic function, after it runs it no longer exists
func (env *Glisp) SourceStream(stream io.Reader) error {
	expressions, err := env.ParseStream(stream)

	if err != nil {
		return err
	}

	return env.SourceExpressions(expressions)
}

func (env *Glisp) SourceFile(file *os.File) error {
	return env.SourceStream(bufio.NewReader(file))
}

func (env *Glisp) LoadExpressions(expressions []Sexp) error {
	gen := NewGenerator(env)
	if !env.ReachedEnd() {
		gen.AddInstruction(PopInstr(0))
	}
	err := gen.GenerateBegin(expressions)
	if err != nil {
		return err
	}

	env.mainfunc.fun = append(env.mainfunc.fun, gen.instructions...)
	env.curfunc = env.mainfunc

	return nil
}

// LoadStream, load this in via running a __main function and setting main on the environment
func (env *Glisp) LoadStream(stream io.Reader) error {
	expressions, err := env.ParseStream(stream)

	if err != nil {
		return err
	}

	return env.LoadExpressions(expressions)
}

func (env *Glisp) EvalString(str string) (Sexp, error) {
	err := env.LoadString(str)
	if err != nil {
		return SexpNull, err
	}

	return env.Run()
}

func (env *Glisp) LoadFile(file *os.File) error {
	return env.LoadStream(bufio.NewReader(file))
}

func (env *Glisp) LoadString(str string) error {
	return env.LoadStream(bytes.NewBuffer([]byte(str)))
}

func (env *Glisp) AddFunction(name string, function GlispUserFunction) {
	env.AddGlobal(name, MakeUserFunction(name, function))
}

func (env *Glisp) AddGlobal(name string, obj Sexp) {
	sym := env.MakeSymbol(name)
	env.scopestack.elements[0].(Scope)[sym.number] = obj
}

func (env *Glisp) AddMacro(name string, function GlispUserFunction) {
	sym := env.MakeSymbol(name)
	env.macros[sym.number] = MakeUserFunction(name, function)
}

func (env *Glisp) ImportEval() {
	env.AddFunction("eval", EvalFunction)
}

func (env *Glisp) DumpFunctionByName(name string) error {
	obj, found := env.FindObject(name)
	if !found {
		return errors.New(fmt.Sprintf("%q not found", name))
	}

	var fun GlispFunction
	switch t := obj.(type) {
	case SexpFunction:
		if !t.user {
			fun = t.fun
		} else {
			return errors.New("not a glisp function")
		}
	default:
		return errors.New("not a function")
	}
	DumpFunction(fun)
	return nil
}

func DumpFunction(fun GlispFunction) {
	for _, instr := range fun {
		fmt.Println("\t" + instr.InstrString())
	}
}

func (env *Glisp) DumpEnvironment() {
	fmt.Println("Instructions:")
	if !env.curfunc.user {
		DumpFunction(env.curfunc.fun)
	}
	fmt.Println("Stack:")
	env.datastack.PrintStack()
	fmt.Printf("PC: %d\n", env.pc)
}

func (env *Glisp) ReachedEnd() bool {
	return env.pc == env.CurrentFunctionSize()
}

func (env *Glisp) GetStackTrace(err error) string {
	str := fmt.Sprintf("error in %s:%d: %v\n",
		env.curfunc.name, env.pc, err)
	for !env.addrstack.IsEmpty() {
		fun, pos, _ := env.addrstack.PopAddr()
		str += fmt.Sprintf("in %s:%d\n", fun.name, pos)
	}
	return str
}

func (env *Glisp) Clear() {
	env.datastack.tos = -1
	env.scopestack.tos = 0
	env.addrstack.tos = -1
	env.mainfunc = MakeFunction("__main", 0, false, make([]Instruction, 0))
	env.curfunc = env.mainfunc
	env.pc = 0
}

func (env *Glisp) FindObject(name string) (Sexp, bool) {
	sym := env.MakeSymbol(name)
	obj, err := env.scopestack.LookupSymbol(sym)
	if err != nil {
		return SexpNull, false
	}
	return obj, true
}

func (env *Glisp) Apply(fun SexpFunction, args []Sexp) (Sexp, error) {
	if fun.user {
		return fun.userfun(env, fun.name, args)
	}

	env.pc = -2
	for _, expr := range args {
		env.datastack.PushExpr(expr)
	}

	//log.Print("Apply Calling ", fun, " with ", len(args))
	err := env.CallFunction(fun, len(args))
	if err != nil {
		return SexpNull, err
	}

	return env.Run()
}

func (env *Glisp) Run() (Sexp, error) {
	for env.pc != -1 && !env.ReachedEnd() {
		instr := env.curfunc.fun[env.pc]
		err := instr.Execute(env)
		if err != nil {
			return SexpNull, err
		}
	}

	return env.datastack.PopExpr()
}

func (env *Glisp) AddPreHook(fun PreHook) {
	env.before = append(env.before, fun)
}

func (env *Glisp) AddPostHook(fun PostHook) {
	env.after = append(env.after, fun)
}
<|endoftext|>


<commit_before>// Package scan TCP ports
package scan

import (
	"fmt"
	"net"
	"regexp"
	"strconv"
	"sync"
	"time"

	"github.com/mehrdadrad/mylg/cli"
)

// Scan represents the scan parameters
type Scan struct {
	minPort int
	maxPort int
	target  string
}

// NewScan creats scan object
func NewScan(args string) (Scan, error) {
	var (
		scan Scan
		flag map[string]interface{}
		err  error
	)

	args, flag = cli.Flag(args)
	// help
	if _, ok := flag["help"]; ok {
		help()
		return scan, fmt.Errorf("")
	}

	pRange := cli.SetFlag(flag, "p", "1-500").(string)

	re := regexp.MustCompile(`(\d+)-(\d+)`)
	f := re.FindStringSubmatch(pRange)
	if len(f) == 3 {
		scan.target = args
		scan.minPort, err = strconv.Atoi(f[1])
		scan.maxPort, err = strconv.Atoi(f[2])
		if err != nil {
			return scan, err
		}
	}
	if !scan.IsCIDR() {
		ipAddr, err := net.ResolveIPAddr("ip4", scan.target)
		if err != nil {
			return scan, err
		}
		scan.target = ipAddr.String()
	}
	return scan, nil
}

// IsCIDR checks the target if it's CIDR
func (s Scan) IsCIDR() bool {
	_, _, err := net.ParseCIDR(s.target)
	if err != nil {
		return false
	}
	return true
}

// Run tries to scan wide range ports (TCP)
func (s Scan) Run() {
	if !s.IsCIDR() {
		host(s.target, s.minPort, s.maxPort)
	}
}

// host tries to scan a single host
func host(ipAddr string, minPort, maxPort int) {
	var (
		wg      sync.WaitGroup
		tStart  = time.Now()
		counter int
	)
	for i := minPort; i <= maxPort; i++ {
		wg.Add(1)
		go func(i int) {
			conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ipAddr, i), 1*time.Second)
			if err != nil {
				wg.Done()
				return
			}
			counter++
			fmt.Printf("%d/tcp open\n", i)
			wg.Done()
			err = conn.Close()
			if err != nil {
				println(err.Error())
			}
		}(i)
		time.Sleep(8 * time.Millisecond)
	}
	wg.Wait()
	if counter == 0 {
		println("there isn't any opened port")
	} else {
		elapsed := fmt.Sprintf("%.3f seconds", time.Since(tStart).Seconds())
		println("Scan done:", counter, "opened port(s) found in", elapsed)
	}
}

// help represents guide to user
func help() {
	println(`
    usage:
          scan ip/host [-p portrange]
    example:
          scan www.google.com -p 1-500
	`)
}
<commit_msg>added table to output<commit_after>// Package scan TCP ports
package scan

import (
	"fmt"
	"net"
	"os"
	"regexp"
	"strconv"
	"sync"
	"time"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/olekukonko/tablewriter"
)

// Scan represents the scan parameters
type Scan struct {
	minPort int
	maxPort int
	target  string
}

// NewScan creats scan object
func NewScan(args string) (Scan, error) {
	var (
		scan Scan
		flag map[string]interface{}
		err  error
	)

	args, flag = cli.Flag(args)
	// help
	if _, ok := flag["help"]; ok {
		help()
		return scan, fmt.Errorf("")
	}

	pRange := cli.SetFlag(flag, "p", "1-500").(string)

	re := regexp.MustCompile(`(\d+)-(\d+)`)
	f := re.FindStringSubmatch(pRange)
	if len(f) == 3 {
		scan.target = args
		scan.minPort, err = strconv.Atoi(f[1])
		scan.maxPort, err = strconv.Atoi(f[2])
		if err != nil {
			return scan, err
		}
	}
	if !scan.IsCIDR() {
		ipAddr, err := net.ResolveIPAddr("ip4", scan.target)
		if err != nil {
			return scan, err
		}
		scan.target = ipAddr.String()
	}
	return scan, nil
}

// IsCIDR checks the target if it's CIDR
func (s Scan) IsCIDR() bool {
	_, _, err := net.ParseCIDR(s.target)
	if err != nil {
		return false
	}
	return true
}

// Run tries to scan wide range ports (TCP)
func (s Scan) Run() {
	if !s.IsCIDR() {
		host(s.target, s.minPort, s.maxPort)
	}
}

// host tries to scan a single host
func host(ipAddr string, minPort, maxPort int) {
	var (
		wg      sync.WaitGroup
		tStart  = time.Now()
		counter int
	)

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Protocol", "Port", "Status", "Description"})

	for i := minPort; i <= maxPort; i++ {
		wg.Add(1)
		go func(i int) {
			conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ipAddr, i), 1*time.Second)
			if err != nil {
				wg.Done()
				return
			}
			counter++
			table.Append([]string{"TCP", fmt.Sprintf("%d", i), "Open", ""})
			wg.Done()
			err = conn.Close()
			if err != nil {
				println(err.Error())
			}
		}(i)
		time.Sleep(8 * time.Millisecond)
	}

	wg.Wait()
	table.Render()

	if counter == 0 {
		println("there isn't any opened port")
	} else {
		elapsed := fmt.Sprintf("%.3f seconds", time.Since(tStart).Seconds())
		println("Scan done:", counter, "opened port(s) found in", elapsed)
	}
}

// help represents guide to user
func help() {
	println(`
    usage:
          scan ip/host [-p portrange]
    example:
          scan www.google.com -p 1-500
	`)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"log"
	"net/url"
	"os"
	"sort"
	"sync"

	"github.com/flynn/flynn-controller/client"
	ct "github.com/flynn/flynn-controller/types"
	"github.com/flynn/flynn-host/types"
	"github.com/flynn/go-dockerclient"
	"github.com/flynn/go-flynn/cluster"
)

func main() {
	controller, err := client.New(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}
	cl, err := cluster.NewClient()
	if err != nil {
		log.Fatal(err)
	}
	c := newContext(cl)
	// TODO: initial load of data
	// TODO: periodic full cluster sync for anti-entropy
	go c.watchFormations(controller)
}

func newContext(cl clusterClient) *context {
	return &context{
		clusterClient: cl,
		formations:    NewFormations(),
		hosts:         newHostClients(),
		jobs:          newJobMap(),
	}
}

type context struct {
	clusterClient
	formations *Formations

	hosts *hostClients
	jobs  *jobMap
}

type clusterClient interface {
	ListHosts() (map[string]host.Host, error)
	AddJobs(req *host.AddJobsReq) (*host.AddJobsRes, error)
	ConnectHost(id string) (*cluster.Host, error)
}

type formationStreamer interface {
	StreamFormations(chan<- *ct.ExpandedFormation) *error
}

func (c *context) watchFormations(fs formationStreamer) {
	ch := make(chan *ct.ExpandedFormation)
	fs.StreamFormations(ch)

	for ef := range ch {
		f := NewFormation(c, ef)
		c.formations.Add(f)
		go f.Rectify()
	}

	// TODO: log disconnect and restart
	// TODO: trigger cluster sync
}

func (c *context) watchHost(id string) {
	if !c.hosts.Add(id) {
		return
	}
	defer c.hosts.Remove(id)

	h, err := c.ConnectHost(id)
	if err != nil {
		// TODO: log/handle error
	}
	c.hosts.Set(id, h)

	ch := make(chan host.Event)
	h.StreamEvents("all", ch)
	for event := range ch {
		if event.Event != "error" && event.Event != "stop" {
			continue
		}
		job := c.jobs.Get(id, event.JobID)
		if job == nil {
			continue
		}

		c.jobs.Remove(id, event.JobID)
		go job.Formation.RemoveJob(job.Type, id, event.JobID)
	}
	// TODO: check error/reconnect
}

func newHostClients() *hostClients {
	return &hostClients{hosts: make(map[string]*cluster.Host)}
}

type hostClients struct {
	hosts map[string]*cluster.Host
	mtx   sync.RWMutex
}

func (h *hostClients) Add(id string) bool {
	h.mtx.Lock()
	defer h.mtx.Unlock()
	if _, exists := h.hosts[id]; exists {
		return false
	}
	h.hosts[id] = nil
	return true
}

func (h *hostClients) Set(id string, client *cluster.Host) {
	h.mtx.Lock()
	h.hosts[id] = client
	h.mtx.Unlock()
}

func (h *hostClients) Remove(id string) {
	h.mtx.Lock()
	delete(h.hosts, id)
	h.mtx.Unlock()
}

func (h *hostClients) Get(id string) *cluster.Host {
	h.mtx.RLock()
	defer h.mtx.RUnlock()
	return h.hosts[id]
}

func newJobMap() *jobMap {
	return &jobMap{jobs: make(map[jobKey]*Job)}
}

type jobMap struct {
	jobs map[jobKey]*Job
	mtx  sync.RWMutex
}

func (m *jobMap) Add(hostID, jobID string, job *Job) {
	m.mtx.Lock()
	m.jobs[jobKey{hostID, jobID}] = job
	m.mtx.Unlock()
}

func (m *jobMap) Remove(host, job string) {
	m.mtx.Lock()
	delete(m.jobs, jobKey{host, job})
	m.mtx.Unlock()
}

func (m *jobMap) Get(host, job string) *Job {
	m.mtx.RLock()
	defer m.mtx.RUnlock()
	return m.jobs[jobKey{host, job}]
}

type jobKey struct {
	hostID, jobID string
}

type formationKey struct {
	appID, releaseID string
}

func NewFormations() *Formations {
	return &Formations{formations: make(map[formationKey]*Formation)}
}

type Formations struct {
	formations map[formationKey]*Formation
	mtx        sync.RWMutex
}

func (fs *Formations) Get(appID, releaseID string) *Formation {
	fs.mtx.RLock()
	defer fs.mtx.RUnlock()
	return fs.formations[formationKey{appID, releaseID}]
}

func (fs *Formations) Add(f *Formation) {
	fs.mtx.Lock()
	fs.formations[f.key()] = f
	fs.mtx.Unlock()
}

func (fs *Formations) Delete(f *Formation) {
	fs.mtx.Lock()
	delete(fs.formations, f.key())
	fs.mtx.Unlock()
}

func NewFormation(c *context, ef *ct.ExpandedFormation) *Formation {
	return &Formation{
		App:       ef.App,
		Release:   ef.Release,
		Artifact:  ef.Artifact,
		Processes: ef.Processes,
		jobs:      make(jobTypeMap),
		c:         c,
	}
}

type Job struct {
	Type      string
	Formation *Formation
}

type jobTypeMap map[string]map[jobKey]*Job

func (m jobTypeMap) Add(typ, host, id string) *Job {
	jobs, ok := m[typ]
	if !ok {
		jobs = make(map[jobKey]*Job)
		m[typ] = jobs
	}
	job := &Job{Type: typ}
	jobs[jobKey{host, id}] = job
	return job
}

func (m jobTypeMap) Remove(typ, host, id string) {
	if jobs, ok := m[typ]; ok {
		delete(jobs, jobKey{host, id})
	}
}

func (m jobTypeMap) Get(typ, host, id string) *Job {
	return m[typ][jobKey{host, id}]
}

type Formation struct {
	mtx       sync.Mutex
	App       *ct.App
	Release   *ct.Release
	Artifact  *ct.Artifact
	Processes map[string]int

	jobs jobTypeMap
	c    *context
}

func (f *Formation) key() formationKey {
	return formationKey{f.App.ID, f.Release.ID}
}

func (f *Formation) Rectify() {
	f.mtx.Lock()
	defer f.mtx.Unlock()
	f.rectify()
}

func (f *Formation) RemoveJob(typ, hostID, jobID string) {
	f.mtx.Lock()
	defer f.mtx.Unlock()

	f.jobs.Remove(typ, hostID, jobID)
	f.rectify()
}

func (f *Formation) rectify() {
	// update job counts
	for t, expected := range f.Processes {
		diff := expected - len(f.jobs[t])
		if diff > 0 {
			f.add(diff, t)
		} else if diff < 0 {
			f.remove(-diff, t)
		}
	}

	// remove process types
	for t, jobs := range f.jobs {
		if _, exists := f.Processes[t]; !exists {
			f.remove(len(jobs), t)
		}
	}
}

func (f *Formation) add(n int, name string) {
	config, err := f.jobConfig(name)
	if err != nil {
		// TODO: log/handle error
	}
	for i := 0; i < n; i++ {
		hosts, err := f.c.ListHosts()
		if err != nil {
			// TODO: log/handle error
		}
		if len(hosts) == 0 {
			// TODO: log/handle error
		}
		hostCounts := make(map[string]int, len(hosts))
		for _, h := range hosts {
			hostCounts[h.ID] = 0
			for _, job := range h.Jobs {
				if f.jobType(job) != name {
					continue
				}
				hostCounts[h.ID]++
			}
		}
		sh := make(sortHosts, 0, len(hosts))
		for id, count := range hostCounts {
			sh = append(sh, sortHost{id, count})
		}
		sh.Sort()

		h := hosts[sh[0].ID]
		go f.c.watchHost(h.ID)

		job := f.jobs.Add(name, h.ID, config.ID)
		job.Formation = f
		f.c.jobs.Add(h.ID, config.ID, job)

		res, err := f.c.AddJobs(&host.AddJobsReq{HostJobs: map[string][]*host.Job{h.ID: {config}}})
		if err != nil || !res.Success {
			f.jobs.Remove(name, h.ID, config.ID)
			f.c.jobs.Remove(h.ID, config.ID)
			// TODO: log/handle error
		}
	}
}

func (f *Formation) jobType(job *host.Job) string {
	if job.Attributes["flynn-controller.app"] != f.App.ID ||
		job.Attributes["flynn-controller.release"] != f.Release.ID {
		return ""
	}
	return job.Attributes["flynn-controller.type"]
}

func (f *Formation) remove(n int, name string) {
	i := 0
	for k := range f.jobs[name] {
		// TODO: robust host handling
		if err := f.c.hosts.Get(k.hostID).StopJob(k.jobID); err != nil {
			// TODO: log/handle error
		}
		f.jobs.Remove(name, k.hostID, k.jobID)
		f.c.jobs.Remove(k.hostID, k.jobID)
		if i++; i == n {
			break
		}
	}
}

func (f *Formation) jobConfig(name string) (*host.Job, error) {
	t := f.Release.Processes[name]
	image, err := dockerImage(f.Artifact.URI)
	if err != nil {
		return nil, err
	}
	return &host.Job{
		ID:       cluster.RandomJobID(""),
		TCPPorts: t.Ports.TCP,
		Attributes: map[string]string{
			"flynn-controller.app":     f.App.ID,
			"flynn-controller.release": f.Release.ID,
			"flynn-controller.type":    name,
		},
		Config: &docker.Config{
			Cmd:   t.Cmd,
			Env:   formatEnv(f.Release.Environment),
			Image: image,
		},
	}, nil
}

func dockerImage(uri string) (string, error) {
	// TODO: ID refs (see https://github.com/dotcloud/docker/issues/4106)
	u, err := url.Parse(uri)
	if err != nil {
		return "", err
	}
	if u.Scheme != "docker" {
		return "", errors.New("scheduler: only docker artifact URIs are currently supported")
	}
	var suffix string
	if tag := u.Query().Get("tag"); tag != "" {
		suffix = ":" + tag
	}
	return u.Host + suffix, nil
}

func formatEnv(env map[string]string) []string {
	res := make([]string, 0, len(env))
	for k, v := range env {
		res = append(res, k+"="+v)
	}
	return res
}

type sortHost struct {
	ID   string
	Jobs int
}

type sortHosts []sortHost

func (h sortHosts) Len() int           { return len(h) }
func (h sortHosts) Less(i, j int) bool { return h[i].Jobs < h[j].Jobs }
func (h sortHosts) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
func (h sortHosts) Sort()              { sort.Sort(h) }
<commit_msg>scheduler: Update cluster.Host to interface<commit_after>package main

import (
	"errors"
	"log"
	"net/url"
	"os"
	"sort"
	"sync"

	"github.com/flynn/flynn-controller/client"
	ct "github.com/flynn/flynn-controller/types"
	"github.com/flynn/flynn-host/types"
	"github.com/flynn/go-dockerclient"
	"github.com/flynn/go-flynn/cluster"
)

func main() {
	controller, err := client.New(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}
	cl, err := cluster.NewClient()
	if err != nil {
		log.Fatal(err)
	}
	c := newContext(cl)
	// TODO: initial load of data
	// TODO: periodic full cluster sync for anti-entropy
	go c.watchFormations(controller)
}

func newContext(cl clusterClient) *context {
	return &context{
		clusterClient: cl,
		formations:    NewFormations(),
		hosts:         newHostClients(),
		jobs:          newJobMap(),
	}
}

type context struct {
	clusterClient
	formations *Formations

	hosts *hostClients
	jobs  *jobMap
}

type clusterClient interface {
	ListHosts() (map[string]host.Host, error)
	AddJobs(req *host.AddJobsReq) (*host.AddJobsRes, error)
	ConnectHost(id string) (cluster.Host, error)
}

type formationStreamer interface {
	StreamFormations(chan<- *ct.ExpandedFormation) *error
}

func (c *context) watchFormations(fs formationStreamer) {
	ch := make(chan *ct.ExpandedFormation)
	fs.StreamFormations(ch)

	for ef := range ch {
		f := NewFormation(c, ef)
		c.formations.Add(f)
		go f.Rectify()
	}

	// TODO: log disconnect and restart
	// TODO: trigger cluster sync
}

func (c *context) watchHost(id string) {
	if !c.hosts.Add(id) {
		return
	}
	defer c.hosts.Remove(id)

	h, err := c.ConnectHost(id)
	if err != nil {
		// TODO: log/handle error
	}
	c.hosts.Set(id, h)

	ch := make(chan host.Event)
	h.StreamEvents("all", ch)
	for event := range ch {
		if event.Event != "error" && event.Event != "stop" {
			continue
		}
		job := c.jobs.Get(id, event.JobID)
		if job == nil {
			continue
		}

		c.jobs.Remove(id, event.JobID)
		go job.Formation.RemoveJob(job.Type, id, event.JobID)
	}
	// TODO: check error/reconnect
}

func newHostClients() *hostClients {
	return &hostClients{hosts: make(map[string]cluster.Host)}
}

type hostClients struct {
	hosts map[string]cluster.Host
	mtx   sync.RWMutex
}

func (h *hostClients) Add(id string) bool {
	h.mtx.Lock()
	defer h.mtx.Unlock()
	if _, exists := h.hosts[id]; exists {
		return false
	}
	h.hosts[id] = nil
	return true
}

func (h *hostClients) Set(id string, client cluster.Host) {
	h.mtx.Lock()
	h.hosts[id] = client
	h.mtx.Unlock()
}

func (h *hostClients) Remove(id string) {
	h.mtx.Lock()
	delete(h.hosts, id)
	h.mtx.Unlock()
}

func (h *hostClients) Get(id string) cluster.Host {
	h.mtx.RLock()
	defer h.mtx.RUnlock()
	return h.hosts[id]
}

func newJobMap() *jobMap {
	return &jobMap{jobs: make(map[jobKey]*Job)}
}

type jobMap struct {
	jobs map[jobKey]*Job
	mtx  sync.RWMutex
}

func (m *jobMap) Add(hostID, jobID string, job *Job) {
	m.mtx.Lock()
	m.jobs[jobKey{hostID, jobID}] = job
	m.mtx.Unlock()
}

func (m *jobMap) Remove(host, job string) {
	m.mtx.Lock()
	delete(m.jobs, jobKey{host, job})
	m.mtx.Unlock()
}

func (m *jobMap) Get(host, job string) *Job {
	m.mtx.RLock()
	defer m.mtx.RUnlock()
	return m.jobs[jobKey{host, job}]
}

type jobKey struct {
	hostID, jobID string
}

type formationKey struct {
	appID, releaseID string
}

func NewFormations() *Formations {
	return &Formations{formations: make(map[formationKey]*Formation)}
}

type Formations struct {
	formations map[formationKey]*Formation
	mtx        sync.RWMutex
}

func (fs *Formations) Get(appID, releaseID string) *Formation {
	fs.mtx.RLock()
	defer fs.mtx.RUnlock()
	return fs.formations[formationKey{appID, releaseID}]
}

func (fs *Formations) Add(f *Formation) {
	fs.mtx.Lock()
	fs.formations[f.key()] = f
	fs.mtx.Unlock()
}

func (fs *Formations) Delete(f *Formation) {
	fs.mtx.Lock()
	delete(fs.formations, f.key())
	fs.mtx.Unlock()
}

func NewFormation(c *context, ef *ct.ExpandedFormation) *Formation {
	return &Formation{
		App:       ef.App,
		Release:   ef.Release,
		Artifact:  ef.Artifact,
		Processes: ef.Processes,
		jobs:      make(jobTypeMap),
		c:         c,
	}
}

type Job struct {
	Type      string
	Formation *Formation
}

type jobTypeMap map[string]map[jobKey]*Job

func (m jobTypeMap) Add(typ, host, id string) *Job {
	jobs, ok := m[typ]
	if !ok {
		jobs = make(map[jobKey]*Job)
		m[typ] = jobs
	}
	job := &Job{Type: typ}
	jobs[jobKey{host, id}] = job
	return job
}

func (m jobTypeMap) Remove(typ, host, id string) {
	if jobs, ok := m[typ]; ok {
		delete(jobs, jobKey{host, id})
	}
}

func (m jobTypeMap) Get(typ, host, id string) *Job {
	return m[typ][jobKey{host, id}]
}

type Formation struct {
	mtx       sync.Mutex
	App       *ct.App
	Release   *ct.Release
	Artifact  *ct.Artifact
	Processes map[string]int

	jobs jobTypeMap
	c    *context
}

func (f *Formation) key() formationKey {
	return formationKey{f.App.ID, f.Release.ID}
}

func (f *Formation) Rectify() {
	f.mtx.Lock()
	defer f.mtx.Unlock()
	f.rectify()
}

func (f *Formation) RemoveJob(typ, hostID, jobID string) {
	f.mtx.Lock()
	defer f.mtx.Unlock()

	f.jobs.Remove(typ, hostID, jobID)
	f.rectify()
}

func (f *Formation) rectify() {
	// update job counts
	for t, expected := range f.Processes {
		diff := expected - len(f.jobs[t])
		if diff > 0 {
			f.add(diff, t)
		} else if diff < 0 {
			f.remove(-diff, t)
		}
	}

	// remove process types
	for t, jobs := range f.jobs {
		if _, exists := f.Processes[t]; !exists {
			f.remove(len(jobs), t)
		}
	}
}

func (f *Formation) add(n int, name string) {
	config, err := f.jobConfig(name)
	if err != nil {
		// TODO: log/handle error
	}
	for i := 0; i < n; i++ {
		hosts, err := f.c.ListHosts()
		if err != nil {
			// TODO: log/handle error
		}
		if len(hosts) == 0 {
			// TODO: log/handle error
		}
		hostCounts := make(map[string]int, len(hosts))
		for _, h := range hosts {
			hostCounts[h.ID] = 0
			for _, job := range h.Jobs {
				if f.jobType(job) != name {
					continue
				}
				hostCounts[h.ID]++
			}
		}
		sh := make(sortHosts, 0, len(hosts))
		for id, count := range hostCounts {
			sh = append(sh, sortHost{id, count})
		}
		sh.Sort()

		h := hosts[sh[0].ID]
		go f.c.watchHost(h.ID)

		job := f.jobs.Add(name, h.ID, config.ID)
		job.Formation = f
		f.c.jobs.Add(h.ID, config.ID, job)

		res, err := f.c.AddJobs(&host.AddJobsReq{HostJobs: map[string][]*host.Job{h.ID: {config}}})
		if err != nil || !res.Success {
			f.jobs.Remove(name, h.ID, config.ID)
			f.c.jobs.Remove(h.ID, config.ID)
			// TODO: log/handle error
		}
	}
}

func (f *Formation) jobType(job *host.Job) string {
	if job.Attributes["flynn-controller.app"] != f.App.ID ||
		job.Attributes["flynn-controller.release"] != f.Release.ID {
		return ""
	}
	return job.Attributes["flynn-controller.type"]
}

func (f *Formation) remove(n int, name string) {
	i := 0
	for k := range f.jobs[name] {
		// TODO: robust host handling
		if err := f.c.hosts.Get(k.hostID).StopJob(k.jobID); err != nil {
			// TODO: log/handle error
		}
		f.jobs.Remove(name, k.hostID, k.jobID)
		f.c.jobs.Remove(k.hostID, k.jobID)
		if i++; i == n {
			break
		}
	}
}

func (f *Formation) jobConfig(name string) (*host.Job, error) {
	t := f.Release.Processes[name]
	image, err := dockerImage(f.Artifact.URI)
	if err != nil {
		return nil, err
	}
	return &host.Job{
		ID:       cluster.RandomJobID(""),
		TCPPorts: t.Ports.TCP,
		Attributes: map[string]string{
			"flynn-controller.app":     f.App.ID,
			"flynn-controller.release": f.Release.ID,
			"flynn-controller.type":    name,
		},
		Config: &docker.Config{
			Cmd:   t.Cmd,
			Env:   formatEnv(f.Release.Environment),
			Image: image,
		},
	}, nil
}

func dockerImage(uri string) (string, error) {
	// TODO: ID refs (see https://github.com/dotcloud/docker/issues/4106)
	u, err := url.Parse(uri)
	if err != nil {
		return "", err
	}
	if u.Scheme != "docker" {
		return "", errors.New("scheduler: only docker artifact URIs are currently supported")
	}
	var suffix string
	if tag := u.Query().Get("tag"); tag != "" {
		suffix = ":" + tag
	}
	return u.Host + suffix, nil
}

func formatEnv(env map[string]string) []string {
	res := make([]string, 0, len(env))
	for k, v := range env {
		res = append(res, k+"="+v)
	}
	return res
}

type sortHost struct {
	ID   string
	Jobs int
}

type sortHosts []sortHost

func (h sortHosts) Len() int           { return len(h) }
func (h sortHosts) Less(i, j int) bool { return h[i].Jobs < h[j].Jobs }
func (h sortHosts) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
func (h sortHosts) Sort()              { sort.Sort(h) }
<|endoftext|>


<commit_before>package sdl

import (
	"bytes"
	"reflect"
	"testing"
	"unsafe"
)

var squareWave = []byte("RIFF,\x00\x00\x00WAVEfmt \x10\x00\x00\x00\x01\x00" +
	"\x01\x00\xab \x00\x00\xab \x00\x00\x01\x00\b\x00data\b\x00\x00\x00\x00" +
	"\x00\x00\x00\xff\xff\xff\xff")

func TestAudioInitQuit(t *testing.T) {
	// figure out what driver will work
	if err := InitSubSystem(INIT_AUDIO); err != nil {
		t.Fatalf("InitSubSystem(INIT_AUDIO): %v", err)
	}
	driver := GetCurrentAudioDriver()

	// reset audio subsystem
	QuitSubSystem(INIT_AUDIO)
	if GetCurrentAudioDriver() != "" {
		t.Fatalf(`GetCurrentAudioDriver() != "" after QuitSubSystem()`)
	}

	// test valid init
	if err := AudioInit(driver); err != nil {
		t.Errorf("AudioInit(%s) returned error: %v", driver, err)
	} else {
		if got := GetCurrentAudioDriver(); got != driver {
			t.Errorf("GetCurrentAudioDriver() == %#v; want %#v", got, driver)
		}

		// test quit
		AudioQuit()
		if GetCurrentAudioDriver() != "" {
			t.Fatalf(`GetCurrentAudioDriver() != "" after AudioQuit()`)
		}
	}

	// test invalid init
	driver = "bogus"
	if err := AudioInit(driver); err == nil {
		t.Errorf("AudioInit(%s) did not return error", driver)
	}
}

func TestLoadWAV_RW(t *testing.T) {
	// load WAV from *RWOps pointing to WAV data
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&squareWave))
	src := RWFromMem(unsafe.Pointer(sliceHeader.Data), len(squareWave))
	buf, spec := LoadWAV_RW(src, false, &AudioSpec{})

	// test returned []byte
	want := []byte{0, 0, 0, 0, 255, 255, 255, 255}
	if buf == nil {
		t.Errorf("LoadWAV_RW() returned nil []byte")
	} else {
		if bytes.Compare(buf, want) != 0 {
			t.Errorf("LoadWAV_RW() returned %v; want %v", buf, want)
		}
		FreeWAV(buf) // make sure we can free without error
	}

	// test returned *AudioSpec
	if spec == nil {
		t.Errorf("LoadWAV_RW() returned nil *AudioSpec")
	} else if spec.Freq != 8363 { // no need to test all the spec fields
		t.Errorf("LoadWAV_RW() returned *AudioSpec with Freq %d; want %d",
			spec.Freq, 8363)
	}
}
<commit_msg>Add test for GetNumAudioDevices(), GetAudioDeviceName()<commit_after>package sdl

import (
	"bytes"
	"reflect"
	"testing"
	"unsafe"
)

var squareWave = []byte("RIFF,\x00\x00\x00WAVEfmt \x10\x00\x00\x00\x01\x00" +
	"\x01\x00\xab \x00\x00\xab \x00\x00\x01\x00\b\x00data\b\x00\x00\x00\x00" +
	"\x00\x00\x00\xff\xff\xff\xff")

func TestAudioDevices(t *testing.T) {
	for _, isCapture := range []bool{false, true} {
		n := GetNumAudioDevices(isCapture)
		for i := 0; i < n; i++ {
			if GetAudioDeviceName(i, isCapture) == "" {
				t.Errorf("GetAudioDeviceName(%v, %v) returned empty string",
					i, isCapture)
			}
		}

		// cover GetAudioDeviceName() even if n < 1
		if name := GetAudioDeviceName(-1, isCapture); name != "" {
			t.Errorf("GetAudioDeviceName(-1, %v) == %#v; want empty string",
				isCapture, name)
		}
	}
}

func TestAudioInitQuit(t *testing.T) {
	// figure out what driver will work
	if err := InitSubSystem(INIT_AUDIO); err != nil {
		t.Fatalf("InitSubSystem(INIT_AUDIO): %v", err)
	}
	driver := GetCurrentAudioDriver()

	// reset audio subsystem
	QuitSubSystem(INIT_AUDIO)
	if GetCurrentAudioDriver() != "" {
		t.Fatalf(`GetCurrentAudioDriver() != "" after QuitSubSystem()`)
	}

	// test valid init
	if err := AudioInit(driver); err != nil {
		t.Errorf("AudioInit(%s) returned error: %v", driver, err)
	} else {
		if got := GetCurrentAudioDriver(); got != driver {
			t.Errorf("GetCurrentAudioDriver() == %#v; want %#v", got, driver)
		}

		// test quit
		AudioQuit()
		if GetCurrentAudioDriver() != "" {
			t.Fatalf(`GetCurrentAudioDriver() != "" after AudioQuit()`)
		}
	}

	// test invalid init
	driver = "bogus"
	if err := AudioInit(driver); err == nil {
		t.Errorf("AudioInit(%s) did not return error", driver)
	}
}

func TestLoadWAV_RW(t *testing.T) {
	// load WAV from *RWOps pointing to WAV data
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&squareWave))
	src := RWFromMem(unsafe.Pointer(sliceHeader.Data), len(squareWave))
	buf, spec := LoadWAV_RW(src, false, &AudioSpec{})

	// test returned []byte
	want := []byte{0, 0, 0, 0, 255, 255, 255, 255}
	if buf == nil {
		t.Errorf("LoadWAV_RW() returned nil []byte")
	} else {
		if bytes.Compare(buf, want) != 0 {
			t.Errorf("LoadWAV_RW() returned %v; want %v", buf, want)
		}
		FreeWAV(buf) // make sure we can free without error
	}

	// test returned *AudioSpec
	if spec == nil {
		t.Errorf("LoadWAV_RW() returned nil *AudioSpec")
	} else if spec.Freq != 8363 { // no need to test all the spec fields
		t.Errorf("LoadWAV_RW() returned *AudioSpec with Freq %d; want %d",
			spec.Freq, 8363)
	}
}
<|endoftext|>


<commit_before>package algoholic

func binarySearchImpl(ns []int, n, offset int) int {
	if len(ns) == 0 {
		return -1
	}

	half := len(ns) / 2
	val := ns[half]

	switch {
	case n == val:
		return half + offset
	case n < val:
		return binarySearchImpl(ns[:half], n, offset)
	default:
		// n > val
		return binarySearchImpl(ns[half+1:], n, offset+half+1)
	}
}

func BinarySearchRecursive(ns []int, n int) int {
	return binarySearchImpl(ns, n, 0)
}

func BinarySearchIterative(ns []int, n int) int {
	if len(ns) == 0 {
		return -1
	}

	from, to := 0, len(ns)-1

	for {
		if from > to {
			return -1
		}

		// Avoid overflow.
		mid := to - (to-from)/2
		val := ns[mid]

		switch {
		case val == n:
			return mid
		case n < val:
			to = mid - 1
		default:
			from = mid + 1
		}
	}
}
<commit_msg>More efficient to check this at the end of the loop.<commit_after>package algoholic

func binarySearchImpl(ns []int, n, offset int) int {
	if len(ns) == 0 {
		return -1
	}

	half := len(ns) / 2
	val := ns[half]

	switch {
	case n == val:
		return half + offset
	case n < val:
		return binarySearchImpl(ns[:half], n, offset)
	default:
		// n > val
		return binarySearchImpl(ns[half+1:], n, offset+half+1)
	}
}

func BinarySearchRecursive(ns []int, n int) int {
	return binarySearchImpl(ns, n, 0)
}

func BinarySearchIterative(ns []int, n int) int {
	if len(ns) == 0 {
		return -1
	}

	from, to := 0, len(ns)-1

	for {
		// Avoid overflow.
		mid := to - (to-from)/2
		val := ns[mid]

		switch {
		case val == n:
			return mid
		case n < val:
			to = mid - 1
		default:
			from = mid + 1
		}

		if from > to {
			return -1
		}
	}
}
<|endoftext|>


<commit_before>package serial

import (
	"fmt"
	"io"
	"os"
	"syscall"
	"unsafe"
)

type serialPort struct {
	f  *os.File
	fd int32
}

const EV_RXCHAR = 0x0001

type structDCB struct {
	DCBlength, BaudRate                            uint32
	flags                                          [4]byte
	wReserved, XonLim, XoffLim                     uint16
	ByteSize, Parity, StopBits                     byte
	XonChar, XoffChar, ErrorChar, EofChar, EvtChar byte
	wReserved1                                     uint16
}

type structTimeouts struct {
	ReadIntervalTimeout         uint32
	ReadTotalTimeoutMultiplier  uint32
	ReadTotalTimeoutConstant    uint32
	WriteTotalTimeoutMultiplier uint32
	WriteTotalTimeoutConstant   uint32
}

var nSetCommState uint32
var nSetCommTimeouts uint32
var nSetCommMask uint32
var nSetupComm uint32
var nWaitCommEvent uint32
var nGetOverlappedResult uint32
var nCreateEvent uint32

/*
func here() {
	_, file, line, _ := runtime.Caller(1)
	fmt.Println("Got to", file, ":", line)
}
*/

func getProcAddr(lib uint32, name string) uint32 {
	addr, err := syscall.GetProcAddress(lib, name)
	if err != 0 {
		panic(name + " " + syscall.Errstr(err))
	}
	return addr
}

func init() {
	k32, err := syscall.LoadLibrary("kernel32.dll")
	if err != 0 {
		panic("LoadLibrary " + syscall.Errstr(err))
	}
	defer syscall.FreeLibrary(k32)

	nSetCommState = getProcAddr(k32, "SetCommState")
	nSetCommTimeouts = getProcAddr(k32, "SetCommTimeouts")
	nSetCommMask = getProcAddr(k32, "SetCommMask")
	nSetupComm = getProcAddr(k32, "SetupComm")
	nWaitCommEvent = getProcAddr(k32, "WaitCommEvent")
	nGetOverlappedResult = getProcAddr(k32, "GetOverlappedResult")
	nCreateEvent = getProcAddr(k32, "CreateEventW")
}

func setCommState(h int32, baud int) os.Error {
	var params structDCB
	params.DCBlength = uint32(unsafe.Sizeof(params))

	params.flags[0] = 0x01  // fBinary
	params.flags[0] |= 0x10 // Assert DSR

	params.BaudRate = uint32(baud)
	params.ByteSize = 8

	//fmt.Printf("%#v\n", params)
	r, _, e := syscall.Syscall(uintptr(nSetCommState), 2, uintptr(h), uintptr(unsafe.Pointer(&params)), 0)
	if r == 0 {
		return os.Errno(e)
	}
	return nil
}

func setCommTimeouts(h int32) os.Error {
	var timeouts structTimeouts
	timeouts.ReadIntervalTimeout = 1<<32 - 1
	timeouts.ReadTotalTimeoutConstant = 0
	r, _, e := syscall.Syscall(uintptr(nSetCommTimeouts), 2, uintptr(h), uintptr(unsafe.Pointer(&timeouts)), 0)
	if r == 0 {
		return os.Errno(e)
	}
	return nil
}

func setupComm(h int32, in, out int) os.Error {
	r, _, e := syscall.Syscall(uintptr(nSetupComm), 3, uintptr(h), uintptr(in), uintptr(out))
	if r == 0 {
		return os.Errno(e)
	}
	return nil
}

func setCommMask(h int32) os.Error {
	r, _, e := syscall.Syscall(uintptr(nSetCommMask), 2, uintptr(h), EV_RXCHAR, 0)
	if r == 0 {
		return os.Errno(e)
	}
	return nil
}

const FILE_FLAGS_OVERLAPPED = 0x40000000

func OpenPort(name string, baud int) (io.ReadWriteCloser, os.Error) {
	//h, e := CreateFile(StringToUTF16Ptr(path), access, sharemode, sa, createmode, FILE_ATTRIBUTE_NORMAL, 0)
	if len(name) > 0 && name[0] != '\\' {
		name = "\\\\.\\" + name
	}

	h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(name),
		syscall.GENERIC_READ|syscall.GENERIC_WRITE,
		0,
		nil,
		syscall.OPEN_EXISTING,
		syscall.FILE_ATTRIBUTE_NORMAL|FILE_FLAGS_OVERLAPPED,
		0)

	if e != 0 {
		return nil, &os.PathError{"open", name, os.Errno(e)}
	}

	f := os.NewFile(int(h), name)

	/*
		f, err := os.Open(name)
		if err != nil {
			return nil, err
		}
		h := int32(f.Fd())
	*/

	if err := setCommState(h, baud); err != nil {
		f.Close()
		return nil, err
	}

	if err := setupComm(h, 64, 64); err != nil {
		f.Close()
		return nil, err
	}

	if err := setCommTimeouts(h); err != nil {
		f.Close()
		return nil, err
	}
	if err := setCommMask(h); err != nil {
		f.Close()
		return nil, err
	}

	port := serialPort{f, h}

	return &port, nil
}

func (p *serialPort) Close() os.Error {
	return p.f.Close()
}

func newOverlapped() (*syscall.Overlapped, os.Error) {
	var overlapped syscall.Overlapped
	r, _, e := syscall.Syscall6(uintptr(nCreateEvent), 4, 0, 1, 0, 0, 0, 0)
	if r == 0 {
		return nil, os.Errno(e)
	}
	overlapped.HEvent = (*uint8)(unsafe.Pointer(r))
	return &overlapped, nil
}

func getOverlappedResult(h int32, overlapped *syscall.Overlapped) (int, os.Error) {
	var n int
	r, _, e := syscall.Syscall6(uintptr(nGetOverlappedResult), 4,
		uintptr(h),
		uintptr(unsafe.Pointer(overlapped)),
		uintptr(unsafe.Pointer(&n)), 1, 0, 0)
	if r == 0 {
		return 0, os.Errno(e)
	}

	return n, nil
}

func (p *serialPort) Write(buf []byte) (int, os.Error) {
	overlapped, err := newOverlapped()
	if err != nil {
		return 0, err
	}

	var n uint32
	e := syscall.WriteFile(p.fd, buf, &n, overlapped)
	if e != 0 {
		if e == syscall.ERROR_IO_PENDING {
			return getOverlappedResult(p.fd, overlapped)
		} else {
			return int(n), os.Errno(e)
		}
	}

	return int(n), nil
}

func waitCommEvent(h int32, overlapped *syscall.Overlapped) os.Error {
	var events uint32
	r, _, e := syscall.Syscall(uintptr(nWaitCommEvent), 3, uintptr(h), uintptr(unsafe.Pointer(&events)), uintptr(unsafe.Pointer(overlapped)))
	if r == 0 {
		return os.Errno(e)
	}
	return nil
}

func (p *serialPort) Read(buf []byte) (int, os.Error) {
	var events uint32

	if p == nil || p.f == nil {
		str := fmt.Sprintf("Invalid port on read %v %v", p, p.f)
		return 0, os.NewError(str)
	}

	overlapped, err := newOverlapped()
	if err != nil {
		return 0, err
	}

loop:
	if err = waitCommEvent(p.fd, overlapped); err != nil {
		if err != os.Errno(syscall.ERROR_IO_PENDING) {
			return 0, err
		}
	}
	_, err = getOverlappedResult(p.fd, overlapped)
	if err != nil {
		return 0, err
	}

	// There is a small race window here between returning from
	// WaitCommEvent and reading from the file.  If we receive
	// data in this window, we will read that data, but the RXFLAG
	// will still be set and next time the WaitCommEvent() will
	// return but we will have already read the data.  That's why
	// we have the stupid goto loop on EOF.
	overlapped, err = newOverlapped()
	if err != nil {
		return 0, err
	}
	var n int
	e := syscall.ReadFile(p.fd, buf, &events, overlapped)
	if e != 0 && e != syscall.ERROR_IO_PENDING {
		return 0, os.Errno(e)
	}
	n, err = getOverlappedResult(p.fd, overlapped)
	if err != nil {
		return n, err
	}

	if n == 0 {
		goto loop
	}

	return n, nil
}
<commit_msg>Close event handles<commit_after>package serial

import (
	"fmt"
	"io"
	"os"
	"syscall"
	"unsafe"
)

type serialPort struct {
	f  *os.File
	fd int32
}

const EV_RXCHAR = 0x0001

type structDCB struct {
	DCBlength, BaudRate                            uint32
	flags                                          [4]byte
	wReserved, XonLim, XoffLim                     uint16
	ByteSize, Parity, StopBits                     byte
	XonChar, XoffChar, ErrorChar, EofChar, EvtChar byte
	wReserved1                                     uint16
}

type structTimeouts struct {
	ReadIntervalTimeout         uint32
	ReadTotalTimeoutMultiplier  uint32
	ReadTotalTimeoutConstant    uint32
	WriteTotalTimeoutMultiplier uint32
	WriteTotalTimeoutConstant   uint32
}

var nSetCommState uint32
var nSetCommTimeouts uint32
var nSetCommMask uint32
var nSetupComm uint32
var nWaitCommEvent uint32
var nGetOverlappedResult uint32
var nCreateEvent uint32

/*
func here() {
	_, file, line, _ := runtime.Caller(1)
	fmt.Println("Got to", file, ":", line)
}
*/

func getProcAddr(lib uint32, name string) uint32 {
	addr, err := syscall.GetProcAddress(lib, name)
	if err != 0 {
		panic(name + " " + syscall.Errstr(err))
	}
	return addr
}

func init() {
	k32, err := syscall.LoadLibrary("kernel32.dll")
	if err != 0 {
		panic("LoadLibrary " + syscall.Errstr(err))
	}
	defer syscall.FreeLibrary(k32)

	nSetCommState = getProcAddr(k32, "SetCommState")
	nSetCommTimeouts = getProcAddr(k32, "SetCommTimeouts")
	nSetCommMask = getProcAddr(k32, "SetCommMask")
	nSetupComm = getProcAddr(k32, "SetupComm")
	nWaitCommEvent = getProcAddr(k32, "WaitCommEvent")
	nGetOverlappedResult = getProcAddr(k32, "GetOverlappedResult")
	nCreateEvent = getProcAddr(k32, "CreateEventW")
}

func setCommState(h int32, baud int) os.Error {
	var params structDCB
	params.DCBlength = uint32(unsafe.Sizeof(params))

	params.flags[0] = 0x01  // fBinary
	params.flags[0] |= 0x10 // Assert DSR

	params.BaudRate = uint32(baud)
	params.ByteSize = 8

	//fmt.Printf("%#v\n", params)
	r, _, e := syscall.Syscall(uintptr(nSetCommState), 2, uintptr(h), uintptr(unsafe.Pointer(&params)), 0)
	if r == 0 {
		return os.Errno(e)
	}
	return nil
}

func setCommTimeouts(h int32) os.Error {
	var timeouts structTimeouts
	timeouts.ReadIntervalTimeout = 1<<32 - 1
	timeouts.ReadTotalTimeoutConstant = 0
	r, _, e := syscall.Syscall(uintptr(nSetCommTimeouts), 2, uintptr(h), uintptr(unsafe.Pointer(&timeouts)), 0)
	if r == 0 {
		return os.Errno(e)
	}
	return nil
}

func setupComm(h int32, in, out int) os.Error {
	r, _, e := syscall.Syscall(uintptr(nSetupComm), 3, uintptr(h), uintptr(in), uintptr(out))
	if r == 0 {
		return os.Errno(e)
	}
	return nil
}

func setCommMask(h int32) os.Error {
	r, _, e := syscall.Syscall(uintptr(nSetCommMask), 2, uintptr(h), EV_RXCHAR, 0)
	if r == 0 {
		return os.Errno(e)
	}
	return nil
}

const FILE_FLAGS_OVERLAPPED = 0x40000000

func OpenPort(name string, baud int) (io.ReadWriteCloser, os.Error) {
	//h, e := CreateFile(StringToUTF16Ptr(path), access, sharemode, sa, createmode, FILE_ATTRIBUTE_NORMAL, 0)
	if len(name) > 0 && name[0] != '\\' {
		name = "\\\\.\\" + name
	}

	h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(name),
		syscall.GENERIC_READ|syscall.GENERIC_WRITE,
		0,
		nil,
		syscall.OPEN_EXISTING,
		syscall.FILE_ATTRIBUTE_NORMAL|FILE_FLAGS_OVERLAPPED,
		0)

	if e != 0 {
		return nil, &os.PathError{"open", name, os.Errno(e)}
	}

	f := os.NewFile(int(h), name)

	/*
		f, err := os.Open(name)
		if err != nil {
			return nil, err
		}
		h := int32(f.Fd())
	*/

	if err := setCommState(h, baud); err != nil {
		f.Close()
		return nil, err
	}

	if err := setupComm(h, 64, 64); err != nil {
		f.Close()
		return nil, err
	}

	if err := setCommTimeouts(h); err != nil {
		f.Close()
		return nil, err
	}
	if err := setCommMask(h); err != nil {
		f.Close()
		return nil, err
	}

	port := serialPort{f, h}

	return &port, nil
}

func (p *serialPort) Close() os.Error {
	return p.f.Close()
}

func newOverlapped() (*syscall.Overlapped, os.Error) {
	var overlapped syscall.Overlapped
	r, _, e := syscall.Syscall6(uintptr(nCreateEvent), 4, 0, 1, 0, 0, 0, 0)
	if r == 0 {
		return nil, os.Errno(e)
	}
	overlapped.HEvent = (*uint8)(unsafe.Pointer(r))
	return &overlapped, nil
}

func getOverlappedResult(h int32, overlapped *syscall.Overlapped) (int, os.Error) {
	var n int
	r, _, e := syscall.Syscall6(uintptr(nGetOverlappedResult), 4,
		uintptr(h),
		uintptr(unsafe.Pointer(overlapped)),
		uintptr(unsafe.Pointer(&n)), 1, 0, 0)
	if r == 0 {
		return 0, os.Errno(e)
	}

	return n, nil
}

func (p *serialPort) Write(buf []byte) (int, os.Error) {
	overlapped, err := newOverlapped()
	if err != nil {
		return 0, err
	}
	defer syscall.CloseHandle(int32(uintptr(unsafe.Pointer(overlapped.HEvent))))

	var n uint32
	e := syscall.WriteFile(p.fd, buf, &n, overlapped)
	if e != 0 {
		if e == syscall.ERROR_IO_PENDING {
			return getOverlappedResult(p.fd, overlapped)
		} else {
			return int(n), os.Errno(e)
		}
	}

	return int(n), nil
}

func waitCommEvent(h int32, overlapped *syscall.Overlapped) os.Error {
	var events uint32
	r, _, e := syscall.Syscall(uintptr(nWaitCommEvent), 3, uintptr(h), uintptr(unsafe.Pointer(&events)), uintptr(unsafe.Pointer(overlapped)))
	if r == 0 {
		return os.Errno(e)
	}
	return nil
}

func (p *serialPort) Read(buf []byte) (int, os.Error) {
	var events uint32

	if p == nil || p.f == nil {
		str := fmt.Sprintf("Invalid port on read %v %v", p, p.f)
		return 0, os.NewError(str)
	}

	overlapped, err := newOverlapped()
	if err != nil {
		return 0, err
	}
	defer syscall.CloseHandle(int32(uintptr(unsafe.Pointer(overlapped.HEvent))))

loop:
	if err = waitCommEvent(p.fd, overlapped); err != nil {
		if err != os.Errno(syscall.ERROR_IO_PENDING) {
			return 0, err
		}
	}
	_, err = getOverlappedResult(p.fd, overlapped)
	if err != nil {
		return 0, err
	}

	// There is a small race window here between returning from
	// WaitCommEvent and reading from the file.  If we receive
	// data in this window, we will read that data, but the RXFLAG
	// will still be set and next time the WaitCommEvent() will
	// return but we will have already read the data.  That's why
	// we have the stupid goto loop on EOF.
	overlapped, err = newOverlapped()
	if err != nil {
		return 0, err
	}
	var n int
	e := syscall.ReadFile(p.fd, buf, &events, overlapped)
	if e != 0 && e != syscall.ERROR_IO_PENDING {
		return 0, os.Errno(e)
	}
	n, err = getOverlappedResult(p.fd, overlapped)
	if err != nil {
		return n, err
	}

	if n == 0 {
		goto loop
	}

	return n, nil
}
<|endoftext|>


<commit_before>package server

import (
	"github.com/goraft/raft"
	"github.com/skynetservices/skydns/msg"
	"github.com/skynetservices/skydns/registry"
	"log"
	"time"
)

// Command for adding service to registry
type AddServiceCommand struct {
	Service msg.Service
}

// Creates a new AddServiceCommand
func NewAddServiceCommand(s msg.Service) *AddServiceCommand {
	s.Expires = getExpirationTime(s.TTL)

	return &AddServiceCommand{s}
}

// Name of command
func (c *AddServiceCommand) CommandName() string {
	return "add-service"
}

// Adds service to registry
func (c *AddServiceCommand) Apply(server raft.Server) (interface{}, error) {
	reg := server.Context().(registry.Registry)
	err := reg.Add(c.Service)

	if err == nil {
		log.Println("Added Service:", c.Service)
	}

	return c.Service, err
}

type UpdateTTLCommand struct {
	UUID    string
	TTL     uint32
	Expires time.Time
}

// NewUpdateTTLCommands returns a new UpdateTTLCommand
func NewUpdateTTLCommand(uuid string, ttl uint32) *UpdateTTLCommand {
	return &UpdateTTLCommand{uuid, ttl, getExpirationTime(ttl)}
}

// Name of command
func (c *UpdateTTLCommand) CommandName() string {
	return "update-ttl"
}

// Updates TTL in registry
func (c *UpdateTTLCommand) Apply(server raft.Server) (interface{}, error) {
	reg := server.Context().(registry.Registry)
	err := reg.UpdateTTL(c.UUID, c.TTL, c.Expires)

	if err == nil {
		log.Println("Updated Service TTL:", c.UUID, c.TTL)
	}

	return c.UUID, err
}

type RemoveServiceCommand struct {
	UUID string
}

// Creates a new RemoveServiceCommand
func NewRemoveServiceCommand(uuid string) *RemoveServiceCommand {
	return &RemoveServiceCommand{uuid}
}

// Name of command
func (c *RemoveServiceCommand) CommandName() string {
	return "remove-service"
}

// Removes service from the registry
func (c *RemoveServiceCommand) Apply(server raft.Server) (interface{}, error) {

	reg := server.Context().(registry.Registry)
	err := reg.RemoveUUID(c.UUID)

	if err == nil {
		log.Println("Removed Service:", c.UUID)
	}

	return c.UUID, err
}

func getExpirationTime(ttl uint32) time.Time {
	return time.Now().Add(time.Duration(ttl) * time.Second)
}

type AddCallbackCommand struct {
	Service msg.Service
	UUID    string // callback uuid
}

func NewAddCallbackCommand(s msg.Service, uuid string) *AddCallbackCommand {
	return &AddCallbackCommand{s, uuid}
}

// Name of command
func (c *AddCallbackCommand) CommandName() string {
	return "add-callback"
}

// Updates callback in registry
func (c *AddCallbackCommand) Apply(server raft.Server) (interface{}, error) {
	reg := server.Context().(registry.Registry)
	err := reg.AddCallback(c.Service, c.UUID)

	if err == nil {
		log.Println("Added Callback:", c.Service, c.UUID)
	}

	return c.Service, err
}

<commit_msg>Put these CommandName functions on a single line<commit_after>package server

import (
	"github.com/goraft/raft"
	"github.com/skynetservices/skydns/msg"
	"github.com/skynetservices/skydns/registry"
	"log"
	"time"
)

// Command for adding service to registry
type AddServiceCommand struct {
	Service msg.Service
}

// Creates a new AddServiceCommand
func NewAddServiceCommand(s msg.Service) *AddServiceCommand {
	s.Expires = getExpirationTime(s.TTL)

	return &AddServiceCommand{s}
}

// Name of command
func (c *AddServiceCommand) CommandName() string { return "add-service" }

// Adds service to registry
func (c *AddServiceCommand) Apply(server raft.Server) (interface{}, error) {
	reg := server.Context().(registry.Registry)
	err := reg.Add(c.Service)

	if err == nil {
		log.Println("Added Service:", c.Service)
	}

	return c.Service, err
}

type UpdateTTLCommand struct {
	UUID    string
	TTL     uint32
	Expires time.Time
}

// NewUpdateTTLCommands returns a new UpdateTTLCommand
func NewUpdateTTLCommand(uuid string, ttl uint32) *UpdateTTLCommand {
	return &UpdateTTLCommand{uuid, ttl, getExpirationTime(ttl)}
}

// Name of command
func (c *UpdateTTLCommand) CommandName() string { return "update-ttl" }

// Updates TTL in registry
func (c *UpdateTTLCommand) Apply(server raft.Server) (interface{}, error) {
	reg := server.Context().(registry.Registry)
	err := reg.UpdateTTL(c.UUID, c.TTL, c.Expires)

	if err == nil {
		log.Println("Updated Service TTL:", c.UUID, c.TTL)
	}

	return c.UUID, err
}

type RemoveServiceCommand struct {
	UUID string
}

// Creates a new RemoveServiceCommand
func NewRemoveServiceCommand(uuid string) *RemoveServiceCommand {
	return &RemoveServiceCommand{uuid}
}

// Name of command
func (c *RemoveServiceCommand) CommandName() string { return "remove-service" }

// Removes service from the registry
func (c *RemoveServiceCommand) Apply(server raft.Server) (interface{}, error) {

	reg := server.Context().(registry.Registry)
	err := reg.RemoveUUID(c.UUID)

	if err == nil {
		log.Println("Removed Service:", c.UUID)
	}

	return c.UUID, err
}

func getExpirationTime(ttl uint32) time.Time {
	return time.Now().Add(time.Duration(ttl) * time.Second)
}

type AddCallbackCommand struct {
	Service msg.Service
	UUID    string // callback uuid
}

func NewAddCallbackCommand(s msg.Service, uuid string) *AddCallbackCommand {
	return &AddCallbackCommand{s, uuid}
}

// Name of command
func (c *AddCallbackCommand) CommandName() string { return "add-callback" }

// Updates callback in registry
func (c *AddCallbackCommand) Apply(server raft.Server) (interface{}, error) {
	reg := server.Context().(registry.Registry)
	err := reg.AddCallback(c.Service, c.UUID)

	if err == nil {
		log.Println("Added Callback:", c.Service, c.UUID)
	}
	return c.Service, err
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"

	"github.com/dimfeld/httptreemux"
	"github.com/sjug/am-go/database"
)

func userHandler(w http.ResponseWriter, r *http.Request, ps map[string]string) {
	num, err := strconv.Atoi(ps["num"])
	if err != nil || num < 1 {
		http.Error(w, "Please enter a numeric collector number.", http.StatusInternalServerError)
		return
	}
	resp, _ := database.GetUserDetailsFromNumber(num)
	json, err := json.Marshal(resp)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Println("Responded with ", string(json))
	w.Header().Set("Content-Type", "application/json")
	w.Write(json)
}

// InitDetails intializes routing for details path
func InitDetails(router *httptreemux.TreeMux) {
	// Collector details
	router.GET("/user/:num", userHandler)
}
<commit_msg>Little bit of response errorr handling if no data returned<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"

	"github.com/dimfeld/httptreemux"
	"github.com/sjug/am-go/database"
)

func userHandler(w http.ResponseWriter, r *http.Request, ps map[string]string) {
	num, err := strconv.Atoi(ps["num"])
	if err != nil || num < 1 {
		http.Error(w, "Please enter a valid collector number.", http.StatusInternalServerError)
		return
	}
	resp, err := database.GetUserDetailsFromNumber(num)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNoContent)
		return
	}
	json, err := json.Marshal(resp)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Println("Responded with ", string(json))
	w.Header().Set("Content-Type", "application/json")
	w.Write(json)
}

// InitDetails intializes routing for details path
func InitDetails(router *httptreemux.TreeMux) {
	// Collector details
	router.GET("/user/:num", userHandler)
}
<|endoftext|>


<commit_before>package goproverbs

import (
	"math/rand"
	"time"
)

var proverbs []string = []string{
	"A little copying is better than a little dependency.",
	"Cgo is not Go.",
	"Cgo must always be guarded with build tags.",
	"Channels orchestrate; mutexes serialize.",
	"Clear is better than clever.",
	"Concurrency is not parallelism.",
	"Design the architecture, name the components, document the details.",
	"Documentation is for users.",
	"Don't communicate by sharing memory, share memory by communicating.",
	"Don't just check errors, handle them gracefully.",
	"Don't panic.",
	"Errors are values.",
	"Gofmt's style is no one's favorite, yet gofmt is everyone's favorite.",
	"Make the zero value useful.",
	"Reflection is never clear.",
	"Syscall must always be guarded with build tags.",
	"The bigger the interface, the weaker the abstraction.",
	"With the unsafe package there are no guarantees.",
	"interface{} says nothing.",
}

func All() []string {
	return proverbs
}

func Random() string {
	s := rand.NewSource(time.Now().UnixNano())
	r := rand.New(s)
	i := r.Intn(len(proverbs))
	return proverbs[i]
}

func First() string {
	return proverbs[0]
}

func Last() string {
	return proverbs[len(proverbs)]
}
<commit_msg>fixup<commit_after>package goproverbs

import (
	"math/rand"
	"time"
)

var proverbs []string = []string{
	"A little copying is better than a little dependency.",
	"Cgo is not Go.",
	"Cgo must always be guarded with build tags.",
	"Channels orchestrate; mutexes serialize.",
	"Clear is better than clever.",
	"Concurrency is not parallelism.",
	"Design the architecture, name the components, document the details.",
	"Documentation is for users.",
	"Don't communicate by sharing memory, share memory by communicating.",
	"Don't just check errors, handle them gracefully.",
	"Don't panic.",
	"Errors are values.",
	"Gofmt's style is no one's favorite, yet gofmt is everyone's favorite.",
	"Make the zero value useful.",
	"Reflection is never clear.",
	"Syscall must always be guarded with build tags.",
	"The bigger the interface, the weaker the abstraction.",
	"With the unsafe package there are no guarantees.",
	"interface{} says nothing.",
}

func All() []string {
	return proverbs
}

func Random() string {
	s := rand.NewSource(time.Now().UnixNano())
	r := rand.New(s)
	i := r.Intn(len(proverbs))
	return proverbs[i]
}

func First() string {
	return proverbs[0]
}

func Last() string {
	return proverbs[len(proverbs)-1]
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 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 goapp

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"sync"
	"time"

	"appengine/datastore"
	"appengine/taskqueue"
	"appengine/urlfetch"
	mpg "github.com/MiniProfiler/go/miniprofiler_gae"
	"github.com/mjibson/goon"
)

func ImportOpmlTask(c mpg.Context, w http.ResponseWriter, r *http.Request) {
	gn := goon.FromContext(c)
	userid := r.FormValue("user")
	data := r.FormValue("data")

	var skip int
	if s, err := strconv.Atoi(r.FormValue("skip")); err == nil {
		skip = s
	}
	c.Debugf("reader import for %v, skip %v", userid, skip)

	var userOpml []*OpmlOutline
	remaining := skip

	var proc func(label string, outlines []*OpmlOutline)
	proc = func(label string, outlines []*OpmlOutline) {
		for _, o := range outlines {
			if o.XmlUrl != "" {
				if remaining > 0 {
					remaining--
				} else if len(userOpml) < IMPORT_LIMIT {
					userOpml = append(userOpml, &OpmlOutline{
						Title:   label,
						Outline: []*OpmlOutline{o},
					})
				}
			}

			if o.Title != "" && len(o.Outline) > 0 {
				proc(o.Title, o.Outline)
			}
		}
	}

	opml := Opml{}
	if err := xml.Unmarshal([]byte(data), &opml); err != nil {
		c.Errorf("opml error: %v", err.Error())
		return
	}
	proc("", opml.Outline)

	// todo: refactor below with similar from ImportReaderTask
	wg := sync.WaitGroup{}
	wg.Add(len(userOpml))
	for i := range userOpml {
		go func(i int) {
			o := userOpml[i].Outline[0]
			if err := addFeed(c, userid, userOpml[i]); err != nil {
				c.Errorf("opml import error: %v", err.Error())
				// todo: do something here?
			}
			c.Debugf("opml import: %s, %s", o.Title, o.XmlUrl)
			wg.Done()
		}(i)
	}
	wg.Wait()

	ud := UserData{Id: "data", Parent: gn.Key(&User{Id: userid})}
	if err := gn.RunInTransaction(func(gn *goon.Goon) error {
		gn.Get(&ud)
		mergeUserOpml(&ud, opml.Outline...)
		_, err := gn.Put(&ud)
		return err
	}, nil); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		c.Errorf("ude update error: %v", err.Error())
		return
	}

	if len(userOpml) == IMPORT_LIMIT {
		task := taskqueue.NewPOSTTask(routeUrl("import-opml-task"), url.Values{
			"data": {data},
			"user": {userid},
			"skip": {strconv.Itoa(skip + IMPORT_LIMIT)},
		})
		taskqueue.Add(c, task, "import-reader")
	}
}

const IMPORT_LIMIT = 20

func ImportReaderTask(c mpg.Context, w http.ResponseWriter, r *http.Request) {
	gn := goon.FromContext(c)
	userid := r.FormValue("user")
	data := r.FormValue("data")

	var skip int
	if s, err := strconv.Atoi(r.FormValue("skip")); err == nil {
		skip = s
	}

	v := struct {
		Subscriptions []struct {
			Id         string `json:"id"`
			Title      string `json:"title"`
			HtmlUrl    string `json:"htmlUrl"`
			Categories []struct {
				Id    string `json:"id"`
				Label string `json:"label"`
			} `json:"categories"`
		} `json:"subscriptions"`
	}{}
	json.Unmarshal([]byte(data), &v)
	c.Debugf("reader import for %v, skip %v, len %v", userid, skip, len(v.Subscriptions))

	end := skip + IMPORT_LIMIT
	if end > len(v.Subscriptions) {
		end = len(v.Subscriptions)
	}

	wg := sync.WaitGroup{}
	wg.Add(end - skip)
	userOpml := make([]*OpmlOutline, end-skip)

	for i := range v.Subscriptions[skip:end] {
		go func(i int) {
			sub := v.Subscriptions[skip+i]
			var label string
			if len(sub.Categories) > 0 {
				label = sub.Categories[0].Label
			}
			outline := &OpmlOutline{
				Title: label,
				Outline: []*OpmlOutline{
					&OpmlOutline{
						XmlUrl: sub.Id[5:],
						Title:  sub.Title,
					},
				},
			}
			userOpml[i] = outline
			if err := addFeed(c, userid, outline); err != nil {
				c.Errorf("reader import error: %v", err.Error())
				// todo: do something here?
			}
			c.Debugf("reader import: %s, %s", sub.Title, sub.Id)
			wg.Done()
		}(i)
	}
	wg.Wait()

	ud := UserData{Id: "data", Parent: gn.Key(&User{Id: userid})}
	if err := gn.RunInTransaction(func(gn *goon.Goon) error {
		gn.Get(&ud)
		mergeUserOpml(&ud, userOpml...)
		_, err := gn.Put(&ud)
		return err
	}, nil); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		c.Errorf("ude update error: %v", err.Error())
		return
	}

	if end < len(v.Subscriptions) {
		task := taskqueue.NewPOSTTask(routeUrl("import-reader-task"), url.Values{
			"data": {data},
			"user": {userid},
			"skip": {strconv.Itoa(skip + IMPORT_LIMIT)},
		})
		taskqueue.Add(c, task, "import-reader")
	}
}

func UpdateFeeds(c mpg.Context, w http.ResponseWriter, r *http.Request) {
	gn := goon.FromContext(c)
	q := datastore.NewQuery(gn.Key(&Feed{}).Kind()).KeysOnly()
	q = q.Filter("n <=", time.Now())
	keys, _ := gn.GetAll(q, nil)
	for _, k := range keys {
		t := taskqueue.NewPOSTTask(routeUrl("update-feed"), url.Values{
			"feed": {k.StringID()},
		})
		if _, err := taskqueue.Add(c, t, "update-feed"); err != nil {
			c.Errorf("taskqueue error: %v", err.Error())
		}
	}
	c.Infof("updating %d feeds", len(keys))
}

func fetchFeed(c mpg.Context, url string) (*Feed, []*Story) {
	cl := urlfetch.Client(c)
	if resp, err := cl.Get(url); err == nil && resp.StatusCode == http.StatusOK {
		b, _ := ioutil.ReadAll(resp.Body)
		return ParseFeed(c, url, b)
	} else if err != nil {
		c.Warningf("fetch feed error: %s", err.Error())
	} else {
		c.Warningf("fetch feed error: status code: %s", resp.Status)
	}
	return nil, nil
}

func updateFeed(c mpg.Context, url string, feed *Feed, stories []*Story) error {
	gn := goon.FromContext(c)
	f := Feed{Url: url}
	if err := gn.Get(&f); err != nil {
		return errors.New(fmt.Sprintf("feed not found: %s", url))
	}

	// Compare the feed's listed update to the story's update.
	// Note: these may not be accurate, hence, only compare them to each other,
	// since they should have the same relative error.
	storyDate := f.Updated

	hasUpdated := !feed.Updated.IsZero()
	isFeedUpdated := f.Updated == feed.Updated
	if !hasUpdated {
		feed.Updated = f.Updated
	}
	feed.Date = f.Date
	f = *feed

	if hasUpdated && isFeedUpdated {
		c.Infof("feed %s already updated to %v, putting", url, feed.Updated)
		f.Updated = time.Now()
		gn.Put(&f)
		return nil
	}

	c.Debugf("hasUpdate: %v, isFeedUpdated: %v, storyDate: %v", hasUpdated, isFeedUpdated, storyDate)

	var newStories []*Story
	for _, s := range stories {
		if s.Updated.IsZero() || !s.Updated.Before(storyDate) {
			newStories = append(newStories, s)
		}
	}
	c.Debugf("%v possible stories to update", len(newStories))

	puts := []interface{}{&f}

	// find non existant stories
	fk := gn.Key(&f)
	getStories := make([]*Story, len(newStories))
	for i, s := range newStories {
		getStories[i] = &Story{Id: s.Id, Parent: fk}
	}
	err := gn.GetMulti(getStories)
	var updateStories []*Story
	for i, s := range getStories {
		if goon.NotFound(err, i) {
			updateStories = append(updateStories, newStories[i])
		} else if !newStories[i].Updated.IsZero() && !newStories[i].Updated.Equal(s.Updated) {
			newStories[i].Created = s.Created
			newStories[i].Published = s.Published
			updateStories = append(updateStories, newStories[i])
		}
	}
	c.Debugf("%v update stories", len(updateStories))

	for _, s := range updateStories {
		puts = append(puts, s)
		gn.Put(&StoryContent{
			Id:      1,
			Parent:  gn.Key(s),
			Content: s.content,
		})
	}

	c.Debugf("putting %v entities", len(puts))
	if len(puts) > 1 {
		f.Date = time.Now()
		if !hasUpdated {
			f.Updated = f.Date
		}
	}
	gn.PutMulti(puts)

	return nil
}

func UpdateFeed(c mpg.Context, w http.ResponseWriter, r *http.Request) {
	gn := goon.FromContext(c)
	url := r.FormValue("feed")
	c.Debugf("update feed %s", url)
	f := Feed{Url: url}
	if err := gn.Get(&f); err == datastore.ErrNoSuchEntity {
		return
	} else if time.Now().Before(f.NextUpdate) {
		c.Infof("feed %v already updated", url)
		return
	}
	if feed, stories := fetchFeed(c, url); feed != nil {
		updateFeed(c, url, feed, stories)
	} else {
		f.Errors++
		v := f.Errors + 1
		const max = 24 * 7
		if v > max {
			v = max
		}
		f.NextUpdate = time.Now().Add(time.Hour * time.Duration(v))
		gn.Put(&f)
		c.Warningf("error with %v (%v), bump next update to %v", url, f.Errors, f.NextUpdate)
	}
}
<commit_msg>Demote to warning<commit_after>/*
 * Copyright (c) 2013 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 goapp

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"sync"
	"time"

	"appengine/datastore"
	"appengine/taskqueue"
	"appengine/urlfetch"
	mpg "github.com/MiniProfiler/go/miniprofiler_gae"
	"github.com/mjibson/goon"
)

func ImportOpmlTask(c mpg.Context, w http.ResponseWriter, r *http.Request) {
	gn := goon.FromContext(c)
	userid := r.FormValue("user")
	data := r.FormValue("data")

	var skip int
	if s, err := strconv.Atoi(r.FormValue("skip")); err == nil {
		skip = s
	}
	c.Debugf("reader import for %v, skip %v", userid, skip)

	var userOpml []*OpmlOutline
	remaining := skip

	var proc func(label string, outlines []*OpmlOutline)
	proc = func(label string, outlines []*OpmlOutline) {
		for _, o := range outlines {
			if o.XmlUrl != "" {
				if remaining > 0 {
					remaining--
				} else if len(userOpml) < IMPORT_LIMIT {
					userOpml = append(userOpml, &OpmlOutline{
						Title:   label,
						Outline: []*OpmlOutline{o},
					})
				}
			}

			if o.Title != "" && len(o.Outline) > 0 {
				proc(o.Title, o.Outline)
			}
		}
	}

	opml := Opml{}
	if err := xml.Unmarshal([]byte(data), &opml); err != nil {
		c.Errorf("opml error: %v", err.Error())
		return
	}
	proc("", opml.Outline)

	// todo: refactor below with similar from ImportReaderTask
	wg := sync.WaitGroup{}
	wg.Add(len(userOpml))
	for i := range userOpml {
		go func(i int) {
			o := userOpml[i].Outline[0]
			if err := addFeed(c, userid, userOpml[i]); err != nil {
				c.Errorf("opml import error: %v", err.Error())
				// todo: do something here?
			}
			c.Debugf("opml import: %s, %s", o.Title, o.XmlUrl)
			wg.Done()
		}(i)
	}
	wg.Wait()

	ud := UserData{Id: "data", Parent: gn.Key(&User{Id: userid})}
	if err := gn.RunInTransaction(func(gn *goon.Goon) error {
		gn.Get(&ud)
		mergeUserOpml(&ud, opml.Outline...)
		_, err := gn.Put(&ud)
		return err
	}, nil); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		c.Errorf("ude update error: %v", err.Error())
		return
	}

	if len(userOpml) == IMPORT_LIMIT {
		task := taskqueue.NewPOSTTask(routeUrl("import-opml-task"), url.Values{
			"data": {data},
			"user": {userid},
			"skip": {strconv.Itoa(skip + IMPORT_LIMIT)},
		})
		taskqueue.Add(c, task, "import-reader")
	}
}

const IMPORT_LIMIT = 20

func ImportReaderTask(c mpg.Context, w http.ResponseWriter, r *http.Request) {
	gn := goon.FromContext(c)
	userid := r.FormValue("user")
	data := r.FormValue("data")

	var skip int
	if s, err := strconv.Atoi(r.FormValue("skip")); err == nil {
		skip = s
	}

	v := struct {
		Subscriptions []struct {
			Id         string `json:"id"`
			Title      string `json:"title"`
			HtmlUrl    string `json:"htmlUrl"`
			Categories []struct {
				Id    string `json:"id"`
				Label string `json:"label"`
			} `json:"categories"`
		} `json:"subscriptions"`
	}{}
	json.Unmarshal([]byte(data), &v)
	c.Debugf("reader import for %v, skip %v, len %v", userid, skip, len(v.Subscriptions))

	end := skip + IMPORT_LIMIT
	if end > len(v.Subscriptions) {
		end = len(v.Subscriptions)
	}

	wg := sync.WaitGroup{}
	wg.Add(end - skip)
	userOpml := make([]*OpmlOutline, end-skip)

	for i := range v.Subscriptions[skip:end] {
		go func(i int) {
			sub := v.Subscriptions[skip+i]
			var label string
			if len(sub.Categories) > 0 {
				label = sub.Categories[0].Label
			}
			outline := &OpmlOutline{
				Title: label,
				Outline: []*OpmlOutline{
					&OpmlOutline{
						XmlUrl: sub.Id[5:],
						Title:  sub.Title,
					},
				},
			}
			userOpml[i] = outline
			if err := addFeed(c, userid, outline); err != nil {
				c.Warning("reader import error: %v", err.Error())
				// todo: do something here?
			}
			c.Debugf("reader import: %s, %s", sub.Title, sub.Id)
			wg.Done()
		}(i)
	}
	wg.Wait()

	ud := UserData{Id: "data", Parent: gn.Key(&User{Id: userid})}
	if err := gn.RunInTransaction(func(gn *goon.Goon) error {
		gn.Get(&ud)
		mergeUserOpml(&ud, userOpml...)
		_, err := gn.Put(&ud)
		return err
	}, nil); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		c.Errorf("ude update error: %v", err.Error())
		return
	}

	if end < len(v.Subscriptions) {
		task := taskqueue.NewPOSTTask(routeUrl("import-reader-task"), url.Values{
			"data": {data},
			"user": {userid},
			"skip": {strconv.Itoa(skip + IMPORT_LIMIT)},
		})
		taskqueue.Add(c, task, "import-reader")
	}
}

func UpdateFeeds(c mpg.Context, w http.ResponseWriter, r *http.Request) {
	gn := goon.FromContext(c)
	q := datastore.NewQuery(gn.Key(&Feed{}).Kind()).KeysOnly()
	q = q.Filter("n <=", time.Now())
	keys, _ := gn.GetAll(q, nil)
	for _, k := range keys {
		t := taskqueue.NewPOSTTask(routeUrl("update-feed"), url.Values{
			"feed": {k.StringID()},
		})
		if _, err := taskqueue.Add(c, t, "update-feed"); err != nil {
			c.Errorf("taskqueue error: %v", err.Error())
		}
	}
	c.Infof("updating %d feeds", len(keys))
}

func fetchFeed(c mpg.Context, url string) (*Feed, []*Story) {
	cl := urlfetch.Client(c)
	if resp, err := cl.Get(url); err == nil && resp.StatusCode == http.StatusOK {
		b, _ := ioutil.ReadAll(resp.Body)
		return ParseFeed(c, url, b)
	} else if err != nil {
		c.Warningf("fetch feed error: %s", err.Error())
	} else {
		c.Warningf("fetch feed error: status code: %s", resp.Status)
	}
	return nil, nil
}

func updateFeed(c mpg.Context, url string, feed *Feed, stories []*Story) error {
	gn := goon.FromContext(c)
	f := Feed{Url: url}
	if err := gn.Get(&f); err != nil {
		return errors.New(fmt.Sprintf("feed not found: %s", url))
	}

	// Compare the feed's listed update to the story's update.
	// Note: these may not be accurate, hence, only compare them to each other,
	// since they should have the same relative error.
	storyDate := f.Updated

	hasUpdated := !feed.Updated.IsZero()
	isFeedUpdated := f.Updated == feed.Updated
	if !hasUpdated {
		feed.Updated = f.Updated
	}
	feed.Date = f.Date
	f = *feed

	if hasUpdated && isFeedUpdated {
		c.Infof("feed %s already updated to %v, putting", url, feed.Updated)
		f.Updated = time.Now()
		gn.Put(&f)
		return nil
	}

	c.Debugf("hasUpdate: %v, isFeedUpdated: %v, storyDate: %v", hasUpdated, isFeedUpdated, storyDate)

	var newStories []*Story
	for _, s := range stories {
		if s.Updated.IsZero() || !s.Updated.Before(storyDate) {
			newStories = append(newStories, s)
		}
	}
	c.Debugf("%v possible stories to update", len(newStories))

	puts := []interface{}{&f}

	// find non existant stories
	fk := gn.Key(&f)
	getStories := make([]*Story, len(newStories))
	for i, s := range newStories {
		getStories[i] = &Story{Id: s.Id, Parent: fk}
	}
	err := gn.GetMulti(getStories)
	var updateStories []*Story
	for i, s := range getStories {
		if goon.NotFound(err, i) {
			updateStories = append(updateStories, newStories[i])
		} else if !newStories[i].Updated.IsZero() && !newStories[i].Updated.Equal(s.Updated) {
			newStories[i].Created = s.Created
			newStories[i].Published = s.Published
			updateStories = append(updateStories, newStories[i])
		}
	}
	c.Debugf("%v update stories", len(updateStories))

	for _, s := range updateStories {
		puts = append(puts, s)
		gn.Put(&StoryContent{
			Id:      1,
			Parent:  gn.Key(s),
			Content: s.content,
		})
	}

	c.Debugf("putting %v entities", len(puts))
	if len(puts) > 1 {
		f.Date = time.Now()
		if !hasUpdated {
			f.Updated = f.Date
		}
	}
	gn.PutMulti(puts)

	return nil
}

func UpdateFeed(c mpg.Context, w http.ResponseWriter, r *http.Request) {
	gn := goon.FromContext(c)
	url := r.FormValue("feed")
	c.Debugf("update feed %s", url)
	f := Feed{Url: url}
	if err := gn.Get(&f); err == datastore.ErrNoSuchEntity {
		return
	} else if time.Now().Before(f.NextUpdate) {
		c.Infof("feed %v already updated", url)
		return
	}
	if feed, stories := fetchFeed(c, url); feed != nil {
		updateFeed(c, url, feed, stories)
	} else {
		f.Errors++
		v := f.Errors + 1
		const max = 24 * 7
		if v > max {
			v = max
		}
		f.NextUpdate = time.Now().Add(time.Hour * time.Duration(v))
		gn.Put(&f)
		c.Warningf("error with %v (%v), bump next update to %v", url, f.Errors, f.NextUpdate)
	}
}
<|endoftext|>


<commit_before>package gorobokassa

import (
	"crypto/md5"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

const (
	queryOutSumm     = "OutSum"
	queryInvID       = "InvId"
	queryCRC         = "SignatureValue"
	queryDescription = "Desc"
	queryLogin       = "MrchLogin"
	robokassaHost    = "auth.robokassa.ru"
	robokassaPath    = "Merchant/Index.aspx"
	scheme           = "https"
	delim            = ":"
)

// Client для генерации URL и проверки уведомлений
type Client struct {
	login          string
	firstPassword  string
	secondPassword string
}

// URL переадресации пользователя на оплату
func (client *Client) URL(invoice, value int, description string) string {
	return buildRedirectURL(client.login, client.firstPassword, invoice, value, description)
}

// CheckResult получение уведомления об исполнении операции (ResultURL)
func (client *Client) CheckResult(r *http.Request) bool {
	return verifyRequest(client.secondPassword, r)
}

// CheckSuccess проверка параметров в скрипте завершения операции (SuccessURL)
func (client *Client) CheckSuccess(r *http.Request) bool {
	return verifyRequest(client.firstPassword, r)
}

// New Client
func New(login, password1, password2 string) *Client {
	return &Client{login, password1, password2}
}

// CRC of joint values with delimeter
func CRC(v ...interface{}) string {
	s := make([]string, len(v))
	for key, value := range v {
		s[key] = fmt.Sprintf("%v", value)
	}
	h := md5.New()
	io.WriteString(h, strings.Join(s, delim))
	return fmt.Sprintf("%x", h.Sum(nil))
}

func buildRedirectURL(login, password string, invoice, value int, description string) string {
	q := url.URL{}
	q.Host = robokassaHost
	q.Scheme = scheme
	q.Path = robokassaPath

	params := url.Values{}
	params.Add(queryLogin, login)
	params.Add(queryOutSumm, strconv.Itoa(value))
	params.Add(queryInvID, strconv.Itoa(invoice))
	params.Add(queryDescription, description)
	params.Add(queryCRC, CRC(login, value, invoice, password))

	q.RawQuery = params.Encode()
	return q.String()
}

func verifyResult(password string, invoice, value int, crc string) bool {
	return strings.ToUpper(crc) == strings.ToUpper(CRC(value, invoice, password))
}

func verifyRequest(password string, r *http.Request) bool {
	q := r.URL.Query()
	value, err := strconv.Atoi(q.Get(queryOutSumm))
	if err != nil {
		log.Println(err)
		return false
	}
	invoice, err := strconv.Atoi(q.Get(queryInvID))
	if err != nil {
		log.Println(err)
		return false
	}
	crc := q.Get(queryCRC)
	return verifyResult(password, invoice, value, crc)
}
<commit_msg>added invoice<commit_after>package gorobokassa

import (
	"crypto/md5"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

const (
	queryOutSumm     = "OutSum"
	queryInvID       = "InvId"
	queryCRC         = "SignatureValue"
	queryDescription = "Desc"
	queryLogin       = "MrchLogin"
	robokassaHost    = "auth.robokassa.ru"
	robokassaPath    = "Merchant/Index.aspx"
	scheme           = "https"
	delim            = ":"
)

var (
	ErrBadRequest = errors.New("bad request")
)

// Client для генерации URL и проверки уведомлений
type Client struct {
	login          string
	firstPassword  string
	secondPassword string
}

// URL переадресации пользователя на оплату
func (client *Client) URL(invoice, value int, description string) string {
	return buildRedirectURL(client.login, client.firstPassword, invoice, value, description)
}

// CheckResult получение уведомления об исполнении операции (ResultURL)
func (client *Client) CheckResult(r *http.Request) bool {
	return verifyRequest(client.secondPassword, r)
}

func (client *Client) ResultInvoice(r *http.Request) (int, int, error) {
	return getInvoice(client.secondPassword, r)
}

// CheckSuccess проверка параметров в скрипте завершения операции (SuccessURL)
func (client *Client) CheckSuccess(r *http.Request) bool {
	return verifyRequest(client.firstPassword, r)
}

// New Client
func New(login, password1, password2 string) *Client {
	return &Client{login, password1, password2}
}

// CRC of joint values with delimeter
func CRC(v ...interface{}) string {
	s := make([]string, len(v))
	for key, value := range v {
		s[key] = fmt.Sprintf("%v", value)
	}
	h := md5.New()
	io.WriteString(h, strings.Join(s, delim))
	return fmt.Sprintf("%x", h.Sum(nil))
}

func buildRedirectURL(login, password string, invoice, value int, description string) string {
	q := url.URL{}
	q.Host = robokassaHost
	q.Scheme = scheme
	q.Path = robokassaPath

	params := url.Values{}
	params.Add(queryLogin, login)
	params.Add(queryOutSumm, strconv.Itoa(value))
	params.Add(queryInvID, strconv.Itoa(invoice))
	params.Add(queryDescription, description)
	params.Add(queryCRC, CRC(login, value, invoice, password))

	q.RawQuery = params.Encode()
	return q.String()
}

func verifyResult(password string, invoice, value int, crc string) bool {
	return strings.ToUpper(crc) == strings.ToUpper(CRC(value, invoice, password))
}

func getInvoice(password string, r *http.Request) (int, int, error) {
	q := r.URL.Query()
	value, err := strconv.Atoi(q.Get(queryOutSumm))
	if err != nil {
		log.Println(err)
		return 0, 0, ErrBadRequest
	}
	invoice, err := strconv.Atoi(q.Get(queryInvID))
	if err != nil {
		log.Println(err)
		return 0, 0, ErrBadRequest
	}
	crc := q.Get(queryCRC)
	if !verifyResult(password, invoice, value, crc) {
		return 0, 0, ErrBadRequest
	}
	return invoice, value, nil
}

func verifyRequest(password string, r *http.Request) bool {
	_, _, err := getInvoice(password, r)
	return err == nil
}
<|endoftext|>