<commit_before>package main

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"sort"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
)

const (
	port = ":8001"
	file = "dump.gob"
	unknown = "unknown"
)

var (
	tracks = make(Tracker)
	tmpl   = template.Must(template.ParseFiles("index.html"))
	zzz    bool
)

type Tracker map[Window]*Track

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

type Window struct {
	Class string
	Name  string
}

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

func (t Track) String() string {
	return fmt.Sprint(t.Spent)
}

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

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

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

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

func (x Xorg) name(w xproto.Window) string {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		return unknown
	}
	if string(name.Value) == "" {
		name, err = x.property(w, x.nameAtom)
		if err != nil || string(name.Value) == "" {
			return unknown
		}
	}
	return string(name.Value)
}

func (x Xorg) class(w xproto.Window) string {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		return unknown
	}
	i := bytes.IndexByte(class.Value, 0)
	if i == -1 || string(class.Value[:i]) == "" {
		return unknown
	}
	return string(class.Value[:i])
}

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

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

func (x Xorg) update(t Tracker) (prev *Track) {
	if win, ok := x.winName(); ok {
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Start = time.Now()
		prev = t[win]
	}
	return
}

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

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

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

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

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

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

	x.spy(x.root)

	return x
}

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

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

func (t Tracker) cleanup(d time.Duration) {
	for k, v := range t {
		if time.Since(v.Start) > d {
			log.Println("removing", k)
			delete(t, k)
		}
	}
}

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

func (t Tracker) store(fname string) {
	dump, err := os.Create(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	err = enc.Encode(t)
	if err != nil {
		log.Println(err)
	}
}

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

type Records []Record

type Record struct {
	Class string
	Name  string
	Spent time.Duration
	Odd   bool
}

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

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

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

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

func main() {
	tracks.load(file)
	go tracks.collect()
	go func() {
		for {
			tracks.cleanup(8 * time.Hour)
			tracks.store(file)
			time.Sleep(time.Minute)
		}
	}()
	log.Println("listen on", port)
	http.HandleFunc("/", indexHandler)
	http.ListenAndServe(port, nil)
}
<commit_msg>gofmt<commit_after>package main

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"sort"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
)

const (
	port    = ":8001"
	file    = "dump.gob"
	unknown = "unknown"
)

var (
	tracks = make(Tracker)
	tmpl   = template.Must(template.ParseFiles("index.html"))
	zzz    bool
)

type Tracker map[Window]*Track

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

type Window struct {
	Class string
	Name  string
}

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

func (t Track) String() string {
	return fmt.Sprint(t.Spent)
}

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

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

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

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

func (x Xorg) name(w xproto.Window) string {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		return unknown
	}
	if string(name.Value) == "" {
		name, err = x.property(w, x.nameAtom)
		if err != nil || string(name.Value) == "" {
			return unknown
		}
	}
	return string(name.Value)
}

func (x Xorg) class(w xproto.Window) string {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		return unknown
	}
	i := bytes.IndexByte(class.Value, 0)
	if i == -1 || string(class.Value[:i]) == "" {
		return unknown
	}
	return string(class.Value[:i])
}

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

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

func (x Xorg) update(t Tracker) (prev *Track) {
	if win, ok := x.winName(); ok {
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Start = time.Now()
		prev = t[win]
	}
	return
}

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

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

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

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

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

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

	x.spy(x.root)

	return x
}

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

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

func (t Tracker) cleanup(d time.Duration) {
	for k, v := range t {
		if time.Since(v.Start) > d {
			log.Println("removing", k)
			delete(t, k)
		}
	}
}

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

