<commit_before>/*
Copyright 2011 Google Inc.

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 main

import (
	"fmt"
	"html"
	"html/template"
	"io"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"
)

func taskList(w http.ResponseWriter, r *http.Request) {
	drawTemplate(w, "taskList", tmplData{
		"Title": "Tasks",
		"Tasks": GetTasks(),
		"Log":   logBuf.String(),
	})
}

func killTask(w http.ResponseWriter, r *http.Request, t *Task) {
	st := t.Status()
	in := st.Running
	if in == nil {
		http.Error(w, "task not running", 500)
		return
	}
	pid, _ := strconv.Atoi(r.FormValue("pid"))
	if in.Pid() != pid || pid == 0 {
		http.Error(w, "active task pid doesn't match pid parameter", 500)
		return
	}
	t.Stop()
	drawTemplate(w, "killTask", tmplData{
		"Title": "Kill",
		"Task":  t,
		"PID":   pid,
	})
}

func taskView(w http.ResponseWriter, r *http.Request) {
	taskName := r.URL.Path[len("/task/"):]
	t, ok := GetTask(taskName)
	if !ok {
		http.NotFound(w, r)
		return
	}
	mode := r.FormValue("mode")
	switch mode {
	case "kill":
		killTask(w, r, t)
		return
	default:
		http.Error(w, "unknown mode", 400)
		return
	case "":
	}

	data := tmplData{
		"Title": t.Name + " status",
		"Task":  t,
	}

	st := t.Status()
	in := st.Running
	if in != nil {
		data["PID"] = in.Pid()
		data["Output"] = in.Output()
		data["Cmd"] = in.lr
		data["StartTime"] = in.startTime
		data["StartAgo"] = time.Now().Sub(in.startTime)
	}

	// list failures in reverse-chronological order
	{
		f := st.Failures
		r := make([]*TaskInstance, len(f))
		for i := range f {
			r[len(r)-i-1] = f[i]
		}
		data["Failures"] = r
	}

	drawTemplate(w, "viewTask", data)
}

func runWebServer(ln net.Listener) {
	mux := http.NewServeMux()
	// TODO: wrap mux in auth handler, making it available only to
	// TCP connections from localhost and owned by the uid/gid of
	// the running process.
	mux.HandleFunc("/", taskList)
	mux.HandleFunc("/task/", taskView)
	s := &http.Server{
		Handler: mux,
	}
	err := s.Serve(ln)
	if err != nil {
		logger.Fatalf("webserver exiting: %v", err)
	}
}

type tmplData map[string]interface{}

func drawTemplate(w io.Writer, name string, data tmplData) {
	err := templates[name].ExecuteTemplate(w, "root", data)
	if err != nil {
		logger.Println(err)
	}
}

var templates = make(map[string]*template.Template)

func init() {
	for name, html := range templateHTML {
		t := template.New(name).Funcs(templateFuncs)
		template.Must(t.Parse(html))
		template.Must(t.Parse(rootHTML))
		templates[name] = t
	}
}

const rootHTML = `
{{define "root"}}

		<title>{{.Title}} - runsit</title>

{{.Title}}

		{{template "body" .}}

{{end}}
`

var templateHTML = map[string]string{
	"taskList": `
	{{define "body"}}

Running

		{{range .Tasks}}

{{.Name}}: {{maybePre .Status.Summary}}

		{{end}}

Log

{{.Log}}

	{{end}}
`,
	"killTask": `
	{{define "body"}}

Killed pid {{.PID}}.

Back to {{.Task.Name}} status.

	{{end}}
`,
	"viewTask": `
	{{define "body"}}

[Tasks]

{{maybePre .Task.Status.Summary}}

		{{with .Cmd}}
		{{/* TODO: embolden arg[0] */}}

command: {{range .Argv}}{{maybeQuote .}} {{end}}

		{{end}}

		{{if .PID}}

Running Instance

Started {{.StartTime}}, {{.StartAgo}} ago.

PID={{.PID}} [kill]

		{{end}}

		{{with .Output}}{{template "output" .}}{{end}}

		{{with .Failures}}

Failures

		{{range .}}{{template "output" .Output}}{{end}}
		{{end}}

	{{end}}
	{{define "output"}}

		{{range .}}

{{.Data}}

		{{end}}

	{{end}}
`,
}

var templateFuncs = template.FuncMap{
	"maybeQuote": maybeQuote,
	"maybePre":   maybePre,
}

func maybeQuote(s string) string {
	if strings.Contains(s, " ") || strings.Contains(s, `"`) {
		return fmt.Sprintf("%q", s)
	}
	return s
}

func maybePre(s string) interface{} {
	if strings.Contains(s, "\n") {
		return template.HTML("
" + html.EscapeString(s) + "
")
	}
	return s
}
<commit_msg>show hostname on all pages; gets confusing with many runsits<commit_after>/*
Copyright 2011 Google Inc.

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 main

import (
	"fmt"
	"html"
	"html/template"
	"io"
	"os"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"
)

func taskList(w http.ResponseWriter, r *http.Request) {
	hostname, _ := os.Hostname()
	drawTemplate(w, "taskList", tmplData{
		"Title": "Tasks on " + hostname,
		"Tasks": GetTasks(),
		"Log":   logBuf.String(),
	})
}

func killTask(w http.ResponseWriter, r *http.Request, t *Task) {
	st := t.Status()
	in := st.Running
	if in == nil {
		http.Error(w, "task not running", 500)
		return
	}
	pid, _ := strconv.Atoi(r.FormValue("pid"))
	if in.Pid() != pid || pid == 0 {
		http.Error(w, "active task pid doesn't match pid parameter", 500)
		return
	}
	t.Stop()
	drawTemplate(w, "killTask", tmplData{
		"Title": "Kill",
		"Task":  t,
		"PID":   pid,
	})
}

func taskView(w http.ResponseWriter, r *http.Request) {
	taskName := r.URL.Path[len("/task/"):]
	t, ok := GetTask(taskName)
	if !ok {
		http.NotFound(w, r)
		return
	}
	mode := r.FormValue("mode")
	switch mode {
	case "kill":
		killTask(w, r, t)
		return
	default:
		http.Error(w, "unknown mode", 400)
		return
	case "":
	}

	data := tmplData{
		"Title": t.Name + " status",
		"Task":  t,
	}

	st := t.Status()
	in := st.Running
	if in != nil {
		data["PID"] = in.Pid()
		data["Output"] = in.Output()
		data["Cmd"] = in.lr
		data["StartTime"] = in.startTime
		data["StartAgo"] = time.Now().Sub(in.startTime)
	}

	// list failures in reverse-chronological order
	{
		f := st.Failures
		r := make([]*TaskInstance, len(f))
		for i := range f {
			r[len(r)-i-1] = f[i]
		}
		data["Failures"] = r
	}

	drawTemplate(w, "viewTask", data)
}

func runWebServer(ln net.Listener) {
	mux := http.NewServeMux()
	// TODO: wrap mux in auth handler, making it available only to
	// TCP connections from localhost and owned by the uid/gid of
	// the running process.
	mux.HandleFunc("/", taskList)
	mux.HandleFunc("/task/", taskView)
	s := &http.Server{
		Handler: mux,
	}
	err := s.Serve(ln)
	if err != nil {
		logger.Fatalf("webserver exiting: %v", err)
	}
}

type tmplData map[string]interface{}

func drawTemplate(w io.Writer, name string, data tmplData) {
	if name != "taskList" {
		hostname, _ := os.Hostname()
		data["RootLink"] = "/"
		data["Hostname"] = hostname
	}
	err := templates[name].ExecuteTemplate(w, "root", data)
	if err != nil {
		logger.Println(err)
	}
}

var templates = make(map[string]*template.Template)

func init() {
	for name, html := range templateHTML {
		t := template.New(name).Funcs(templateFuncs)
		template.Must(t.Parse(html))
		template.Must(t.Parse(rootHTML))
		templates[name] = t
	}
}

const rootHTML = `
{{define "root"}}

		<title>{{.Title}} - runsit</title>

                {{if .RootLink}}

runsit on {{.Hostname}}.
                {{end}}

{{.Title}}

		{{template "body" .}}

{{end}}
`

var templateHTML = map[string]string{
	"taskList": `
	{{define "body"}}

Running

		{{range .Tasks}}

{{.Name}}: {{maybePre .Status.Summary}}

		{{end}}

Log

{{.Log}}

	{{end}}
`,
	"killTask": `
	{{define "body"}}

Killed pid {{.PID}}.

Back to {{.Task.Name}} status.

	{{end}}
`,
	"viewTask": `
	{{define "body"}}

{{maybePre .Task.Status.Summary}}

		{{with .Cmd}}
		{{/* TODO: embolden arg[0] */}}

command: {{range .Argv}}{{maybeQuote .}} {{end}}

		{{end}}

		{{if .PID}}

Running Instance

Started {{.StartTime}}, {{.StartAgo}} ago.

PID={{.PID}} [kill]

		{{end}}

		{{with .Output}}{{template "output" .}}{{end}}

		{{with .Failures}}

Failures

		{{range .}}{{template "output" .Output}}{{end}}
		{{end}}

	{{end}}
	{{define "output"}}

		{{range .}}

{{.Data}}

		{{end}}

	{{end}}
`,
}

var templateFuncs = template.FuncMap{
	"maybeQuote": maybeQuote,
	"maybePre":   maybePre,
}

func maybeQuote(s string) string {
	if strings.Contains(s, " ") || strings.Contains(s, `"`) {
		return fmt.Sprintf("%q", s)
	}
	return s
}

func maybePre(s string) interface{} {
	if strings.Contains(s, "\n") {
		return template.HTML("
" + html.EscapeString(s) + "
")
	}
	return s
}
<|endoftext|>


<commit_before>package main

import (
	log "code.google.com/p/log4go"
	"fmt"
	"github.com/codegangsta/martini"
	"github.com/codegangsta/martini-contrib/render"
	"net/http"
)

func runWebServer(jeego *Jeego) {
	go func() {
		log.Info("Starting web server on port %d", jeego.config.WebServerPort)

		m := martini.Classic()
		m.Use(render.Renderer())

		// API: nodes list
		m.Get("/api/nodes", func(r render.Render) {
			r.JSON(200, map[string]interface{}{"nodes": jeego.database.nodes})
		})

		addr := fmt.Sprintf(":%d", jeego.config.WebServerPort)
		http.ListenAndServe(addr, m)
	}()
}
<commit_msg>web: use pat instead of martini / implements /api/nodes and /api/nodes/:id endpoints / go fmt<commit_after>package main

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

	log "code.google.com/p/log4go"
	"github.com/bmizerany/pat"
)

// helper
func respondsWithError404(w http.ResponseWriter) {
	w.WriteHeader(http.StatusNotFound)
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	io.WriteString(w, "404: Not Found")
}

// helper
func respondsWithError400(w http.ResponseWriter) {
	w.WriteHeader(http.StatusBadRequest)
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	io.WriteString(w, "400: Bad Request")
}

// helper
func respondsWithJSON(w http.ResponseWriter, data map[string]interface{}) {
	response, err := json.Marshal(data)
	if err != nil {
		panic(log.Critical(err))
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(response)
}

func addAccessControlHeaders(w http.ResponseWriter, meth string) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", meth)
	w.Header().Set("Access-Control-Allow-Headers", "Origin, Accept, Content-Type")
}

// GET /api/nodes
func wrapHandlerNodes(jeego *Jeego) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		addAccessControlHeaders(w, "GET")

		respondsWithJSON(w, map[string]interface{}{"nodes": jeego.database.nodes})
	}
}

// GET /api/nodes/:id
func wrapHandlerNode(jeego *Jeego) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		addAccessControlHeaders(w, "GET")

		nodeId, err := strconv.Atoi(req.URL.Query().Get(":id"))
		if err != nil {
			respondsWithError400(w)
		} else {
			node := jeego.database.nodeForId(nodeId)
			if node != nil {
				respondsWithJSON(w, map[string]interface{}{"node": node})
			} else {
				respondsWithError404(w)
			}
		}
	}
}

func runWebServer(jeego *Jeego) {
	go func() {
		log.Info("Starting web server on port %d", jeego.config.WebServerPort)

		addr := fmt.Sprintf(":%d", jeego.config.WebServerPort)

		mux := pat.New()
		mux.Get("/api/nodes", wrapHandlerNodes(jeego))
		mux.Get("/api/nodes/:id", wrapHandlerNode(jeego))

		http.Handle("/", mux)
		err := http.ListenAndServe(addr, nil)
		if err != nil {
			panic(log.Critical(err))
		}
	}()
}
<|endoftext|>


<commit_before>// -*-  tab-width:4  -*-
package slurm

import (
	"fmt"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	//	"reflect"
)

var _ = fmt.Println

func IsSLURM() bool {
	_, err := exec.LookPath("scontrol")
	return err == nil
}

type NodeStatus struct {
	Partition string
	NpAlloc   int
	NpTotal   int
}

