<commit_before>package gamehack

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"

	"code.google.com/p/goauth2/oauth"
	"github.com/subosito/twilio"

	"appengine"
	"appengine/datastore"
	"appengine/urlfetch"
	"appengine/user"
)

type Notification struct {
	UserID           int64             `json:"userId"`
	StorylineUpdates []StorylineUpdate `json:"storylineUpdates"`
}

type StorylineUpdate struct {
	// TODO: Change to equivalent of enum
	Reason          string `json:"reason"`
	LastSegmentType string `json:"lastSegmentType"`
}

type Location struct {
	Lat float64 `json:"lat"`
	Lon float64 `json:"lon"`
}

type Place struct {
	Id       int      `json:"id"`
	Type     string   `json:"type"`
	Location Location `json:"location"`
}

type UserInfo struct {
	User         string
	PhoneEntries []PhoneEntry
}

type PhoneEntry struct {
	Parent string
	Phone  string
}

var oauthCfg = &oauth.Config{
	ClientId:     clientId,
	ClientSecret: clientSecret,
	AuthURL:      "https://api.moves-app.com/oauth/v1/authorize",
	TokenURL:     "https://api.moves-app.com/oauth/v1/access_token",
	RedirectURL:  "http://localhost:8080/oauth2callback",
	Scope:        "location",
}

func init() {
	http.HandleFunc("/", root)
	http.HandleFunc("/login", login)
	http.HandleFunc("/logout", logout)
	http.HandleFunc("/phone", phone)
	http.HandleFunc("/addphone", addPhone)
	http.HandleFunc("/delphone", delPhone)
	http.HandleFunc("/authorize", authorize)
	http.HandleFunc("/oauth2callback", oauthCallback)
	http.HandleFunc("/notification", handleNotification)
}

func authorize(w http.ResponseWriter, r *http.Request) {
	url := oauthCfg.AuthCodeURL("")
	http.Redirect(w, r, url, http.StatusFound)
}

func oauthCallback(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	code := r.FormValue("code")

	t := &oauth.Transport{
		Config: oauthCfg,
		Transport: &urlfetch.Transport{
			Context:                       c,
			Deadline:                      0,
			AllowInvalidServerCertificate: false,
		},
	}

	token, err := t.Exchange(code)
	if err != nil {
		c.Errorf(err.Error())
		http.Error(w, "Internal server error.", http.StatusInternalServerError)
		return
	}

	tokenCache := cache{
		Context: c,
		Key:     "Oauth",
	}

	err = tokenCache.PutToken(token)
	if err != nil {
		c.Errorf(err.Error())
	}

	t.Token = token

	w.Write([]byte("Authorization flow complete."))
}

func handleNotification(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Invalid method.", http.StatusMethodNotAllowed)
		return
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Error reading request body.", http.StatusBadRequest)
		return
	}

	var notification Notification
	err = json.Unmarshal(body, &notification)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	hasDataUpload := false
	for _, update := range notification.StorylineUpdates {
		if update.Reason == "DataUpload" {
			hasDataUpload = true
			break
		}
	}

	if hasDataUpload {

	}
	/*fmt.Fprintf(w, "%v", notification)
	if err != nil {
		http.Error(w, "Error writing response body.", http.StatusInternalServerError)
		return
	}*/
	/*var place Place
	err = json.Unmarshal(body, &place)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sendText(place, "+15555555555", w, r)*/
}

func sendText(place Place, phone string, w http.ResponseWriter, r *http.Request) {
	a := appengine.NewContext(r)
	f := urlfetch.Client(a)
	c := twilio.NewClient(twilioSid, twilioAuthToken, f)

	params := twilio.MessageParams{
		Body: fmt.Sprintf("Your child is now at lat %f lon %f", place.Location.Lat, place.Location.Lon),
	}
	_, _, err := c.Messages.Send("+15555555555", phone, params)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
	}
}

func root(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, homePage)
}

const homePage = `
<!doctype html>

    <title>Game+Hack</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>

    <form action="/login" method="post">

<input type="submit" value="Sign In">

    </form>

`

func login(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	u := user.Current(c)
	if u == nil {
		url, err := user.LoginURL(c, r.URL.String())
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Location", url)
		w.WriteHeader(http.StatusFound)
		return
	}
	http.Redirect(w, r, "/phone", http.StatusFound)
}

func logout(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	u := user.Current(c)
	if u != nil {
		url, err := user.LogoutURL(c, "/")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Location", url)
		w.WriteHeader(http.StatusFound)
		return
	}
	http.Redirect(w, r, "/", http.StatusFound)
}

var phoneTemplate = template.Must(template.New("phone").Parse(`
<!doctype html>

    <title>Game+Hack</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>

Hello, {{.User}}! Sign Out

    <form action="/addphone" method="post">

Parent: <input type="text" name="parent"/>

Phone: <input type="text" name="phone"/>

<input type="submit" value="Add Phone Number">

    </form>
    <form action="/delphone" method="post">
      <select name="parent">
        <option value=""></option>
        {{range .PhoneEntries}}
          <option value="{{.Parent}}">{{.Parent}}</option>
        {{end}}
      </select>

<input type="submit" value="Remove Phone Number">

    </form>
    {{range .PhoneEntries}}

{{.Parent}}: {{.Phone}}

    {{end}}

`))

func phone(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	u := user.Current(c)
	if u == nil {
		http.Error(w, "Not logged in", http.StatusInternalServerError)
		return
	}
	userInfo := UserInfo{
		User: u.Email,
	}
	_, err := datastore.NewQuery("PhoneEntry").Ancestor(datastore.NewKey(c, "User", u.ID, 0, nil)).GetAll(c, &userInfo.PhoneEntries)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	err = phoneTemplate.Execute(w, &userInfo)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func addPhone(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	u := user.Current(c)
	if u == nil {
		http.Error(w, "Not logged in", http.StatusInternalServerError)
		return
	}
	_, err := datastore.Put(c, datastore.NewKey(c, "PhoneEntry", r.FormValue("parent"), 0, datastore.NewKey(c, "User", u.ID, 0, nil)), &PhoneEntry{
		Parent: r.FormValue("parent"),
		Phone:  r.FormValue("phone"),
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/phone", http.StatusFound)
}

func delPhone(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	u := user.Current(c)
	if u == nil {
		http.Error(w, "Not logged in", http.StatusInternalServerError)
		return
	}
	err := datastore.Delete(c, datastore.NewKey(c, "PhoneEntry", r.FormValue("parent"), 0, datastore.NewKey(c, "User", u.ID, 0, nil)))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/phone", http.StatusFound)
}
<commit_msg>Return http.StatusUnauthorized when not logged in and redirect to root.<commit_after>package gamehack

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"

	"code.google.com/p/goauth2/oauth"
	"github.com/subosito/twilio"

	"appengine"
	"appengine/datastore"
	"appengine/urlfetch"
	"appengine/user"
)

type Notification struct {
	UserID           int64             `json:"userId"`
	StorylineUpdates []StorylineUpdate `json:"storylineUpdates"`
}

type StorylineUpdate struct {
	// TODO: Change to equivalent of enum
	Reason          string `json:"reason"`
	LastSegmentType string `json:"lastSegmentType"`
}

type Location struct {
	Lat float64 `json:"lat"`
	Lon float64 `json:"lon"`
}

type Place struct {
	Id       int      `json:"id"`
	Type     string   `json:"type"`
	Location Location `json:"location"`
}

type UserInfo struct {
	User         string
	PhoneEntries []PhoneEntry
}

type PhoneEntry struct {
	Parent string
	Phone  string
}

var oauthCfg = &oauth.Config{
	ClientId:     clientId,
	ClientSecret: clientSecret,
	AuthURL:      "https://api.moves-app.com/oauth/v1/authorize",
	TokenURL:     "https://api.moves-app.com/oauth/v1/access_token",
	RedirectURL:  "http://localhost:8080/oauth2callback",
	Scope:        "location",
}

func init() {
	http.HandleFunc("/", root)
	http.HandleFunc("/login", login)
	http.HandleFunc("/logout", logout)
	http.HandleFunc("/phone", phone)
	http.HandleFunc("/addphone", addPhone)
	http.HandleFunc("/delphone", delPhone)
	http.HandleFunc("/authorize", authorize)
	http.HandleFunc("/oauth2callback", oauthCallback)
	http.HandleFunc("/notification", handleNotification)
}

func authorize(w http.ResponseWriter, r *http.Request) {
	url := oauthCfg.AuthCodeURL("")
	http.Redirect(w, r, url, http.StatusFound)
}

func oauthCallback(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	code := r.FormValue("code")

	t := &oauth.Transport{
		Config: oauthCfg,
		Transport: &urlfetch.Transport{
			Context:                       c,
			Deadline:                      0,
			AllowInvalidServerCertificate: false,
		},
	}

	token, err := t.Exchange(code)
	if err != nil {
		c.Errorf(err.Error())
		http.Error(w, "Internal server error.", http.StatusInternalServerError)
		return
	}

	tokenCache := cache{
		Context: c,
		Key:     "Oauth",
	}

	err = tokenCache.PutToken(token)
	if err != nil {
		c.Errorf(err.Error())
	}

	t.Token = token

	w.Write([]byte("Authorization flow complete."))
}

func handleNotification(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Invalid method.", http.StatusMethodNotAllowed)
		return
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Error reading request body.", http.StatusBadRequest)
		return
	}

	var notification Notification
	err = json.Unmarshal(body, &notification)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	hasDataUpload := false
	for _, update := range notification.StorylineUpdates {
		if update.Reason == "DataUpload" {
			hasDataUpload = true
			break
		}
	}

	if hasDataUpload {

	}
	/*fmt.Fprintf(w, "%v", notification)
	if err != nil {
		http.Error(w, "Error writing response body.", http.StatusInternalServerError)
		return
	}*/
	/*var place Place
	err = json.Unmarshal(body, &place)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sendText(place, "+15555555555", w, r)*/
}

func sendText(place Place, phone string, w http.ResponseWriter, r *http.Request) {
	a := appengine.NewContext(r)
	f := urlfetch.Client(a)
	c := twilio.NewClient(twilioSid, twilioAuthToken, f)

	params := twilio.MessageParams{
		Body: fmt.Sprintf("Your child is now at lat %f lon %f", place.Location.Lat, place.Location.Lon),
	}
	_, _, err := c.Messages.Send("+15555555555", phone, params)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
	}
}

func root(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, homePage)
}

const homePage = `
<!doctype html>

    <title>Game+Hack</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>

    <form action="/login" method="post">

<input type="submit" value="Sign In">

    </form>

`

func login(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	u := user.Current(c)
	if u == nil {
		url, err := user.LoginURL(c, r.URL.String())
		if err != nil {
			http.Redirect(w, r, "/", http.StatusUnauthorized)
			return
		}
		w.Header().Set("Location", url)
		w.WriteHeader(http.StatusFound)
		return
	}
	http.Redirect(w, r, "/phone", http.StatusFound)
}

func logout(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	u := user.Current(c)
	if u != nil {
		url, err := user.LogoutURL(c, "/")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Location", url)
		w.WriteHeader(http.StatusFound)
		return
	}
	http.Redirect(w, r, "/", http.StatusFound)
}

var phoneTemplate = template.Must(template.New("phone").Parse(`
<!doctype html>

    <title>Game+Hack</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>

Hello, {{.User}}! Sign Out

    <form action="/addphone" method="POST">

Parent: <input type="text" name="parent"/>

Phone: <input type="text" name="phone"/>

<input type="submit" value="Add Phone Number">

    </form>
    <form action="/delphone" method="POST">
      <select name="parent">
        <option value=""></option>
        {{range .PhoneEntries}}
          <option value="{{.Parent}}">{{.Parent}}</option>
        {{end}}
      </select>

<input type="submit" value="Remove Phone Number">

    </form>
    {{range .PhoneEntries}}

{{.Parent}}: {{.Phone}}

    {{end}}

`))