func (t Tracker) store(fname string) {
	dump, err := os.Create(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	err = enc.Encode(t)
	if err != nil {
		log.Println(err)
	}
}

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

type Records []Record

type Record struct {
	Class string
	Name  string
	Spent time.Duration
	Odd   bool
}

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

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

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

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

func main() {
	tracks.load(file)
	go tracks.collect()
	go func() {
		for {
			tracks.cleanup(8 * time.Hour)
			tracks.store(file)
			time.Sleep(time.Minute)
		}
	}()
	log.Println("listen on", port)
	http.HandleFunc("/", indexHandler)
	http.ListenAndServe(port, nil)
}
<|endoftext|>


<commit_before>package gonf
// # Gonf!!!
// Loads a configuration from a file into a map[string]string.

// Currently no support for loading the configuration options into
// their respective types, but that should all come in good time.
//
// Original consideration was to let you specify if you wanted Gonf to
// panic, print, or fatal if the file could not be read/loaded etc but
// that seemed excessive for this iddy biddy package. You're a big coder,
// I'm sure you can manage some proper error handling. :)
import (
  "io/ioutil"
  "strings"
)
// Loads a configuration from a file into a map[string]string.
func GetGonf(fname string) (map[string]string, error) {
  // Default separator for now...
  sep := "\n"

  // Get the file.
  conf, err := ioutil.ReadFile(fname)

  // ### _Be responsible and check for errors!_
  if err != nil {
    return nil, err
  }

  // ### Create somewhere to keep out results
  config := make(map[string]string)

  // ### Parse the file.
  lines := strings.Split(string(conf[:]), sep)

  // ### Analyse what we have for some config...
  // This is a simple process at the moment.
  for _, v := range lines {
    // ### Ignore commented lines
    if len(v) > 0 && v[:1] != "#" {
      // Break out our respective values.
      // __TODO__ Fix this so we can comment inline.
      line := strings.Split(v, "=")
      // Trim our final values so we don't waste our time with gritty whitespace
      // That stuff gets in your teeth, it's horrible...
      config[strings.TrimSpace(line[0])] = strings.TrimSpace(line[1])
      // A moose might also have bit my sister. She didn't whine about it on
      // film though like some cry baby projectionist I know.
    }
  }
  // Oh yeah, actually give the values back.. Heh. That was close.
  return config, err
}
<commit_msg>Error Handling and Delimiters<commit_after>package gonf
// # Gonf!!!
// Loads a configuration from a file into a map[string]string.

// Currently no support for loading the configuration options into
// their respective types, but that should all come in good time.
//
// Original consideration was to let you specify if you wanted Gonf to
// panic, print, or fatal if the file could not be read/loaded etc but
// that seemed excessive for this iddy biddy package. You're a big coder,
// I'm sure you can manage some proper error handling. :)
import (
  "errors"
  "io/ioutil"
  "strings"
)
// Loads a configuration from a file into a map[string]string.
func GetGonf(fname, delim string) (map[string]string, error) {
  // Get the file.
  conf, err := ioutil.ReadFile(fname)

  // ### _Be responsible and check for errors!_
  if err != nil {
    return nil, err
  }
  // ### Avoid zero length config file.
  if len(conf) <= 0 {
    return nil, errors.New("Zero length configuration file!")
  }

  // ### Create somewhere to keep out results
  config := make(map[string]string)

  // ### Parse the file.
  lines := strings.Split(string(conf[:]), delim)

  // ### Analyse what we have for some config...
  // This is a simple process at the moment.
  for _, v := range lines {
    // ### Ignore commented lines
    if len(v) > 0 && v[:1] != "#" {
      // Break out our respective values.
      // __TODO__ Fix this so we can comment inline.
      line := strings.Split(v, "=")
      // Trim our final values so we don't waste our time with gritty whitespace
      // That stuff gets in your teeth, it's horrible...
      config[strings.TrimSpace(line[0])] = strings.TrimSpace(line[1])
      // A moose might also have bit my sister. She didn't whine about it on
      // film though like some cry baby projectionist I know.
    }
  }
  // Oh yeah, actually give the values back.. Heh. That was close.
  return config, err
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package vm

import (
	"fmt"
	"regexp/syntax"
)

// checker holds data for a semantic checker
type checker struct {
	errors ErrorList

	// symtab contains the current scope search path
	symtab SymbolTable
}

// Check performs a semantic check of the ast node, and returns a boolean
// indicating OK; if ok is not true, then error is a list of errors found.
func Check(node node) error {
	c := &checker{}
	Walk(c, node)
	if len(c.errors) > 0 {
		return c.errors
	}
	return nil
}

func (c *checker) VisitBefore(node node) Visitor {
	switch n := node.(type) {

	case *stmtlistNode:
		c.symtab.EnterScope(nil)
		n.s = c.symtab.CurrentScope()

	case *caprefNode:
		if sym, ok := c.symtab.Lookup(n.name, CaprefSymbol); ok {
			n.sym = sym
		} else {
			c.errors.Add(n.Pos(), fmt.Sprintf("Capture group `$%s' was not defined by a regular expression in this or outer scopes.\n\tTry using `(?P<%s>...)' to name the capture group.", n.name, n.name))
			return nil
		}

	case *declNode:
		if sym, ok := c.symtab.Lookup(n.name, IDSymbol); ok {
			c.errors.Add(n.Pos(), fmt.Sprintf("Declaration of `%s' shadows the previous at %s", n.name, sym.loc))
			return nil
		}
		n.sym = c.symtab.Add(n.name, IDSymbol, &n.pos)

	case *defNode:
		if sym, ok := c.symtab.Lookup(n.name, DefSymbol); ok {
			c.errors.Add(n.Pos(), fmt.Sprintf("Definition of decorator `%s' shadows the previous at %s", n.name, sym.loc))
			return nil
		}
		n.sym = c.symtab.Add(n.name, DefSymbol, &n.pos)
		(*n.sym).binding = n

	case *decoNode:
		if sym, ok := c.symtab.Lookup(n.name, DefSymbol); ok {
			if sym.binding == nil {
				c.errors.Add(n.Pos(), fmt.Sprintf("Internal error: Decorator %q not bound to its definition.", n.name))
				return nil
			}
			n.def = sym.binding.(*defNode)
		} else {
			c.errors.Add(n.Pos(), fmt.Sprintf("Decorator `%s' not defined.\n\tTry adding a definition `def %s {}' earlier in the program.", n.name, n.name))
			return nil
		}

	case *idNode:
		if sym, ok := c.symtab.Lookup(n.name, IDSymbol); ok {
			n.sym = sym
		} else {
			c.errors.Add(n.Pos(), fmt.Sprintf("Identifier `%s' not declared.\n\tTry adding `counter %s' to the top of the program.", n.name, n.name))
			return nil
		}

	case *delNode:
		Walk(c, n.n)

	case *regexNode:
		if re, err := syntax.Parse(n.pattern, syntax.Perl); err != nil {
			c.errors.Add(n.Pos(), fmt.Sprintf(err.Error()))
			return nil
		} else {
			n.re_ast = re
			// We can reserve storage for these capturing groups, storing them in
			// the current scope, so that future CAPTUREGROUPs can retrieve their
			// value.  At parse time, we can warn about nonexistent names.
			for i := 1; i <= re.MaxCap(); i++ {
				sym := c.symtab.Add(fmt.Sprintf("%d", i),
					CaprefSymbol, n.Pos())
				sym.binding = n
				sym.addr = i - 1
			}
			for i, capref := range re.CapNames() {
				if capref != "" {
					sym := c.symtab.Add(capref, CaprefSymbol, n.Pos())
					sym.binding = n
					sym.addr = i
				}
			}
		}
	}
	return c
}

func (c *checker) VisitAfter(node node) {
	switch n := node.(type) {
	case *stmtlistNode:
		c.symtab.ExitScope()

	case *binaryExprNode:
		var rType Type
		Tl := n.lhs.Type()
		Tr := n.rhs.Type()
		switch n.op {
		//case DIV, MOD, MUL, MINUS, PLUS, POW:
		// Numeric
		// O ⊢ e1 : Tl, O ⊢ e2 : Tr
		// Tl <= Tr , Tr <= Tl
		// ⇒ O ⊢ e : lub(Tl, Tr)
		// case SHL, SHR, AND, OR, XOR, NOT:
		// 	//  integer
		// O ⊢ e1 :Int, O ⊢ e2 : Int
		// ⇒ O ⊢ e : Int
		// case LT, GT, LE, GE, EQ, NE:
		// 	// comparable
		// O ⊢ e1 : Tl, O ⊢ e2 : Tr
		// Tl <= Tr , Tr <= Tl
		// ⇒ O ⊢ e : lub(Tl, Tr)
		// case ASSIGN:
		// O ⊢ e1 : Tl, O ⊢ e2 : Tr
		// Tl <= Tr
		// ⇒ O ⊢ e : Tl
		default:
			if Tl != Tr {
				c.errors.Add(n.Pos(), fmt.Sprintf("Type mismatch between lhs (%s) and rhs (%s) for op %s", Tl, Tr, n.op))
			}
			rType = Tl
		}
		n.typ = rType

	case *unaryExprNode:
		switch n.op {
		case NOT:
			n.typ = Int
		default:
			n.typ = n.expr.Type()
		}

	}
}
<commit_msg>govet fix<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package vm

import (
	"fmt"
	"regexp/syntax"
)

// checker holds data for a semantic checker
type checker struct {
	errors ErrorList

	// symtab contains the current scope search path
	symtab SymbolTable
}

// Check performs a semantic check of the ast node, and returns a boolean
// indicating OK; if ok is not true, then error is a list of errors found.
func Check(node node) error {
	c := &checker{}
	Walk(c, node)
	if len(c.errors) > 0 {
		return c.errors
	}
	return nil
}

func (c *checker) VisitBefore(node node) Visitor {
	switch n := node.(type) {

	case *stmtlistNode:
		c.symtab.EnterScope(nil)
		n.s = c.symtab.CurrentScope()

	case *caprefNode:
		if sym, ok := c.symtab.Lookup(n.name, CaprefSymbol); ok {
			n.sym = sym
		} else {
			c.errors.Add(n.Pos(), fmt.Sprintf("Capture group `$%s' was not defined by a regular expression in this or outer scopes.\n\tTry using `(?P<%s>...)' to name the capture group.", n.name, n.name))
			return nil
		}

	case *declNode:
		if sym, ok := c.symtab.Lookup(n.name, IDSymbol); ok {
			c.errors.Add(n.Pos(), fmt.Sprintf("Declaration of `%s' shadows the previous at %s", n.name, sym.loc))
			return nil
		}
		n.sym = c.symtab.Add(n.name, IDSymbol, &n.pos)

	case *defNode:
		if sym, ok := c.symtab.Lookup(n.name, DefSymbol); ok {
			c.errors.Add(n.Pos(), fmt.Sprintf("Definition of decorator `%s' shadows the previous at %s", n.name, sym.loc))
			return nil
		}
		n.sym = c.symtab.Add(n.name, DefSymbol, &n.pos)
		(*n.sym).binding = n

	case *decoNode:
		if sym, ok := c.symtab.Lookup(n.name, DefSymbol); ok {
			if sym.binding == nil {
				c.errors.Add(n.Pos(), fmt.Sprintf("Internal error: Decorator %q not bound to its definition.", n.name))
				return nil
			}
			n.def = sym.binding.(*defNode)
		} else {
			c.errors.Add(n.Pos(), fmt.Sprintf("Decorator `%s' not defined.\n\tTry adding a definition `def %s {}' earlier in the program.", n.name, n.name))
			return nil
		}

	case *idNode:
		if sym, ok := c.symtab.Lookup(n.name, IDSymbol); ok {
			n.sym = sym
		} else {
			c.errors.Add(n.Pos(), fmt.Sprintf("Identifier `%s' not declared.\n\tTry adding `counter %s' to the top of the program.", n.name, n.name))
			return nil
		}

	case *delNode:
		Walk(c, n.n)

	case *regexNode:
		if re, err := syntax.Parse(n.pattern, syntax.Perl); err != nil {
			c.errors.Add(n.Pos(), fmt.Sprintf(err.Error()))
			return nil
		} else {
			n.re_ast = re
			// We can reserve storage for these capturing groups, storing them in
			// the current scope, so that future CAPTUREGROUPs can retrieve their
			// value.  At parse time, we can warn about nonexistent names.
			for i := 1; i <= re.MaxCap(); i++ {
				sym := c.symtab.Add(fmt.Sprintf("%d", i),
					CaprefSymbol, n.Pos())
				sym.binding = n
				sym.addr = i - 1
			}
			for i, capref := range re.CapNames() {
				if capref != "" {
					sym := c.symtab.Add(capref, CaprefSymbol, n.Pos())
					sym.binding = n
					sym.addr = i
				}
			}
		}
	}
	return c
}

func (c *checker) VisitAfter(node node) {
	switch n := node.(type) {
	case *stmtlistNode:
		c.symtab.ExitScope()

	case *binaryExprNode:
		var rType Type
		Tl := n.lhs.Type()
		Tr := n.rhs.Type()
		switch n.op {
		//case DIV, MOD, MUL, MINUS, PLUS, POW:
		// Numeric
		// O ⊢ e1 : Tl, O ⊢ e2 : Tr
		// Tl <= Tr , Tr <= Tl
		// ⇒ O ⊢ e : lub(Tl, Tr)
		// case SHL, SHR, AND, OR, XOR, NOT:
		// 	//  integer
		// O ⊢ e1 :Int, O ⊢ e2 : Int
		// ⇒ O ⊢ e : Int
		// case LT, GT, LE, GE, EQ, NE:
		// 	// comparable
		// O ⊢ e1 : Tl, O ⊢ e2 : Tr
		// Tl <= Tr , Tr <= Tl
		// ⇒ O ⊢ e : lub(Tl, Tr)
		// case ASSIGN:
		// O ⊢ e1 : Tl, O ⊢ e2 : Tr
		// Tl <= Tr
		// ⇒ O ⊢ e : Tl
		default:
			if Tl != Tr {
				c.errors.Add(n.Pos(), fmt.Sprintf("Type mismatch between lhs (%v) and rhs (%v) for op %d", Tl, Tr, n.op))
			}
			rType = Tl
		}
		n.typ = rType

	case *unaryExprNode:
		switch n.op {
		case NOT:
			n.typ = Int
		default:
			n.typ = n.expr.Type()
		}

	}
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"os"
	"fmt"
	"bytes"
	"strings"
	"io/ioutil"
	"go/token"
	"go/scanner"
	"go/parser"
	"go/printer"
	"github.com/droundy/goopt"
)

type gopp struct {
	defines map[string]interface{}
	output chan string
	StripComments, ignoring bool
	Prefix string
}

func (g *gopp) DefineValue(key string, value interface{}) {
	g.defines[key] = value
}

func (g *gopp) Define(key string) {
	g.DefineValue(key, nil)
}

func (g *gopp) Undefine(key string) {
	delete(g.defines, key)
}

func (g *gopp) Parse(r io.Reader) error {
	src, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	fset := token.NewFileSet()
	file := fset.AddFile("<stdin>", fset.Base(), len(src))

	s := scanner.Scanner{}
	s.Init(file, src, nil, scanner.ScanComments)

	go func() {
		for {
			_, tok, str := s.Scan()
			if len(str) == 0 { str = tok.String() }
			if tok == token.EOF {
				break
			}

			if tok != token.COMMENT && !g.ignoring {
				val, ok := g.defines[str]
				if ok {
					g.output <- val.(string)
				} else {
					g.output <- str
				}
				continue
			}

			if !strings.HasPrefix(str, g.Prefix) {
				if !g.StripComments && !g.ignoring {
					g.output <- str + "\n"
				}
				continue
			}

			// Trim the prefix from the start.
			strTrim := strings.Replace(str, g.Prefix, "", 1)
			lnr := strings.SplitN(strTrim, " ", 2)
			if len(lnr) < 1 {
				fmt.Println("Invalid gopp comment:", str)
				continue
			}

			cmd := strings.ToLower(lnr[0])

			//fmt.Printf("%q %q %s %v\n", strTrim, cmd, lnr, g.ignoring)

			if cmd == "ifdef" {
				if len(lnr) != 2 { continue }
				def := lnr[1]
				_, ok := g.defines[def]
				g.ignoring = !ok
			} else if cmd == "ifndef" {
				if len(lnr) != 2 { continue }
				def := lnr[1]
				_, ok := g.defines[def]
				g.ignoring = ok
			} else if cmd == "else" {
				g.ignoring = !g.ignoring
			} else if cmd == "endif"  && g.ignoring {
				g.ignoring = false
			} else if cmd == "define" && !g.ignoring {
				if len(lnr) != 2 { continue }
				lnr = strings.SplitN(lnr[1], " ", 2)
				g.DefineValue(lnr[0], lnr[1])
			} else if cmd == "undef" && !g.ignoring {
				if len(lnr) != 2 { continue }
				g.Undefine(lnr[1])
			}

		}
		close(g.output)
	}()
	return nil
}

func (g *gopp) Print(w io.Writer) {
	outbuf := new(bytes.Buffer)
	for tok := range g.output {
		fmt.Fprintf(outbuf, " %s", tok)
	}

	fset := token.NewFileSet()
	file, err := parser.ParseFile(fset, "<stdin>", outbuf, parser.ParseComments)
	if err != nil {
		panic(err)
	}

	printer.Fprint(os.Stdout, fset, file)
}

func NewGopp(strip bool) *gopp {
	return &gopp{
		defines: make(map[string]interface{}),
		output: make(chan string),
		StripComments: strip,
		ignoring: false,
		Prefix: "//gopp:",
	}
}

///////////////////
var defined = goopt.Strings([]string{"-D"}, "NAME[=defn]", "Predefine NAME as a macro. Unless given, default macro value is 1.")
var undefined = goopt.Strings([]string{"-U"}, "NAME", "Cancel any previous/builtin definition of macro NAME.")
var stripComments = goopt.Flag([]string{"-c", "--comments"}, []string{"-C", "--no-comments"}, "Don't eat comments.", "Eat any comments that are found.")
var outputFile = goopt.String([]string{"-o", "--outfile"}, "-", "Output file (default: <stdout>)")

func main() {
	goopt.Description = func() string {
		return "What have I done?!"
	}
	goopt.Version = "0.1"
	goopt.Summary = "Horrifying C-like Go preprocessor."
	goopt.Parse(nil)

	if len(goopt.Args) < 1 {
		fmt.Println("Supply a file to process!")
		return
	}

	file, err := os.Open(goopt.Args[0])
	if err != nil {
		panic(err)
	}

	g := NewGopp(true)
	g.StripComments = !*stripComments
	g.DefineValue("_GOPP", goopt.Version)
	for _, def := range *defined {
		if strings.Contains(def, "=") {
			lnr := strings.SplitN(def, "=", 2)
			g.DefineValue(lnr[0], lnr[1])
		} else {
			g.DefineValue(def, 1)
		}
	}
	for _, udef := range *undefined {
		g.Undefine(udef)
	}

	if err := g.Parse(file); err != nil {
		fmt.Println(err)
		return
	}

	var out io.Writer
	if *outputFile == "-" {
		out = os.Stdout
	} else {
		out, err = os.Open(*outputFile)
		if err != nil {
			panic(err)
		}
	}
	g.Print(out)
}
<commit_msg>General code tidy up, go fmt, etc.<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/droundy/goopt"
	"go/parser"
	"go/printer"
	"go/scanner"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"strings"
)

type Token struct {
	Position token.Pos
	Token    token.Token
	String   string
}

type gopp struct {
	defines                 map[string]interface{}
	output                  chan Token
	StripComments, ignoring bool
	Prefix                  string
}

func (g *gopp) DefineValue(key string, value interface{}) {
	g.defines[key] = value
}

func (g *gopp) Define(key string) {
	g.DefineValue(key, nil)
}

func (g *gopp) Undefine(key string) {
	delete(g.defines, key)
}

func (g *gopp) Parse(r io.Reader) error {
	src, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	fset := token.NewFileSet()
	file := fset.AddFile("<stdin>", fset.Base(), len(src))

	s := scanner.Scanner{}
	s.Init(file, src, nil, scanner.ScanComments)

	go func() {
		for {
			pos, tok_, str := s.Scan()
			if len(str) == 0 {
				str = tok_.String()
			}
			tok := Token{pos, tok_, str}

			if tok.Token == token.EOF {
				break
			}

			if tok.Token != token.COMMENT && !g.ignoring {
				val, ok := g.defines[tok.String]
				if ok {
					tok.String = val.(string)
				}
				g.output <- tok
				continue
			}

			if !strings.HasPrefix(str, g.Prefix) {
				if !g.StripComments && !g.ignoring {
					tok.String += "\n"
					g.output <- tok
				}
				continue
			}

			// Trim the prefix from the start.
			strTrim := strings.Replace(tok.String, g.Prefix, "", 1)
			lnr := strings.SplitN(strTrim, " ", 2)
			if len(lnr) < 1 {
				fmt.Println("Invalid gopp comment:", str)
				continue
			}

			cmd := strings.ToLower(lnr[0])

			//fmt.Printf("%q %q %s %v\n", strTrim, cmd, lnr, g.ignoring)

			if cmd == "ifdef" {
				if len(lnr) != 2 {
					continue
				}
				def := lnr[1]
				_, ok := g.defines[def]
				g.ignoring = !ok
			} else if cmd == "ifndef" {
				if len(lnr) != 2 {
					continue
				}
				def := lnr[1]
				_, ok := g.defines[def]
				g.ignoring = ok
			} else if cmd == "else" {
				g.ignoring = !g.ignoring
			} else if cmd == "endif" && g.ignoring {
				g.ignoring = false
			} else if cmd == "define" && !g.ignoring {
				if len(lnr) != 2 {
					continue
				}
				lnr = strings.SplitN(lnr[1], " ", 2)
				g.DefineValue(lnr[0], lnr[1])
			} else if cmd == "undef" && !g.ignoring {
				if len(lnr) != 2 {
					continue
				}
				g.Undefine(lnr[1])
			}
		}
		close(g.output)
	}()
	return nil
}

func (g *gopp) Print(w io.Writer) {
	outbuf := new(bytes.Buffer)
	for tok := range g.output {
		fmt.Fprintf(outbuf, " %s", tok.String)
	}

	fset := token.NewFileSet()
	file, err := parser.ParseFile(fset, "<stdin>", outbuf, parser.ParseComments)
	if err != nil {
		panic(err)
	}

	printer.Fprint(os.Stdout, fset, file)
}

// This resets the bits of the gopp which you should really redefine
// each time you want to parse a new file.
func (g *gopp) Reset() {
	g.defines = make(map[string]interface{})
	g.output = make(chan Token)
	g.ignoring = false
}

func NewGopp(strip bool) *gopp {
	return &gopp{
		defines:       make(map[string]interface{}),
		output:        make(chan Token),
		StripComments: strip,
		ignoring:      false,
		Prefix:        "//gopp:",
	}
}

///////////////////

func IsDirectory(path string) (bool, error) {
	dir, err := os.Stat(path)
	if err == nil {
		return dir != nil && dir.IsDir(), nil
	}
	if os.IsNotExist(err) {
		return false, err
	}
	return false, err
}

func build() error {
	defined := goopt.Strings(
		[]string{"-D"},
		"NAME[=defn]",
		"Predefine NAME as a macro. Unless given, default macro value is 1.")
	undefined := goopt.Strings([]string{"-U"},
		"NAME",
		"Cancel any previous/builtin definition of macro NAME.")
	stripComments := goopt.Flag([]string{"-c", "--comments"},
		[]string{"-C", "--no-comments"},
		"Don't eat comments.",
		"Eat any comments that are found.")
	outputTo := goopt.String([]string{"-o"},
		"-",
		"Output (default: <stdout> for files, `_build` for directories)")
	/*panicErrors := goopt.Flag([]string{"-p", "--panic"},
	[]string{},
	"panic() on errors, rather than just logging them.",
	"")*/

	// Because you can't supply an arg list to goopt..
	if len(os.Args) > 2 {
		os.Args = append([]string{os.Args[0]}, os.Args[2:]...)
	} else {
		os.Args = os.Args[:1]
	}

	goopt.Description = func() string {
		return "What have I done?!"
	}
	goopt.Version = "0.2"
	goopt.Summary = "Horrifying C-like Go preprocessor."
	goopt.Parse(nil)

	if len(goopt.Args) < 1 {
		fmt.Println("Supply a file to process! Here's --help:")
		fmt.Print(goopt.Help())
		return nil
	}

	g := NewGopp(true)
	g.StripComments = !*stripComments
	g.DefineValue("_GOPP", goopt.Version)
	for _, def := range *defined {
		if strings.Contains(def, "=") {
			lnr := strings.SplitN(def, "=", 2)
			g.DefineValue(lnr[0], lnr[1])
		} else {
			g.DefineValue(def, 1)
		}
	}

	for _, udef := range *undefined {
		g.Undefine(udef)
	}

	isDir, err := IsDirectory(goopt.Args[0])
	if err != nil {
		return err
	}

	if isDir {
		// TODO: Handle walking path and parsing.
	} else {
		file, err := os.Open(goopt.Args[0])
		if err != nil {
			return err
		}

		if err := g.Parse(file); err != nil {
			return err
		}

		var out io.Writer
		if *outputTo == "-" {
			out = os.Stdout
		} else {
			out, err = os.Open(*outputTo)
			if err != nil {
				return err
			}
		}
		g.Print(out)
	}
	return nil
}

func clean() error {
	return nil
}

var commands = map[string]func() error{
	"build": build,
	"clean": clean,
}

func main() {
	printWrong := func() {
		fmt.Println("Wrong! Try one of these: (you can append --help)")
		for c := range commands {
			fmt.Printf("  %s %s\n", os.Args[0], c)
		}
	}

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

	f, ok := commands[os.Args[1]]
	if !ok {
		printWrong()
		return
	}
	if err := f(); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
)

const (
	GitDir    = ".git"
	GitIgnore = ".gitignore"
	GostFile  = ".gost"
	SetEnv    = "setenv.bash"
)

var (
	GOPATH string

	dir = ""
)

func main() {
	log.SetFlags(0)

	if len(os.Args) < 2 {
		failAndUsage("Please specify a command")
	}
	cmd := strings.ToLower(os.Args[1])
	switch cmd {
	case "init":
		doinit()
	case "get":
		get()
	case "push":
		push()
	default:
		failAndUsage("Unknown command: %s", cmd)
	}
}

// doinit does the initialization of a gost repo
func doinit() {
	var err error
	dir, err = os.Getwd()
	if err != nil {
		log.Fatalf("Unable to determine current directory: %s", err)
	}

	if exists(GitDir) {
		log.Fatalf("%s already contains a .git folder, can't initialize gost", dir)
	}

	// Initialize a git repo
	run("git", "init")

	// Write initial files
	writeAndCommit(GitIgnore, DefaultGitIgnore)
	writeAndCommit(GostFile, DefaultGostFile)
	writeAndCommit(SetEnv, SetEnvFile)

	// Done
	log.Print("Initialized git repo, please update your GOPATH and PATH. setenv.bash does this for you.")
	log.Print("  source ./setenv.bash")
}

// get is like go get except that it replaces github packages with subtrees,
// adds non-github packages to git as source.
func get() {
	requireGostGOPATH()

	flags := flag.NewFlagSet("get", flag.ExitOnError)
	update := flags.Bool("u", false, "update existing from remote")
	flags.Parse(os.Args[2:])

	pkg, branch := pkgAndBranch(flags.Args())

	fetchSubtree(pkg, branch, *update, map[string]bool{})
	removeGitFolders()

	run("git", "add", "src")
	run("git", "commit", "-m", fmt.Sprintf("[gost] Added %s and its dependencies", pkg))

	run("go", "install", pkg)
}

// push pushes the changes for a given repo to git
func push() {
	requireGostGOPATH()

	flags := flag.NewFlagSet("push", flag.ExitOnError)
	updateFirst := flags.Bool("u", false, "update existing from remote before pushing")
	flags.Parse(os.Args[2:])

	pkg, branch := pkgAndBranch(flags.Args())
	parts := strings.Split(strings.Trim(pkg, "/"), "/")
	if len(parts) > 2 {
		log.Printf("Pushing single package %s", pkg)
		err := doPush(pkg, branch, *updateFirst)
		if err != nil {
			log.Fatalf("Unable to push package %s: %s", pkg, err)
		}
	} else {
		log.Printf("Pushing all subpackages of %s", pkg)
		entries, err := ioutil.ReadDir(path.Join(GOPATH, "src", pkg))
		if err != nil {
			log.Fatalf("Unable to list subpackages of %s", pkg)
		}
		for _, entry := range entries {
			if entry.IsDir() {
				_, dir := path.Split(entry.Name())
				fullPkg := path.Join(pkg, dir)
				err := doPush(fullPkg, branch, *updateFirst)
				if err != nil {
					log.Printf("Unable to push package %s: %s", fullPkg, err)
				}
			}
		}
	}
}

func doPush(pkg string, branch string, updateFirst bool) error {
	pkgRoot := rootOf(pkg)
	srcPath := path.Join("src", pkgRoot)
	ghPath := repopath(pkgRoot)
	if updateFirst {
		prefix := path.Join("src", pkgRoot)
		log.Printf("Updating %s before pushing", pkgRoot)
		_, err := doRun("git", "subtree", "pull", "--squash",
			"--prefix", prefix, ghPath, branch)
		if err != nil {
			return err
		}
	}
	_, err := doRun("git", "subtree", "push", "--prefix", srcPath, ghPath, branch)
	return err
}

func pkgAndBranch(args []string) (string, string) {
	if len(args) < 2 {
		log.Fatal("Please specify a package and a branch")
	}

	pkg := args[0]
	if !isValidRepo(pkg) {
		log.Fatal("gost only supports pushing packages to github.com or bitbucket.org")
	}

	branch := args[1]
	log.Printf("Using branch %s", branch)

	return pkg, branch
}

func fetchSubtree(pkg string, branch string, update bool, alreadyFetched map[string]bool) {
	pkgRoot := rootOf(pkg)
	if alreadyFetched[pkgRoot] {
		return
	}

	prefix := path.Join("src", pkgRoot)
	if exists(prefix) {
		if update {
			run("git", "subtree", "pull", "--squash",
				"--prefix", prefix,
				repopath(pkgRoot),
				branch)
		} else {
			log.Printf("%s already exists, declining to add as subtree", prefix)
		}
	} else {
		run("git", "subtree", "add", "--squash",
			"--prefix", prefix,
			repopath(pkgRoot),
			branch)
	}
	alreadyFetched[pkgRoot] = true
	fetchDeps(pkg, "master", update, alreadyFetched)
}

func fetchDeps(pkg string, branch string, update bool, alreadyFetched map[string]bool) {
	depsString := run("go", "list", "-f", "{{range .Deps}}{{.}} {{end}} {{range .TestImports}}{{.}} {{end}}", pkg)
	deps := parseDeps(depsString)

	nonGithubDeps := []string{}
	for _, dep := range deps {
		dep = strings.TrimSpace(dep)
		if dep == "" || dep == "." {
			continue
		}
		if isValidRepo(dep) {
			fetchSubtree(dep, branch, update, alreadyFetched)
		} else {
			nonGithubDeps = append(nonGithubDeps, dep)
		}
	}

	for _, dep := range nonGithubDeps {
		goGet(dep, update, alreadyFetched)
	}
}

func goGet(pkg string, update bool, alreadyFetched map[string]bool) {
	isRelative := strings.HasPrefix(pkg, ".")
	if alreadyFetched[pkg] || isRelative {
		return
	}
	run("go", "get", pkg)
	alreadyFetched[pkg] = true
}

func writeAndCommit(file string, content string) {
	if exists(file) {
		log.Fatalf("%s already contains %s, can't initialize gost", dir, file)
	}

	err := ioutil.WriteFile(file, []byte(content), 0644)
	if err != nil {
		log.Fatalf("Unable to write %s: %s", file, err)
	}

	// Write and commit
	run("git", "add", file)
	run("git", "commit", file, "-m", "[gost] Initialized "+file)

	log.Printf("Initialized and commited %s", file)
}

func requireGostGOPATH() {
	GOPATH = os.Getenv("GOPATH")
	if GOPATH == "" {
		log.Fatal("Please set your GOPATH")
	}
	requireFileInGOPATH(GostFile)
	requireFileInGOPATH(GitDir)
}

func requireFileInGOPATH(file string) {
	if !exists(path.Join(GOPATH, file)) {
		log.Fatalf("Unable to find '%s' in the GOPATH '%s', please make sure you've run gost init within your GOPATH.", file, GOPATH)
	}
}

func exists(file string) bool {
	_, err := os.Stat(file)
	return err == nil
}

func isValidRepo(pkg string) bool {
	return ((strings.Index(pkg, "github.com/") == 0) || (strings.Index(pkg, "bitbucket.org/") == 0))
}

// rootOf extracts the path up to the github repo
func rootOf(pkg string) string {
	pkgParts := strings.Split(pkg, "/")
	return path.Join(pkgParts[:3]...)
}

func repopath(pkg string) string {
	return fmt.Sprintf("https://%s.git", pkg)
}

func parseDeps(depsString string) []string {
	depsString = strings.Replace(depsString, "[", "", -1)
	depsString = strings.Replace(depsString, "]", "", -1)
	return strings.Split(depsString, " ")
}

// removeGitFolders removes all .git folders under the src tree so that any git
// repos that didn't come from GitHub (e.g. gopkg.in) won't be treated as
// submodules.
func removeGitFolders() {
	filepath.Walk(path.Join(GOPATH, "src"), func(dir string, info os.FileInfo, oldErr error) error {
		_, file := path.Split(dir)
		if file == GitDir {
			log.Printf("Removing git folder at %s", dir)
			err := os.RemoveAll(dir)
			if err != nil {
				log.Printf("WARNING - unable to remove git folder at %s", err)
			}
		}
		return nil
	})
}

func run(prg string, args ...string) string {
	out, err := doRun(prg, args...)
	if err != nil {
		log.Fatal(err)
	}
	return out
}

func doRun(prg string, args ...string) (string, error) {
	cmd := exec.Command(prg, args...)
	log.Printf("Running %s %s", prg, strings.Join(args, " "))
	cmd.Dir = GOPATH
	out, err := cmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("%s says %s", prg, string(out))
	}
	return string(out), nil
}