func CollectFreeSlots() map[NodeStatus]int {
	n2p := nodeToPartition()
	nodes := scontrolShow("nodes")
	counts := make(map[NodeStatus]int)

	for _, n := range nodes {
		CPUAlloc, _ := strconv.Atoi(n["CPUAlloc"])
		CPUTot, _ := strconv.Atoi(n["CPUTot"])
		name := n2p[n["NodeHostName"]]
		if name == "" {
			continue
		}

		s := NodeStatus{n2p[n["NodeHostName"]], CPUAlloc, CPUTot}
		_, ok := counts[s]
		if ok {
			counts[s] += 1
		} else {
			counts[s] = 1
		}
	}
	return counts
}

// Mapping from NodeHostName to PartitionName for each
// node
func nodeToPartition() map[string]string {
	partitions := scontrolShow("partition")
	n2plist := make(map[string][]string)

	for _, part := range partitions {
		for _, nodeGroup := range strings.Split(part["Nodes"], ",") {
			for _, node := range expandBracket(nodeGroup) {
				_, ok := n2plist[node]
				if ok {
					n2plist[node] = append(n2plist[node], part["PartitionName"])
				} else {
					n2plist[node] = []string{part["PartitionName"]}
				}
			}
		}
	}

	out := make(map[string]string)
	for k, v := range n2plist {
		out[k] = strings.Join(v, ",")
	}

	return out
}

func scontrolShow(cmd string) []map[string]string {
	data, _ := exec.Command("scontrol", "show", "-o", cmd).Output()
	values := make([]map[string]string, 0)

	for _, line := range strings.Split(string(data), "\n") {
		if len(line) == 0 {
			continue
		}

		fields := strings.Fields(line)
		row := make(map[string]string)
		for _, field := range fields {
			items := strings.Split(field, "=")
			if len(items) == 2 {
				row[items[0]] = items[1]
			}
		}
		values = append(values, row)
	}
	return values
}

func expandBracket(s string) []string {
	m := regexp.MustCompile(`(.*)\[(\d+)\-(\d+)(?:,(\d+)\-(\d+))*\]`)
	groups := m.FindStringSubmatch(s)
	out := make([]string, 0)
	if len(groups) == 0 {
		return out
	}

	// for i, g := range groups {
	// 	fmt.Println(i, g, len(g))
	// }

	prefix := groups[1]
	for i := 2; i < len(groups); i += 2 {
		//	 	leading0 := (groups[i][0:1] == "0")
		if len(groups[i]) > 0 && len(groups[i+1]) > 0 {
			first, _ := strconv.Atoi(groups[i])
			last, _ := strconv.Atoi(groups[i+1])
			for j := first; j < last+1; j++ {
				suffix := strconv.Itoa(j)
				out = append(out, prefix+suffix)
			}
		}
	}

	//fmt.Println(out)
	return out
}
<commit_msg>Fix free-slots on xstream<commit_after>// -*-  tab-width:4  -*-
package slurm

import (
	"fmt"
	"os/exec"
	"strconv"
	"strings"
)

var _ = fmt.Println

func IsSLURM() bool {
	_, err := exec.LookPath("scontrol")
	return err == nil
}

type NodeStatus struct {
	Partition string
	NpAlloc   int
	NpTotal   int
}

func CollectFreeSlots() map[NodeStatus]int {
	n2p := nodeToPartition()
	nodes := scontrolShow("nodes")
	counts := make(map[NodeStatus]int)

	for _, n := range nodes {
		CPUAlloc, _ := strconv.Atoi(n["CPUAlloc"])
		CPUTot, _ := strconv.Atoi(n["CPUTot"])
		name := n2p[n["NodeHostName"]]
		if name == "" {
			continue
		}

		s := NodeStatus{n2p[n["NodeHostName"]], CPUAlloc, CPUTot}
		_, ok := counts[s]
		if ok {
			counts[s] += 1
		} else {
			counts[s] = 1
		}
	}

    return counts
}

// Mapping from NodeHostName to PartitionName for each
// node
func nodeToPartition() map[string]string {
    partitions := scontrolShow("partition")
    n2plist := make(map[string][]string)

    for _, part := range partitions {
        for _, node := range expandBracket(part["Nodes"]) {
            _, ok := n2plist[node]
            if ok {
                n2plist[node] = append(n2plist[node], part["PartitionName"])
            } else {
                n2plist[node] = []string{part["PartitionName"]}
            }
        }
    }

    out := make(map[string]string)
    for k, v := range n2plist {
        out[k] = strings.Join(v, ",")
    }

	return out
}

func scontrolShow(cmd string) []map[string]string {
	data, _ := exec.Command("scontrol", "show", "-o", cmd).Output()
	values := make([]map[string]string, 0)

	for _, line := range strings.Split(string(data), "\n") {
		if len(line) == 0 {
			continue
		}

		fields := strings.Fields(line)
		row := make(map[string]string)
		for _, field := range fields {
			items := strings.Split(field, "=")
			if len(items) == 2 {
				row[items[0]] = items[1]
			}
		}
		values = append(values, row)
	}
	return values
}

func expandBracket(s string) []string {
	data, _ := exec.Command("scontrol", "show", "hostnames",s).Output()
	values := make([]string, 0)
	for _, line := range strings.Split(string(data), "\n") {
        values = append(values, line);
	}
	return values
}
<|endoftext|>


<commit_before>package dns

// XfrToken is used when doing [IA]xfr with a remote server.
type XfrToken struct {
	RR    []RR  // the set of RRs in the answer section of the AXFR reply message
	Error error // if something went wrong, this contains the error
}

// XfrReceive performs a [AI]xfr request (depends on the message's Qtype). It returns
// a channel of XfrToken on which the replies from the server are sent. At the end of
// the transfer the channel is closed.
// It panics if the Qtype does not equal TypeAXFR or TypeIXFR. The messages are TSIG checked if
// needed, no other post-processing is performed. The caller must dissect the returned
// messages.
//
// Basic use pattern for receiving an AXFR:
//
//	// m contains the AXFR request
//	t, e := client.XfrReceive(m, "127.0.0.1:53")
//	for r := range t {
//		// ... deal with r.RR or r.Error
//	}
func (c *Client) XfrReceive(q *Msg, a string) (chan *XfrToken, error) {
	w := new(reply)
	w.client = c
	w.addr = a
	w.req = q
	if err := w.dial(); err != nil {
		return nil, err
	}
	if err := w.send(q); err != nil {
		return nil, err
	}
	e := make(chan *XfrToken)
	switch q.Question[0].Qtype {
	case TypeAXFR:
		go w.axfrReceive(q, e)
		return e, nil
	case TypeIXFR:
		go w.ixfrReceive(q, e)
		return e, nil
	default:
		return nil, nil
	}
	panic("dns: not reached")
}

func (w *reply) axfrReceive(q *Msg, c chan *XfrToken) {
	first := true
	defer w.conn.Close()
	defer close(c)
	for {
		in, err := w.receive()
		if err != nil {
			c <- &XfrToken{nil, err}
			return
		}
		if in.Id != q.Id {
			c <- &XfrToken{in.Answer, ErrId}
			return
		}
		if first {
			if !checkXfrSOA(in, true) {
				c <- &XfrToken{in.Answer, ErrSoa}
				return
			}
			first = !first
		}

		if !first {
			w.tsigTimersOnly = true // Subsequent envelopes use this.
			if checkXfrSOA(in, false) {
				c <- &XfrToken{in.Answer, nil}
				return
			}
			c <- &XfrToken{in.Answer, nil}
		}
	}
	panic("dns: not reached")
}

func (w *reply) ixfrReceive(q *Msg, c chan *XfrToken) {
	var serial uint32 // The first serial seen is the current server serial
	first := true
	defer w.conn.Close()
	defer close(c)
	for {
		in, err := w.receive()
		if err != nil {
			c <- &XfrToken{in.Answer, err}
			return
		}
		if q.Id != in.Id {
			c <- &XfrToken{in.Answer, ErrId}
			return
		}
		if first {
			// A single SOA RR signals "no changes"
			if len(in.Answer) == 1 && checkXfrSOA(in, true) {
				c <- &XfrToken{in.Answer, nil}
				return
			}

			// Check if the returned answer is ok
			if !checkXfrSOA(in, true) {
				c <- &XfrToken{in.Answer, ErrSoa}
				return
			}
			// This serial is important
			serial = in.Answer[0].(*RR_SOA).Serial
			first = !first
		}

		// Now we need to check each message for SOA records, to see what we need to do
		if !first {
			w.tsigTimersOnly = true
			// If the last record in the IXFR contains the servers' SOA,  we should quit
			if v, ok := in.Answer[len(in.Answer)-1].(*RR_SOA); ok {
				if v.Serial == serial {
					c <- &XfrToken{in.Answer, nil}
					return
				}
			}
			c <- &XfrToken{in.Answer, nil}
		}
	}
	panic("dns: not reached")
}

// Check if he SOA record exists in the Answer section of
// the packet. If first is true the first RR must be a SOA
// if false, the last one should be a SOA.
func checkXfrSOA(in *Msg, first bool) bool {
	if len(in.Answer) > 0 {
		if first {
			return in.Answer[0].Header().Rrtype == TypeSOA
		} else {
			return in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA
		}
	}
	return false
}

// XfrSend performs an outgoing [AI]xfr depending on the request message. The
// caller is responsible for sending the correct sequence of RR sets through
// the channel c. For reasons of symmetry XfrToken is re-used.
// Errors are signaled via the error pointer, when an error occurs the function
// sets the error and returns (it does not close the channel).
// TSIG and enveloping is handled by XfrSend.
//
// Basic use pattern for sending an AXFR:
//
//	// q contains the AXFR request
//	c := make(chan *XfrToken)
//	var e *error
//	err := XfrSend(w, q, c, e)
//	w.Hijack()		// hijack the connection so that the library doesn't close it
//	for _, rrset := range rrsets {	// rrset is a []RR
//		c <- &{XfrToken{RR: rrset}
//		if e != nil {
//			close(c)
//			break
//		}
//	}
//	// w.Close() // Don't! Let the client close the connection
func XfrSend(w ResponseWriter, q *Msg, c chan *XfrToken, e *error) error {
	switch q.Question[0].Qtype {
	case TypeAXFR, TypeIXFR:
		go axfrSend(w, q, c, e)
		return nil
	default:
		return nil
	}
	panic("dns: not reached")
}

// TODO(mg): count the RRs and the resulting size.
func axfrSend(w ResponseWriter, req *Msg, c chan *XfrToken, e *error) {
	rep := new(Msg)
	rep.SetReply(req)
	rep.Authoritative = true

	for x := range c {
		// assume it fits
		rep.Answer = append(rep.Answer, x.RR...)
		if err := w.WriteMsg(rep); e != nil {
			*e = err
			return
		}
		w.TsigTimersOnly(true)
		rep.Answer = nil
	}
}
<commit_msg>Rename XfrToken to XfrMsg<commit_after>package dns

// XfrMsg is used when doing [IA]xfr with a remote server.
type XfrMsg struct {
	RR    []RR  // The set of RRs in the answer section of the AXFR reply message.
	Error error // If something went wrong, this contains the error.
}

// XfrReceive performs a [AI]xfr request (depends on the message's Qtype). It returns
// a channel of *XfrMsg on which the replies from the server are sent. At the end of
// the transfer the channel is closed.
// It panics if the Qtype does not equal TypeAXFR or TypeIXFR. The messages are TSIG checked if
// needed, no other post-processing is performed. The caller must dissect the returned
// messages.
//
// Basic use pattern for receiving an AXFR:
//
//	// m contains the AXFR request
//	t, e := client.XfrReceive(m, "127.0.0.1:53")
//	for r := range t {
//		// ... deal with r.RR or r.Error
//	}
func (c *Client) XfrReceive(q *Msg, a string) (chan *XfrMsg, error) {
	w := new(reply)
	w.client = c
	w.addr = a
	w.req = q
	if err := w.dial(); err != nil {
		return nil, err
	}
	if err := w.send(q); err != nil {
		return nil, err
	}
	e := make(chan *XfrMsg)
	switch q.Question[0].Qtype {
	case TypeAXFR:
		go w.axfrReceive(q, e)
		return e, nil
	case TypeIXFR:
		go w.ixfrReceive(q, e)
		return e, nil
	default:
		return nil, nil
	}
	panic("dns: not reached")
}

func (w *reply) axfrReceive(q *Msg, c chan *XfrMsg) {
	first := true
	defer w.conn.Close()
	defer close(c)
	for {
		in, err := w.receive()
		if err != nil {
			c <- &XfrMsg{nil, err}
			return
		}
		if in.Id != q.Id {
			c <- &XfrMsg{in.Answer, ErrId}
			return
		}
		if first {
			if !checkXfrSOA(in, true) {
				c <- &XfrMsg{in.Answer, ErrSoa}
				return
			}
			first = !first
		}

		if !first {
			w.tsigTimersOnly = true // Subsequent envelopes use this.
			if checkXfrSOA(in, false) {
				c <- &XfrMsg{in.Answer, nil}
				return
			}
			c <- &XfrMsg{in.Answer, nil}
		}
	}
	panic("dns: not reached")
}