func phone(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	u := user.Current(c)
	if u == nil {
		http.Redirect(w, r, "/", http.StatusUnauthorized)
		return
	}
	userInfo := UserInfo{
		User: u.Email,
	}
	_, err := datastore.NewQuery("PhoneEntry").Ancestor(datastore.NewKey(c, "User", u.ID, 0, nil)).GetAll(c, &userInfo.PhoneEntries)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	err = phoneTemplate.Execute(w, &userInfo)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func addPhone(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	u := user.Current(c)
	if u == nil {
		http.Redirect(w, r, "/", http.StatusUnauthorized)
		return
	}
	_, err := datastore.Put(c, datastore.NewKey(c, "PhoneEntry", r.FormValue("parent"), 0, datastore.NewKey(c, "User", u.ID, 0, nil)), &PhoneEntry{
		Parent: r.FormValue("parent"),
		Phone:  r.FormValue("phone"),
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/phone", http.StatusFound)
}

func delPhone(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	u := user.Current(c)
	if u == nil {
		http.Redirect(w, r, "/", http.StatusUnauthorized)
		return
	}
	err := datastore.Delete(c, datastore.NewKey(c, "PhoneEntry", r.FormValue("parent"), 0, datastore.NewKey(c, "User", u.ID, 0, nil)))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/phone", http.StatusFound)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"github.com/stretchr/testify/assert"
	"regexp"
	"testing"
)

type ht struct {
	h holder
	t string
}

func TestGetTCPMap(t *testing.T) {
	r := map[string]string{}

	getTCPMap("testdata/proc/net/tcp", r)
	assert.NotEmpty(t, r)
	assert.Equal(t, 6, len(r))
	assert.Equal(t, "18EB", r["21620"])
	fmt.Print(r)

	getTCPMap("testdata/proc/net/tcp6", r)
	assert.NotEmpty(t, r)
	assert.Equal(t, 6, len(r))
	assert.Equal(t, "18EB", r["21620"])
	fmt.Print(r)
}

func TestShortPipe(t *testing.T) {
	proc = "testdata/proc"

	m := map[string]string{"21620": "18EB"}
	buf := new(bytes.Buffer)
	emitBare(mapPorts(m, getSockets(findPids())), buf)
	s := buf.String()
	assert.Contains(t, s, "1446\t6379")
	assert.Regexp(t, "^[0-9]+\\s[0-9]+\\s*\\n$", s)

}

func TestEmitBare(t *testing.T) {

	tcs := []ht{
		{h: holder{pid: "1446", port: int64(6379)}, t: "1446\t6379\t\t\n"},
		{h: holder{pid: "1446", port: int64(6379), ports: "ports"}, t: "1446\tports\t\t\n"},
		{h: holder{pid: "1446", port: int64(6379), cmd: []byte("cmd ")}, t: "1446\t6379\t\tcmd \n"},
	}

	for _, tc := range tcs {
		buf := new(bytes.Buffer)
		emitBare(from(tc.h), buf)

		s := buf.String()
		assert.Equal(t, tc.t, s)
	}

}

func TestEmitFormatted(t *testing.T) {
	tcs := []ht{
		{h: holder{pid: "1446", port: int64(6379)}, t: "pid   port\n1446  6379  \n"},
		{h: holder{pid: "1446", port: int64(6379), ports: "ports"}, t: "pid   port\n1446  ports  \n"},
		{
			h: holder{
				pid:  "1446",
				port: int64(6379),
				cmd:  []byte("cmd "),
				details: map[detailType]interface{}{
					color: colorDetail{22},
				},
			},
			t: "pid   port\n1446  6379  cmd \n"},
	}

	for _, tc := range tcs {
		buf := new(bytes.Buffer)
		emitFormatted(from(tc.h), buf)

		s := buf.String()
		assert.Equal(t, tc.t, s)
	}

}

func from(vi ...holder) chan holder {
	in := make(chan holder)
	go func() {
		for _, v := range vi {
			in <- v
		}
		close(in)
	}()
	return in
}

func TestMapCommands(t *testing.T) {
	proc = "testdata/proc"
	in := make(chan holder)
	out := mapCommands(in)

	in <- holder{pid: "1446"}
	po := <-out
	assert.Regexp(t, "/usr/bin/redis-server.*", string(po.cmd))

	in <- holder{pid: "not-there"}
	po = <-out
	assert.Equal(t, "", string(po.cmd))

	close(in)
	_, bad := <-out
	assert.False(t, bad)
}

func TestGather(t *testing.T) {
	in := make(chan holder)
	out := gather(in)
	m := make(map[string]holder)

	in <- holder{pid: "1446", port: int64(12)}
	in <- holder{pid: "1446", port: int64(21)}
	in <- holder{pid: "1446", port: int64(2)}
	in <- holder{pid: "1447", port: int64(3)}

	close(in)

	for o := range out {
		m[o.pid] = o
	}

	assert.Len(t, m, 2)
	assert.Equal(t, "2,12,21", m["1446"].ports)
	assert.Equal(t, int64(2), m["1446"].port)
	assert.Equal(t, "3", m["1447"].ports)
}

func TestAlias(t *testing.T) {
	in := make(chan holder)
	out := mapAliases(in)

	in <- holder{pid: "1446", port: int64(12), cmd: []byte("/x/y/java -Dblag webstorm foo bar")}
	po := <-out
	assert.Equal(t, "webstorm", string(po.cmd))
	_, ok := po.getColor()
	assert.True(t, ok)

	in <- holder{cmd: []byte("/opt/ccc/not-there")}
	po = <-out
	assert.Equal(t, "/opt/ccc/not-there", string(po.cmd))
	_, ok = po.getColor()
	assert.False(t, ok)

	close(in)

}

func TestSortJ(t *testing.T) {
	in1 := holder{pid: "1000", port: int64(100), cmd: []byte("a cmd")}
	in2 := holder{pid: "2000", port: int64(200), cmd: []byte("b cmd")}
	in3 := holder{pid: "3000", port: int64(300), cmd: []byte("c cmd")}
	in11 := holder{pid: "1000", port: int64(100), cmd: []byte("a cmd")}

	test := func(si int, f ...holder) {
		in := make(chan holder)
		out := sortJ(in, si)
		for _, fi := range f {
			in <- fi
		}
		close(in)

		assert.Equal(t, in1, <-out)
		assert.Equal(t, in2, <-out)
		assert.Equal(t, in3, <-out)
		_, bad := <-out
		assert.False(t, bad)
	}

	test(0, in1, in3, in2)
	test(0, in2, in3, in1)
	test(1, in1, in3, in2)
	test(2, in1, in3, in2)

	test2 := func(si int, f ...holder) {
		in := make(chan holder)
		out := sortJ(in, si)
		for _, fi := range f {
			in <- fi
		}
		close(in)
		_, ok := <-out
		assert.True(t, ok)
		_, ok = <-out
		assert.True(t, ok)
		_, bad := <-out
		assert.False(t, bad)
	}
	test2(0, in1, in11)
	test2(1, in1, in11)
	test2(2, in1, in11)

}

func TestGreps(t *testing.T) {
	in := []holder{
		{cmd: []byte("foo")},
		{},
		{cmd: []byte("bar")},
	}

	out := grep(regexp.MustCompile(".o."), from(in...))

	po, ok := <-out
	assert.True(t, ok)
	assert.Equal(t, "foo", string(po.cmd))

	po, extra := <-out
	assert.False(t, extra)
}
<commit_msg>tests<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/stretchr/testify/assert"
	"regexp"
	"testing"
)

type ht struct {
	h holder
	t string
}

func TestGetTCPMap(t *testing.T) {
	r := map[string]string{}

	getTCPMap("testdata/proc/net/tcp", r)
	assert.NotEmpty(t, r)
	assert.Equal(t, 6, len(r))
	assert.Equal(t, "18EB", r["21620"])
	fmt.Print(r)

	getTCPMap("testdata/proc/net/tcp6", r)
	assert.NotEmpty(t, r)
	assert.Equal(t, 6, len(r))
	assert.Equal(t, "18EB", r["21620"])
	fmt.Print(r)
}

func TestShortPipe(t *testing.T) {
	proc = "testdata/proc"

	m := map[string]string{"21620": "18EB"}
	buf := new(bytes.Buffer)
	emitBare(mapPorts(m, getSockets(findPids())), buf)
	s := buf.String()
	assert.Contains(t, s, "1446\t6379")
	assert.Regexp(t, "^[0-9]+\\s[0-9]+\\s*\\n$", s)

}

func TestEmitBare(t *testing.T) {

	tcs := []ht{
		{h: holder{pid: "1446", port: int64(6379)}, t: "1446\t6379\t\t\n"},
		{h: holder{pid: "1446", port: int64(6379), ports: "ports"}, t: "1446\tports\t\t\n"},
		{h: holder{pid: "1446", port: int64(6379), cmd: []byte("cmd ")}, t: "1446\t6379\t\tcmd \n"},
	}

	for _, tc := range tcs {
		buf := new(bytes.Buffer)
		emitBare(from(tc.h), buf)

		s := buf.String()
		assert.Equal(t, tc.t, s)
	}

}

func TestEmitFormatted(t *testing.T) {
	tcs := []ht{
		{h: holder{pid: "1446", port: int64(6379)}, t: "pid   port\n1446  6379  \n"},
		{h: holder{pid: "1446", port: int64(6379), ports: "ports"}, t: "pid   port\n1446  ports  \n"},
		{
			h: holder{
				pid:     "1446",
				port:    int64(6379),
				cmd:     []byte("cmd "),
				details: details{cmdColor: 22},
			},
			t: "pid   port\n1446  6379  cmd \n"},
	}

	for _, tc := range tcs {
		buf := new(bytes.Buffer)
		emitFormatted(from(tc.h), buf)

		s := buf.String()
		assert.Equal(t, tc.t, s)
	}

}

func from(vi ...holder) chan holder {
	in := make(chan holder)
	go func() {
		for _, v := range vi {
			in <- v
		}
		close(in)
	}()
	return in
}

func TestMapCommands(t *testing.T) {
	proc = "testdata/proc"
	in := make(chan holder)
	out := mapCommands(in)

	in <- holder{pid: "1446"}
	po := <-out
	assert.Regexp(t, "/usr/bin/redis-server.*", string(po.cmd))

	in <- holder{pid: "not-there"}
	po = <-out
	assert.Equal(t, "", string(po.cmd))

	close(in)
	_, bad := <-out
	assert.False(t, bad)
}

func TestGather(t *testing.T) {
	in := make(chan holder)
	out := gather(in)
	m := make(map[string]holder)

	in <- holder{pid: "1446", port: int64(12)}
	in <- holder{pid: "1446", port: int64(21)}
	in <- holder{pid: "1446", port: int64(2)}
	in <- holder{pid: "1447", port: int64(3)}

	close(in)

	for o := range out {
		m[o.pid] = o
	}

	assert.Len(t, m, 2)
	assert.Equal(t, "2,12,21", m["1446"].ports)
	assert.Equal(t, int64(2), m["1446"].port)
	assert.Equal(t, "3", m["1447"].ports)
}

func TestAlias(t *testing.T) {
	in := make(chan holder)
	out := mapAliases(in)

	in <- holder{pid: "1446", port: int64(12), cmd: []byte("/x/y/java -Dblag webstorm foo bar")}
	po := <-out
	assert.Equal(t, "webstorm", string(po.cmd))
	//todo magic number
	assert.Equal(t, 51, po.details.cmdColor)

	in <- holder{cmd: []byte("/opt/ccc/not-there")}
	po = <-out
	assert.Equal(t, 0, po.details.cmdColor)

	close(in)

}

func TestSortJ(t *testing.T) {
	in1 := holder{pid: "1000", port: int64(100), cmd: []byte("a cmd")}
	in2 := holder{pid: "2000", port: int64(200), cmd: []byte("b cmd")}
	in3 := holder{pid: "3000", port: int64(300), cmd: []byte("c cmd")}
	in11 := holder{pid: "1000", port: int64(100), cmd: []byte("a cmd")}

	test := func(si int, f ...holder) {
		in := make(chan holder)
		out := sortJ(in, si)
		for _, fi := range f {
			in <- fi
		}
		close(in)

		assert.Equal(t, in1, <-out)
		assert.Equal(t, in2, <-out)
		assert.Equal(t, in3, <-out)
		_, bad := <-out
		assert.False(t, bad)
	}

	test(0, in1, in3, in2)
	test(0, in2, in3, in1)
	test(1, in1, in3, in2)
	test(2, in1, in3, in2)

	test2 := func(si int, f ...holder) {
		in := make(chan holder)
		out := sortJ(in, si)
		for _, fi := range f {
			in <- fi
		}
		close(in)
		_, ok := <-out
		assert.True(t, ok)
		_, ok = <-out
		assert.True(t, ok)
		_, bad := <-out
		assert.False(t, bad)
	}
	test2(0, in1, in11)
	test2(1, in1, in11)
	test2(2, in1, in11)

}

func TestGreps(t *testing.T) {
	in := []holder{
		{cmd: []byte("foo")},
		{},
		{cmd: []byte("bar")},
	}

	out := grep(regexp.MustCompile(".o."), from(in...))

	po, ok := <-out
	assert.True(t, ok)
	assert.Equal(t, "foo", string(po.cmd))

	po, extra := <-out
	assert.False(t, extra)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"strings"
	"testing"
)

func testcmd(cmd string) (string, error) {
	fmt.Println("(T) " + cmd)
	switch {
	case cmd == "sudo ls -a1F /mnt/sda1/var/lib/docker/vfs/dir":
		return currenttest.vs.ls(), nil
	case cmd == "docker ps -aq --no-trunc":
		return "", nil
	case strings.HasPrefix(cmd, "docker inspect -f '{{ .Name }},{{ range $key, $value := .Volumes }}{{ $key }},{{ $value }}##~#{{ end }}' "):
		return "", nil
	case strings.HasPrefix(cmd, "sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):
		deleted := cmd[len("sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):]
		deletions = append(deletions, deleted)
		return "", nil
	case strings.HasPrefix(cmd, "sudo readlink /mnt/sda1/var/lib/docker/vfs/dir/"):
		if strings.Contains(cmd, ",nonexistent") {
			return "", errors.New("non-existent linked folder")
		}
		return "", nil
	case strings.HasPrefix(cmd, "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/"):
		if cmd == "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/" {
			return "", errors.New("non-ls linked folder")
		}
		return "", nil
	default:
		currentT.Fatalf("test '%s': unknown command!\n", cmd)
		return fmt.Sprintf("test '%s'", cmd), errors.New("unknown command")
	}
}

type volspecs []string
type Test struct {
	title string
	vs    volspecs
	res   []int
}

func (vs volspecs) ls() string {
	if len(vs) == 0 {
		return ""
	}
	res := ""
	for i, spec := range vs {
		if strings.HasSuffix(spec, "/") {
			spec = spec[:len(spec)-1]
			res = res + spec + strings.Repeat(fmt.Sprintf("%d", i), 64-len(spec)) + "/\n"
		}
		if strings.HasSuffix(spec, "@") {
			mp := "." + strings.Replace(spec, ";", "###", -1)
			mp = strings.Replace(mp, "/", ",#,", -1)
			res = res + mp + "\n"
		}
	}
	return res
}

var deletions = []string{}
var tests = []Test{
	Test{"empty vfs", []string{}, []int{0, 0, 0, 0, 0}},
	Test{"two volumes", []string{"fa/", "fb/"}, []int{0, 0, 2, 2, 0}},
	Test{"Invalid (ill-formed) markers must be deleted", []string{"cainv/path/a@"}, []int{0, 0, 0, 0, -1}},
	Test{"Invalid (no readlink) markers must be deleted", []string{"ca;/path/nonexistenta@", "cb;/path/nonexistentb@"}, []int{0, 0, 0, 0, -2}},
	Test{"Invalid (no ls) markers must be deleted", []string{"ca;/path/nolsa@", "cb;/path/nolsb@"}, []int{0, 0, 0, 0, -2}},
}
var currenttest Test
var currentT *testing.T

// TestContainers test different vfs scenarios
func TestContainers(t *testing.T) {
	cmd = testcmd
	currentT = t
	for i, test := range tests {
		currenttest = test
		deletions = []string{}
		main()
		tc := Containers()
		toc := OrphanedContainers()
		tv := Volumes()
		tov := OrphanedVolumes()
		tm := Markers()
		if len(tc) != test.res[0] {
			t.Errorf("Test %d: '%s' expected '%d' containers, got '%d'", i+1, test.title, test.res[0], len(tc))
		}
		if len(toc) != test.res[1] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned containers, got '%d'", i+1, test.title, test.res[1], len(toc))
		}
		if len(tv) != test.res[2] {
			t.Errorf("Test %d: '%s' expected '%d' volumes, got '%d'", i+1, test.title, test.res[2], len(tv))
		}
		if len(tov) != test.res[3] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned volumes, got '%d'", i+1, test.title, test.res[3], len(tov))
		}
		if nbmarkers(tm) != test.res[4] {
			t.Errorf("Test %d: '%s' expected '%d' markers, got '%d'", i+1, test.title, test.res[4], nbmarkers(tm))
		}
		fmt.Println("----------------")
	}
}

func nbmarkers(tm markers) int {
	res := len(tm)
	for _, d := range deletions {
		if strings.HasPrefix(d, ".") {
			res = res - 1
		}
	}
	return res
}
<commit_msg>gcl_test.go: a marker with .name###path now includes linked vdir name<commit_after>package main

import (
	"errors"
	"fmt"
	"regexp"
	"strings"
	"testing"
)

func testcmd(cmd string) (string, error) {
	fmt.Println("(T) " + cmd)
	switch {
	case cmd == "sudo ls -a1F /mnt/sda1/var/lib/docker/vfs/dir":
		return currenttest.vs.ls(), nil
	case cmd == "docker ps -aq --no-trunc":
		return "", nil
	case strings.HasPrefix(cmd, "docker inspect -f '{{ .Name }},{{ range $key, $value := .Volumes }}{{ $key }},{{ $value }}##~#{{ end }}' "):
		return "", nil
	case strings.HasPrefix(cmd, "sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):
		deleted := cmd[len("sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):]
		deletions = append(deletions, deleted)
		return "", nil
	case strings.HasPrefix(cmd, "sudo readlink /mnt/sda1/var/lib/docker/vfs/dir/"):
		if strings.Contains(cmd, ",nonexistent") {
			return "", errors.New("non-existent linked folder")
		}
		r := regexp.MustCompile(`.*\$([^#]+)###.*`)
		ss := r.FindStringSubmatch(cmd)
		if len(ss) == 2 {
			return ss[1], nil
		}
		return "", nil
	case strings.HasPrefix(cmd, "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/"):
		if cmd == "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/" {
			return "", errors.New("non-ls linked folder")
		}
		return "", nil
	default:
		currentT.Fatalf("test '%s': unknown command!\n", cmd)
		return fmt.Sprintf("test '%s'", cmd), errors.New("unknown command")
	}
}

type volspecs []string
type Test struct {
	title string
	vs    volspecs
	res   []int
}

func (vs volspecs) ls() string {
	if len(vs) == 0 {
		return ""
	}
	res := ""
	for i, spec := range vs {
		if strings.HasSuffix(spec, "/") {
			spec = spec[:len(spec)-1]
			res = res + spec + strings.Repeat(fmt.Sprintf("%d", i), 64-len(spec)) + "/\n"
		}
		if strings.HasSuffix(spec, "@") {
			mp := "." + strings.Replace(spec, ";", "###", -1)
			mp = strings.Replace(mp, "/", ",#,", -1)
			res = res + mp + "\n"
		}
	}
	return res
}

var deletions = []string{}
var tests = []Test{
	Test{"empty vfs", []string{}, []int{0, 0, 0, 0, 0}},
	Test{"two volumes", []string{"fa/", "fb/"}, []int{0, 0, 2, 2, 0}},
	Test{"Invalid (ill-formed) markers must be deleted", []string{"cainv/path/a@"}, []int{0, 0, 0, 0, -1}},
	Test{"Invalid (no readlink) markers must be deleted", []string{"ca;/path/nonexistenta@", "cb;/path/nonexistentb@"}, []int{0, 0, 0, 0, -2}},
	Test{"Invalid (no ls) markers must be deleted", []string{"ca;/path/nolsa@", "cb;/path/nolsb@"}, []int{0, 0, 0, 0, -2}},
}
var currenttest Test
var currentT *testing.T

// TestContainers test different vfs scenarios
func TestContainers(t *testing.T) {
	cmd = testcmd
	currentT = t
	for i, test := range tests {
		currenttest = test
		deletions = []string{}
		main()
		tc := Containers()
		toc := OrphanedContainers()
		tv := Volumes()
		tov := OrphanedVolumes()
		tm := Markers()
		if len(tc) != test.res[0] {
			t.Errorf("Test %d: '%s' expected '%d' containers, got '%d'", i+1, test.title, test.res[0], len(tc))
		}
		if len(toc) != test.res[1] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned containers, got '%d'", i+1, test.title, test.res[1], len(toc))
		}
		if len(tv) != test.res[2] {
			t.Errorf("Test %d: '%s' expected '%d' volumes, got '%d'", i+1, test.title, test.res[2], len(tv))
		}
		if len(tov) != test.res[3] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned volumes, got '%d'", i+1, test.title, test.res[3], len(tov))
		}
		if nbmarkers(tm) != test.res[4] {
			t.Errorf("Test %d: '%s' expected '%d' markers, got '%d'", i+1, test.title, test.res[4], nbmarkers(tm))
		}
		fmt.Println("----------------")
	}
}

func nbmarkers(tm markers) int {
	res := len(tm)
	for _, d := range deletions {
		if strings.HasPrefix(d, ".") {
			res = res - 1
		}
	}
	return res
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/binary"
	log "github.com/GameGophers/libs/nsq-logger"
	"io"
	"net"
	"net/http"
	_ "net/http/pprof"
	"os"
	"time"
)

import (
	_ "ipc"
	_ "numbers"
	. "types"
	"utils"
)

const (
	_port = ":8888"
)

const (
	SERVICE = "[AGENT]"
)

func main() {
	defer utils.PrintPanicStack()
	go func() {
		log.Info(http.ListenAndServe("0.0.0.0:6060", nil))
	}()

	log.SetPrefix(SERVICE)

	// server startup procedure
	startup()

	tcpAddr, err := net.ResolveTCPAddr("tcp4", _port)
	checkError(err)

	listener, err := net.ListenTCP("tcp", tcpAddr)
	checkError(err)

	log.Info("listening on:", listener.Addr())

	// loop accepting
	for {
		conn, err := listener.AcceptTCP()
		if err != nil {
			log.Warning("accept failed:", err)
			continue
		}
		go handleClient(conn)

		// check server close signal
		select {
		case <-die:
			listener.Close()
			goto FINAL
		default:
		}
	}
FINAL:
	// server closed, wait forever
	for {
		<-time.After(time.Second)
	}
}

// start a goroutine when a new connection is accepted
func handleClient(conn *net.TCPConn) {
	defer utils.PrintPanicStack()
	// set per-connection socket buffer
	conn.SetReadBuffer(SO_RCVBUF)

	// set initial socket buffer
	conn.SetWriteBuffer(SO_SNDBUF)

	// initial network control struct
	header := make([]byte, 2)
	in := make(chan []byte)
	defer func() {
		close(in) // session will close
	}()

	// pre-allocated packet buffer for each connection
	prealloc_buf := make([]byte, PREALLOC_BUFSIZE)
	index := 0

	// create a new session object for the connection
	var sess Session
	host, port, err := net.SplitHostPort(conn.RemoteAddr().String())
	if err != nil {
		log.Error("cannot get remote address:", err)
		return
	}
	sess.IP = net.ParseIP(host)
	log.Infof("new connection from:%v port:%v", host, port)

	// session die signal
	sess_die := make(chan bool)

	// create a write buffer
	out := new_buffer(conn, sess_die)
	go out.start()

	// start one agent for handling packet
	wg.Add(1)
	go agent(&sess, in, out, sess_die)

	// network loop
	for {
		// solve dead link problem
		conn.SetReadDeadline(time.Now().Add(TCP_READ_DEADLINE * time.Second))
		n, err := io.ReadFull(conn, header)
		if err != nil {
			log.Warningf("read header failed, ip:%v reason:%v size:%v", sess.IP, err, n)
			return
		}
		size := binary.BigEndian.Uint16(header)

		// alloc a byte slice for reading
		if index+int(size) > PREALLOC_BUFSIZE {
			index = 0
			prealloc_buf = make([]byte, PREALLOC_BUFSIZE)
		}
		data := prealloc_buf[index : index+int(size)]
		index += int(size)

		// read msg
		n, err = io.ReadFull(conn, data)
		if err != nil {
			log.Warningf("read msg failed, ip:%v reason:%v size:%v", sess.IP, err, n)
			return
		}

		select {
		case in <- data: // data queued
		case <-sess_die:
			log.Warningf("connection closed by logic, flag:%v ip:%v", sess.Flag, sess.IP)
			return
		}
	}
}

func checkError(err error) {
	if err != nil {
		log.Critical(err)
		os.Exit(-1)
	}
}
<commit_msg>rename pprof<commit_after>package main

import (
	"encoding/binary"
	log "github.com/GameGophers/libs/nsq-logger"
	_ "github.com/GameGophers/libs/statsd-pprof"
	"io"
	"net"
	"net/http"
	"os"
	"time"
)

import (
	_ "ipc"
	_ "numbers"
	. "types"
	"utils"
)

const (
	_port = ":8888"
)

const (
	SERVICE = "[AGENT]"
)

func main() {
	defer utils.PrintPanicStack()
	go func() {
		log.Info(http.ListenAndServe("0.0.0.0:6060", nil))
	}()

	log.SetPrefix(SERVICE)

	// server startup procedure
	startup()

	tcpAddr, err := net.ResolveTCPAddr("tcp4", _port)
	checkError(err)

	listener, err := net.ListenTCP("tcp", tcpAddr)
	checkError(err)

	log.Info("listening on:", listener.Addr())

	// loop accepting
	for {
		conn, err := listener.AcceptTCP()
		if err != nil {
			log.Warning("accept failed:", err)
			continue
		}
		go handleClient(conn)

		// check server close signal
		select {
		case <-die:
			listener.Close()
			goto FINAL
		default:
		}
	}
FINAL:
	// server closed, wait forever
	for {
		<-time.After(time.Second)
	}
}

// start a goroutine when a new connection is accepted
func handleClient(conn *net.TCPConn) {
	defer utils.PrintPanicStack()
	// set per-connection socket buffer
	conn.SetReadBuffer(SO_RCVBUF)

	// set initial socket buffer
	conn.SetWriteBuffer(SO_SNDBUF)

	// initial network control struct
	header := make([]byte, 2)
	in := make(chan []byte)
	defer func() {
		close(in) // session will close
	}()

	// pre-allocated packet buffer for each connection
	prealloc_buf := make([]byte, PREALLOC_BUFSIZE)
	index := 0

	// create a new session object for the connection
	var sess Session
	host, port, err := net.SplitHostPort(conn.RemoteAddr().String())
	if err != nil {
		log.Error("cannot get remote address:", err)
		return
	}
	sess.IP = net.ParseIP(host)
	log.Infof("new connection from:%v port:%v", host, port)

	// session die signal
	sess_die := make(chan bool)

	// create a write buffer
	out := new_buffer(conn, sess_die)
	go out.start()

	// start one agent for handling packet
	wg.Add(1)
	go agent(&sess, in, out, sess_die)

	// network loop
	for {
		// solve dead link problem
		conn.SetReadDeadline(time.Now().Add(TCP_READ_DEADLINE * time.Second))
		n, err := io.ReadFull(conn, header)
		if err != nil {
			log.Warningf("read header failed, ip:%v reason:%v size:%v", sess.IP, err, n)
			return
		}
		size := binary.BigEndian.Uint16(header)

		// alloc a byte slice for reading
		if index+int(size) > PREALLOC_BUFSIZE {
			index = 0
			prealloc_buf = make([]byte, PREALLOC_BUFSIZE)
		}
		data := prealloc_buf[index : index+int(size)]
		index += int(size)

		// read msg
		n, err = io.ReadFull(conn, data)
		if err != nil {
			log.Warningf("read msg failed, ip:%v reason:%v size:%v", sess.IP, err, n)
			return
		}

		select {
		case in <- data: // data queued
		case <-sess_die:
			log.Warningf("connection closed by logic, flag:%v ip:%v", sess.Flag, sess.IP)
			return
		}
	}
}

func checkError(err error) {
	if err != nil {
		log.Critical(err)
		os.Exit(-1)
	}
}
<|endoftext|>


<commit_before>package db

import (
	"fmt"

	"github.com/boltdb/bolt"
)

// ItemStoreQuery represents a query running naively over the ItemStore
//
// This is a naive and inefficient query. This is mostly used for ensuring
// the correctness of more efficient query methods.
//
// An ItemStoreQuery can be rerun by reinitializing the ctx; this
// typically happens when the query is Run.
type ItemStoreQuery struct {
	// Type and flavor of the item we're looking up
	rootType, rootFlavor StringHeapID
	// Minimum mod values we are required to find
	// are pointed to by their StringHeapID for easy lookup
	minModMap map[StringHeapID]uint16
	// League we are searching for
	league LeagueHeapID
	// How many items we are limited to finding
	maxDesired int
}

// NewItemStoreQuery returns an ItemStoreQuery with no context
func NewItemStoreQuery(rootType, rootFlavor StringHeapID,
	mods []StringHeapID, minModValues []uint16,
	league LeagueHeapID,
	maxDesired int) ItemStoreQuery {

	minModMap := make(map[StringHeapID]uint16)

	return ItemStoreQuery{
		rootType, rootFlavor,
		minModMap,
		league, maxDesired,
	}

}

// checkItem determines if a given item satisfies the query
func (q *ItemStoreQuery) checkItem(item Item) bool {
	countPresent := 0

	// Check each mod present on the provided item
	// against the mods we need.
	for _, mod := range item.Mods {
		required, ok := q.minModMap[mod.Mod]
		if !ok {
			continue
		}
		if len(mod.Values) < 1 || mod.Values[0] > required {
			countPresent++
		}
	}

	return countPresent >= len(q.minModMap)
}

// Run initialises transaction context for a query and attempts
// to find desired items.
func (q *ItemStoreQuery) Run(db *bolt.DB) ([]ID, error) {

	// Preallocate the ids to fit the max we want but also
	// allow us to use append rather than deal with indices
	ids := make([]ID, q.maxDesired)[:0]

	err := db.View(func(tx *bolt.Tx) error {

		b := getLeagueItemBucket(q.league, tx)
		if b == nil {
			return fmt.Errorf("failed to get league item bucket, LeagueHeapID=%d",
				q.league)
		}

		// Grab and set the cursor to last
		c := b.Cursor()
		k, v := c.Last()
		if k == nil {
			return fmt.Errorf("failed to get last item in itemstore, empty bucket")
		}
		// Test the item we got back
		var item Item
		_, err := item.UnmarshalMsg(v)
		if err != nil {
			return fmt.Errorf("failed to UnmarshalMsg itemstore item, err=%s",
				err)
		}
		if q.checkItem(item) {
			var id ID
			copy(id[:], k)
			ids = append(ids, id)
		}

		// Perform the actual search along the itemstore
		//
		// We go until we exhaust the entire store or find as many as we need
		for index := 0; len(ids) < q.maxDesired; index++ {

			// Grab a pair
			k, v := c.Prev()
			// Ignore nested buckets but also
			// handle reaching the start of the bucket
			if k == nil {
				// Both nil means we're done
				if v == nil {
					break
				}
				continue
			}
			// Test the item we got back
			var item Item
			_, err := item.UnmarshalMsg(v)
			if err != nil {
				return fmt.Errorf("failed to UnmarshalMsg itemstore item, err=%s",
					err)
			}
			if q.checkItem(item) {
				var id ID
				copy(id[:], k)
				ids = append(ids, id)
			}
		}

		return nil
	})

	return ids, err
}
<commit_msg>db ItemStoreQuery correctness<commit_after>package db

import (
	"fmt"

	"github.com/boltdb/bolt"
)

// ItemStoreQuery represents a query running naively over the ItemStore
//
// This is a naive and inefficient query. This is mostly used for ensuring
// the correctness of more efficient query methods.
//
// An ItemStoreQuery can be rerun by reinitializing the ctx; this
// typically happens when the query is Run.
type ItemStoreQuery struct {
	// Type and flavor of the item we're looking up
	rootType, rootFlavor StringHeapID
	// Minimum mod values we are required to find
	// are pointed to by their StringHeapID for easy lookup
	minModMap map[StringHeapID]uint16
	// League we are searching for
	league LeagueHeapID
	// How many items we are limited to finding
	maxDesired int
}

// NewItemStoreQuery returns an ItemStoreQuery with no context
//
// If len(mods) != len(minModValues), we panic; so don't give us garbage
func NewItemStoreQuery(rootType, rootFlavor StringHeapID,
	mods []StringHeapID, minModValues []uint16,
	league LeagueHeapID,
	maxDesired int) ItemStoreQuery {

	minModMap := make(map[StringHeapID]uint16)
	for i, mod := range mods {
		minModMap[mod] = minModValues[i]
	}

	return ItemStoreQuery{
		rootType, rootFlavor,
		minModMap,
		league, maxDesired,
	}

}

// checkItem determines if a given item satisfies the query
func (q *ItemStoreQuery) checkItem(item Item) bool {

	// Perform trivial check before expensive mod check
	validRoot := q.rootType == item.RootType
	validFlavor := q.rootFlavor == item.RootFlavor
	if !(validRoot && validFlavor) {
		return false
	}

	// Check each mod present on the provided item
	// against the mods we need.
	countPresent := 0
	for _, mod := range item.Mods {
		required, ok := q.minModMap[mod.Mod]
		if !ok {
			continue
		}
		if len(mod.Values) < 1 || mod.Values[0] > required {
			countPresent++
		}
	}

	return countPresent >= len(q.minModMap)
}

// checkPair determines if a pair is acceptable for our query
func (q *ItemStoreQuery) checkPair(k, v []byte) (bool, error) {
	var item Item
	_, err := item.UnmarshalMsg(v)
	if err != nil {
		return false, fmt.Errorf("failed to UnmarshalMsg itemstore item, err=%s",
			err)
	}
	return q.checkItem(item), nil
}

// Run initialises transaction context for a query and attempts
// to find desired items.
func (q *ItemStoreQuery) Run(db *bolt.DB) ([]ID, error) {

	// Preallocate the ids to fit the max we want but also
	// allow us to use append rather than deal with indices
	ids := make([]ID, q.maxDesired)[:0]

	err := db.View(func(tx *bolt.Tx) error {

		b := getLeagueItemBucket(q.league, tx)
		if b == nil {
			return fmt.Errorf("failed to get league item bucket, LeagueHeapID=%d",
				q.league)
		}

		// Grab and set the cursor to last
		c := b.Cursor()
		k, v := c.Last()
		if k == nil {
			return fmt.Errorf("failed to get last item in itemstore, empty bucket")
		}
		// Test the item we got back as long as it isn't a bucket
		if len(v) > 0 {
			valid, err := q.checkPair(k, v)
			if err != nil {
				return fmt.Errorf("failed to check pair in itemstore, err=%s",
					err)
			}
			if valid {
				var id ID
				copy(id[:], k)
				ids = append(ids, id)
			}
		}

		// Perform the actual search along the itemstore
		//
		// We go until we exhaust the entire store or find as many as we need
		for index := 0; len(ids) < q.maxDesired; index++ {

			// Grab a pair
			k, v := c.Prev()
			// Ignore nested buckets but also
			// handle reaching the start of the bucket
			if len(v) == 0 {
				// Both nil means we're done
				if k == nil {
					break
				}
				continue
			}
			valid, err := q.checkPair(k, v)
			if err != nil {
				return fmt.Errorf("failed to check pair in itemstore, err=%s",
					err)
			}
			if valid {
				var id ID
				copy(id[:], k)
				ids = append(ids, id)
			}
		}

		return nil
	})

	return ids, err
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"rollcage/core"
	"strings"
	"time"

	"github.com/cactus/cobra"
	"github.com/cactus/gologit"
)

func snapshotCmdRun(cmd *cobra.Command, args []string) {
	// requires root
	if !core.IsRoot() {
		gologit.Fatalf("Must be root to snapshot\n")
	}

	jailpath := core.GetJailByTagOrUUID(args[0])
	if jailpath == "" {
		gologit.Fatalf("No jail found by '%s'\n", args[0])
	}

	var snapname string
	if len(args) > 1 {
		snapname = strings.TrimLeft(args[1], "@")
	} else {
		snapname = fmt.Sprintf(
			"ioc-%s", time.Now().Format("2006-01-02_15:04:05"))
	}

	core.ZFSMust("snapshot", fmt.Sprintf("%s/root@%s", jailpath, snapname))
}

func init() {
	cmd := &cobra.Command{
		Use:   "snapshot UUID|TAG snapshotname",
		Short: "Create a zfs snapshot for jail",
		Run:   snapshotCmdRun,
		PreRun: func(cmd *cobra.Command, args []string) {
			if len(args) == 0 {
				gologit.Fatalln("Required UUID|TAG not provided")
			}
		},
	}

	RootCmd.AddCommand(cmd)
}
<commit_msg>support recursive snapshot<commit_after>package commands

import (
	"fmt"
	"rollcage/core"
	"strings"
	"time"

	"github.com/cactus/cobra"
	"github.com/cactus/gologit"
)

var recusiveSnapshot bool

func snapshotCmdRun(cmd *cobra.Command, args []string) {
	// requires root
	if !core.IsRoot() {
		gologit.Fatalf("Must be root to snapshot\n")
	}

	jailpath := core.GetJailByTagOrUUID(args[0])
	if jailpath == "" {
		gologit.Fatalf("No jail found by '%s'\n", args[0])
	}

	var snapname string
	if len(args) > 1 {
		snapname = strings.TrimLeft(args[1], "@")
	} else {
		snapname = fmt.Sprintf(
			"ioc-%s", time.Now().Format("2006-01-02_15:04:05"))
	}

	zfsCmd := []string{"snapshot"}
	if recusiveSnapshot {
		zfsCmd = append(zfsCmd, "-r")
	}
	zfsCmd = append(zfsCmd, fmt.Sprintf("%s/root@%s", jailpath, snapname))
	core.ZFSMust(zfsCmd...)
}

func init() {
	cmd := &cobra.Command{
		Use:   "snapshot UUID|TAG snapshotname",
		Short: "Create a zfs snapshot for jail",
		Run:   snapshotCmdRun,
		PreRun: func(cmd *cobra.Command, args []string) {
			if len(args) == 0 {
				gologit.Fatalln("Required UUID|TAG not provided")
			}
		},
	}

	cmd.Flags().BoolVarP(
		&recusiveSnapshot, "recursive", "r", false,
		"do a recursive snapshot of the jail root")

	RootCmd.AddCommand(cmd)
}
<|endoftext|>


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

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

// A vcsCmd describes how to use a version control system
// like Mercurial, Git, or Subversion.
type vcsCmd struct {
	name string
	cmd  string // name of binary to invoke command

	createCmd   string // command to download a fresh copy of a repository
	downloadCmd string // command to download updates into an existing repository

	tagCmd     []tagCmd // commands to list tags
	tagDefault string   // default tag to use
	tagSyncCmd string   // command to sync to specific tag
}

// A tagCmd describes a command to list available tags
// that can be passed to tagSyncCmd.
type tagCmd struct {
	cmd     string // command to list tags
	pattern string // regexp to extract tags from list
}

// vcsList lists the known version control systems
var vcsList = []*vcsCmd{
	vcsHg,
	vcsGit,
	vcsSvn,
	vcsBzr,
}

// vcsByCmd returns the version control system for the given
// command name (hg, git, svn, bzr).
func vcsByCmd(cmd string) *vcsCmd {
	for _, vcs := range vcsList {
		if vcs.cmd == cmd {
			return vcs
		}
	}
	return nil
}

// vcsHg describes how to use Mercurial.
var vcsHg = &vcsCmd{
	name: "Mercurial",
	cmd:  "hg",

	createCmd:   "clone -U {repo} {dir}",
	downloadCmd: "pull",

	// We allow both tag and branch names as 'tags'
	// for selecting a version.  This lets people have
	// a go.release.r60 branch and a go.1 branch
	// and make changes in both, without constantly
	// editing .hgtags.
	tagCmd: []tagCmd{
		{"tags", `^(\S+)`},
		{"branches", `^(\S+)`},
	},
	tagDefault: "default",
	tagSyncCmd: "update -r {tag}",
}

// vcsGit describes how to use Git.
var vcsGit = &vcsCmd{
	name: "Git",
	cmd:  "git",

	createCmd:   "clone {repo} {dir}",
	downloadCmd: "fetch",

	tagCmd:     []tagCmd{{"tag", `^(\S+)$`}},
	tagDefault: "master",
	tagSyncCmd: "checkout {tag}",
}

// vcsBzr describes how to use Bazaar.
var vcsBzr = &vcsCmd{
	name: "Bazaar",
	cmd:  "bzr",

	createCmd:   "branch {repo} {dir}",
	downloadCmd: "pull --overwrite", // TODO: REALLY?

	tagCmd:     []tagCmd{{"tags", `^(\S+)`}},
	tagDefault: "revno:-1",
	tagSyncCmd: "update -r {tag}",
}

// vcsSvn describes how to use Subversion.
var vcsSvn = &vcsCmd{
	name: "Subversion",
	cmd:  "svn",

	createCmd:   "checkout {repo} {dir}",
	downloadCmd: "update",

	// There is no tag command in subversion.
	// The branch information is all in the path names.
}

func (v *vcsCmd) String() string {
	return v.name
}

// run runs the command line cmd in the given directory.
// keyval is a list of key, value pairs.  run expands
// instances of {key} in cmd into value, but only after
// splitting cmd into individual arguments.
// If an error occurs, run prints the command line and the
// command's combined stdout+stderr to standard error.
// Otherwise run discards the command's output.
func (v *vcsCmd) run(dir string, cmd string, keyval ...string) error {
	_, err := v.run1(dir, false, cmd, keyval)
	return err
}

// runOutput is like run but returns the output of the command.
func (v *vcsCmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) {
	return v.run1(dir, true, cmd, keyval)
}

// run1 is the generalized implementation of run and runOutput.
func (v *vcsCmd) run1(dir string, output bool, cmdline string, keyval []string) ([]byte, error) {
	m := make(map[string]string)
	for i := 0; i < len(keyval); i += 2 {
		m[keyval[i]] = keyval[i+1]
	}
	args := strings.Fields(cmdline)
	for i, arg := range args {
		args[i] = expand(m, arg)
	}

	cmd := exec.Command(v.cmd, args...)
	cmd.Dir = dir
	if buildX {
		fmt.Printf("cd %s\n", dir)
		fmt.Printf("%s %s\n", v.cmd, strings.Join(args, " "))
	}
	var buf bytes.Buffer
	cmd.Stdout = &buf
	cmd.Stderr = &buf
	out := buf.Bytes()
	err := cmd.Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
		os.Stderr.Write(out)
		return nil, err
	}
	return out, nil
}

// create creates a new copy of repo in dir.
// The parent of dir must exist; dir must not.
func (v *vcsCmd) create(dir, repo string) error {
	return v.run(".", v.createCmd, "dir", dir, "repo", repo)
}

// download downloads any new changes for the repo in dir.
func (v *vcsCmd) download(dir string) error {
	return v.run(dir, v.downloadCmd)
}

// tags returns the list of available tags for the repo in dir.
func (v *vcsCmd) tags(dir string) ([]string, error) {
	var tags []string
	for _, tc := range v.tagCmd {
		out, err := v.runOutput(dir, tc.cmd)
		if err != nil {
			return nil, err
		}
		re := regexp.MustCompile(`(?m-s)` + tc.pattern)
		tags = append(tags, re.FindAllString(string(out), -1)...)
	}
	return tags, nil
}

// tagSync syncs the repo in dir to the named tag,
// which either is a tag returned by tags or is v.tagDefault.
func (v *vcsCmd) tagSync(dir, tag string) error {
	if v.tagSyncCmd == "" {
		return nil
	}
	return v.run(dir, v.tagSyncCmd, "tag", tag)
}

// A vcsPath is describes how to convert an import path into a
// version control system and repository name.
type vcsPath struct {
	prefix string                              // prefix this description applies to
	re     string                              // pattern for import path
	repo   string                              // repository to use (expand with match of re)
	vcs    string                              // version control system to use (expand with match of re)
	check  func(match map[string]string) error // additional checks

	regexp *regexp.Regexp // cached compiled form of re
}

// vcsForImportPath analyzes importPath to determine the
// version control system, and code repository to use.
// On return, repo is the repository URL and root is the
// import path corresponding to the root of the repository
// (thus root is a prefix of importPath).
func vcsForImportPath(importPath string) (vcs *vcsCmd, repo, root string, err error) {
	for _, srv := range vcsPaths {
		if !strings.HasPrefix(importPath, srv.prefix) {
			continue
		}
		m := srv.regexp.FindStringSubmatch(importPath)
		if m == nil {
			if srv.prefix != "" {
				return nil, "", "", fmt.Errorf("invalid %s import path %q", srv.prefix, importPath)
			}
			continue
		}

		// Build map of named subexpression matches for expand.
		match := map[string]string{
			"prefix": srv.prefix,
			"import": importPath,
		}
		for i, name := range srv.regexp.SubexpNames() {
			if name != "" && match[name] == "" {
				match[name] = m[i]
			}
		}
		if srv.vcs != "" {
			match["vcs"] = expand(match, srv.vcs)
		}
		if srv.repo != "" {
			match["repo"] = expand(match, srv.repo)
		}
		if srv.check != nil {
			if err := srv.check(match); err != nil {
				return nil, "", "", err
			}
		}
		vcs := vcsByCmd(match["vcs"])
		if vcs == nil {
			return nil, "", "", fmt.Errorf("unknown version control system %q", match["vcs"])
		}
		return vcs, match["repo"], match["root"], nil
	}
	return nil, "", "", fmt.Errorf("unrecognized import path %q", importPath)
}

// expand rewrites s to replace {k} with match[k] for each key k in match.
func expand(match map[string]string, s string) string {
	for k, v := range match {
		s = strings.Replace(s, "{"+k+"}", v, -1)
	}
	return s
}

// vcsPaths lists the known vcs paths.
var vcsPaths = []*vcsPath{
	// Google Code - new syntax
	{
		prefix: "code.google.com/",
		re:     `^(?P<root>code\.google\.com/p/(?P<project>[a-z0-9\-]+)(\.(?P<subrepo>[a-z0-9\-]+))?)(/[A-Za-z0-9_.\-]+)*$`,
		repo:   "https://{root}",
		check:  googleCodeVCS,
	},

	// Google Code - old syntax
	{
		re:    `^(?P<project>[a-z0-9_\-.]+)\.googlecode\.com/(git|hg|svn)(?P<path>/.*)?$`,
		check: oldGoogleCode,
	},

	// Github
	{
		prefix: "github.com/",
		re:     `^(?P<root>github\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`,
		repo:   "https://{root}",
		check:  noVCSSuffix,
	},

	// Bitbucket
	{
		prefix: "bitbucket.org/",
		re:     `^(?P<root>bitbucket\.org/(?P<bitname>[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
		repo:   "https://{root}",
		check:  bitbucketVCS,
	},

	// Launchpad
	{
		prefix: "launchpad.net/",
		re:     `^(?P<root>launchpad\.net/([A-Za-z0-9_.\-]+(/[A-Za-z0-9_.\-]+)?|~[A-Za-z0-9_.\-]+/(\+junk|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
		vcs:    "bzr",
		repo:   "https://{root}",
	},

	// General syntax for any server.
	{
		re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`,
	},
}

func init() {
	// fill in cached regexps.
	// Doing this eagerly discovers invalid regexp syntax
	// without having to run a command that needs that regexp.
	for _, srv := range vcsPaths {
		srv.regexp = regexp.MustCompile(srv.re)
	}
}

// noVCSSuffix checks that the repository name does not
// end in .foo for any version control system foo.
// The usual culprit is ".git".
func noVCSSuffix(match map[string]string) error {
	repo := match["repo"]
	for _, vcs := range vcsList {
		if strings.HasSuffix(repo, "."+vcs.cmd) {
			return fmt.Errorf("invalid version control suffix in %s path", match["prefix"])
		}
	}
	return nil
}

var googleCheckout = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`)

// googleCodeVCS determines the version control system for
// a code.google.com repository, by scraping the project's
// /source/checkout page.
func googleCodeVCS(match map[string]string) error {
	if err := noVCSSuffix(match); err != nil {
		return err
	}
	data, err := httpGET(expand(match, "https://code.google.com/p/{project}/source/checkout?repo={subrepo}"))
	if err != nil {
		return err
	}

	if m := googleCheckout.FindSubmatch(data); m != nil {
		if vcs := vcsByCmd(string(m[1])); vcs != nil {
			// Subversion requires the old URLs.
			// TODO: Test.
			if vcs == vcsSvn {
				if match["subrepo"] != "" {
					return fmt.Errorf("sub-repositories not supported in Google Code Subversion projects")
				}
				match["repo"] = expand(match, "https://{project}.googlecode.com/svn")
			}
			match["vcs"] = vcs.cmd
			return nil
		}
	}

	return fmt.Errorf("unable to detect version control system for code.google.com/ path")
}

// oldGoogleCode is invoked for old-style foo.googlecode.com paths.
// It prints an error giving the equivalent new path.
func oldGoogleCode(match map[string]string) error {
	return fmt.Errorf("invalid Google Code import path: use %s instead",
		expand(match, "code.google.com/p/{project}{path}"))
}

// bitbucketVCS determines the version control system for a
// BitBucket repository, by using the BitBucket API.
func bitbucketVCS(match map[string]string) error {
	if err := noVCSSuffix(match); err != nil {
		return err
	}

	var resp struct {
		SCM string `json:"scm"`
	}
	url := expand(match, "https://api.bitbucket.org/1.0/repositories/{bitname}")
	data, err := httpGET(url)
	if err != nil {
		return err
	}
	if err := json.Unmarshal(data, &resp); err != nil {
		return fmt.Errorf("decoding %s: %v", url, err)
	}

	if vcsByCmd(resp.SCM) != nil {
		match["vcs"] = resp.SCM
		if resp.SCM == "git" {
			match["repo"] += ".git"
		}
		return nil
	}

	return fmt.Errorf("unable to detect version control system for bitbucket.org/ path")
}
<commit_msg>cmd/go: fix get github<commit_after>// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

// A vcsCmd describes how to use a version control system
// like Mercurial, Git, or Subversion.
type vcsCmd struct {
	name string
	cmd  string // name of binary to invoke command

	createCmd   string // command to download a fresh copy of a repository
	downloadCmd string // command to download updates into an existing repository

	tagCmd     []tagCmd // commands to list tags
	tagDefault string   // default tag to use
	tagSyncCmd string   // command to sync to specific tag
}

// A tagCmd describes a command to list available tags
// that can be passed to tagSyncCmd.
type tagCmd struct {
	cmd     string // command to list tags
	pattern string // regexp to extract tags from list
}

// vcsList lists the known version control systems
var vcsList = []*vcsCmd{
	vcsHg,
	vcsGit,
	vcsSvn,
	vcsBzr,
}

// vcsByCmd returns the version control system for the given
// command name (hg, git, svn, bzr).
func vcsByCmd(cmd string) *vcsCmd {
	for _, vcs := range vcsList {
		if vcs.cmd == cmd {
			return vcs
		}
	}
	return nil
}

// vcsHg describes how to use Mercurial.
var vcsHg = &vcsCmd{
	name: "Mercurial",
	cmd:  "hg",

	createCmd:   "clone -U {repo} {dir}",
	downloadCmd: "pull",

	// We allow both tag and branch names as 'tags'
	// for selecting a version.  This lets people have
	// a go.release.r60 branch and a go.1 branch
	// and make changes in both, without constantly
	// editing .hgtags.
	tagCmd: []tagCmd{
		{"tags", `^(\S+)`},
		{"branches", `^(\S+)`},
	},
	tagDefault: "default",
	tagSyncCmd: "update -r {tag}",
}

// vcsGit describes how to use Git.
var vcsGit = &vcsCmd{
	name: "Git",
	cmd:  "git",

	createCmd:   "clone {repo} {dir}",
	downloadCmd: "fetch",

	tagCmd:     []tagCmd{{"tag", `^(\S+)$`}},
	tagDefault: "master",
	tagSyncCmd: "checkout {tag}",
}

// vcsBzr describes how to use Bazaar.
var vcsBzr = &vcsCmd{
	name: "Bazaar",
	cmd:  "bzr",

	createCmd:   "branch {repo} {dir}",
	downloadCmd: "pull --overwrite", // TODO: REALLY?

	tagCmd:     []tagCmd{{"tags", `^(\S+)`}},
	tagDefault: "revno:-1",
	tagSyncCmd: "update -r {tag}",
}

// vcsSvn describes how to use Subversion.
var vcsSvn = &vcsCmd{
	name: "Subversion",
	cmd:  "svn",

	createCmd:   "checkout {repo} {dir}",
	downloadCmd: "update",

	// There is no tag command in subversion.
	// The branch information is all in the path names.
}

func (v *vcsCmd) String() string {
	return v.name
}

// run runs the command line cmd in the given directory.
// keyval is a list of key, value pairs.  run expands
// instances of {key} in cmd into value, but only after
// splitting cmd into individual arguments.
// If an error occurs, run prints the command line and the
// command's combined stdout+stderr to standard error.
// Otherwise run discards the command's output.
func (v *vcsCmd) run(dir string, cmd string, keyval ...string) error {
	_, err := v.run1(dir, false, cmd, keyval)
	return err
}

// runOutput is like run but returns the output of the command.
func (v *vcsCmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) {
	return v.run1(dir, true, cmd, keyval)
}

// run1 is the generalized implementation of run and runOutput.
func (v *vcsCmd) run1(dir string, output bool, cmdline string, keyval []string) ([]byte, error) {
	m := make(map[string]string)
	for i := 0; i < len(keyval); i += 2 {
		m[keyval[i]] = keyval[i+1]
	}
	args := strings.Fields(cmdline)
	for i, arg := range args {
		args[i] = expand(m, arg)
	}

	cmd := exec.Command(v.cmd, args...)
	cmd.Dir = dir
	if buildX {
		fmt.Printf("cd %s\n", dir)
		fmt.Printf("%s %s\n", v.cmd, strings.Join(args, " "))
	}
	var buf bytes.Buffer
	cmd.Stdout = &buf
	cmd.Stderr = &buf
	out := buf.Bytes()
	err := cmd.Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
		os.Stderr.Write(out)
		return nil, err
	}
	return out, nil
}

// create creates a new copy of repo in dir.
// The parent of dir must exist; dir must not.
func (v *vcsCmd) create(dir, repo string) error {
	return v.run(".", v.createCmd, "dir", dir, "repo", repo)
}

// download downloads any new changes for the repo in dir.
func (v *vcsCmd) download(dir string) error {
	return v.run(dir, v.downloadCmd)
}

// tags returns the list of available tags for the repo in dir.
func (v *vcsCmd) tags(dir string) ([]string, error) {
	var tags []string
	for _, tc := range v.tagCmd {
		out, err := v.runOutput(dir, tc.cmd)
		if err != nil {
			return nil, err
		}
		re := regexp.MustCompile(`(?m-s)` + tc.pattern)
		tags = append(tags, re.FindAllString(string(out), -1)...)
	}
	return tags, nil
}

// tagSync syncs the repo in dir to the named tag,
// which either is a tag returned by tags or is v.tagDefault.
func (v *vcsCmd) tagSync(dir, tag string) error {
	if v.tagSyncCmd == "" {
		return nil
	}
	return v.run(dir, v.tagSyncCmd, "tag", tag)
}

// A vcsPath is describes how to convert an import path into a
// version control system and repository name.
type vcsPath struct {
	prefix string                              // prefix this description applies to
	re     string                              // pattern for import path
	repo   string                              // repository to use (expand with match of re)
	vcs    string                              // version control system to use (expand with match of re)
	check  func(match map[string]string) error // additional checks

	regexp *regexp.Regexp // cached compiled form of re
}

// vcsForImportPath analyzes importPath to determine the
// version control system, and code repository to use.
// On return, repo is the repository URL and root is the
// import path corresponding to the root of the repository
// (thus root is a prefix of importPath).
func vcsForImportPath(importPath string) (vcs *vcsCmd, repo, root string, err error) {
	for _, srv := range vcsPaths {
		if !strings.HasPrefix(importPath, srv.prefix) {
			continue
		}
		m := srv.regexp.FindStringSubmatch(importPath)
		if m == nil {
			if srv.prefix != "" {
				return nil, "", "", fmt.Errorf("invalid %s import path %q", srv.prefix, importPath)
			}
			continue
		}

		// Build map of named subexpression matches for expand.
		match := map[string]string{
			"prefix": srv.prefix,
			"import": importPath,
		}
		for i, name := range srv.regexp.SubexpNames() {
			if name != "" && match[name] == "" {
				match[name] = m[i]
			}
		}
		if srv.vcs != "" {
			match["vcs"] = expand(match, srv.vcs)
		}
		if srv.repo != "" {
			match["repo"] = expand(match, srv.repo)
		}
		if srv.check != nil {
			if err := srv.check(match); err != nil {
				return nil, "", "", err
			}
		}
		vcs := vcsByCmd(match["vcs"])
		if vcs == nil {
			return nil, "", "", fmt.Errorf("unknown version control system %q", match["vcs"])
		}
		return vcs, match["repo"], match["root"], nil
	}
	return nil, "", "", fmt.Errorf("unrecognized import path %q", importPath)
}

// expand rewrites s to replace {k} with match[k] for each key k in match.
func expand(match map[string]string, s string) string {
	for k, v := range match {
		s = strings.Replace(s, "{"+k+"}", v, -1)
	}
	return s
}

// vcsPaths lists the known vcs paths.
var vcsPaths = []*vcsPath{
	// Google Code - new syntax
	{
		prefix: "code.google.com/",
		re:     `^(?P<root>code\.google\.com/p/(?P<project>[a-z0-9\-]+)(\.(?P<subrepo>[a-z0-9\-]+))?)(/[A-Za-z0-9_.\-]+)*$`,
		repo:   "https://{root}",
		check:  googleCodeVCS,
	},

	// Google Code - old syntax
	{
		re:    `^(?P<project>[a-z0-9_\-.]+)\.googlecode\.com/(git|hg|svn)(?P<path>/.*)?$`,
		check: oldGoogleCode,
	},

	// Github
	{
		prefix: "github.com/",
		re:     `^(?P<root>github\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`,
		vcs:    "git",
		repo:   "https://{root}",
		check:  noVCSSuffix,
	},

	// Bitbucket
	{
		prefix: "bitbucket.org/",
		re:     `^(?P<root>bitbucket\.org/(?P<bitname>[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
		repo:   "https://{root}",
		check:  bitbucketVCS,
	},

	// Launchpad
	{
		prefix: "launchpad.net/",
		re:     `^(?P<root>launchpad\.net/([A-Za-z0-9_.\-]+(/[A-Za-z0-9_.\-]+)?|~[A-Za-z0-9_.\-]+/(\+junk|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
		vcs:    "bzr",
		repo:   "https://{root}",
	},

	// General syntax for any server.
	{
		re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`,
	},
}

func init() {
	// fill in cached regexps.
	// Doing this eagerly discovers invalid regexp syntax
	// without having to run a command that needs that regexp.
	for _, srv := range vcsPaths {
		srv.regexp = regexp.MustCompile(srv.re)
	}
}

// noVCSSuffix checks that the repository name does not
// end in .foo for any version control system foo.
// The usual culprit is ".git".
func noVCSSuffix(match map[string]string) error {
	repo := match["repo"]
	for _, vcs := range vcsList {
		if strings.HasSuffix(repo, "."+vcs.cmd) {
			return fmt.Errorf("invalid version control suffix in %s path", match["prefix"])
		}
	}
	return nil
}

var googleCheckout = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`)

// googleCodeVCS determines the version control system for
// a code.google.com repository, by scraping the project's
// /source/checkout page.
func googleCodeVCS(match map[string]string) error {
	if err := noVCSSuffix(match); err != nil {
		return err
	}
	data, err := httpGET(expand(match, "https://code.google.com/p/{project}/source/checkout?repo={subrepo}"))
	if err != nil {
		return err
	}

	if m := googleCheckout.FindSubmatch(data); m != nil {
		if vcs := vcsByCmd(string(m[1])); vcs != nil {
			// Subversion requires the old URLs.
			// TODO: Test.
			if vcs == vcsSvn {
				if match["subrepo"] != "" {
					return fmt.Errorf("sub-repositories not supported in Google Code Subversion projects")
				}
				match["repo"] = expand(match, "https://{project}.googlecode.com/svn")
			}
			match["vcs"] = vcs.cmd
			return nil
		}
	}

	return fmt.Errorf("unable to detect version control system for code.google.com/ path")
}

// oldGoogleCode is invoked for old-style foo.googlecode.com paths.
// It prints an error giving the equivalent new path.
func oldGoogleCode(match map[string]string) error {
	return fmt.Errorf("invalid Google Code import path: use %s instead",
		expand(match, "code.google.com/p/{project}{path}"))
}

// bitbucketVCS determines the version control system for a
// BitBucket repository, by using the BitBucket API.
func bitbucketVCS(match map[string]string) error {
	if err := noVCSSuffix(match); err != nil {
		return err
	}

	var resp struct {
		SCM string `json:"scm"`
	}
	url := expand(match, "https://api.bitbucket.org/1.0/repositories/{bitname}")
	data, err := httpGET(url)
	if err != nil {
		return err
	}
	if err := json.Unmarshal(data, &resp); err != nil {
		return fmt.Errorf("decoding %s: %v", url, err)
	}

	if vcsByCmd(resp.SCM) != nil {
		match["vcs"] = resp.SCM
		if resp.SCM == "git" {
			match["repo"] += ".git"
		}
		return nil
	}

	return fmt.Errorf("unable to detect version control system for bitbucket.org/ path")
}
<|endoftext|>


<commit_before>package irc

import (
	//	"github.com/thoj/go-ircevent"
	"crypto/tls"
	"testing"
	"time"
)

func TestConnectionEmtpyServer(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	err := irccon.Connect("")
	if err == nil {
		t.Fatal("emtpy server string not detected")
	}
}

func TestConnectionDoubleColon(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	err := irccon.Connect("::")
	if err == nil {
		t.Fatal("wrong number of ':' not detected")
	}
}

func TestConnectionMissingHost(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	err := irccon.Connect(":6667")
	if err == nil {
		t.Fatal("missing host not detected")
	}
}

func TestConnectionMissingPort(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	err := irccon.Connect("chat.freenode.net:")
	if err == nil {
		t.Fatal("missing port not detected")
	}
}

func TestConnectionNegativePort(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	err := irccon.Connect("chat.freenode.net:-1")
	if err == nil {
		t.Fatal("negative port number not detected")
	}
}

func TestConnectionTooLargePort(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	err := irccon.Connect("chat.freenode.net:65536")
	if err == nil {
		t.Fatal("too large port number not detected")
	}
}

func TestConnectionMissingLog(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	irccon.Log = nil
	err := irccon.Connect("chat.freenode.net:6667")
	if err == nil {
		t.Fatal("missing 'Log' not detected")
	}
}

func TestConnectionEmptyUser(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	// user may be changed after creation
	irccon.user = ""
	err := irccon.Connect("chat.freenode.net:6667")
	if err == nil {
		t.Fatal("empty 'user' not detected")
	}
}

func TestConnectionEmptyNick(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	// nick may be changed after creation
	irccon.nick = ""
	err := irccon.Connect("chat.freenode.net:6667")
	if err == nil {
		t.Fatal("empty 'nick' not detected")
	}
}

func TestRemoveCallback(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	irccon.VerboseCallbackHandler = true
	irccon.Debug = true

	done := make(chan int, 10)

	irccon.AddCallback("TEST", func(e *Event) { done <- 1 })
	id := irccon.AddCallback("TEST", func(e *Event) { done <- 2 })
	irccon.AddCallback("TEST", func(e *Event) { done <- 3 })

	// Should remove callback at index 1
	irccon.RemoveCallback("TEST", id)

	irccon.RunCallbacks(&Event{
		Code: "TEST",
	})

	var results []int

	results = append(results, <-done)
	results = append(results, <-done)

	if len(results) != 2 || results[0] == 2 || results[1] == 2 {
		t.Error("Callback 2 not removed")
	}
}

func TestWildcardCallback(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	irccon.VerboseCallbackHandler = true
	irccon.Debug = true

	done := make(chan int, 10)

	irccon.AddCallback("TEST", func(e *Event) { done <- 1 })
	irccon.AddCallback("*", func(e *Event) { done <- 2 })

	irccon.RunCallbacks(&Event{
		Code: "TEST",
	})

	var results []int

	results = append(results, <-done)
	results = append(results, <-done)

	if len(results) != 2 || !(results[0] == 1 && results[1] == 2) {
		t.Error("Wildcard callback not called")
	}
}

func TestClearCallback(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	irccon.VerboseCallbackHandler = true
	irccon.Debug = true

	done := make(chan int, 10)

	irccon.AddCallback("TEST", func(e *Event) { done <- 0 })
	irccon.AddCallback("TEST", func(e *Event) { done <- 1 })
	irccon.ClearCallback("TEST")
	irccon.AddCallback("TEST", func(e *Event) { done <- 2 })
	irccon.AddCallback("TEST", func(e *Event) { done <- 3 })

	irccon.RunCallbacks(&Event{
		Code: "TEST",
	})

	var results []int

	results = append(results, <-done)
	results = append(results, <-done)

	if len(results) != 2 || !(results[0] == 2 && results[1] == 3) {
		t.Error("Callbacks not cleared")
	}
}

func TestIRCemptyNick(t *testing.T) {
	irccon := IRC("", "go-eventirc")
	irccon = nil
	if irccon != nil {
		t.Error("empty nick didn't result in error")
		t.Fail()
	}
}

func TestIRCemptyUser(t *testing.T) {
	irccon := IRC("go-eventirc", "")
	if irccon != nil {
		t.Error("empty user didn't result in error")
	}
}
func TestConnection(t *testing.T) {
	irccon1 := IRC("go-eventirc1", "go-eventirc1")
	irccon1.VerboseCallbackHandler = true
	irccon1.Debug = true
	irccon2 := IRC("go-eventirc2", "go-eventirc2")
	irccon2.VerboseCallbackHandler = true
	irccon2.Debug = true
	err := irccon1.Connect("irc.freenode.net:6667")
	if err != nil {
		t.Log(err.Error())
		t.Fatal("Can't connect to freenode.")
	}
	err = irccon2.Connect("irc.freenode.net:6667")
	if err != nil {
		t.Log(err.Error())
		t.Fatal("Can't connect to freenode.")
	}
	irccon1.AddCallback("001", func(e *Event) { irccon1.Join("#go-eventirc") })
	irccon2.AddCallback("001", func(e *Event) { irccon2.Join("#go-eventirc") })
	con2ok := false
	irccon1.AddCallback("366", func(e *Event) {
		t := time.NewTicker(1 * time.Second)
		i := 10
		for {
			<-t.C
			irccon1.Privmsgf("#go-eventirc", "Test Message%d\n", i)
			if con2ok {
				i -= 1
			}
			if i == 0 {
				t.Stop()
				irccon1.Quit()
			}
		}
	})

	irccon2.AddCallback("366", func(e *Event) {
		irccon2.Privmsg("#go-eventirc", "Test Message\n")
		con2ok = true
		irccon2.Nick("go-eventnewnick")
	})

	irccon2.AddCallback("PRIVMSG", func(e *Event) {
		t.Log(e.Message())
		if e.Message() == "Test Message5" {
			irccon2.Quit()
		}
	})

	irccon2.AddCallback("NICK", func(e *Event) {
		if irccon2.nickcurrent == "go-eventnewnick" {
			t.Fatal("Nick change did not work!")
		}
	})
	go irccon2.Loop()
	irccon1.Loop()
}

func TestConnectionSSL(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	irccon.VerboseCallbackHandler = true
	irccon.Debug = true
	irccon.UseTLS = true
	irccon.TLSConfig = &tls.Config{InsecureSkipVerify: true}
	err := irccon.Connect("irc.freenode.net:7000")
	if err != nil {
		t.Log(err.Error())
		t.Fatal("Can't connect to freenode.")
	}
	irccon.AddCallback("001", func(e *Event) { irccon.Join("#go-eventirc") })

	irccon.AddCallback("366", func(e *Event) {
		irccon.Privmsg("#go-eventirc", "Test Message\n")
		time.Sleep(2 * time.Second)
		irccon.Quit()
	})

	irccon.Loop()
}
<commit_msg>Test empty Action<commit_after>package irc

import (
	//	"github.com/thoj/go-ircevent"
	"crypto/tls"
	"testing"
	"time"
)

func TestParserCrash1(t *testing.T) {
	Fuzz([]byte("pRIVMSG \x01ACTION\x01"))
}

func TestConnectionEmtpyServer(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	err := irccon.Connect("")
	if err == nil {
		t.Fatal("emtpy server string not detected")
	}
}

func TestConnectionDoubleColon(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	err := irccon.Connect("::")
	if err == nil {
		t.Fatal("wrong number of ':' not detected")
	}
}

func TestConnectionMissingHost(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	err := irccon.Connect(":6667")
	if err == nil {
		t.Fatal("missing host not detected")
	}
}

func TestConnectionMissingPort(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	err := irccon.Connect("chat.freenode.net:")
	if err == nil {
		t.Fatal("missing port not detected")
	}
}

func TestConnectionNegativePort(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	err := irccon.Connect("chat.freenode.net:-1")
	if err == nil {
		t.Fatal("negative port number not detected")
	}
}

func TestConnectionTooLargePort(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	err := irccon.Connect("chat.freenode.net:65536")
	if err == nil {
		t.Fatal("too large port number not detected")
	}
}

func TestConnectionMissingLog(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	irccon.Log = nil
	err := irccon.Connect("chat.freenode.net:6667")
	if err == nil {
		t.Fatal("missing 'Log' not detected")
	}
}

func TestConnectionEmptyUser(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	// user may be changed after creation
	irccon.user = ""
	err := irccon.Connect("chat.freenode.net:6667")
	if err == nil {
		t.Fatal("empty 'user' not detected")
	}
}

func TestConnectionEmptyNick(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	// nick may be changed after creation
	irccon.nick = ""
	err := irccon.Connect("chat.freenode.net:6667")
	if err == nil {
		t.Fatal("empty 'nick' not detected")
	}
}

func TestRemoveCallback(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	irccon.VerboseCallbackHandler = true
	irccon.Debug = true

	done := make(chan int, 10)

	irccon.AddCallback("TEST", func(e *Event) { done <- 1 })
	id := irccon.AddCallback("TEST", func(e *Event) { done <- 2 })
	irccon.AddCallback("TEST", func(e *Event) { done <- 3 })

	// Should remove callback at index 1
	irccon.RemoveCallback("TEST", id)

	irccon.RunCallbacks(&Event{
		Code: "TEST",
	})

	var results []int

	results = append(results, <-done)
	results = append(results, <-done)

	if len(results) != 2 || results[0] == 2 || results[1] == 2 {
		t.Error("Callback 2 not removed")
	}
}

func TestWildcardCallback(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	irccon.VerboseCallbackHandler = true
	irccon.Debug = true

	done := make(chan int, 10)

	irccon.AddCallback("TEST", func(e *Event) { done <- 1 })
	irccon.AddCallback("*", func(e *Event) { done <- 2 })

	irccon.RunCallbacks(&Event{
		Code: "TEST",
	})

	var results []int

	results = append(results, <-done)
	results = append(results, <-done)

	if len(results) != 2 || !(results[0] == 1 && results[1] == 2) {
		t.Error("Wildcard callback not called")
	}
}

func TestClearCallback(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	irccon.VerboseCallbackHandler = true
	irccon.Debug = true

	done := make(chan int, 10)

	irccon.AddCallback("TEST", func(e *Event) { done <- 0 })
	irccon.AddCallback("TEST", func(e *Event) { done <- 1 })
	irccon.ClearCallback("TEST")
	irccon.AddCallback("TEST", func(e *Event) { done <- 2 })
	irccon.AddCallback("TEST", func(e *Event) { done <- 3 })

	irccon.RunCallbacks(&Event{
		Code: "TEST",
	})

	var results []int

	results = append(results, <-done)
	results = append(results, <-done)

	if len(results) != 2 || !(results[0] == 2 && results[1] == 3) {
		t.Error("Callbacks not cleared")
	}
}

func TestIRCemptyNick(t *testing.T) {
	irccon := IRC("", "go-eventirc")
	irccon = nil
	if irccon != nil {
		t.Error("empty nick didn't result in error")
		t.Fail()
	}
}

func TestIRCemptyUser(t *testing.T) {
	irccon := IRC("go-eventirc", "")
	if irccon != nil {
		t.Error("empty user didn't result in error")
	}
}
func TestConnection(t *testing.T) {
	irccon1 := IRC("go-eventirc1", "go-eventirc1")
	irccon1.VerboseCallbackHandler = true
	irccon1.Debug = true
	irccon2 := IRC("go-eventirc2", "go-eventirc2")
	irccon2.VerboseCallbackHandler = true
	irccon2.Debug = true
	err := irccon1.Connect("irc.freenode.net:6667")
	if err != nil {
		t.Log(err.Error())
		t.Fatal("Can't connect to freenode.")
	}
	err = irccon2.Connect("irc.freenode.net:6667")
	if err != nil {
		t.Log(err.Error())
		t.Fatal("Can't connect to freenode.")
	}
	irccon1.AddCallback("001", func(e *Event) { irccon1.Join("#go-eventirc") })
	irccon2.AddCallback("001", func(e *Event) { irccon2.Join("#go-eventirc") })
	con2ok := false
	irccon1.AddCallback("366", func(e *Event) {
		t := time.NewTicker(1 * time.Second)
		i := 10
		for {
			<-t.C
			irccon1.Privmsgf("#go-eventirc", "Test Message%d\n", i)
			if con2ok {
				i -= 1
			}
			if i == 0 {
				t.Stop()
				irccon1.Quit()
			}
		}
	})

	irccon2.AddCallback("366", func(e *Event) {
		irccon2.Privmsg("#go-eventirc", "Test Message\n")
		con2ok = true
		irccon2.Nick("go-eventnewnick")
	})

	irccon2.AddCallback("PRIVMSG", func(e *Event) {
		t.Log(e.Message())
		if e.Message() == "Test Message5" {
			irccon2.Quit()
		}
	})

	irccon2.AddCallback("NICK", func(e *Event) {
		if irccon2.nickcurrent == "go-eventnewnick" {
			t.Fatal("Nick change did not work!")
		}
	})
	go irccon2.Loop()
	irccon1.Loop()
}

func TestConnectionSSL(t *testing.T) {
	irccon := IRC("go-eventirc", "go-eventirc")
	irccon.VerboseCallbackHandler = true
	irccon.Debug = true
	irccon.UseTLS = true
	irccon.TLSConfig = &tls.Config{InsecureSkipVerify: true}
	err := irccon.Connect("irc.freenode.net:7000")
	if err != nil {
		t.Log(err.Error())
		t.Fatal("Can't connect to freenode.")
	}
	irccon.AddCallback("001", func(e *Event) { irccon.Join("#go-eventirc") })

	irccon.AddCallback("366", func(e *Event) {
		irccon.Privmsg("#go-eventirc", "Test Message\n")
		time.Sleep(2 * time.Second)
		irccon.Quit()
	})

	irccon.Loop()
}
<|endoftext|>


<commit_before>package completion

import (
	"bytes"
	"fmt"
	"os"
	"strings"
	"unicode"

	"../conio"
	"../lua"
)

type CompletionList struct {
	AllLine string
	List    []string
	RawWord string // have quotation
	Word    string
	Pos     int
}

func listUpComplete(this *conio.Buffer) (*CompletionList, error) {
	var err error
	rv := CompletionList{}

	// environment completion.
	rv.AllLine = this.String()
	rv.List, rv.Pos, err = listUpEnv(rv.AllLine)
	if len(rv.List) > 0 && rv.Pos >= 0 && err == nil {
		rv.RawWord = rv.AllLine[rv.Pos:]
		rv.Word = rv.RawWord
		return &rv, nil
	}

	// filename or commandname completion
	rv.RawWord, rv.Pos = this.CurrentWord()
	rv.Word = strings.Replace(rv.RawWord, "\"", "", -1)
	if rv.Pos > 0 {
		rv.List, err = listUpFiles(rv.Word)
	} else {
		rv.List, err = listUpCommands(rv.Word)
	}
	L, Lok := this.Session.Tag.(lua.Lua)
	if !Lok {
		panic("conio.LineEditor.Tag is not lua.Lua")
	}
	L.GetGlobal("nyagos")
	L.GetField(-1, "completion_hook")
	L.Remove(-2) // remove nyagos-table
	if L.IsFunction(-1) {
		L.NewTable()
		L.PushString(rv.RawWord)
		L.SetField(-2, "rawword")
		L.Push(rv.Pos + 1)
		L.SetField(-2, "pos")
		L.PushString(rv.AllLine)
		L.SetField(-2, "text")
		L.PushString(rv.Word)
		L.SetField(-2, "word")
		L.NewTable()
		for key, val := range rv.List {
			L.Push(1 + key)
			L.PushString(val)
			L.SetTable(-3)
		}
		L.SetField(-2, "list")
		if err := L.Call(1, 1); err != nil {
			fmt.Println(err)
		}
		if L.IsTable(-1) {
			list := make([]string, 0, len(rv.List)+32)
			wordUpr := strings.ToUpper(rv.Word)
			for i := 1; true; i++ {
				L.Push(i)
				L.GetTable(-2)
				str, strErr := L.ToString(-1)
				L.Pop(1)
				if strErr != nil || str == "" {
					break
				}
				strUpr := strings.ToUpper(str)
				if strings.HasPrefix(strUpr, wordUpr) {
					list = append(list, str)
				}
			}
			if len(list) > 0 {
				rv.List = list
			}
		}
	}
	L.Pop(1) // remove something not function or result-table
	return &rv, err
}

func KeyFuncCompletionList(this *conio.Buffer) conio.Result {
	comp, err := listUpComplete(this)
	if err != nil {
		return conio.CONTINUE
	}
	fmt.Print("\n")
	conio.BoxPrint(comp.List, os.Stdout)
	this.RepaintAll()
	return conio.CONTINUE
}

func CommonPrefix(list []string) string {
	if len(list) < 1 {
		return ""
	}
	common := list[0]
	for _, f := range list[1:] {
		cr := strings.NewReader(common)
		fr := strings.NewReader(f)
		i := 0
		var buffer bytes.Buffer
		for {
			ch, _, cerr := cr.ReadRune()
			fh, _, ferr := fr.ReadRune()
			if cerr != nil || ferr != nil || unicode.ToUpper(ch) != unicode.ToUpper(fh) {
				break
			}
			buffer.WriteRune(ch)
			i++
		}
		common = buffer.String()
	}
	return common
}

func endWithRoot(path string) bool {
	return strings.HasSuffix(path, "\\") || strings.HasSuffix(path, "/")
}

func KeyFuncCompletion(this *conio.Buffer) conio.Result {
	comp, err := listUpComplete(this)
	if err != nil || comp.List == nil || len(comp.List) <= 0 {
		return conio.CONTINUE
	}

	slashToBackSlash := true
	firstFoundSlashPos := strings.IndexRune(comp.Word, '/')
	firstFoundBackSlashPos := strings.IndexRune(comp.Word, '\\')
	if firstFoundSlashPos >= 0 && (firstFoundBackSlashPos == -1 || firstFoundSlashPos < firstFoundBackSlashPos) {
		slashToBackSlash = false
	}

	commonStr := CommonPrefix(comp.List)
	needQuote := strings.ContainsRune(comp.Word, '"')
	if !needQuote {
		for _, node := range comp.List {
			if strings.ContainsAny(node, " &") {
				needQuote = true
				break
			}
		}
	}
	if needQuote {
		buffer := make([]byte, 0, 100)
		buffer = append(buffer, '"')
		buffer = append(buffer, commonStr...)
		if len(comp.List) == 1 && !endWithRoot(comp.List[0]) {
			buffer = append(buffer, '"')
		}
		commonStr = string(buffer)
	}
	if len(comp.List) == 1 && !endWithRoot(commonStr) {
		commonStr += " "
	}
	if slashToBackSlash {
		commonStr = strings.Replace(commonStr, "/", "\\", -1)
	}
	if comp.RawWord == commonStr {
		fmt.Print("\n")
		conio.BoxPrint(comp.List, os.Stdout)
		this.RepaintAll()
		return conio.CONTINUE
	}
	this.ReplaceAndRepaint(comp.Pos, commonStr)
	return conio.CONTINUE
}
<commit_msg>Fixed completion panic enbugged 4f4606eaac622cc073a4dc4876a725f4edd4801d<commit_after>package completion

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

	"../conio"
	"../interpreter"
	"../lua"
)

type CompletionList struct {
	AllLine string
	List    []string
	RawWord string // have quotation
	Word    string
	Pos     int
}

func listUpComplete(this *conio.Buffer) (*CompletionList, error) {
	var err error
	rv := CompletionList{}

	// environment completion.
	rv.AllLine = this.String()
	rv.List, rv.Pos, err = listUpEnv(rv.AllLine)
	if len(rv.List) > 0 && rv.Pos >= 0 && err == nil {
		rv.RawWord = rv.AllLine[rv.Pos:]
		rv.Word = rv.RawWord
		return &rv, nil
	}

	// filename or commandname completion
	rv.RawWord, rv.Pos = this.CurrentWord()
	rv.Word = strings.Replace(rv.RawWord, "\"", "", -1)
	if rv.Pos > 0 {
		rv.List, err = listUpFiles(rv.Word)
	} else {
		rv.List, err = listUpCommands(rv.Word)
	}
	var L lua.Lua
	var L_ok bool

	if it, it_ok := this.Session.Tag.(*interpreter.Interpreter); !it_ok {
		if L, L_ok = this.Session.Tag.(lua.Lua); !L_ok {
			return nil, errors.New("listUpComplete: could not get lua instance")
		}
	} else {
		L, L_ok = it.Tag.(lua.Lua)
	}
	if !L_ok {
		return nil, errors.New("listUpComplete: could not get lua instance")
	}

	L.GetGlobal("nyagos")
	L.GetField(-1, "completion_hook")
	L.Remove(-2) // remove nyagos-table
	if L.IsFunction(-1) {
		L.NewTable()
		L.PushString(rv.RawWord)
		L.SetField(-2, "rawword")
		L.Push(rv.Pos + 1)
		L.SetField(-2, "pos")
		L.PushString(rv.AllLine)
		L.SetField(-2, "text")
		L.PushString(rv.Word)
		L.SetField(-2, "word")
		L.NewTable()
		for key, val := range rv.List {
			L.Push(1 + key)
			L.PushString(val)
			L.SetTable(-3)
		}
		L.SetField(-2, "list")
		if err := L.Call(1, 1); err != nil {
			fmt.Println(err)
		}
		if L.IsTable(-1) {
			list := make([]string, 0, len(rv.List)+32)
			wordUpr := strings.ToUpper(rv.Word)
			for i := 1; true; i++ {
				L.Push(i)
				L.GetTable(-2)
				str, strErr := L.ToString(-1)
				L.Pop(1)
				if strErr != nil || str == "" {
					break
				}
				strUpr := strings.ToUpper(str)
				if strings.HasPrefix(strUpr, wordUpr) {
					list = append(list, str)
				}
			}
			if len(list) > 0 {
				rv.List = list
			}
		}
	}
	L.Pop(1) // remove something not function or result-table
	return &rv, err
}

func KeyFuncCompletionList(this *conio.Buffer) conio.Result {
	comp, err := listUpComplete(this)
	if err != nil {
		return conio.CONTINUE
	}
	fmt.Print("\n")
	conio.BoxPrint(comp.List, os.Stdout)
	this.RepaintAll()
	return conio.CONTINUE
}

func CommonPrefix(list []string) string {
	if len(list) < 1 {
		return ""
	}
	common := list[0]
	for _, f := range list[1:] {
		cr := strings.NewReader(common)
		fr := strings.NewReader(f)
		i := 0
		var buffer bytes.Buffer
		for {
			ch, _, cerr := cr.ReadRune()
			fh, _, ferr := fr.ReadRune()
			if cerr != nil || ferr != nil || unicode.ToUpper(ch) != unicode.ToUpper(fh) {
				break
			}
			buffer.WriteRune(ch)
			i++
		}
		common = buffer.String()
	}
	return common
}

func endWithRoot(path string) bool {
	return strings.HasSuffix(path, "\\") || strings.HasSuffix(path, "/")
}

func KeyFuncCompletion(this *conio.Buffer) conio.Result {
	comp, err := listUpComplete(this)
	if err != nil || comp.List == nil || len(comp.List) <= 0 {
		return conio.CONTINUE
	}

	slashToBackSlash := true
	firstFoundSlashPos := strings.IndexRune(comp.Word, '/')
	firstFoundBackSlashPos := strings.IndexRune(comp.Word, '\\')
	if firstFoundSlashPos >= 0 && (firstFoundBackSlashPos == -1 || firstFoundSlashPos < firstFoundBackSlashPos) {
		slashToBackSlash = false
	}

	commonStr := CommonPrefix(comp.List)
	needQuote := strings.ContainsRune(comp.Word, '"')
	if !needQuote {
		for _, node := range comp.List {
			if strings.ContainsAny(node, " &") {
				needQuote = true
				break
			}
		}
	}
	if needQuote {
		buffer := make([]byte, 0, 100)
		buffer = append(buffer, '"')
		buffer = append(buffer, commonStr...)
		if len(comp.List) == 1 && !endWithRoot(comp.List[0]) {
			buffer = append(buffer, '"')
		}
		commonStr = string(buffer)
	}
	if len(comp.List) == 1 && !endWithRoot(commonStr) {
		commonStr += " "
	}
	if slashToBackSlash {
		commonStr = strings.Replace(commonStr, "/", "\\", -1)
	}
	if comp.RawWord == commonStr {
		fmt.Print("\n")
		conio.BoxPrint(comp.List, os.Stdout)
		this.RepaintAll()
		return conio.CONTINUE
	}
	this.ReplaceAndRepaint(comp.Pos, commonStr)
	return conio.CONTINUE
}
<|endoftext|>