func failAndUsage(msg string, args ...interface{}) {
	log.Printf(msg, args...)
	log.Fatal(`
Commands:
	init - initialize a git repo in the current directory and set GOPATH to here
	get  - like go get, except that all github dependencies are imported as subtrees
	push - push the named package back upstream to its source repo into the specified branch
`)
}

const DefaultGitIgnore = `pkg
bin
.DS_Store
*.cov
`

const DefaultGostFile = `a gost lives here`

const SetEnvFile = `#!/bin/bash

DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
export GOPATH=$DIR
export PATH=$GOPATH/bin:$PATH
`
<commit_msg>Updated bitbucket support to use git protocol scheme<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
)

const (
	GitDir    = ".git"
	GitIgnore = ".gitignore"
	GostFile  = ".gost"
	SetEnv    = "setenv.bash"
)

var (
	GOPATH string

	dir = ""
)

func main() {
	log.SetFlags(0)

	if len(os.Args) < 2 {
		failAndUsage("Please specify a command")
	}
	cmd := strings.ToLower(os.Args[1])
	switch cmd {
	case "init":
		doinit()
	case "get":
		get()
	case "push":
		push()
	default:
		failAndUsage("Unknown command: %s", cmd)
	}
}

// doinit does the initialization of a gost repo
func doinit() {
	var err error
	dir, err = os.Getwd()
	if err != nil {
		log.Fatalf("Unable to determine current directory: %s", err)
	}

	if exists(GitDir) {
		log.Fatalf("%s already contains a .git folder, can't initialize gost", dir)
	}

	// Initialize a git repo
	run("git", "init")

	// Write initial files
	writeAndCommit(GitIgnore, DefaultGitIgnore)
	writeAndCommit(GostFile, DefaultGostFile)
	writeAndCommit(SetEnv, SetEnvFile)

	// Done
	log.Print("Initialized git repo, please update your GOPATH and PATH. setenv.bash does this for you.")
	log.Print("  source ./setenv.bash")
}