func (w *reply) ixfrReceive(q *Msg, c chan *XfrMsg) {
	var serial uint32 // The first serial seen is the current server serial
	first := true
	defer w.conn.Close()
	defer close(c)
	for {
		in, err := w.receive()
		if err != nil {
			c <- &XfrMsg{in.Answer, err}
			return
		}
		if q.Id != in.Id {
			c <- &XfrMsg{in.Answer, ErrId}
			return
		}
		if first {
			// A single SOA RR signals "no changes"
			if len(in.Answer) == 1 && checkXfrSOA(in, true) {
				c <- &XfrMsg{in.Answer, nil}
				return
			}

			// Check if the returned answer is ok
			if !checkXfrSOA(in, true) {
				c <- &XfrMsg{in.Answer, ErrSoa}
				return
			}
			// This serial is important
			serial = in.Answer[0].(*RR_SOA).Serial
			first = !first
		}

		// Now we need to check each message for SOA records, to see what we need to do
		if !first {
			w.tsigTimersOnly = true
			// If the last record in the IXFR contains the servers' SOA,  we should quit
			if v, ok := in.Answer[len(in.Answer)-1].(*RR_SOA); ok {
				if v.Serial == serial {
					c <- &XfrMsg{in.Answer, nil}
					return
				}
			}
			c <- &XfrMsg{in.Answer, nil}
		}
	}
	panic("dns: not reached")
}

// Check if he SOA record exists in the Answer section of
// the packet. If first is true the first RR must be a SOA
// if false, the last one should be a SOA.
func checkXfrSOA(in *Msg, first bool) bool {
	if len(in.Answer) > 0 {
		if first {
			return in.Answer[0].Header().Rrtype == TypeSOA
		} else {
			return in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA
		}
	}
	return false
}

// XfrSend performs an outgoing [AI]xfr depending on the request message. The
// caller is responsible for sending the correct sequence of RR sets through
// the channel c. For reasons of symmetry XfrMsg is re-used.
// Errors are signaled via the error pointer, when an error occurs the function
// sets the error and returns (it does not close the channel).
// TSIG and enveloping is handled by XfrSend.
//
// Basic use pattern for sending an AXFR:
//
//	// q contains the AXFR request
//	c := make(chan *XfrMsg)
//	var e *error
//	err := XfrSend(w, q, c, e)
//	w.Hijack()		// hijack the connection so that the library doesn't close it
//	for _, rrset := range rrsets {	// rrset is a []RR
//		c <- &{XfrMsg{RR: rrset}
//		if e != nil {
//			close(c)
//			break
//		}
//	}
//	// w.Close() // Don't! Let the client close the connection
func XfrSend(w ResponseWriter, q *Msg, c chan *XfrMsg, e *error) error {
	switch q.Question[0].Qtype {
	case TypeAXFR, TypeIXFR:
		go axfrSend(w, q, c, e)
		return nil
	default:
		return nil
	}
	panic("dns: not reached")
}

// TODO(mg): count the RRs and the resulting size.
func axfrSend(w ResponseWriter, req *Msg, c chan *XfrMsg, e *error) {
	rep := new(Msg)
	rep.SetReply(req)
	rep.Authoritative = true

	for x := range c {
		// assume it fits
		rep.Answer = append(rep.Answer, x.RR...)
		if err := w.WriteMsg(rep); e != nil {
			*e = err
			return
		}
		w.TsigTimersOnly(true)
		rep.Answer = nil
	}
}
<|endoftext|>


<commit_before>package tenho

import (
	"fmt"
	"math/rand"
	"sort"
	"time"
)

type OptionStruct struct {
	NoKokushi   bool
	NoChitoitsu bool
	NoNormal    bool
}

var option OptionStruct

func Start(o OptionStruct) {
	option = o

	start := time.Now().UnixNano()
	var i float64
	i = 0
	for {
		i++
		end := time.Now().UnixNano()
		diff := float64(end-start) / 1000000000
		m := i / diff
		out := 0
		if m >= 0 {
			out = int(m)
		}
		seed := time.Now().UnixNano()
		var hai string
		var ok bool
		hai, ok = tryOnce(seed)
		if int(i)%10000 == 0 {
			fmt.Printf("\r%v回試行  %v秒経過 %v回/秒 %v", i, diff, out, hai)
		}
		if ok {
			fmt.Printf("\r%v回試行  %v秒経過 %v回/秒 %v", i, diff, out, hai)
			break
		}
		//fmt.Printf("\r%v回試行  %v秒経過 %v回/秒 %v", i, diff, out, hai)
		//if i > 100000 {
		//	break
		//}
	}
	fmt.Printf("\n")
}

func tryOnce(seed int64) (string, bool) {
	hand := ShuffledHand(seed)
	hai := hand.HaiString()
	ok := hand.Solve()
	return hai, ok
}

// http://d.hatena.ne.jp/hake/20150930/p1
func shuffle(hand Hand) {
	for i := len(hand); i > 1; i-- {
		j := rand.Intn(i) // 0 .. i-1 の乱数発生
		hand[i-1], hand[j] = hand[j], hand[i-1]
	}
}

const HandSize = 14
const MahjongSetSize = 136

var defaultSet []int

func GetMahjongSet() []int {
	if defaultSet == nil {
		size := MahjongSetSize
		defaultSet = make([]int, size, size)
		for i := 0; i < size; i++ {
			defaultSet[i] = i / 4
		}
	}
	return defaultSet
}

func ShuffledHand(seed int64) Hand {
	rand.Seed(seed)

	hand := make(Hand, MahjongSetSize, MahjongSetSize)
	copy(hand, GetMahjongSet())
	hand2 := make(Hand, 0, 0)
	var j int

	for k := MahjongSetSize; k > MahjongSetSize-HandSize; k-- {
		j = rand.Intn(k)
		hand2 = append(hand2, hand[j])
		hand = append(hand[:j], hand[j+1:]...)
	}

	return hand2
}

type Hand []int

// 牌文字への変換(スペース区切り)
func (hand Hand) HaiString() string {
	// http://qiita.com/ruiu/items/2bb83b29baeae2433a79
	// サイズ0、内部バッファの長さ69の[]byteの値を割り当てる
	b := make([]byte, 0, 70)

	// bに文字列を追加
	for j := 0; j < HandSize; j++ {
		// コードポイント上、普通の麻雀牌はU+1F000からの34個。
		// U+1F000 is 'MAHJONG TILE EAST WIND' ('東')
		// https://codepoints.net/U+1F000
		b = append(b, string(hand[j]+0x1F000)...) // appendするには...が必要
		// 自分のMacではスペース区切りでないとうまく表示されないためスペースを挿入する
		// U+0020 is 'SPACE'
		// https://codepoints.net/U+0020
		b = append(b, string(0x20)...) // appendするには...が必要
	}
	return string(b)
}

// 七対子判定
func (hand Hand) solveChitoitsu() bool {
	//カウンタ
	c := map[int]int{}

	for _, v := range hand {
		count, ok := c[v]
		if ok {
			if count == 1 {
				c[v] = 2
			} else {
				// c[v] == 2
				return false
			}
		} else {
			c[v] = 1
		}
		//8個チェック
		if len(c) >= 8 {
			return false
		}
	}
	return true
}

// 国士無双判定
func (hand Hand) solveKokushi() bool {
	sort.Ints(hand)
	//比較するために配列にする
	var a [HandSize]int
	for i := 0; i < HandSize; i++ {
		a[i] = hand[i]
	}

	//あがりパターン列挙
	agaris := [13][14]int{
		[14]int{0, 0, 1, 2, 3, 4, 5, 6, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 1, 2, 3, 4, 5, 6, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 2, 3, 4, 5, 6, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 3, 4, 5, 6, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 4, 5, 6, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 5, 6, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 6, 6, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 6, 7, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 6, 7, 15, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 6, 7, 15, 16, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 6, 7, 15, 16, 24, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 6, 7, 15, 16, 24, 25, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 6, 7, 15, 16, 24, 25, 33, 33},
	}
	for _, v := range agaris {
		if v == a {
			return true
		}
	}
	return false
}

// あがり判定する
func (hand Hand) Solve() bool {
	return (!(option.NoKokushi) && hand.solveKokushi()) || (!(option.NoChitoitsu) && hand.solveChitoitsu()) || (!(option.NoNormal) && hand.GroupSuit().Solve())
}

type SuitGroup struct {
	innerSuitGroup
	color int
}

type innerSuitGroup []int

func NewSuitGroup(color int) *SuitGroup {
	s := SuitGroup{innerSuitGroup{}, color}
	return &s
}

type SuitsGroupedHand map[int]SuitGroup

const (
	Jihai = iota
	Manzu
	Sozu
	Pinzu
)

// スート分類
func (hand Hand) GroupSuit() SuitsGroupedHand {
	m := SuitsGroupedHand{
		Jihai: *NewSuitGroup(Jihai),
		Manzu: *NewSuitGroup(Manzu),
		Sozu:  *NewSuitGroup(Sozu),
		Pinzu: *NewSuitGroup(Pinzu),
	}
	for _, i := range hand {
		quo := (i - 7 + 9) / 9
		var mod int
		if i-7 >= 0 {
			mod = (i - 7) % 9
		} else {
			mod = i
		}
		s := m[quo]
		s.append(mod)
		m[quo] = s
	}
	return m
}

func (m SuitsGroupedHand) Solve() bool {
	return m.a_pair_existible() && m.valid_33332()
}
func (m SuitsGroupedHand) a_pair_existible() bool {
	//スートのサイズを3で割った時
	//あまりが2であるスートグループが1つであること
	c := 0
	for _, a := range m {
		switch len(a.list()) % 3 {
		case 0:
			// noop
		case 1:
			return false
		case 2:
			c++
		}
	}
	return c == 1
}

func (m SuitsGroupedHand) valid_33332() bool {
	for i := 0; i < 4; i++ {
		if !m[i].valid_suit_group(i) {
			return false
		}
	}
	return true
}

func (a innerSuitGroup) list() innerSuitGroup {
	return a
}

func (a *innerSuitGroup) SetSuitGroup(b innerSuitGroup) {
	*a = b
}

func (a *innerSuitGroup) append(w int) {
	*a = append(*a, w)
}

// 33332形を形成するスートグループがどうかを判定
func (a SuitGroup) valid_suit_group(i int) bool {
	// 対子が含まれているスートグループがただ1つある前提

	//ソート
	sort.Ints(a.list())
	if len(a.list())%3 == 2 {
		//ペアを探す
		pair_numbers := a.pairable_numbers()
		//ペア候補がなかったらぬける
		if len(pair_numbers) == 0 {
			return false
		}
		//ペア候補毎に繰り返し処理
		for _, v := range pair_numbers {
			//ペアとなる２枚を除去
			rest := NewSuitGroup(i)
			c := 2
			for _, w := range a.list() {
				// ペア候補以外は新スライスに入れる
				// ペア候補は３枚目以降は新スライスに入れる
				if w != v || c <= 0 {
					rest.SetSuitGroup(append(rest.list(), w))
				}
				if w == v {
					c--
				}
			}
			if rest.valid_3cards() {
				return true
			} else {
				continue
			}
		}
		return false
	} else if len(a.list())%3 == 0 {
		return a.valid_3cards()
	}
	// 到達しないはず
	panic("到達しないはず")
}

func (a SuitGroup) valid_3cards() bool {
	// 刻子や順子のみで構成されている場合true
	// a is sorted
	// a.size % 3 is0
	for {
		if a.remove_kotsu() {
			continue
		}
		break
	}

	// 字牌でなければ順子チェック
	if a.color != Jihai {
		for {
			if a.remove_shuntsu() {
				continue
			}
			break
		}
	}
	return len(a.list()) == 0
}

func (a *innerSuitGroup) remove_kotsu() bool {
	// 刻子を除去できればtrue
	// a is sorted
	x := *a
	if len(x) < 3 {
		return false
	}
	if x[0] == x[1] && x[0] == x[2] {
		*a = x[3:]
		return true
	}
	return false
}

func (a *innerSuitGroup) remove_shuntsu() bool {
	// 順子を除去できればtrue
	// a is sorted
	rest := innerSuitGroup{}
	first := -1
	second := -1
	found := false
	for _, v := range *a {
		if found {
			rest = append(rest, v)
			continue
		}
		if first == -1 {
			first = v
		} else if second == -1 && first+1 == v {
			second = v
		} else if second != -1 && first+2 == v {
			//flush
			first = -1
			second = -1
			found = true
		} else {
			rest = append(rest, v)
		}
	}
	*a = rest
	return found
}

func (a innerSuitGroup) pairable_numbers() innerSuitGroup {
	// a is sorted
	counter := []int{}
	var x, y int // 2つ前と1つ前
	for _, v := range a {
		if y == v && x != v {
			counter = append(counter, v)
		} else {
			y = v
		}
	}
	return counter
}
<commit_msg>Refactor Start()<commit_after>package tenho

import (
	"fmt"
	"math/rand"
	"sort"
	"time"
)

type OptionStruct struct {
	NoKokushi   bool
	NoChitoitsu bool
	NoNormal    bool
}

var option OptionStruct

func Start(o OptionStruct) {
	option = o

	start := time.Now().UnixNano()
	var i float64
	i = 0
	var hai string
	var ok bool
	for {
		i++
		seed := time.Now().UnixNano()
		hai, ok = tryOnce(seed)
		if int(i)%10000 == 0 {
			end := time.Now().UnixNano()
			diff := float64(end-start) / 1000000000
			m := i / diff
			out := 0
			if m >= 0 {
				out = int(m)
			}
			fmt.Printf("\r%v回試行  %v秒経過 %v回/秒 %v", i, diff, out, hai)
		}
		if ok {
			end := time.Now().UnixNano()
			diff := float64(end-start) / 1000000000
			m := i / diff
			out := 0
			if m >= 0 {
				out = int(m)
			}
			fmt.Printf("\r%v回試行  %v秒経過 %v回/秒 %v", i, diff, out, hai)
			break
		}
	}
	fmt.Printf("\n")
}

func tryOnce(seed int64) (string, bool) {
	hand := ShuffledHand(seed)
	hai := hand.HaiString()
	ok := hand.Solve()
	return hai, ok
}

// http://d.hatena.ne.jp/hake/20150930/p1
func shuffle(hand Hand) {
	for i := len(hand); i > 1; i-- {
		j := rand.Intn(i) // 0 .. i-1 の乱数発生
		hand[i-1], hand[j] = hand[j], hand[i-1]
	}
}

const HandSize = 14
const MahjongSetSize = 136

var defaultSet []int

func GetMahjongSet() []int {
	if defaultSet == nil {
		size := MahjongSetSize
		defaultSet = make([]int, size, size)
		for i := 0; i < size; i++ {
			defaultSet[i] = i / 4
		}
	}
	return defaultSet
}

func ShuffledHand(seed int64) Hand {
	rand.Seed(seed)

	hand := make(Hand, MahjongSetSize, MahjongSetSize)
	copy(hand, GetMahjongSet())
	hand2 := make(Hand, 0, 0)
	var j int

	for k := MahjongSetSize; k > MahjongSetSize-HandSize; k-- {
		j = rand.Intn(k)
		hand2 = append(hand2, hand[j])
		hand = append(hand[:j], hand[j+1:]...)
	}

	return hand2
}

type Hand []int

// 牌文字への変換(スペース区切り)
func (hand Hand) HaiString() string {
	// http://qiita.com/ruiu/items/2bb83b29baeae2433a79
	// サイズ0、内部バッファの長さ69の[]byteの値を割り当てる
	b := make([]byte, 0, 70)

	// bに文字列を追加
	for j := 0; j < HandSize; j++ {
		// コードポイント上、普通の麻雀牌はU+1F000からの34個。
		// U+1F000 is 'MAHJONG TILE EAST WIND' ('東')
		// https://codepoints.net/U+1F000
		b = append(b, string(hand[j]+0x1F000)...) // appendするには...が必要
		// 自分のMacではスペース区切りでないとうまく表示されないためスペースを挿入する
		// U+0020 is 'SPACE'
		// https://codepoints.net/U+0020
		b = append(b, string(0x20)...) // appendするには...が必要
	}
	return string(b)
}

// 七対子判定
func (hand Hand) solveChitoitsu() bool {
	//カウンタ
	c := map[int]int{}

	for _, v := range hand {
		count, ok := c[v]
		if ok {
			if count == 1 {
				c[v] = 2
			} else {
				// c[v] == 2
				return false
			}
		} else {
			c[v] = 1
		}
		//8個チェック
		if len(c) >= 8 {
			return false
		}
	}
	return true
}

// 国士無双判定
func (hand Hand) solveKokushi() bool {
	sort.Ints(hand)
	//比較するために配列にする
	var a [HandSize]int
	for i := 0; i < HandSize; i++ {
		a[i] = hand[i]
	}

	//あがりパターン列挙
	agaris := [13][14]int{
		[14]int{0, 0, 1, 2, 3, 4, 5, 6, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 1, 2, 3, 4, 5, 6, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 2, 3, 4, 5, 6, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 3, 4, 5, 6, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 4, 5, 6, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 5, 6, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 6, 6, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 6, 7, 7, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 6, 7, 15, 15, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 6, 7, 15, 16, 16, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 6, 7, 15, 16, 24, 24, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 6, 7, 15, 16, 24, 25, 25, 33},
		[14]int{0, 1, 2, 3, 4, 5, 6, 7, 15, 16, 24, 25, 33, 33},
	}
	for _, v := range agaris {
		if v == a {
			return true
		}
	}
	return false
}

// あがり判定する
func (hand Hand) Solve() bool {
	return (!(option.NoKokushi) && hand.solveKokushi()) || (!(option.NoChitoitsu) && hand.solveChitoitsu()) || (!(option.NoNormal) && hand.GroupSuit().Solve())
}

type SuitGroup struct {
	innerSuitGroup
	color int
}

type innerSuitGroup []int

func NewSuitGroup(color int) *SuitGroup {
	s := SuitGroup{innerSuitGroup{}, color}
	return &s
}

type SuitsGroupedHand map[int]SuitGroup

const (
	Jihai = iota
	Manzu
	Sozu
	Pinzu
)

// スート分類
func (hand Hand) GroupSuit() SuitsGroupedHand {
	m := SuitsGroupedHand{
		Jihai: *NewSuitGroup(Jihai),
		Manzu: *NewSuitGroup(Manzu),
		Sozu:  *NewSuitGroup(Sozu),
		Pinzu: *NewSuitGroup(Pinzu),
	}
	for _, i := range hand {
		quo := (i - 7 + 9) / 9
		var mod int
		if i-7 >= 0 {
			mod = (i - 7) % 9
		} else {
			mod = i
		}
		s := m[quo]
		s.append(mod)
		m[quo] = s
	}
	return m
}

func (m SuitsGroupedHand) Solve() bool {
	return m.a_pair_existible() && m.valid_33332()
}
func (m SuitsGroupedHand) a_pair_existible() bool {
	//スートのサイズを3で割った時
	//あまりが2であるスートグループが1つであること
	c := 0
	for _, a := range m {
		switch len(a.list()) % 3 {
		case 0:
			// noop
		case 1:
			return false
		case 2:
			c++
		}
	}
	return c == 1
}

func (m SuitsGroupedHand) valid_33332() bool {
	for i := 0; i < 4; i++ {
		if !m[i].valid_suit_group(i) {
			return false
		}
	}
	return true
}

func (a innerSuitGroup) list() innerSuitGroup {
	return a
}

func (a *innerSuitGroup) SetSuitGroup(b innerSuitGroup) {
	*a = b
}

func (a *innerSuitGroup) append(w int) {
	*a = append(*a, w)
}

// 33332形を形成するスートグループがどうかを判定
func (a SuitGroup) valid_suit_group(i int) bool {
	// 対子が含まれているスートグループがただ1つある前提

	//ソート
	sort.Ints(a.list())
	if len(a.list())%3 == 2 {
		//ペアを探す
		pair_numbers := a.pairable_numbers()
		//ペア候補がなかったらぬける
		if len(pair_numbers) == 0 {
			return false
		}
		//ペア候補毎に繰り返し処理
		for _, v := range pair_numbers {
			//ペアとなる２枚を除去
			rest := NewSuitGroup(i)
			c := 2
			for _, w := range a.list() {
				// ペア候補以外は新スライスに入れる
				// ペア候補は３枚目以降は新スライスに入れる
				if w != v || c <= 0 {
					rest.SetSuitGroup(append(rest.list(), w))
				}
				if w == v {
					c--
				}
			}
			if rest.valid_3cards() {
				return true
			} else {
				continue
			}
		}
		return false
	} else if len(a.list())%3 == 0 {
		return a.valid_3cards()
	}
	// 到達しないはず
	panic("到達しないはず")
}

func (a SuitGroup) valid_3cards() bool {
	// 刻子や順子のみで構成されている場合true
	// a is sorted
	// a.size % 3 is0
	for {
		if a.remove_kotsu() {
			continue
		}
		break
	}

	// 字牌でなければ順子チェック
	if a.color != Jihai {
		for {
			if a.remove_shuntsu() {
				continue
			}
			break
		}
	}
	return len(a.list()) == 0
}

func (a *innerSuitGroup) remove_kotsu() bool {
	// 刻子を除去できればtrue
	// a is sorted
	x := *a
	if len(x) < 3 {
		return false
	}
	if x[0] == x[1] && x[0] == x[2] {
		*a = x[3:]
		return true
	}
	return false
}

func (a *innerSuitGroup) remove_shuntsu() bool {
	// 順子を除去できればtrue
	// a is sorted
	rest := innerSuitGroup{}
	first := -1
	second := -1
	found := false
	for _, v := range *a {
		if found {
			rest = append(rest, v)
			continue
		}
		if first == -1 {
			first = v
		} else if second == -1 && first+1 == v {
			second = v
		} else if second != -1 && first+2 == v {
			//flush
			first = -1
			second = -1
			found = true
		} else {
			rest = append(rest, v)
		}
	}
	*a = rest
	return found
}

func (a innerSuitGroup) pairable_numbers() innerSuitGroup {
	// a is sorted
	counter := []int{}
	var x, y int // 2つ前と1つ前
	for _, v := range a {
		if y == v && x != v {
			counter = append(counter, v)
		} else {
			y = v
		}
	}
	return counter
}
<|endoftext|>


<commit_before>// Package srcset `srcset` provides a parser for the HTML5 `srcset` attribute, based on the
// [WHATWG reference algorithm](https://html.spec.whatwg.org/multipage/embedded-content.html#parse-a-srcset-attribute).
// TODO: This works, but I dislike the state manipulation.
// Use more go-like structures for reading and tokenization, like bufio.Scanner
package srcset

import (
	"regexp"
	"strconv"
)

// ImageSource is a structure that contains an image definition.
type ImageSource struct {
	URL     string
	Width   *int64
	Height  *int64
	Density *float64
}

// SourceSet is the result of parsing the value of a srcset attribute.
// A SourceSet consists of multiple ImageSource instances.
type SourceSet []ImageSource

const (
	comma       = ','
	leftParens  = '('
	rightParens = ')'
)

const (
	stateNone = iota
	stateInDescriptor
	stateInParens
	stateAfterDescriptor
)

var (
	regexLeadingSpaces         = regexp.MustCompile("^[ \t\n\r\u000c]+")
	regexLeadingCommasOrSpaces = regexp.MustCompile("^[, \t\n\r\u000c]+")
	regexLeadingNotSpaces      = regexp.MustCompile("^[^ \t\n\r\u000c]+")
	regexTrailingCommas        = regexp.MustCompile("[,]+$")
	regexNonNegativeInteger    = regexp.MustCompile(`^\d+$`)
	regexFloatingPoint         = regexp.MustCompile(`^-?(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?$`)
)

// Parse takes the value of a srcset attribute and parses it.
func Parse(input string) SourceSet {
	var (
		url         string
		pos         = 0
		currState   = stateNone
		end         = len(input)
		candidates  = SourceSet{}
		descriptors = []string{}
	)

	collectChars := func(rx *regexp.Regexp) string {
		if match := rx.FindString(input[pos:]); match != "" {
			pos += len(match)
			return match
		}

		return ""
	}

	isSpace := func(c rune) bool {
		return (c == '\u0020' || // space
			c == '\u0009' || // horizontal tab
			c == '\u000A' || // new line
			c == '\u000C' || // form feed
			c == '\u000D') // carriage return
	}

	parseDescriptors := func() {
		var (
			isErr = false
			h     *int64
			w     *int64
			d     *float64
		)

		for _, desc := range descriptors {
			lastIdx := len(desc) - 1
			lastChar, numericVal := desc[lastIdx], desc[:lastIdx]
			intVal, intErr := strconv.ParseInt(numericVal, 10, 64)
			floatVal, floatErr := strconv.ParseFloat(numericVal, 64)

			if regexNonNegativeInteger.MatchString(numericVal) && lastChar == 'w' {
				if w != nil || d != nil {
					isErr = true
				}
				if intErr != nil || intVal == 0 {
					isErr = true
				} else {
					w = &intVal
				}
			} else if regexFloatingPoint.MatchString(numericVal) && lastChar == 'x' {
				if w != nil || d != nil || h != nil {
					isErr = true
				}
				if floatErr != nil || floatVal < 0 {
					isErr = true
				} else {
					d = &floatVal
				}
			} else if regexNonNegativeInteger.MatchString(numericVal) && lastChar == 'h' {
				if h != nil || d != nil {
					isErr = true
				}
				if intErr != nil || intVal == 0 {
					isErr = true
				} else {
					h = &intVal
				}
			} else {
				isErr = true
			}
		}

		if !isErr {
			candidates = append(candidates, ImageSource{
				URL:     url,
				Density: d,
				Width:   w,
				Height:  h,
			})
		}
	}

	tokenize := func() {
		collectChars(regexLeadingSpaces)
		currDescriptor := ""
		currState = stateInDescriptor

		for {
			if pos == len(input) {
				if currState != stateAfterDescriptor && currDescriptor != "" {
					descriptors = append(descriptors, currDescriptor)
				}

				parseDescriptors()
				return
			}

			c := rune(input[pos])

			switch currState {
			case stateInDescriptor:
				if isSpace(c) {
					if currDescriptor != "" {
						descriptors = append(descriptors, currDescriptor)
						currDescriptor = ""
						currState = stateAfterDescriptor
					}
				} else if c == comma {
					pos++
					if currDescriptor != "" {
						descriptors = append(descriptors, currDescriptor)
						parseDescriptors()
						return
					}
				} else if c == leftParens {
					currDescriptor += string(c)
					currState = stateInParens
				} else {
					currDescriptor += string(c)
				}

			case stateInParens:
				if c == rightParens {
					currDescriptor += string(c)
					currState = stateInDescriptor
				} else {
					currDescriptor += string(c)
				}

			case stateAfterDescriptor:
				if isSpace(c) {

				} else {
					currState = stateInDescriptor
					pos--
				}
			}

			pos++
		}
	}

	for {
		collectChars(regexLeadingCommasOrSpaces)
		if pos >= end {
			return candidates
		}

		url = collectChars(regexLeadingNotSpaces)
		descriptors = []string{}

		if url[len(url)-1] == ',' {
			url = regexTrailingCommas.ReplaceAllString(url, "")
			parseDescriptors()
		} else {
			tokenize()
		}
	}
}
<commit_msg>refactor to make heavier use of switch<commit_after>// Package srcset `srcset` provides a parser for the HTML5 `srcset` attribute, based on the
// [WHATWG reference algorithm](https://html.spec.whatwg.org/multipage/embedded-content.html#parse-a-srcset-attribute).
// TODO: This works, but I dislike the state manipulation.
// Use more go-like structures for reading and tokenization, like bufio.Scanner
package srcset