// get is like go get except that it replaces github packages with subtrees,
// adds non-github packages to git as source.
func get() {
	requireGostGOPATH()

	flags := flag.NewFlagSet("get", flag.ExitOnError)
	update := flags.Bool("u", false, "update existing from remote")
	flags.Parse(os.Args[2:])

	pkg, branch := pkgAndBranch(flags.Args())

	fetchSubtree(pkg, branch, *update, map[string]bool{})
	removeGitFolders()

	run("git", "add", "src")
	run("git", "commit", "-m", fmt.Sprintf("[gost] Added %s and its dependencies", pkg))

	run("go", "install", pkg)
}

// push pushes the changes for a given repo to git
func push() {
	requireGostGOPATH()

	flags := flag.NewFlagSet("push", flag.ExitOnError)
	updateFirst := flags.Bool("u", false, "update existing from remote before pushing")
	flags.Parse(os.Args[2:])

	pkg, branch := pkgAndBranch(flags.Args())
	parts := strings.Split(strings.Trim(pkg, "/"), "/")
	if len(parts) > 2 {
		log.Printf("Pushing single package %s", pkg)
		err := doPush(pkg, branch, *updateFirst)
		if err != nil {
			log.Fatalf("Unable to push package %s: %s", pkg, err)
		}
	} else {
		log.Printf("Pushing all subpackages of %s", pkg)
		entries, err := ioutil.ReadDir(path.Join(GOPATH, "src", pkg))
		if err != nil {
			log.Fatalf("Unable to list subpackages of %s", pkg)
		}
		for _, entry := range entries {
			if entry.IsDir() {
				_, dir := path.Split(entry.Name())
				fullPkg := path.Join(pkg, dir)
				err := doPush(fullPkg, branch, *updateFirst)
				if err != nil {
					log.Printf("Unable to push package %s: %s", fullPkg, err)
				}
			}
		}
	}
}