import (
	"regexp"
	"strconv"
)

// ImageSource is a structure that contains an image definition.
type ImageSource struct {
	URL     string
	Width   *int64
	Height  *int64
	Density *float64
}

// SourceSet is the result of parsing the value of a srcset attribute.
// A SourceSet consists of multiple ImageSource instances.
type SourceSet []ImageSource

const (
	comma       = ','
	leftParens  = '('
	rightParens = ')'
)

const (
	stateNone = iota
	stateInDescriptor
	stateInParens
	stateAfterDescriptor
)

var (
	regexLeadingSpaces         = regexp.MustCompile("^[ \t\n\r\u000c]+")
	regexLeadingCommasOrSpaces = regexp.MustCompile("^[, \t\n\r\u000c]+")
	regexLeadingNotSpaces      = regexp.MustCompile("^[^ \t\n\r\u000c]+")
	regexTrailingCommas        = regexp.MustCompile("[,]+$")
	regexNonNegativeInteger    = regexp.MustCompile(`^\d+$`)
	regexFloatingPoint         = regexp.MustCompile(`^-?(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?$`)
)

func isSpace(c rune) bool {
	switch c {
	case
		'\u0020', // space
		'\u0009', // horizontal tab
		'\u000A', // new line
		'\u000C', // form feed
		'\u000D': // carriage return
		return true
	default:
		return false
	}
}