func doPush(pkg string, branch string, updateFirst bool) error {
	pkgRoot := rootOf(pkg)
	srcPath := path.Join("src", pkgRoot)
	rpath := repopath(pkgRoot)
	if updateFirst {
		prefix := path.Join("src", pkgRoot)
		log.Printf("Updating %s before pushing", pkgRoot)
		_, err := doRun("git", "subtree", "pull", "--squash",
			"--prefix", prefix, rpath, branch)
		if err != nil {
			return err
		}
	}
	_, err := doRun("git", "subtree", "push", "--prefix", srcPath, rpath, branch)
	return err
}

func pkgAndBranch(args []string) (string, string) {
	if len(args) < 2 {
		log.Fatal("Please specify a package and a branch")
	}

	pkg := args[0]
	if !supportsSubtrees(pkg) {
		log.Fatal("gost only supports pushing packages to github.com or bitbucket.org")
	}

	branch := args[1]
	log.Printf("Using branch %s", branch)

	return pkg, branch
}

func fetchSubtree(pkg string, branch string, update bool, alreadyFetched map[string]bool) {
	pkgRoot := rootOf(pkg)
	if alreadyFetched[pkgRoot] {
		return
	}

	prefix := path.Join("src", pkgRoot)
	if exists(prefix) {
		if update {
			run("git", "subtree", "pull", "--squash",
				"--prefix", prefix,
				repopath(pkgRoot),
				branch)
		} else {
			log.Printf("%s already exists, declining to add as subtree", prefix)
		}
	} else {
		run("git", "subtree", "add", "--squash",
			"--prefix", prefix,
			repopath(pkgRoot),
			branch)
	}
	alreadyFetched[pkgRoot] = true
	fetchDeps(pkg, "master", update, alreadyFetched)
}

func fetchDeps(pkg string, branch string, update bool, alreadyFetched map[string]bool) {
	depsString := run("go", "list", "-f", "{{range .Deps}}{{.}} {{end}} {{range .TestImports}}{{.}} {{end}}", pkg)
	deps := parseDeps(depsString)

	nonGithubDeps := []string{}
	for _, dep := range deps {
		dep = strings.TrimSpace(dep)
		if dep == "" || dep == "." {
			continue
		}
		if supportsSubtrees(dep) {
			fetchSubtree(dep, branch, update, alreadyFetched)
		} else {
			nonGithubDeps = append(nonGithubDeps, dep)
		}
	}

	for _, dep := range nonGithubDeps {
		goGet(dep, update, alreadyFetched)
	}
}

func goGet(pkg string, update bool, alreadyFetched map[string]bool) {
	isRelative := strings.HasPrefix(pkg, ".")
	if alreadyFetched[pkg] || isRelative {
		return
	}
	run("go", "get", pkg)
	alreadyFetched[pkg] = true
}

func writeAndCommit(file string, content string) {
	if exists(file) {
		log.Fatalf("%s already contains %s, can't initialize gost", dir, file)
	}

	err := ioutil.WriteFile(file, []byte(content), 0644)
	if err != nil {
		log.Fatalf("Unable to write %s: %s", file, err)
	}

	// Write and commit
	run("git", "add", file)
	run("git", "commit", file, "-m", "[gost] Initialized "+file)

	log.Printf("Initialized and commited %s", file)
}

func requireGostGOPATH() {
	GOPATH = os.Getenv("GOPATH")
	if GOPATH == "" {
		log.Fatal("Please set your GOPATH")
	}
	requireFileInGOPATH(GostFile)
	requireFileInGOPATH(GitDir)
}

func requireFileInGOPATH(file string) {
	if !exists(path.Join(GOPATH, file)) {
		log.Fatalf("Unable to find '%s' in the GOPATH '%s', please make sure you've run gost init within your GOPATH.", file, GOPATH)
	}
}

func exists(file string) bool {
	_, err := os.Stat(file)
	return err == nil
}

func supportsSubtrees(pkg string) bool {
	return isGitHub(pkg) || isBitBucket(pkg)
}

// rootOf extracts the path up to the github repo
func rootOf(pkg string) string {
	pkgParts := strings.Split(pkg, "/")
	return path.Join(pkgParts[:3]...)
}

func repopath(pkg string) string {
	if isBitBucket(pkg) {
		return fmt.Sprintf("git@bitbucket.org:%s.git", pkg[14:])
	}
	return fmt.Sprintf("https://%s.git", pkg)
}

func isGitHub(pkg string) bool {
	return strings.Index(pkg, "github.com/") == 0
}

func isBitBucket(pkg string) bool {
	return strings.Index(pkg, "bitbucket.org/") == 0
}

func parseDeps(depsString string) []string {
	depsString = strings.Replace(depsString, "[", "", -1)
	depsString = strings.Replace(depsString, "]", "", -1)
	return strings.Split(depsString, " ")
}

// removeGitFolders removes all .git folders under the src tree so that any git
// repos that didn't come from GitHub (e.g. gopkg.in) won't be treated as
// submodules.
func removeGitFolders() {
	filepath.Walk(path.Join(GOPATH, "src"), func(dir string, info os.FileInfo, oldErr error) error {
		_, file := path.Split(dir)
		if file == GitDir {
			log.Printf("Removing git folder at %s", dir)
			err := os.RemoveAll(dir)
			if err != nil {
				log.Printf("WARNING - unable to remove git folder at %s", err)
			}
		}
		return nil
	})
}

func run(prg string, args ...string) string {
	out, err := doRun(prg, args...)
	if err != nil {
		log.Fatal(err)
	}
	return out
}

func doRun(prg string, args ...string) (string, error) {
	cmd := exec.Command(prg, args...)
	log.Printf("Running %s %s", prg, strings.Join(args, " "))
	cmd.Dir = GOPATH
	out, err := cmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("%s says %s", prg, string(out))
	}
	return string(out), nil
}

func failAndUsage(msg string, args ...interface{}) {
	log.Printf(msg, args...)
	log.Fatal(`
Commands:
	init - initialize a git repo in the current directory and set GOPATH to here
	get  - like go get, except that all github dependencies are imported as subtrees
	push - push the named package back upstream to its source repo into the specified branch
`)
}

const DefaultGitIgnore = `pkg
bin
.DS_Store
*.cov
`

const DefaultGostFile = `a gost lives here`

const SetEnvFile = `#!/bin/bash

DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
export GOPATH=$DIR
export PATH=$GOPATH/bin:$PATH
`
<|endoftext|>