// Parse takes the value of a srcset attribute and parses it.
func Parse(input string) SourceSet {
	var (
		url         string
		pos         = 0
		currState   = stateNone
		end         = len(input)
		candidates  = SourceSet{}
		descriptors = []string{}
	)

	collectChars := func(rx *regexp.Regexp) string {
		if match := rx.FindString(input[pos:]); match != "" {
			pos += len(match)
			return match
		}

		return ""
	}

	parseDescriptors := func() {
		var (
			isErr = false
			h     *int64
			w     *int64
			d     *float64
		)

		for _, desc := range descriptors {
			lastIdx := len(desc) - 1
			lastChar, numericVal := desc[lastIdx], desc[:lastIdx]
			intVal, intErr := strconv.ParseInt(numericVal, 10, 64)
			floatVal, floatErr := strconv.ParseFloat(numericVal, 64)

			switch {
			case regexNonNegativeInteger.MatchString(numericVal) && lastChar == 'w':
				if w != nil || d != nil {
					isErr = true
				}
				if intErr != nil || intVal == 0 {
					isErr = true
				} else {
					w = &intVal
				}
			case regexFloatingPoint.MatchString(numericVal) && lastChar == 'x':
				if w != nil || d != nil || h != nil {
					isErr = true
				}
				if floatErr != nil || floatVal < 0 {
					isErr = true
				} else {
					d = &floatVal
				}
			case regexNonNegativeInteger.MatchString(numericVal) && lastChar == 'h':
				if h != nil || d != nil {
					isErr = true
				}
				if intErr != nil || intVal == 0 {
					isErr = true
				} else {
					h = &intVal
				}
			default:
				isErr = true
			}
		}

		if !isErr {
			candidates = append(candidates, ImageSource{
				URL:     url,
				Density: d,
				Width:   w,
				Height:  h,
			})
		}
	}

	tokenize := func() {
		collectChars(regexLeadingSpaces)
		currDescriptor := ""
		currState = stateInDescriptor

		for {
			if pos == len(input) {
				if currState != stateAfterDescriptor && currDescriptor != "" {
					descriptors = append(descriptors, currDescriptor)
				}

				parseDescriptors()
				return
			}

			c := rune(input[pos])

			switch currState {
			case stateInDescriptor:
				switch {
				case isSpace(c):
					if currDescriptor != "" {
						descriptors = append(descriptors, currDescriptor)
						currDescriptor = ""
						currState = stateAfterDescriptor
					}
				case c == comma:
					pos++
					if currDescriptor != "" {
						descriptors = append(descriptors, currDescriptor)
						parseDescriptors()
						return
					}
				case c == leftParens:
					currDescriptor += string(c)
					currState = stateInParens
				default:
					currDescriptor += string(c)
				}

			case stateInParens:
				switch c {
				case rightParens:
					currDescriptor += string(c)
					currState = stateInDescriptor
				default:
					currDescriptor += string(c)
				}

			case stateAfterDescriptor:
				switch {
				case isSpace(c):
				default:
					currState = stateInDescriptor
					pos--
				}
			}

			pos++
		}
	}

	for {
		collectChars(regexLeadingCommasOrSpaces)
		if pos >= end {
			return candidates
		}

		url = collectChars(regexLeadingNotSpaces)
		descriptors = []string{}

		if url[len(url)-1] == ',' {
			url = regexTrailingCommas.ReplaceAllString(url, "")
			parseDescriptors()
		} else {
			tokenize()
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"text/template"

	"github.com/crowdmob/goamz/s3"
	units "github.com/docker/go-units"
)

const (
	index string = `<!DOCTYPE html>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Docker Master Binaries</title>
    <link rel="stylesheet" href="/static/style.css" />

Docker Master Binaries

These binaries are built and updated with each commit to the master branch of Docker. Want to use that cool new feature that was just merged? Download your system's binary and check out the master docs at docs.master.dockerproject.com.

                    Name
                    Size
                    Uploaded Date

			{{ range $key, $value := . }}

					{{ $value.Key | base }}
					{{ $value.Size | size }}
					{{ $value.LastModified }}

			{{ end }}

`
)

// create the index.html file
func createIndexFile(bucket *s3.Bucket, bucketpath string) error {
	// list all the files
	files, err := listFiles(bucketpath, bucketpath, "", 2000, bucket)
	if err != nil {
		return fmt.Errorf("Listing all files in bucket failed: %v", err)
	}

	// create a temp file for the index
	tmp, err := ioutil.TempFile("", "index.html")
	if err != nil {
		return fmt.Errorf("Creating temp file failed: %v", err)
	}
	defer os.RemoveAll(tmp.Name())

	// set up custom functions
	funcMap := template.FuncMap{
		"ext": func(name string) string {
			if strings.HasSuffix(name, ".sha256") || strings.HasSuffix(name, ".md5") {
				return "text"
			}
			return "default"
		},
		"base": func(name string) string {
			parts := strings.Split(name, "/")
			return strings.Join(parts[1:len(parts)-1], "/")
		},
		"size": func(s int64) string {
			return units.HumanSize(float64(s))
		},
	}

	// parse & execute the template
	tmpl := template.Must(template.New("").Funcs(funcMap).Parse(index))
	if err := tmpl.ExecuteTemplate(tmp, "layout", files); err != nil {
		return fmt.Errorf("Execute template failed: %v", err)
	}

	// push the file to s3
	if err = uploadFileToS3(bucket, tmp.Name(), path.Join(bucketpath, "index.html")); err != nil {
		return fmt.Errorf("Uploading %s to s3 failed: %v", tmp.Name(), err)
	}

	return nil
}
<commit_msg>fix template<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"text/template"

	"github.com/crowdmob/goamz/s3"
	units "github.com/docker/go-units"
)

const (
	index string = `<!DOCTYPE html>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Docker Master Binaries</title>
    <link rel="stylesheet" href="/static/style.css" />

Docker Master Binaries

These binaries are built and updated with each commit to the master branch of Docker. Want to use that cool new feature that was just merged? Download your system's binary and check out the master docs at docs.master.dockerproject.com.

                    Name
                    Size
                    Uploaded Date

			{{ range $key, $value := . }}

					{{ $value.Key | base }}
					{{ $value.Size | size }}
					{{ $value.LastModified }}

			{{ end }}

`
)

// create the index.html file
func createIndexFile(bucket *s3.Bucket, bucketpath string) error {
	// list all the files
	files, err := listFiles(bucketpath, bucketpath, "", 2000, bucket)
	if err != nil {
		return fmt.Errorf("Listing all files in bucket failed: %v", err)
	}

	// create a temp file for the index
	tmp, err := ioutil.TempFile("", "index.html")
	if err != nil {
		return fmt.Errorf("Creating temp file failed: %v", err)
	}
	defer os.RemoveAll(tmp.Name())

	// set up custom functions
	funcMap := template.FuncMap{
		"ext": func(name string) string {
			if strings.HasSuffix(name, ".sha256") || strings.HasSuffix(name, ".md5") {
				return "text"
			}
			return "default"
		},
		"base": func(name string) string {
			parts := strings.Split(name, "/")
			return strings.Join(parts[1:len(parts)-1], "/")
		},
		"size": func(s int64) string {
			return units.HumanSize(float64(s))
		},
	}

	// parse & execute the template
	tmpl := template.Must(template.New("").Funcs(funcMap).Parse(index))
	if err := tmpl.Execute(tmp, files); err != nil {
		return fmt.Errorf("Execute template failed: %v", err)
	}

	// push the file to s3
	if err = uploadFileToS3(bucket, tmp.Name(), path.Join(bucketpath, "index.html")); err != nil {
		return fmt.Errorf("Uploading %s to s3 failed: %v", tmp.Name(), err)
	}

	return nil
}
<|endoftext|>


<commit_before>package hole

import (
    "io"
    "bytes"
    "sync"
)

type ReadStream struct {
    buffer [][]byte
    bufferSize int
    eof error
    locker *sync.RWMutex
    waiter *sync.RWMutex
}

type WriteStream struct {
    sessionId []byte
    conn Conn
}

func (r *ReadStream) FeedData (buf []byte) {
    r.locker.Lock()
    r.buffer = append(r.buffer, buf)
    r.bufferSize = r.bufferSize + len(buf)
    r.locker.Unlock()

    r.waiter.Unlock()
}

func (r *ReadStream) FeedEOF () {
    r.eof = io.EOF
    r.waiter.Unlock()
}

func (r *ReadStream) Read(buf []byte) (length int, err error) {
    nRead := len(buf)
    for {
        if r.bufferSize >= nRead || r.eof != nil {
            break
        }
        r.waiter.Lock()
    }

    if r.bufferSize == 0 {
        err = r.eof
        return
    }

    r.locker.Lock()
    data := bytes.Join(r.buffer, nil)
    if r.bufferSize > nRead {
        copy(buf[0:], data[:nRead])
        r.buffer = [][]byte{data[nRead:]}
        r.bufferSize = r.bufferSize - nRead
        length = nRead
    } else {
        copy(buf[0:len(data)], data)
        r.buffer = [][]byte{}
        length = r.bufferSize
        r.bufferSize = 0
    }
    r.locker.Unlock()
    return
}

func (w *WriteStream) Write(data []byte) (n int, err error) {
    n, err = w.conn.Write(EncodePacket(w.sessionId, data))
    return
}

func (w *WriteStream) Close() error {
    // return w.conn.Close()
    return nil
}
<commit_msg>send EOF, when stream close<commit_after>package hole

import (
    "io"
    "bytes"
    "sync"
)

type ReadStream struct {
    buffer [][]byte
    bufferSize int
    eof error
    locker *sync.RWMutex
    waiter *sync.RWMutex
}

type WriteStream struct {
    sessionId []byte
    conn Conn
}

func (r *ReadStream) FeedData (buf []byte) {
    r.locker.Lock()
    r.buffer = append(r.buffer, buf)
    r.bufferSize = r.bufferSize + len(buf)
    r.locker.Unlock()

    r.waiter.Unlock()
}

func (r *ReadStream) FeedEOF () {
    r.eof = io.EOF
    r.waiter.Unlock()
}

func (r *ReadStream) Read(buf []byte) (length int, err error) {
    nRead := len(buf)
    for {
        if r.bufferSize >= nRead || r.eof != nil {
            break
        }
        r.waiter.Lock()
    }

    if r.bufferSize == 0 {
        err = r.eof
        return
    }

    r.locker.Lock()
    data := bytes.Join(r.buffer, nil)
    if r.bufferSize > nRead {
        copy(buf[0:], data[:nRead])
        r.buffer = [][]byte{data[nRead:]}
        r.bufferSize = r.bufferSize - nRead
        length = nRead
    } else {
        copy(buf[0:len(data)], data)
        r.buffer = [][]byte{}
        length = r.bufferSize
        r.bufferSize = 0
    }
    r.locker.Unlock()
    return
}

func (w *WriteStream) Write(data []byte) (n int, err error) {
    n, err = w.conn.Write(EncodePacket(w.sessionId, data))
    return
}

func (w *WriteStream) Close() error {
    // return w.conn.Close()
    _, err := w.Write([]byte("EOF"))
    return err
}
<|endoftext|>


<commit_before>package quic

type State byte

const (
	OPEN State = iota
	HALF_CLOSED
	CLOSED
)

type Stream struct {
	*Conn
	ID     uint32
	State  State
	Window *Window
}

func NewStream(streamID uint32, conn *Conn) (stream *Stream) {
	stream = &Stream{
		Conn:   conn,
		ID:     streamID,
		State:  OPEN,
		Window: NewWindow(),
	}
	return
}

func ReadStreamLevelFrame(conn *Conn, f StreamLevelFrame) error {
	id := f.GetStreamID()
	stream, ok := conn.Streams[id]

	switch frame := f.(type) {
	case *StreamFrame:
		if !ok {
			// implecitely created
			stream = conn.GenStream(id)
		}
		if frame.Fin == true {
			// Normal termination
		}
		stream.ApplyStreamFrame(frame)
	case *WindowUpdateFrame:
		if !ok {
			return QUIC_PACKET_FOR_NONEXISTENT_STREAM
		}
		stream.ApplyWindowUpdateFrame(frame)

	case *BlockedFrame:
		if !ok {
			return QUIC_PACKET_FOR_NONEXISTENT_STREAM
		}
		stream.ApplyBlockedFrame(frame)
	case *RstStreamFrame:
		// Abrupt termination
		if !ok {
			return QUIC_PACKET_FOR_NONEXISTENT_STREAM
		}
		stream.ApplyRstStream(frame)
	}
	return nil
}

func (self *Stream) ApplyStreamFrame(f *StreamFrame) {

}

func (self *Stream) ApplyBlockedFrame(f *BlockedFrame) {

}

func (self *Stream) ApplyWindowUpdateFrame(f *WindowUpdateFrame) {

}

func (self *Stream) ApplyRstStream(f *RstStreamFrame) {

}
<commit_msg>implement stream state change, and String<commit_after>package quic

import "fmt"

type State byte

const (
	OPEN State = iota
	HALF_CLOSED
	CLOSED
)

func (s State) String() string {
	return []string{
		"OPEN",
		"HALF_CLOSED",
		"CLOSED",
	}[s]
}

type Stream struct {
	*Conn
	ID        uint32
	State     State
	PeerState State
	Window    *Window
}

func NewStream(streamID uint32, conn *Conn) (stream *Stream) {
	stream = &Stream{
		Conn:      conn,
		ID:        streamID,
		State:     OPEN,
		PeerState: OPEN,
		Window:    NewWindow(),
	}
	return
}

func ReadStreamLevelFrame(conn *Conn, f StreamLevelFrame) error {
	id := f.GetStreamID()
	stream, ok := conn.Streams[id]

	switch frame := f.(type) {
	case *StreamFrame:
		if !ok {
			// implecitely created
			stream = conn.GenStream(id)
		}
		stream.ApplyStreamFrame(frame)
	case *WindowUpdateFrame:
		if !ok {
			return QUIC_PACKET_FOR_NONEXISTENT_STREAM
		}
		stream.ApplyWindowUpdateFrame(frame)
	case *BlockedFrame:
		if !ok {
			return QUIC_PACKET_FOR_NONEXISTENT_STREAM
		}
		stream.ApplyBlockedFrame(frame)
	case *RstStreamFrame:
		// Abrupt termination
		if !ok {
			return QUIC_PACKET_FOR_NONEXISTENT_STREAM
		}
		stream.ApplyRstStream(frame)
	}
	return nil
}

func (self *Stream) ApplyStreamFrame(f *StreamFrame) {
	if f.Fin {
		self.PeerState = HALF_CLOSED
		if self.State == HALF_CLOSED {
			self.State = CLOSED
			self.PeerState = CLOSED
		}
	}
	if self.PeerState == HALF_CLOSED || self.PeerState == CLOSED {
		// TODO : emit error
	}
}

func (self *Stream) ApplyBlockedFrame(f *BlockedFrame) {

}

func (self *Stream) ApplyWindowUpdateFrame(f *WindowUpdateFrame) {

}

func (self *Stream) ApplyRstStream(f *RstStreamFrame) {

}

func (self *Stream) SendStreamFrame(f *StreamFrame) {
	if self.State == HALF_CLOSED || self.State == CLOSED {
		// TODO : emit error
		// cannot send
	}
	if f.Fin {
		self.State = HALF_CLOSED
		if self.PeerState == HALF_CLOSED {
			self.State = CLOSED
			self.PeerState = CLOSED
		}
	}
}

func (self *Stream) String() string {
	str := fmt.Sprintf("Stream ID:%d\n\tLocal State: %s\n\tPeer  State: %s",
		self.ID, self.State.String(), self.PeerState.String())
	return str
}
<|endoftext|>


<commit_before>package models

import (
	"github.com/jinzhu/gorm"
)

type SugarSource string

const (
	SourceMeter = "meter"
	SourceCGM   = "cgm"
)

// Sugar represents a measured blood sugar
type Sugar struct {
	gorm.Model
	Occurred
	Value  int
	Source SugarSource `gorm:"size:64"`
}
<commit_msg>Sugar OccuredAt should be unique<commit_after>package models

import (
	"github.com/jinzhu/gorm"
)

type SugarSource string

const (
	SourceMeter = "meter"
	SourceCGM   = "cgm"
)

// Sugar represents a measured blood sugar
type Sugar struct {
	gorm.Model
	Occurred `gorm:"unique"`
	Value    int
	Source   SugarSource `gorm:"size:64"`
}
<|endoftext|>


<commit_before>package tailer

import (
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"sync"

	"github.com/golang/glog"
)

var (
	// RegexNotWatch sets the file extension to avoid watching
	RegexNotWatch = regexp.MustCompile("^(?:tailer\\.|gobzip-|\\..+\\.swp)")
)

// Tailer init the service functions
type Tailer struct {
	ch          chan bool
	waitGroup   *sync.WaitGroup
	publisher   Publisher
	matchLine   *regexp.Regexp
	filesToTail []string
	fileLock    sync.Mutex
}

// Make a new Tailer
func NewTailer(publishToNats bool, config Config) (*Tailer, error) {
	var err error
	t := &Tailer{
		ch:          make(chan bool),
		waitGroup:   &sync.WaitGroup{},
		filesToTail: []string{},
	}
	if len(config.Match) > 0 {
		glog.Warningf("Filter line by regex: %s", config.Match)
		t.matchLine, err = regexp.Compile(config.Match)
		if err != nil {
			return nil, err
		}
	}
	if publishToNats {
		natsURL := os.Getenv("NATS_CLUSTER")
		if natsURL == "" {
			natsURL = "nats://localhost:4222"
		}
		t.publisher, err = NewNatsPublisher(natsURL)
		if err != nil {
			return nil, err
		}
	} else {
		t.publisher = &SimplePublisher{}
	}
	return t, nil
}

func (s *Tailer) Serve(watchDirs []string, fileGlob string) {
	// examine the input dir and select how many files to watch and publish
	for _, dir := range watchDirs {
		fileGlobPattern := fmt.Sprintf("%s/%s", dir, fileGlob)
		files, _ := filepath.Glob(fileGlobPattern)
		s.filesToTail = append(s.filesToTail, files...)
		glog.Warningf("Files to watch now: %v", s.filesToTail)
		go s.watchDir(dir)
	}

	for _, filePath := range filesToTail {
		go s.tailFile(filePath)
	}

	s.waitGroup.Wait()
}
<commit_msg>fix build error<commit_after>package tailer

import (
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"sync"

	"github.com/golang/glog"
)

var (
	// RegexNotWatch sets the file extension to avoid watching
	RegexNotWatch = regexp.MustCompile("^(?:tailer\\.|gobzip-|\\..+\\.swp)")
)

// Tailer init the service functions
type Tailer struct {
	ch          chan bool
	waitGroup   *sync.WaitGroup
	publisher   Publisher
	matchLine   *regexp.Regexp
	filesToTail []string
	fileLock    sync.Mutex
}

// Make a new Tailer
func NewTailer(publishToNats bool, config Config) (*Tailer, error) {
	var err error
	t := &Tailer{
		ch:          make(chan bool),
		waitGroup:   &sync.WaitGroup{},
		filesToTail: []string{},
	}
	if len(config.Match) > 0 {
		glog.Warningf("Filter line by regex: %s", config.Match)
		t.matchLine, err = regexp.Compile(config.Match)
		if err != nil {
			return nil, err
		}
	}
	if publishToNats {
		natsURL := os.Getenv("NATS_CLUSTER")
		if natsURL == "" {
			natsURL = "nats://localhost:4222"
		}
		t.publisher, err = NewNatsPublisher(natsURL)
		if err != nil {
			return nil, err
		}
	} else {
		t.publisher = &SimplePublisher{}
	}
	return t, nil
}

func (s *Tailer) Serve(watchDirs []string, fileGlob string) {
	// examine the input dir and select how many files to watch and publish
	for _, dir := range watchDirs {
		fileGlobPattern := fmt.Sprintf("%s/%s", dir, fileGlob)
		files, _ := filepath.Glob(fileGlobPattern)
		s.filesToTail = append(s.filesToTail, files...)
		glog.Warningf("Files to watch now: %v", s.filesToTail)
		go s.watchDir(dir)
	}

	for _, filePath := range s.filesToTail {
		go s.tailFile(filePath)
	}

	s.waitGroup.Wait()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/datasektionen/taitan/pages"
	"golang.org/x/exp/inotify"
)

var (
	debug     bool   // Show debug level messages.
	info      bool   // Show info level messages.
	responses Atomic // Our parsed responses.
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] ROOT\n", os.Args[0])
	flag.PrintDefaults()
	os.Exit(1)
}

func init() {
	flag.BoolVar(&debug, "vv", false, "Print debug messages.")
	flag.BoolVar(&info, "v", false, "Print info messages.")
	flag.Usage = usage
	flag.Parse()
}

func getEnv(env string) string {
	e := os.Getenv(env)
	if e == "" {
		log.Fatalf("$%s environmental variable is not set.\n", env)
	}
	return e
}

func getRoot() string {
	content := getEnv("CONTENT_URL")
	u, err := url.Parse(content)
	if err != nil {
		log.Fatalln("getContent: ", err)
	}

	// https://<token>@github.com/username/repo.git
	u.User = url.User(getEnv("TOKEN"))

	base := filepath.Base(u.Path)
	return strings.TrimSuffix(base, filepath.Ext(base))
}

func getContent() {
	content := getEnv("CONTENT_URL")
	u, err := url.Parse(content)
	if err != nil {
		log.Fatalln("getContent: ", err)
	}

	// https://<token>@github.com/username/repo.git
	u.User = url.User(getEnv("TOKEN"))

	root := getRoot()
	if _, err = os.Stat(root); os.IsNotExist(err) {
		log.Debugln("No root directory - cloning content url!")
		cmd := exec.Command("git", "clone", u.String())
		err = cmd.Start()
		if err != nil {
			log.Fatal(err)
		}
		log.Debugln("Waiting for git clone to finish...")
		err = cmd.Wait()
		if err != nil {
			log.Warnln("Cloned with error: %v\n", err)
		}
	} else {
		log.Debugln("Found root directory - pulling updates!")
		cmd := exec.Command("git", fmt.Sprintf("--git-dir=%s/.git", root), "pull")
		err = cmd.Start()
		if err != nil {
			log.Fatal(err)
		}
		log.Debugln("Waiting for git pull to finish...")
		err = cmd.Wait()
		if err != nil {
			log.Warnf("Pulled with error: %v\n", err)
		}
	}
}

// setVerbosity sets the amount of messages printed.
func setVerbosity() {
	switch {
	case debug:
		log.SetLevel(log.DebugLevel)
	case info:
		log.SetLevel(log.InfoLevel)
	default:
		log.SetLevel(log.WarnLevel)
	}
}

// Atomic responses.
type Atomic struct {
	sync.Mutex
	Resps map[string]*pages.Resp
}

func validRoot(root string) {
	fi, err := os.Stat(root)
	if err != nil {
		if os.IsNotExist(err) {
			log.Fatalf("Directory doesn't exist: %q", root)
		}
		log.Fatalln(err)
	}
	if !fi.IsDir() {
		log.Fatalf("Supplied path is not a directory: %q", root)
	}
}

func main() {
	setVerbosity()

	// Get port or die.
	port := getEnv("PORT")

	// Get content or die.
	getContent()
	go func() {
		for {
			time.Sleep(time.Second * 20)
			getContent()
		}
	}()

	root := getRoot()
	log.WithField("Root", root).Info("Our root directory")

	// We'll parse and store the responses ahead of time.
	resps, err := pages.Load(root)
	if err != nil {
		log.Fatalf("pages.Load: unexpected error: %s", err)
	}
	log.WithField("Resps", resps).Debug("The parsed responses")
	responses = Atomic{Resps: resps}

	// Watch the directory for any changes. If the directory has any changes we'll
	// update our responses.
	go watch(root)

	log.Info("Starting server.")
	log.Info("Listening on port: ", port)

	// Our request handler.
	http.HandleFunc("/", handler)

	// Listen on port and serve with our handler.
	err = http.ListenAndServe(":"+port, nil)
	if err != nil {
		panic(err)
	}
}

func watch(root string) {
	wd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	path := filepath.Join(wd, root)
	watcher, err := inotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}
	err = watcher.AddWatch(path,
		inotify.IN_CLOSE_WRITE|
			inotify.IN_CREATE|
			inotify.IN_DELETE|
			inotify.IN_MODIFY|
			inotify.IN_MOVED_FROM|
			inotify.IN_MOVED_TO|
			inotify.IN_MOVE)
	if err != nil {
		log.Fatal(err)
	}
	last := time.Now()
	for {
		select {
		case ev := <-watcher.Event:
			if time.Now().Sub(last) < 10*time.Second {
				continue
			}
			log.Println("event:", ev)
			last = time.Now()
			responses.Lock()
			responses.Resps, err = pages.Load(root)
			if err != nil {
				log.Error(err)
			}
			responses.Unlock()
		case err := <-watcher.Error:
			log.Warn("error:", err)
		}
	}
}

// handler parses and serves responses to our file queries.
func handler(res http.ResponseWriter, req *http.Request) {
	// Requested URL. We extract the path.
	query := req.URL.Path
	log.WithField("query", query).Info("Recieved query")

	clean := filepath.Clean(query)
	log.WithField("clean", clean).Info("Sanitized path")

	responses.Lock()
	r, ok := responses.Resps[clean]
	responses.Unlock()
	if !ok {
		log.WithField("page", clean).Warn("Page doesn't exist")
		res.WriteHeader(http.StatusNotFound)
		return
	}
	log.Info("Marshaling the response.")
	buf, err := json.Marshal(r)
	if err != nil {
		log.Warnf("handler: unexpected error: %#v\n", err)
		res.WriteHeader(http.StatusInternalServerError)
		return
	}
	log.Info("Serve the response.")
	log.Debug("Response: %#v\n", string(buf))
	res.Header().Set("Content-Type", "application/json")
	res.Write(buf)
}
<commit_msg>webhook test<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/datasektionen/taitan/pages"
	"golang.org/x/exp/inotify"
)

var (
	debug     bool   // Show debug level messages.
	info      bool   // Show info level messages.
	responses Atomic // Our parsed responses.
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] ROOT\n", os.Args[0])
	flag.PrintDefaults()
	os.Exit(1)
}

func init() {
	flag.BoolVar(&debug, "vv", false, "Print debug messages.")
	flag.BoolVar(&info, "v", false, "Print info messages.")
	flag.Usage = usage
	flag.Parse()
}

func getEnv(env string) string {
	e := os.Getenv(env)
	if e == "" {
		log.Fatalf("$%s environmental variable is not set.\n", env)
	}
	return e
}

func getRoot() string {
	content := getEnv("CONTENT_URL")
	u, err := url.Parse(content)
	if err != nil {
		log.Fatalln("getContent: ", err)
	}

	// https://<token>@github.com/username/repo.git
	u.User = url.User(getEnv("TOKEN"))

	base := filepath.Base(u.Path)
	return strings.TrimSuffix(base, filepath.Ext(base))
}

func getContent() {
	content := getEnv("CONTENT_URL")
	u, err := url.Parse(content)
	if err != nil {
		log.Fatalln("getContent: ", err)
	}

	// https://<token>@github.com/username/repo.git
	u.User = url.User(getEnv("TOKEN"))

	root := getRoot()
	if _, err = os.Stat(root); os.IsNotExist(err) {
		log.Debugln("No root directory - cloning content url!")
		cmd := exec.Command("git", "clone", u.String())
		err = cmd.Start()
		if err != nil {
			log.Fatal(err)
		}
		log.Debugln("Waiting for git clone to finish...")
		err = cmd.Wait()
		if err != nil {
			log.Warnln("Cloned with error: %v\n", err)
		}
	} else {
		log.Debugln("Found root directory - pulling updates!")
		cmd := exec.Command("git", fmt.Sprintf("--git-dir=%s/.git", root), "pull")
		err = cmd.Start()
		if err != nil {
			log.Fatal(err)
		}
		log.Debugln("Waiting for git pull to finish...")
		err = cmd.Wait()
		if err != nil {
			log.Warnf("Pulled with error: %v\n", err)
		}
	}
}

// setVerbosity sets the amount of messages printed.
func setVerbosity() {
	switch {
	case debug:
		log.SetLevel(log.DebugLevel)
	case info:
		log.SetLevel(log.InfoLevel)
	default:
		log.SetLevel(log.WarnLevel)
	}
}

// Atomic responses.
type Atomic struct {
	sync.Mutex
	Resps map[string]*pages.Resp
}

func validRoot(root string) {
	fi, err := os.Stat(root)
	if err != nil {
		if os.IsNotExist(err) {
			log.Fatalf("Directory doesn't exist: %q", root)
		}
		log.Fatalln(err)
	}
	if !fi.IsDir() {
		log.Fatalf("Supplied path is not a directory: %q", root)
	}
}

func main() {
	setVerbosity()

	// Get port or die.
	port := getEnv("PORT")

	// Get content or die.
	getContent()
	go func() {
		for {
			time.Sleep(time.Second * 20)
			getContent()
		}
	}()

	root := getRoot()
	log.WithField("Root", root).Info("Our root directory")

	// We'll parse and store the responses ahead of time.
	resps, err := pages.Load(root)
	if err != nil {
		log.Fatalf("pages.Load: unexpected error: %s", err)
	}
	log.WithField("Resps", resps).Debug("The parsed responses")
	responses = Atomic{Resps: resps}

	// Watch the directory for any changes. If the directory has any changes we'll
	// update our responses.
	go watch(root)

	log.Info("Starting server.")
	log.Info("Listening on port: ", port)

	// Our request handler.
	http.HandleFunc("/", handler)

	// Listen on port and serve with our handler.
	err = http.ListenAndServe(":"+port, nil)
	if err != nil {
		panic(err)
	}
}

func watch(root string) {
	wd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	path := filepath.Join(wd, root)
	watcher, err := inotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}
	err = watcher.AddWatch(path,
		inotify.IN_CLOSE_WRITE|
			inotify.IN_CREATE|
			inotify.IN_DELETE|
			inotify.IN_MODIFY|
			inotify.IN_MOVED_FROM|
			inotify.IN_MOVED_TO|
			inotify.IN_MOVE)
	if err != nil {
		log.Fatal(err)
	}
	last := time.Now()
	for {
		select {
		case ev := <-watcher.Event:
			if time.Now().Sub(last) < 10*time.Second {
				continue
			}
			log.Println("event:", ev)
			last = time.Now()
			responses.Lock()
			responses.Resps, err = pages.Load(root)
			if err != nil {
				log.Error(err)
			}
			responses.Unlock()
		case err := <-watcher.Error:
			log.Warn("error:", err)
		}
	}
}

// handler parses and serves responses to our file queries.
func handler(res http.ResponseWriter, req *http.Request) {
	if req.Header.Get("X-Github-Event") != "" {
		log.Debugln("GITHUB EVENT - BO YAH - run get content here :D")
	}
	// Requested URL. We extract the path.
	query := req.URL.Path
	log.WithField("query", query).Info("Recieved query")

	clean := filepath.Clean(query)
	log.WithField("clean", clean).Info("Sanitized path")

	responses.Lock()
	r, ok := responses.Resps[clean]
	responses.Unlock()
	if !ok {
		log.WithField("page", clean).Warn("Page doesn't exist")
		res.WriteHeader(http.StatusNotFound)
		return
	}
	log.Info("Marshaling the response.")
	buf, err := json.Marshal(r)
	if err != nil {
		log.Warnf("handler: unexpected error: %#v\n", err)
		res.WriteHeader(http.StatusInternalServerError)
		return
	}
	log.Info("Serve the response.")
	log.Debug("Response: %#v\n", string(buf))
	res.Header().Set("Content-Type", "application/json")
	res.Write(buf)
}
<|endoftext|>


<commit_before>package share

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/jcelliott/lumber"

	"github.com/nanobox-io/nanobox/commands/server"
	"github.com/nanobox-io/nanobox/models"
	"github.com/nanobox-io/nanobox/util"
)

type Request struct {
	Path    string
	UID     int
	GID     int
	MountIP string
}

// EXPORTSFILE ...
var EXPORTSFILE = "/etc/exports"

func Exists(path string) bool {
	// read exports file
	existingFile, err := ioutil.ReadFile(EXPORTSFILE)
	if err != nil {
		// if i cant read the etc exports it doesnt exist
		return false
	}

	// get the provider because i need the mount ip
	provider, err := models.LoadProvider()
	if err != nil {
		return false
	}

	lineCheck := fmt.Sprintf("%s -alldirs -mapall=%v:%v", provider.MountIP, uid(), gid())

	lines := strings.Split(string(existingFile), "\n")

	for _, line := range lines {
		// get existing line
		if strings.Contains(line, lineCheck) {
			return strings.Contains(line, path+" ") || strings.Contains(line, path+"\" ")
		}
	}
	return false
}

func Add(path string) error {

	// get the provider because i need the mount ip
	provider, err := models.LoadProvider()
	if err != nil {
		return err
	}

	// create a rpc request
	req := Request{
		Path:    path,
		UID:     uid(),
		GID:     gid(),
		MountIP: provider.MountIP,
	}

	resp := &Response{}

	// in testing we will call the rpc function directly
	if flag.Lookup("test.v") != nil {
		shareRPC := &ShareRPC{}
		err := shareRPC.Add(req, resp)
		if err != nil || !resp.Success {
			err = fmt.Errorf("failed to add share %v %v", err, resp.Message)
		}
		return err
	}

	// have the server run the share command
	err = server.ClientRun("ShareRPC.Add", req, resp)
	if err != nil || !resp.Success {
		err = fmt.Errorf("failed to add share %v %v", err, resp.Message)
	}
	return err
}

// the rpc function run from the server
func (sh *ShareRPC) Add(req Request, resp *Response) error {
	fmt.Printf("req: %#v\n", req)

	// read exports file
	existingFile, err := ioutil.ReadFile(EXPORTSFILE)
	if err != nil {
		// if the file didnt exist lets create an empty existingFile
		existingFile = []byte("")
	}

	lineCheck := fmt.Sprintf("%s -alldirs -mapall=%v:%v", req.MountIP, req.UID, req.GID)

	lines := strings.Split(string(existingFile), "\n")

	found := false
	for i, line := range lines {
		// get existing line
		if strings.Contains(line, lineCheck) {
			// add our path to the line
			// check to see if this path has already been added
			if !(strings.Contains(line, req.Path+" ") || strings.Contains(line, req.Path+"\" ")) {
				lines[i] = fmt.Sprintf("\"%s\" %s", req.Path, line)
			}

			lines[i] = cleanLine(lines[i], lineCheck)
			found = true
			break
		}
	}
	if !found {
		lines = append(lines, fmt.Sprintf("\"%s\" %s", req.Path, lineCheck))
	}

	// save
	if err := ioutil.WriteFile(EXPORTSFILE, []byte(strings.Join(lines, "\n")), 0644); err != nil {
		return err
	}

	if err := reloadServer(); err != nil {
		return err
	}
	resp.Success = true
	return nil
}

func Remove(path string) error {

	// get the provider because i need the mount ip
	provider, err := models.LoadProvider()
	if err != nil {
		return err
	}

	// create a rpc request
	req := Request{
		Path:    path,
		UID:     uid(),
		GID:     gid(),
		MountIP: provider.MountIP,
	}

	resp := &Response{}

	// in testing we will call the rpc function directly
	if flag.Lookup("test.v") != nil {
		shareRPC := &ShareRPC{}
		err := shareRPC.Remove(req, resp)
		if err != nil || !resp.Success {
			err = fmt.Errorf("failed to add share %v %v", err, resp.Message)
		}
		return err
	}

	// have the server run the share command
	err = server.ClientRun("ShareRPC.Remove", req, resp)
	if err != nil || !resp.Success {
		err = fmt.Errorf("failed to add share %v %v", err, resp.Message)
	}
	return err

}