<commit_before>/*
   Copyright The containerd Authors.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package integration

import (
	"fmt"
	"io/ioutil"
	"testing"

	cri "github.com/containerd/containerd/integration/cri-api/pkg/apis"
	"github.com/pelletier/go-toml"
	"github.com/sirupsen/logrus"
	"github.com/stretchr/testify/require"
	runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
)

// ImageList holds public image references
type ImageList struct {
	Alpine           string
	BusyBox          string
	Pause            string
	ResourceConsumer string
	VolumeCopyUp     string
	VolumeOwnership  string
}

var (
	imageService cri.ImageManagerService
	imageMap     map[int]string
	imageList    ImageList
	pauseImage   string // This is the same with default sandbox image
)

func initImages(imageListFile string) {
	imageList = ImageList{
		Alpine:           "docker.io/library/alpine:latest",
		BusyBox:          "docker.io/library/busybox:latest",
		Pause:            "k8s.gcr.io/pause:3.6",
		ResourceConsumer: "k8s.gcr.io/e2e-test-images/resource-consumer:1.9",
		VolumeCopyUp:     "gcr.io/k8s-cri-containerd/volume-copy-up:2.0",
		VolumeOwnership:  "gcr.io/k8s-cri-containerd/volume-ownership:2.0",
	}

	if imageListFile != "" {
		fileContent, err := ioutil.ReadFile(imageListFile)
		if err != nil {
			panic(fmt.Errorf("Error reading '%v' file contents: %v", imageList, err))
		}

		err = toml.Unmarshal(fileContent, &imageList)
		if err != nil {
			panic(fmt.Errorf("Error unmarshalling '%v' TOML file: %v", imageList, err))
		}
	}

	logrus.Infof("Using the following image list: %+v", imageList)

	imageMap = initImageMap(imageList)
	pauseImage = GetImage(Pause)
}

const (
	// None is to be used for unset/default images
	None = iota
	// Alpine image
	Alpine
	// BusyBox image
	BusyBox
	// Pause image
	Pause
	// ResourceConsumer image
	ResourceConsumer
	// VolumeCopyUp image
	VolumeCopyUp
	// VolumeOwnership image
	VolumeOwnership
)

func initImageMap(imageList ImageList) map[int]string {
	images := map[int]string{}
	images[Alpine] = imageList.Alpine
	images[BusyBox] = imageList.BusyBox
	images[Pause] = imageList.Pause
	images[ResourceConsumer] = imageList.ResourceConsumer
	images[VolumeCopyUp] = imageList.VolumeCopyUp
	images[VolumeOwnership] = imageList.VolumeOwnership
	return images
}

// GetImage returns the fully qualified URI to an image (including version)
func GetImage(image int) string {
	return imageMap[image]
}

// EnsureImageExists pulls the given image, ensures that no error was encountered
// while pulling it.
func EnsureImageExists(t *testing.T, imageName string) string {
	img, err := imageService.ImageStatus(&runtime.ImageSpec{Image: imageName})
	require.NoError(t, err)
	if img != nil {
		t.Logf("Image %q already exists, not pulling.", imageName)
		return img.Id
	}

	t.Logf("Pull test image %q", imageName)
	imgID, err := imageService.PullImage(&runtime.ImageSpec{Image: imageName}, nil, nil)
	require.NoError(t, err)

	return imgID
}
<commit_msg>fix error string format<commit_after>/*
   Copyright The containerd Authors.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package integration

import (
	"fmt"
	"io/ioutil"
	"testing"

	cri "github.com/containerd/containerd/integration/cri-api/pkg/apis"
	"github.com/pelletier/go-toml"
	"github.com/sirupsen/logrus"
	"github.com/stretchr/testify/require"
	runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
)

// ImageList holds public image references
type ImageList struct {
	Alpine           string
	BusyBox          string
	Pause            string
	ResourceConsumer string
	VolumeCopyUp     string
	VolumeOwnership  string
}

var (
	imageService cri.ImageManagerService
	imageMap     map[int]string
	imageList    ImageList
	pauseImage   string // This is the same with default sandbox image
)

func initImages(imageListFile string) {
	imageList = ImageList{
		Alpine:           "docker.io/library/alpine:latest",
		BusyBox:          "docker.io/library/busybox:latest",
		Pause:            "k8s.gcr.io/pause:3.6",
		ResourceConsumer: "k8s.gcr.io/e2e-test-images/resource-consumer:1.9",
		VolumeCopyUp:     "gcr.io/k8s-cri-containerd/volume-copy-up:2.0",
		VolumeOwnership:  "gcr.io/k8s-cri-containerd/volume-ownership:2.0",
	}

	if imageListFile != "" {
		fileContent, err := ioutil.ReadFile(imageListFile)
		if err != nil {
			panic(fmt.Errorf("error reading '%v' file contents: %v", imageList, err))
		}

		err = toml.Unmarshal(fileContent, &imageList)
		if err != nil {
			panic(fmt.Errorf("error unmarshalling '%v' TOML file: %v", imageList, err))
		}
	}

	logrus.Infof("Using the following image list: %+v", imageList)

	imageMap = initImageMap(imageList)
	pauseImage = GetImage(Pause)
}

const (
	// None is to be used for unset/default images
	None = iota
	// Alpine image
	Alpine
	// BusyBox image
	BusyBox
	// Pause image
	Pause
	// ResourceConsumer image
	ResourceConsumer
	// VolumeCopyUp image
	VolumeCopyUp
	// VolumeOwnership image
	VolumeOwnership
)

func initImageMap(imageList ImageList) map[int]string {
	images := map[int]string{}
	images[Alpine] = imageList.Alpine
	images[BusyBox] = imageList.BusyBox
	images[Pause] = imageList.Pause
	images[ResourceConsumer] = imageList.ResourceConsumer
	images[VolumeCopyUp] = imageList.VolumeCopyUp
	images[VolumeOwnership] = imageList.VolumeOwnership
	return images
}

// GetImage returns the fully qualified URI to an image (including version)
func GetImage(image int) string {
	return imageMap[image]
}

// EnsureImageExists pulls the given image, ensures that no error was encountered
// while pulling it.
func EnsureImageExists(t *testing.T, imageName string) string {
	img, err := imageService.ImageStatus(&runtime.ImageSpec{Image: imageName})
	require.NoError(t, err)
	if img != nil {
		t.Logf("Image %q already exists, not pulling.", imageName)
		return img.Id
	}

	t.Logf("Pull test image %q", imageName)
	imgID, err := imageService.PullImage(&runtime.ImageSpec{Image: imageName}, nil, nil)
	require.NoError(t, err)

	return imgID
}
<|endoftext|>


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

package http

import (
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"os"
	"time"

	"github.com/cayleygraph/cayley/clog"
	"github.com/julienschmidt/httprouter"

	"github.com/cayleygraph/cayley/graph"
	"github.com/cayleygraph/cayley/internal/gephi"
	"github.com/cayleygraph/cayley/server/http"
)

var AssetsPath string
var defaultAssetPaths = []string{
  ".", "..", "./assets",
  "/usr/local/share/cayley/assets",
  os.ExpandEnv("$GOPATH/src/github.com/cayleygraph/cayley"),
}
var assetsDirs = []string{"templates", "static", "docs"}

func hasAssets(path string) bool {
	if len(assetsDirs) == 0 {
		return false
	}
	for _, dir := range assetsDirs {
		if _, err := os.Stat(fmt.Sprint(path, "/", dir)); os.IsNotExist(err) {
			return false
		}
	}
	return true
}

func findAssetsPath() (string, error) {
	if AssetsPath != "" {
		if hasAssets(AssetsPath) {
			return AssetsPath, nil
		}
		return "", fmt.Errorf("cannot find assets at %q", AssetsPath)
	}
	for _, path := range defaultAssetPaths {
		if hasAssets(path) {
			return path, nil
		}
	}
	return "", nil
}

type statusWriter struct {
	http.ResponseWriter
	code *int
}

func (w *statusWriter) WriteHeader(code int) {
	*(w.code) = code
}

func LogRequest(handler httprouter.Handle) httprouter.Handle {
	return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		start := time.Now()
		addr := req.Header.Get("X-Real-IP")
		if addr == "" {
			addr = req.Header.Get("X-Forwarded-For")
			if addr == "" {
				addr = req.RemoteAddr
			}
		}
		code := 200
		rw := &statusWriter{ResponseWriter: w, code: &code}
		clog.Infof("started %s %s for %s", req.Method, req.URL.Path, addr)
		handler(rw, req, params)
		clog.Infof("completed %v %s %s in %v", code, http.StatusText(code), req.URL.Path, time.Since(start))
	}
}

func jsonResponse(w http.ResponseWriter, code int, err interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(code)
	w.Write([]byte(`{"error": `))
	data, _ := json.Marshal(fmt.Sprint(err))
	w.Write(data)
	w.Write([]byte(`}`))
}

type TemplateRequestHandler struct {
	templates *template.Template
}

func (h *TemplateRequestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	uiType := params.ByName("ui_type")
	if r.URL.Path == "/" {
		uiType = "query"
	}
	err := h.templates.ExecuteTemplate(w, uiType+".html", h)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

type API struct {
	config *Config
	handle *graph.Handle
}

func (api *API) GetHandleForRequest(r *http.Request) (*graph.Handle, error) {
	return cayleyhttp.HandleForRequest(api.handle, "single", nil, r)
}

func (api *API) RWOnly(handler httprouter.Handle) httprouter.Handle {
	if api.config.ReadOnly {
		return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
			jsonResponse(w, http.StatusForbidden, "Database is read-only.")
		}
	}
	return handler
}

func CORSFunc(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	if origin := req.Header.Get("Origin"); origin != "" {
		w.Header().Set("Access-Control-Allow-Origin", origin)
		w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
		w.Header().Set("Access-Control-Allow-Headers",
			"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
	}
}

func CORS(h httprouter.Handle) httprouter.Handle {
	return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		CORSFunc(w, req, params)
		h(w, req, params)
	}
}

func (api *API) APIv1(r *httprouter.Router) {
	r.POST("/api/v1/query/:query_lang", CORS(LogRequest(api.ServeV1Query)))
	r.POST("/api/v1/shape/:query_lang", CORS(LogRequest(api.ServeV1Shape)))
	r.POST("/api/v1/write", CORS(api.RWOnly(LogRequest(api.ServeV1Write))))
	r.POST("/api/v1/write/file/nquad", CORS(api.RWOnly(LogRequest(api.ServeV1WriteNQuad))))
	r.POST("/api/v1/delete", CORS(api.RWOnly(LogRequest(api.ServeV1Delete))))
}

type Config struct {
	ReadOnly bool
	Timeout  time.Duration
	Batch    int
}

func SetupRoutes(handle *graph.Handle, cfg *Config) error {
	r := httprouter.New()
	api := &API{config: cfg, handle: handle}
	r.OPTIONS("/*path", CORSFunc)
	api.APIv1(r)

	api2 := cayleyhttp.NewAPIv2(handle)
	api2.SetReadOnly(cfg.ReadOnly)
	api2.SetBatchSize(cfg.Batch)
	api2.SetQueryTimeout(cfg.Timeout)
	api2.RegisterOn(r, CORS, LogRequest)

	gs := &gephi.GraphStreamHandler{QS: handle.QuadStore}
	const gephiPath = "/gephi/gs"
	r.GET(gephiPath, CORS(gs.ServeHTTP))

	if assets, err := findAssetsPath(); err != nil {
		return err
	} else if assets != "" {
		clog.Infof("using assets from %q", assets)
		docs := &DocRequestHandler{assets: assets}
		r.GET("/docs/:docpage", docs.ServeHTTP)

		var templates = template.Must(template.ParseGlob(fmt.Sprint(assets, "/templates/*.tmpl")))
		templates.ParseGlob(fmt.Sprint(assets, "/templates/*.html"))
		root := &TemplateRequestHandler{templates: templates}
		r.GET("/ui/:ui_type", root.ServeHTTP)
		r.GET("/", root.ServeHTTP)
		http.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir(fmt.Sprint(assets, "/static/")))))
	}

	http.Handle("/", r)
	return nil
}
<commit_msg>also search for assets relative to the binary; fixes #720<commit_after>// Copyright 2014 The Cayley Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package http

import (
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/cayleygraph/cayley/clog"
	"github.com/julienschmidt/httprouter"

	"github.com/cayleygraph/cayley/graph"
	"github.com/cayleygraph/cayley/internal/gephi"
	"github.com/cayleygraph/cayley/server/http"
)

var AssetsPath string
var defaultAssetPaths = []string{
	".", "..", "./assets",
	"/usr/local/share/cayley/assets",
	os.ExpandEnv("$GOPATH/src/github.com/cayleygraph/cayley"),
}
var assetsDirs = []string{"templates", "static", "docs"}

func hasAssets(path string) bool {
	if len(assetsDirs) == 0 {
		return false
	}
	for _, dir := range assetsDirs {
		if _, err := os.Stat(fmt.Sprint(path, "/", dir)); os.IsNotExist(err) {
			return false
		}
	}
	return true
}

func findAssetsPath() (string, error) {
	if AssetsPath != "" {
		if hasAssets(AssetsPath) {
			return AssetsPath, nil
		}
		return "", fmt.Errorf("cannot find assets at %q", AssetsPath)
	}
	var bin string
	if len(os.Args) != 0 {
		bin = filepath.Dir(os.Args[0])
	}
	for _, path := range defaultAssetPaths {
		if hasAssets(path) {
			return path, nil
		}
		if !filepath.IsAbs(path) {
			path = filepath.Join(bin, path)
			if hasAssets(path) {
				return path, nil
			}
		}
	}
	return "", nil
}

type statusWriter struct {
	http.ResponseWriter
	code *int
}

func (w *statusWriter) WriteHeader(code int) {
	*(w.code) = code
}

func LogRequest(handler httprouter.Handle) httprouter.Handle {
	return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		start := time.Now()
		addr := req.Header.Get("X-Real-IP")
		if addr == "" {
			addr = req.Header.Get("X-Forwarded-For")
			if addr == "" {
				addr = req.RemoteAddr
			}
		}
		code := 200
		rw := &statusWriter{ResponseWriter: w, code: &code}
		clog.Infof("started %s %s for %s", req.Method, req.URL.Path, addr)
		handler(rw, req, params)
		clog.Infof("completed %v %s %s in %v", code, http.StatusText(code), req.URL.Path, time.Since(start))
	}
}

func jsonResponse(w http.ResponseWriter, code int, err interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(code)
	w.Write([]byte(`{"error": `))
	data, _ := json.Marshal(fmt.Sprint(err))
	w.Write(data)
	w.Write([]byte(`}`))
}

type TemplateRequestHandler struct {
	templates *template.Template
}

func (h *TemplateRequestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	uiType := params.ByName("ui_type")
	if r.URL.Path == "/" {
		uiType = "query"
	}
	err := h.templates.ExecuteTemplate(w, uiType+".html", h)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

type API struct {
	config *Config
	handle *graph.Handle
}

func (api *API) GetHandleForRequest(r *http.Request) (*graph.Handle, error) {
	return cayleyhttp.HandleForRequest(api.handle, "single", nil, r)
}

func (api *API) RWOnly(handler httprouter.Handle) httprouter.Handle {
	if api.config.ReadOnly {
		return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
			jsonResponse(w, http.StatusForbidden, "Database is read-only.")
		}
	}
	return handler
}

func CORSFunc(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	if origin := req.Header.Get("Origin"); origin != "" {
		w.Header().Set("Access-Control-Allow-Origin", origin)
		w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
		w.Header().Set("Access-Control-Allow-Headers",
			"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
	}
}

func CORS(h httprouter.Handle) httprouter.Handle {
	return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		CORSFunc(w, req, params)
		h(w, req, params)
	}
}

func (api *API) APIv1(r *httprouter.Router) {
	r.POST("/api/v1/query/:query_lang", CORS(LogRequest(api.ServeV1Query)))
	r.POST("/api/v1/shape/:query_lang", CORS(LogRequest(api.ServeV1Shape)))
	r.POST("/api/v1/write", CORS(api.RWOnly(LogRequest(api.ServeV1Write))))
	r.POST("/api/v1/write/file/nquad", CORS(api.RWOnly(LogRequest(api.ServeV1WriteNQuad))))
	r.POST("/api/v1/delete", CORS(api.RWOnly(LogRequest(api.ServeV1Delete))))
}

type Config struct {
	ReadOnly bool
	Timeout  time.Duration
	Batch    int
}

func SetupRoutes(handle *graph.Handle, cfg *Config) error {
	r := httprouter.New()
	api := &API{config: cfg, handle: handle}
	r.OPTIONS("/*path", CORSFunc)
	api.APIv1(r)

	api2 := cayleyhttp.NewAPIv2(handle)
	api2.SetReadOnly(cfg.ReadOnly)
	api2.SetBatchSize(cfg.Batch)
	api2.SetQueryTimeout(cfg.Timeout)
	api2.RegisterOn(r, CORS, LogRequest)

	gs := &gephi.GraphStreamHandler{QS: handle.QuadStore}
	const gephiPath = "/gephi/gs"
	r.GET(gephiPath, CORS(gs.ServeHTTP))

	if assets, err := findAssetsPath(); err != nil {
		return err
	} else if assets != "" {
		clog.Infof("using assets from %q", assets)
		docs := &DocRequestHandler{assets: assets}
		r.GET("/docs/:docpage", docs.ServeHTTP)

		var templates = template.Must(template.ParseGlob(fmt.Sprint(assets, "/templates/*.tmpl")))
		templates.ParseGlob(fmt.Sprint(assets, "/templates/*.html"))
		root := &TemplateRequestHandler{templates: templates}
		r.GET("/ui/:ui_type", root.ServeHTTP)
		r.GET("/", root.ServeHTTP)
		http.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir(fmt.Sprint(assets, "/static/")))))
	}

	http.Handle("/", r)
	return nil
}
<|endoftext|>


<commit_before>package interpreter

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"
	"unicode"

	"../dos"
)

type StatementT struct {
	Args     []string
	Redirect []*Redirecter
	Term     string
}

var prefix []string = []string{" 0<", " 1>", " 2>"}

var PercentFunc = map[string]func() string{
	"CD": func() string {
		wd, err := os.Getwd()
		if err == nil {
			return wd
		} else {
			return ""
		}
	},
	"ERRORLEVEL": func() string {
		return ErrorLevelStr
	},
}

var rxUnicode = regexp.MustCompile("^[uU]\\+?([0-9a-fA-F]+)$")

func OurGetEnv(name string) (string, bool) {
	value := os.Getenv(name)
	if value != "" {
		return value, true
	} else if m := rxUnicode.FindStringSubmatch(name); m != nil {
		ucode, _ := strconv.ParseInt(m[1], 16, 32)
		return fmt.Sprintf("%c", rune(ucode)), true
	} else if f, ok := PercentFunc[strings.ToUpper(name)]; ok {
		return f(), true
	} else {
		return "", false
	}
}

func chomp(buffer *bytes.Buffer) {
	original := buffer.String()
	buffer.Reset()
	var lastchar rune
	for i, ch := range original {
		if i > 0 {
			buffer.WriteRune(lastchar)
		}
		lastchar = ch
	}
}

const NOTQUOTED = '\000'

const EMPTY_COMMAND_FOUND = "Empty command found"

func dequote(source *bytes.Buffer) string {
	var buffer bytes.Buffer

	lastchar := ' '
	quoteNow := NOTQUOTED
	yenCount := 0
	for {
		ch, _, err := source.ReadRune()
		if err != nil {
			break
		}
		if ch == '~' && unicode.IsSpace(lastchar) {
			if home := dos.GetHome(); home != "" {
				buffer.WriteString(home)
			} else {
				buffer.WriteRune('~')
			}
			lastchar = '~'
			continue
		}
		if ch == '%' && quoteNow != '\'' && yenCount%2 == 0 {
			var nameBuf bytes.Buffer
			for {
				ch, _, err = source.ReadRune()
				if err != nil {
					buffer.WriteRune('%')
					nameBuf.WriteTo(&buffer)
					return buffer.String()
				}
				if ch == '%' {
					if value, ok := OurGetEnv(nameBuf.String()); ok {
						buffer.WriteString(value)
					} else {
						buffer.WriteRune('%')
						nameBuf.WriteTo(&buffer)
						buffer.WriteRune('%')
					}
					break
				}
				if ch == '=' {
					source.UnreadRune()
					buffer.WriteRune('%')
					nameBuf.WriteTo(&buffer)
					break
				}
				nameBuf.WriteRune(ch)
			}
			continue
		}

		if quoteNow != NOTQUOTED && ch == quoteNow && yenCount%2 == 0 {
			// Close Quotation.
			for ; yenCount >= 2; yenCount -= 2 {
				buffer.WriteRune('\\')
			}
			quoteNow = NOTQUOTED
		} else if (ch == '\'' || ch == '"') && quoteNow == NOTQUOTED && yenCount%2 == 0 {
			// Open Qutation.
			for ; yenCount >= 2; yenCount -= 2 {
				buffer.WriteRune('\\')
			}
			quoteNow = ch
			if ch == lastchar {
				buffer.WriteRune(ch)
			}
		} else {
			if ch == '\\' {
				yenCount++
			} else if ch == '\'' || ch == '"' {
				for ; yenCount >= 2; yenCount -= 2 {
					buffer.WriteRune('\\')
				}
				yenCount = 0
				buffer.WriteRune(ch)
			} else {
				for ; yenCount > 0; yenCount-- {
					buffer.WriteRune('\\')
				}
				buffer.WriteRune(ch)
			}
		}
		lastchar = ch
	}
	for ; yenCount > 0; yenCount-- {
		buffer.WriteRune('\\')
	}
	return buffer.String()
}

func terminate(statements *[]*StatementT,
	isRedirected *bool,
	redirect *[]*Redirecter,
	buffer *bytes.Buffer,
	args *[]string,
	term string) {

	statement1 := new(StatementT)
	if buffer.Len() > 0 {
		if *isRedirected && len(*redirect) > 0 {
			(*redirect)[len(*redirect)-1].SetPath(dequote(buffer))
			*isRedirected = false
			statement1.Args = *args
		} else {
			statement1.Args = append(*args, dequote(buffer))
		}
		buffer.Reset()
	} else if len(*args) <= 0 {
		return
	} else {
		statement1.Args = *args
	}
	statement1.Redirect = *redirect
	*redirect = make([]*Redirecter, 0, 3)
	*args = make([]string, 0)
	statement1.Term = term
	*statements = append(*statements, statement1)
}

func parse1(text string) ([]*StatementT, error) {
	quoteNow := NOTQUOTED
	yenCount := 0
	statements := make([]*StatementT, 0)
	args := make([]string, 0)
	lastchar := ' '
	var buffer bytes.Buffer
	isNextRedirect := false
	redirect := make([]*Redirecter, 0, 3)

	TermWord := func() {
		if isNextRedirect && len(redirect) > 0 {
			redirect[len(redirect)-1].SetPath(dequote(&buffer))
		} else {
			if buffer.Len() > 0 {
				args = append(args, dequote(&buffer))
			}
		}
		buffer.Reset()
	}

	reader := strings.NewReader(text)
	for reader.Len() > 0 {
		ch, chSize, chErr := reader.ReadRune()
		if chSize <= 0 {
			break
		}
		if chErr != nil {
			return nil, chErr
		}
		if quoteNow == NOTQUOTED {
			if yenCount%2 == 0 && (ch == '"' || ch == '\'') {
				quoteNow = ch
			}
		} else if yenCount%2 == 0 && ch == quoteNow {
			quoteNow = NOTQUOTED
		}
		if quoteNow != NOTQUOTED {
			buffer.WriteRune(ch)
		} else if ch == ' ' {
			if buffer.Len() > 0 {
				TermWord()
				isNextRedirect = false
			}
		} else if lastchar == ' ' && ch == ';' {
			terminate(&statements, &isNextRedirect, &redirect, &buffer, &args, ";")
		} else if ch == '|' {
			if lastchar == '|' {
				if len(statements) <= 0 {
					return nil, errors.New(EMPTY_COMMAND_FOUND)
				}
				statements[len(statements)-1].Term = "||"
			} else {
				terminate(&statements, &isNextRedirect, &redirect, &buffer, &args, "|")
			}
		} else if ch == '&' {
			switch lastchar {
			case '&':
				if len(statements) <= 0 {
					return nil, errors.New(EMPTY_COMMAND_FOUND)
				}
				statements[len(statements)-1].Term = "&&"
			case '|':
				if len(statements) <= 0 {
					return nil, errors.New(EMPTY_COMMAND_FOUND)
				}
				statements[len(statements)-1].Term = "|&"
			case '>':
				// >&[n]
				ch2, ch2siz, ch2err := reader.ReadRune()
				if ch2err != nil {
					return nil, ch2err
				}
				if ch2siz <= 0 {
					return nil, errors.New("Too Near EOF for >&")
				}
				red := redirect[len(redirect)-1]
				switch ch2 {
				case '1':
					red.DupFrom(1)
				case '2':
					red.DupFrom(2)
				default:
					return nil, errors.New("Syntax error after >&")
				}
				isNextRedirect = false
			default:
				terminate(&statements, &isNextRedirect, &redirect, &buffer, &args, "&")
			}
		} else if ch == '>' {
			switch lastchar {
			case '1':
				// 1>
				chomp(&buffer)
				TermWord()
				redirect = append(redirect, NewRedirecter(1))
			case '2':
				// 2>
				chomp(&buffer)
				TermWord()
				redirect = append(redirect, NewRedirecter(2))
			case '>':
				// >>
				TermWord()
				if len(redirect) >= 0 {
					redirect[len(redirect)-1].SetAppend()
				}
			default:
				// >
				TermWord()
				redirect = append(redirect, NewRedirecter(1))
			}
			isNextRedirect = true
		} else if ch == '<' {
			TermWord()
			redirect = append(redirect, NewRedirecter(0))
			isNextRedirect = true
		} else {
			buffer.WriteRune(ch)
		}
		if ch == '\\' {
			yenCount++
		} else {
			yenCount = 0
		}
		lastchar = ch
	}
	terminate(&statements, &isNextRedirect, &redirect, &buffer, &args, " ")
	return statements, nil
}

// Make arrays whose elements are pipelines
func parse2(statements []*StatementT) [][]*StatementT {
	result := make([][]*StatementT, 1)
	for _, statement1 := range statements {
		result[len(result)-1] = append(result[len(result)-1], statement1)
		switch statement1.Term {
		case "|", "|&":

		default:
			result = append(result, make([]*StatementT, 0))
		}
	}
	if len(result[len(result)-1]) <= 0 {
		result = result[0 : len(result)-1]
	}
	return result
}

func Parse(text string) ([][]*StatementT, error) {
	result1, err := parse1(text)
	if err != nil {
		return nil, err
	}
	result2 := parse2(result1)
	return result2, nil
}
<commit_msg>refactoring parser. (function terminate -> closure)<commit_after>package interpreter

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"
	"unicode"

	"../dos"
)

type StatementT struct {
	Args     []string
	Redirect []*Redirecter
	Term     string
}

var prefix []string = []string{" 0<", " 1>", " 2>"}

var PercentFunc = map[string]func() string{
	"CD": func() string {
		wd, err := os.Getwd()
		if err == nil {
			return wd
		} else {
			return ""
		}
	},
	"ERRORLEVEL": func() string {
		return ErrorLevelStr
	},
}

var rxUnicode = regexp.MustCompile("^[uU]\\+?([0-9a-fA-F]+)$")

func OurGetEnv(name string) (string, bool) {
	value := os.Getenv(name)
	if value != "" {
		return value, true
	} else if m := rxUnicode.FindStringSubmatch(name); m != nil {
		ucode, _ := strconv.ParseInt(m[1], 16, 32)
		return fmt.Sprintf("%c", rune(ucode)), true
	} else if f, ok := PercentFunc[strings.ToUpper(name)]; ok {
		return f(), true
	} else {
		return "", false
	}
}

func chomp(buffer *bytes.Buffer) {
	original := buffer.String()
	buffer.Reset()
	var lastchar rune
	for i, ch := range original {
		if i > 0 {
			buffer.WriteRune(lastchar)
		}
		lastchar = ch
	}
}

const NOTQUOTED = '\000'

const EMPTY_COMMAND_FOUND = "Empty command found"

func dequote(source *bytes.Buffer) string {
	var buffer bytes.Buffer

	lastchar := ' '
	quoteNow := NOTQUOTED
	yenCount := 0
	for {
		ch, _, err := source.ReadRune()
		if err != nil {
			break
		}
		if ch == '~' && unicode.IsSpace(lastchar) {
			if home := dos.GetHome(); home != "" {
				buffer.WriteString(home)
			} else {
				buffer.WriteRune('~')
			}
			lastchar = '~'
			continue
		}
		if ch == '%' && quoteNow != '\'' && yenCount%2 == 0 {
			var nameBuf bytes.Buffer
			for {
				ch, _, err = source.ReadRune()
				if err != nil {
					buffer.WriteRune('%')
					nameBuf.WriteTo(&buffer)
					return buffer.String()
				}
				if ch == '%' {
					if value, ok := OurGetEnv(nameBuf.String()); ok {
						buffer.WriteString(value)
					} else {
						buffer.WriteRune('%')
						nameBuf.WriteTo(&buffer)
						buffer.WriteRune('%')
					}
					break
				}
				if ch == '=' {
					source.UnreadRune()
					buffer.WriteRune('%')
					nameBuf.WriteTo(&buffer)
					break
				}
				nameBuf.WriteRune(ch)
			}
			continue
		}

		if quoteNow != NOTQUOTED && ch == quoteNow && yenCount%2 == 0 {
			// Close Quotation.
			for ; yenCount >= 2; yenCount -= 2 {
				buffer.WriteRune('\\')
			}
			quoteNow = NOTQUOTED
		} else if (ch == '\'' || ch == '"') && quoteNow == NOTQUOTED && yenCount%2 == 0 {
			// Open Qutation.
			for ; yenCount >= 2; yenCount -= 2 {
				buffer.WriteRune('\\')
			}
			quoteNow = ch
			if ch == lastchar {
				buffer.WriteRune(ch)
			}
		} else {
			if ch == '\\' {
				yenCount++
			} else if ch == '\'' || ch == '"' {
				for ; yenCount >= 2; yenCount -= 2 {
					buffer.WriteRune('\\')
				}
				yenCount = 0
				buffer.WriteRune(ch)
			} else {
				for ; yenCount > 0; yenCount-- {
					buffer.WriteRune('\\')
				}
				buffer.WriteRune(ch)
			}
		}
		lastchar = ch
	}
	for ; yenCount > 0; yenCount-- {
		buffer.WriteRune('\\')
	}
	return buffer.String()
}

func parse1(text string) ([]*StatementT, error) {
	quoteNow := NOTQUOTED
	yenCount := 0
	statements := make([]*StatementT, 0)
	args := make([]string, 0)
	lastchar := ' '
	var buffer bytes.Buffer
	isNextRedirect := false
	redirect := make([]*Redirecter, 0, 3)

	terminate := func(term string) {
		statement1 := new(StatementT)
		if buffer.Len() > 0 {
			if isNextRedirect && len(redirect) > 0 {
				redirect[len(redirect)-1].SetPath(dequote(&buffer))
				isNextRedirect = false
				statement1.Args = args
			} else {
				statement1.Args = append(args, dequote(&buffer))
			}
			buffer.Reset()
		} else if len(args) <= 0 {
			return
		} else {
			statement1.Args = args
		}
		statement1.Redirect = redirect
		redirect = make([]*Redirecter, 0, 3)
		args = make([]string, 0)
		statement1.Term = term
		statements = append(statements, statement1)
	}

	TermWord := func() {
		if isNextRedirect && len(redirect) > 0 {
			redirect[len(redirect)-1].SetPath(dequote(&buffer))
		} else {
			if buffer.Len() > 0 {
				args = append(args, dequote(&buffer))
			}
		}
		buffer.Reset()
	}

	reader := strings.NewReader(text)
	for reader.Len() > 0 {
		ch, chSize, chErr := reader.ReadRune()
		if chSize <= 0 {
			break
		}
		if chErr != nil {
			return nil, chErr
		}
		if quoteNow == NOTQUOTED {
			if yenCount%2 == 0 && (ch == '"' || ch == '\'') {
				quoteNow = ch
			}
		} else if yenCount%2 == 0 && ch == quoteNow {
			quoteNow = NOTQUOTED
		}
		if quoteNow != NOTQUOTED {
			buffer.WriteRune(ch)
		} else if ch == ' ' {
			if buffer.Len() > 0 {
				TermWord()
				isNextRedirect = false
			}
		} else if lastchar == ' ' && ch == ';' {
			terminate(";")
		} else if ch == '|' {
			if lastchar == '|' {
				if len(statements) <= 0 {
					return nil, errors.New(EMPTY_COMMAND_FOUND)
				}
				statements[len(statements)-1].Term = "||"
			} else {
				terminate("|")
			}
		} else if ch == '&' {
			switch lastchar {
			case '&':
				if len(statements) <= 0 {
					return nil, errors.New(EMPTY_COMMAND_FOUND)
				}
				statements[len(statements)-1].Term = "&&"
			case '|':
				if len(statements) <= 0 {
					return nil, errors.New(EMPTY_COMMAND_FOUND)
				}
				statements[len(statements)-1].Term = "|&"
			case '>':
				// >&[n]
				ch2, ch2siz, ch2err := reader.ReadRune()
				if ch2err != nil {
					return nil, ch2err
				}
				if ch2siz <= 0 {
					return nil, errors.New("Too Near EOF for >&")
				}
				red := redirect[len(redirect)-1]
				switch ch2 {
				case '1':
					red.DupFrom(1)
				case '2':
					red.DupFrom(2)
				default:
					return nil, errors.New("Syntax error after >&")
				}
				isNextRedirect = false
			default:
				terminate("&")
			}
		} else if ch == '>' {
			switch lastchar {
			case '1':
				// 1>
				chomp(&buffer)
				TermWord()
				redirect = append(redirect, NewRedirecter(1))
			case '2':
				// 2>
				chomp(&buffer)
				TermWord()
				redirect = append(redirect, NewRedirecter(2))
			case '>':
				// >>
				TermWord()
				if len(redirect) >= 0 {
					redirect[len(redirect)-1].SetAppend()
				}
			default:
				// >
				TermWord()
				redirect = append(redirect, NewRedirecter(1))
			}
			isNextRedirect = true
		} else if ch == '<' {
			TermWord()
			redirect = append(redirect, NewRedirecter(0))
			isNextRedirect = true
		} else {
			buffer.WriteRune(ch)
		}
		if ch == '\\' {
			yenCount++
		} else {
			yenCount = 0
		}
		lastchar = ch
	}
	terminate(" ")
	return statements, nil
}

// Make arrays whose elements are pipelines
func parse2(statements []*StatementT) [][]*StatementT {
	result := make([][]*StatementT, 1)
	for _, statement1 := range statements {
		result[len(result)-1] = append(result[len(result)-1], statement1)
		switch statement1.Term {
		case "|", "|&":

		default:
			result = append(result, make([]*StatementT, 0))
		}
	}
	if len(result[len(result)-1]) <= 0 {
		result = result[0 : len(result)-1]
	}
	return result
}

func Parse(text string) ([][]*StatementT, error) {
	result1, err := parse1(text)
	if err != nil {
		return nil, err
	}
	result2 := parse2(result1)
	return result2, nil
}
<|endoftext|>