// the rpc function run from the server
func (sh *ShareRPC) Remove(req Request, resp *Response) error {

	quotedPath := fmt.Sprintf("\"%s\"", req.Path)

	// read exports file
	existingFile, err := ioutil.ReadFile(EXPORTSFILE)
	if err != nil {
		// if the error exists the file didnt exist.
		lumber.Error("failed to read etc/exports: %s", err)
		return nil
	}

	lineCheck := fmt.Sprintf("%s -alldirs -mapall=%v:%v", req.MountIP, req.UID, req.GID)

	existingLines := strings.Split(string(existingFile), "\n")
	newLines := []string{}

	for _, line := range existingLines {
		// get existing line
		if !strings.Contains(line, lineCheck) {
			newLines = append(newLines, line)
			continue
		}

		// recreate the line without our path or quoted path
		line = strings.Replace(line, fmt.Sprintf("%s ", req.Path), "", 1)
		line = strings.Replace(line, fmt.Sprintf("%s ", quotedPath), "", 1)
		if line != lineCheck {
			// if there is still any paths left in our line
			line = cleanLine(line, lineCheck)
			newLines = append(newLines, line)
		}
	}

	// save
	if err := ioutil.WriteFile(EXPORTSFILE, []byte(strings.Join(newLines, "\n")), 0644); err != nil {
		return err
	}

	err = reloadServer()
	if err == nil {
		resp.Success = true
	}
	return err
}

// reloadServer will reload the nfs server with the new export configuration
func reloadServer() error {

	// dont reload the server when testing
	if flag.Lookup("test.v") != nil {
		return nil
	}

	if err := util.Retry(startNFSD, 5, time.Second); err != nil {
		lumber.Error("nfsd enable: %s", err)
		return err
	}

	// check the exports to make sure a reload will be successful; TODO: provide a
	// clear message for a direction to fix
	cmd := exec.Command("nfsd", "checkexports")
	if b, err := cmd.CombinedOutput(); err != nil {
		lumber.Debug("checkexports: %s", b)
		return fmt.Errorf("checkexports: %s %s", b, err.Error())
	}

	// update exports; TODO: provide a clear error message for a direction to fix
	cmd = exec.Command("nfsd", "update")
	if b, err := cmd.CombinedOutput(); err != nil {
		lumber.Debug("update: %s", b)
		return fmt.Errorf("update: %s %s", b, err.Error())
	}

	return nil
}

func startNFSD() error {
	// make sure nfsd is running
	cmd := exec.Command("nfsd", "enable")
	if b, err := cmd.CombinedOutput(); err != nil {
		lumber.Debug("enable nfs: %s", b)
		return fmt.Errorf("enable nfs: %s %s", b, err.Error())
	}

	// request it to start but if everythign is working starting could cause an error
	// we dont want to check
	exec.Command("nfsd", "start").CombinedOutput()

	// add a short delay because nfsd takes some time
	<-time.After(time.Second)

	// check to see if nfsd is running
	b, _ := exec.Command("netstat", "-ln").CombinedOutput()
	if !strings.Contains(string(b), ".111 ") {
		return fmt.Errorf("nfsd ports not in use")
	}
	return nil
}

func cleanLine(line, lineCheck string) string {
	paths := strings.Split(strings.Replace(line, lineCheck, "", 1), " ")
	goodPaths := []string{}
	for _, path := range paths {
		// remove the quotes from the path
		path = strings.Replace(path, "\"", "", -1)
		fileInfo, err := os.Stat(path)
		if err != nil || !fileInfo.IsDir() {
			// continue on if the file doest exist or if it is not a directory
			continue
		}
		goodPaths = append(goodPaths, path)
	}
	goodPaths = removeDuplicates(goodPaths)
	return fmt.Sprintf("\"%s\" %s", strings.Join(goodPaths, "\" \""), lineCheck)
}

// takes a set of paths and removes duplicates as well as cleaning up any child paths
func removeDuplicates(paths []string) []string {
	rtn := []string{}
	// look through the paths
	for i, path := range paths {
		// default to adding the path as a non duplicate
		add := true
		for j, originalPath := range paths {

			// if im looking at the same path then ignore it
			if i == j {
				continue
			}

			// if i find an element that is shorter but the same directory structure
			// dont add the longer path
			if strings.HasPrefix(path, originalPath) {
				add = false
			}

		}

		// if I didnt detect a shorter path then I need to add this one
		if add {
			rtn = append(rtn, path)
		}
	}
	return rtn
}
<commit_msg>make it so we no longer have false positives on parent checks fixes #459<commit_after>package share

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/jcelliott/lumber"

	"github.com/nanobox-io/nanobox/commands/server"
	"github.com/nanobox-io/nanobox/models"
	"github.com/nanobox-io/nanobox/util"
)

type Request struct {
	Path    string
	UID     int
	GID     int
	MountIP string
}

// EXPORTSFILE ...
var EXPORTSFILE = "/etc/exports"

func Exists(path string) bool {
	// read exports file
	existingFile, err := ioutil.ReadFile(EXPORTSFILE)
	if err != nil {
		// if i cant read the etc exports it doesnt exist
		return false
	}

	// get the provider because i need the mount ip
	provider, err := models.LoadProvider()
	if err != nil {
		return false
	}

	lineCheck := fmt.Sprintf("%s -alldirs -mapall=%v:%v", provider.MountIP, uid(), gid())

	lines := strings.Split(string(existingFile), "\n")

	for _, line := range lines {
		// get existing line
		if strings.Contains(line, lineCheck) {
			return strings.Contains(line, path+" ") || strings.Contains(line, path+"\" ")
		}
	}
	return false
}

func Add(path string) error {

	// get the provider because i need the mount ip
	provider, err := models.LoadProvider()
	if err != nil {
		return err
	}

	// create a rpc request
	req := Request{
		Path:    path,
		UID:     uid(),
		GID:     gid(),
		MountIP: provider.MountIP,
	}

	resp := &Response{}

	// in testing we will call the rpc function directly
	if flag.Lookup("test.v") != nil {
		shareRPC := &ShareRPC{}
		err := shareRPC.Add(req, resp)
		if err != nil || !resp.Success {
			err = fmt.Errorf("failed to add share %v %v", err, resp.Message)
		}
		return err
	}

	// have the server run the share command
	err = server.ClientRun("ShareRPC.Add", req, resp)
	if err != nil || !resp.Success {
		err = fmt.Errorf("failed to add share %v %v", err, resp.Message)
	}
	return err
}

// the rpc function run from the server
func (sh *ShareRPC) Add(req Request, resp *Response) error {
	fmt.Printf("req: %#v\n", req)

	// read exports file
	existingFile, err := ioutil.ReadFile(EXPORTSFILE)
	if err != nil {
		// if the file didnt exist lets create an empty existingFile
		existingFile = []byte("")
	}

	lineCheck := fmt.Sprintf("%s -alldirs -mapall=%v:%v", req.MountIP, req.UID, req.GID)

	lines := strings.Split(string(existingFile), "\n")

	found := false
	for i, line := range lines {
		// get existing line
		if strings.Contains(line, lineCheck) {
			// add our path to the line
			// check to see if this path has already been added
			if !(strings.Contains(line, req.Path+" ") || strings.Contains(line, req.Path+"\" ")) {
				lines[i] = fmt.Sprintf("\"%s\" %s", req.Path, line)
			}

			lines[i] = cleanLine(lines[i], lineCheck)
			found = true
			break
		}
	}
	if !found {
		lines = append(lines, fmt.Sprintf("\"%s\" %s", req.Path, lineCheck))
	}

	// save
	if err := ioutil.WriteFile(EXPORTSFILE, []byte(strings.Join(lines, "\n")), 0644); err != nil {
		return err
	}

	if err := reloadServer(); err != nil {
		return err
	}
	resp.Success = true
	return nil
}

func Remove(path string) error {

	// get the provider because i need the mount ip
	provider, err := models.LoadProvider()
	if err != nil {
		return err
	}

	// create a rpc request
	req := Request{
		Path:    path,
		UID:     uid(),
		GID:     gid(),
		MountIP: provider.MountIP,
	}

	resp := &Response{}

	// in testing we will call the rpc function directly
	if flag.Lookup("test.v") != nil {
		shareRPC := &ShareRPC{}
		err := shareRPC.Remove(req, resp)
		if err != nil || !resp.Success {
			err = fmt.Errorf("failed to add share %v %v", err, resp.Message)
		}
		return err
	}

	// have the server run the share command
	err = server.ClientRun("ShareRPC.Remove", req, resp)
	if err != nil || !resp.Success {
		err = fmt.Errorf("failed to add share %v %v", err, resp.Message)
	}
	return err

}

// the rpc function run from the server
func (sh *ShareRPC) Remove(req Request, resp *Response) error {

	quotedPath := fmt.Sprintf("\"%s\"", req.Path)

	// read exports file
	existingFile, err := ioutil.ReadFile(EXPORTSFILE)
	if err != nil {
		// if the error exists the file didnt exist.
		lumber.Error("failed to read etc/exports: %s", err)
		return nil
	}

	lineCheck := fmt.Sprintf("%s -alldirs -mapall=%v:%v", req.MountIP, req.UID, req.GID)

	existingLines := strings.Split(string(existingFile), "\n")
	newLines := []string{}

	for _, line := range existingLines {
		// get existing line
		if !strings.Contains(line, lineCheck) {
			newLines = append(newLines, line)
			continue
		}

		// recreate the line without our path or quoted path
		line = strings.Replace(line, fmt.Sprintf("%s ", req.Path), "", 1)
		line = strings.Replace(line, fmt.Sprintf("%s ", quotedPath), "", 1)
		if line != lineCheck {
			// if there is still any paths left in our line
			line = cleanLine(line, lineCheck)
			newLines = append(newLines, line)
		}
	}

	// save
	if err := ioutil.WriteFile(EXPORTSFILE, []byte(strings.Join(newLines, "\n")), 0644); err != nil {
		return err
	}

	err = reloadServer()
	if err == nil {
		resp.Success = true
	}
	return err
}

// reloadServer will reload the nfs server with the new export configuration
func reloadServer() error {

	// dont reload the server when testing
	if flag.Lookup("test.v") != nil {
		return nil
	}

	if err := util.Retry(startNFSD, 5, time.Second); err != nil {
		lumber.Error("nfsd enable: %s", err)
		return err
	}

	// check the exports to make sure a reload will be successful; TODO: provide a
	// clear message for a direction to fix
	cmd := exec.Command("nfsd", "checkexports")
	if b, err := cmd.CombinedOutput(); err != nil {
		lumber.Debug("checkexports: %s", b)
		return fmt.Errorf("checkexports: %s %s", b, err.Error())
	}

	// update exports; TODO: provide a clear error message for a direction to fix
	cmd = exec.Command("nfsd", "update")
	if b, err := cmd.CombinedOutput(); err != nil {
		lumber.Debug("update: %s", b)
		return fmt.Errorf("update: %s %s", b, err.Error())
	}

	return nil
}

func startNFSD() error {
	// make sure nfsd is running
	cmd := exec.Command("nfsd", "enable")
	if b, err := cmd.CombinedOutput(); err != nil {
		lumber.Debug("enable nfs: %s", b)
		return fmt.Errorf("enable nfs: %s %s", b, err.Error())
	}

	// request it to start but if everythign is working starting could cause an error
	// we dont want to check
	exec.Command("nfsd", "start").CombinedOutput()

	// add a short delay because nfsd takes some time
	<-time.After(time.Second)

	// check to see if nfsd is running
	b, _ := exec.Command("netstat", "-ln").CombinedOutput()
	if !strings.Contains(string(b), ".111 ") {
		return fmt.Errorf("nfsd ports not in use")
	}
	return nil
}

func cleanLine(line, lineCheck string) string {
	paths := strings.Split(strings.Replace(line, lineCheck, "", 1), " ")
	goodPaths := []string{}
	for _, path := range paths {
		// remove the quotes from the path
		path = strings.Replace(path, "\"", "", -1)
		fileInfo, err := os.Stat(path)
		if err != nil || !fileInfo.IsDir() {
			// continue on if the file doest exist or if it is not a directory
			continue
		}
		goodPaths = append(goodPaths, path)
	}
	goodPaths = removeDuplicates(goodPaths)
	return fmt.Sprintf("\"%s\" %s", strings.Join(goodPaths, "\" \""), lineCheck)
}

// takes a set of paths and removes duplicates as well as cleaning up any child paths
func removeDuplicates(paths []string) []string {
	rtn := []string{}
	// look through the paths
	for i, path := range paths {
		// default to adding the path as a non duplicate
		add := true
		for j, originalPath := range paths {

			// if im looking at the same path then ignore it
			if i == j {
				continue
			}

			// if i find an element that is shorter but the same directory structure
			// dont add the longer path
			if strings.HasPrefix(path, originalPath+"/") {
				add = false
			}

		}

		// if I didnt detect a shorter path then I need to add this one
		if add {
			rtn = append(rtn, path)
		}
	}
	return rtn
}
<|endoftext|>