<commit_before>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	neturl "net/url"
	"os"
	"strings"
)

// ES scroll state - id + client/host string
type Scroll struct {
	id         string
	hostPrefix string
	client     *http.Client
}

// Connection to a specific index in ES
type EsConn struct {
	hostPrefix string
	path       string
	client     *http.Client
}

// Index metadata
type Index struct {
	Aliases  map[string]interface{} `json:"aliases"`
	Mappings map[string]interface{} `json:"mappings"`
	Settings map[string]interface{} `json:"settings"`
	Warmers  map[string]interface{} `json:"warmers"`
}

// http helper - check status and read the whole response body
func readRequest(client *http.Client, req *http.Request) (body []byte, err error) {
	resp, err := client.Do(req)
	if err != nil {
		return
	}
	defer resp.Body.Close()
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	if resp.StatusCode >= 300 {
		log.Fatalf("Http error[%s]: %s", resp.Status, string(body))
	}
	return
}

func ConnectES(url string) (EsConn, error) {
	if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
		url = "http://" + url
	}
	u, err := neturl.ParseRequestURI(url)
	if err != nil {
		return EsConn{}, err
	}
	hostPrefix := fmt.Sprintf("http://%s", u.Host)
	return EsConn{hostPrefix: hostPrefix, path: u.Path, client: http.DefaultClient}, nil
}

func (this EsConn) NewScroll(size int) (scroll Scroll, err error) {
	query := fmt.Sprintf(`
        {
            "query": {
            	"match_all": {}
            },
            "size": %d
        }
    `, size)
	url := fmt.Sprintf("%s%s/_search?search_type=scan&scroll=1m", this.hostPrefix, this.path)
	request, err := http.NewRequest("GET", url, bytes.NewBufferString(query))
	if err != nil {
		return Scroll{}, err
	}
	resp, err := readRequest(this.client, request)
	if err != nil {
		return
	}
	scrollID, err := getScrollId(resp)
	if err != nil {
		return
	}
	scroll = Scroll{id: scrollID, hostPrefix: this.hostPrefix, client: this.client}
	return
}

func getScrollId(data []byte) (string, error) {
	v := struct {
		ScrollID string `json:"_scroll_id"`
	}{}
	err := json.Unmarshal(data, &v)
	return v.ScrollID, err
}

func (this EsConn) Bulk(typeName string, bulk []string) (err error) {
	bodyStr := strings.Join(bulk, "")
	body := bytes.NewBufferString(bodyStr)
	url := fmt.Sprintf("%s%s/%s/_bulk", this.hostPrefix, this.path, typeName)
	request, err := http.NewRequest("PUT", url, body)
	if err != nil {
		return err
	}
	_, err = readRequest(this.client, request)
	return
}

// delete index of EsConn
func (this EsConn) DeleteIndex() (err error) {
	req, err := http.NewRequest("DELETE", this.hostPrefix+this.path, bytes.NewReader([]byte{}))
	if err != nil {
		return
	}
	_, err = readRequest(this.client, req)
	return
}

// Returns name of the first type in the mapping (that's what we use - FIXME for more general ways)
func (this EsConn) PutIndex(metaString string) (string, error) {
	// meta - all metadata including the old index name (top-level)
	meta := make(map[string]Index)
	err := json.Unmarshal([]byte(metaString), &meta)
	if err != nil {
		return "", err
	}
	var oldIndexName string
	for k := range meta { // FIXME: iterating over map of 1 item to get the only key ?
		oldIndexName = k
	}
	var typeName string
	for k := range meta[oldIndexName].Mappings {
		typeName = k
	}
	log.Printf("Transfering index `%s` type `%s`", oldIndexName, typeName)
	// metaBlob - all index meta data
	metaBlob, err := json.Marshal(meta[oldIndexName])
	if err != nil {
		return "", err
	}
	reqMapping, err := http.NewRequest("PUT", this.hostPrefix+this.path, bytes.NewBuffer(metaBlob))
	if err != nil {
		return "", err
	}
	_, err = readRequest(this.client, reqMapping)
	return typeName, err
}

func (this EsConn) GetIndex() (string, error) {
	resp, err := this.client.Get(this.hostPrefix + this.path)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	if resp.StatusCode >= 400 {
		log.Fatalf("Elastic error[%s]: %s", resp.Status, string(data))
	}
	return string(data), nil
}

func (this Scroll) Next() ([]map[string]interface{}, error) {
	resp, err := this.client.Get(this.hostPrefix + "/_search/scroll?scroll=1m&scroll_id=" + this.id)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	data, errRead := ioutil.ReadAll(resp.Body)
	if errRead != nil {
		return nil, errRead
	}

	hits := struct {
		Hits struct {
			Hits []map[string]interface{} `json:"hits"`
		} `json:"hits"`
	}{}
	json.Unmarshal(data, &hits)
	return hits.Hits.Hits, errRead
}

func exportTask(p Params) error {
	log.Printf("Export %s --> %s\n", p.in, p.out)
	if !p.force {
		_, err := os.Open(p.out)
		if err == nil {
			log.Fatalf("File `%s` already exits, use --force to overwrite.", p.out)
		}
	}
	sink, err := os.Create(p.out)
	if err != nil {
		return err
	}
	defer sink.Close()
	es, err := ConnectES(p.in)
	if err != nil {
		return err
	}
	index, err := es.GetIndex()
	if err != nil {
		return err
	}
	_, err = sink.WriteString(index)
	if err != nil {
		return err
	}
	_, err = sink.WriteString("\n")
	if err != nil {
		return err
	}

	scroll, err := es.NewScroll(p.window)
	if err != nil {
		return err
	}

	for hits, err := scroll.Next(); len(hits) != 0 && err == nil; hits, err = scroll.Next() {
		log.Printf("Got %d docs.\n", len(hits))
		for _, h := range hits {
			// create is faster then index but must avoid collision (index is empty at start)
			id := fmt.Sprintf("{ \"create\" : { \"_id\" : \"%v\" } }\n", h["_id"])
			io.WriteString(sink, id)
			bytes, err := json.Marshal(h["_source"])
			if err != nil {
				return err
			}
			_, err = sink.Write(bytes)
			io.WriteString(sink, "\n")
			if err != nil {
				return err
			}
		}
	}

	return err
}

func importTask(p Params) (err error) {
	log.Printf("Import %s --> %s", p.in, p.out)
	src, err := os.Open(p.in)
	if err != nil {
		return
	}
	defer src.Close()
	reader := bufio.NewReader(src)
	index, err := reader.ReadString('\n')
	es, err := ConnectES(p.out)
	if err != nil {
		return
	}
	if p.force {
		err = es.DeleteIndex()
		if err != nil {
			return
		}
	}
	typeName, err := es.PutIndex(index)
	if err != nil {
		return
	}
	log.Printf("Type: `%v`", typeName)
	batch := []string{}
	for {
		item, err := reader.ReadString('\n')
		if err == io.EOF {
			es.Bulk(typeName, batch)
			break
		}
		batch = append(batch, item)
		if len(batch) == p.window*2 {
			log.Printf("Imported %d", p.window)
			es.Bulk(typeName, batch)
			batch = batch[:0]
		}
	}
	return
}

func copyTask(p Params) (err error) {
	log.Printf("Copy %s --> %s\n", p.in, p.out)
	es1, err := ConnectES(p.in)
	if err != nil {
		return
	}
	es2, err := ConnectES(p.out)
	if err != nil {
		return
	}
	index, err := es1.GetIndex()
	if err != nil {
		return
	}
	if p.force {
		err = es2.DeleteIndex()
		if err != nil {
			return
		}
	}
	typeName, err := es2.PutIndex(index)

	scroll, err := es1.NewScroll(p.window)
	if err != nil {
		return err
	}

	var batch []string
	for hits, err := scroll.Next(); len(hits) != 0 && err == nil; hits, err = scroll.Next() {
		log.Printf("Got %d docs.\n", len(hits))
		for _, h := range hits {
			id := fmt.Sprintf("{ \"create\" : { \"_id\" : \"%v\" } }\n", h["_id"])
			batch = append(batch, id)
			bytes, err := json.Marshal(h["_source"])
			if err != nil {
				return err
			}
			batch = append(batch, string(bytes)+"\n")
		}
		err = es2.Bulk(typeName, batch)
		batch = batch[:0]
		if err != nil {
			return err
		}
		log.Printf("Put %d docs.\n", len(hits))
	}
	return
}

type Params struct {
	in     string
	out    string
	window int
	force  bool
}

type Command func(Params) error

/**
Commands:
	./estool export --in <url-of-index> --out <path-to-store>
	./estool import --in <path-to-store> --out <url-to-new-index>
	./estool copy --in <url-of-src-index> --out <url-out-index>
*/
func main() {
	commandTab := map[string]Command{
		"export": exportTask,
		"import": importTask,
		"copy":   copyTask,
	}
	args := os.Args
	commands := flag.NewFlagSet("ES-tool", flag.ExitOnError)
	input := commands.String("in", "", "input path/URL")
	output := commands.String("out", "", "output path/URL")
	force := commands.Bool("force", false, "force overwrite of existing index/file")
	window := commands.Int("window", 10, "size of scroll/scan window")

	if len(args) == 1 {
		log.Printf("No command supplied, valid commands :\n")
		for c, _ := range commandTab {
			log.Printf("\t%s\n", c)
		}
		return
	}
	cmd := args[1]
	task, ok := commandTab[cmd]
	if !ok {
		log.Fatalf("Invalid command: %s\n", cmd)
	}
	commands.Parse(args[2:])
	if *input == "" {
		log.Fatalf("No in param provided")
	}
	if *output == "" {
		log.Fatalf("No out param provided")
	}
	err := task(Params{in: *input, out: *output, window: *window, force: *force})
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Ignore failing to delete non-existing mapping<commit_after>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	neturl "net/url"
	"os"
	"strings"
)

// ES scroll state - id + client/host string
type Scroll struct {
	id         string
	hostPrefix string
	client     *http.Client
}

// Connection to a specific index in ES
type EsConn struct {
	hostPrefix string
	path       string
	client     *http.Client
}

// Index metadata
type Index struct {
	Aliases  map[string]interface{} `json:"aliases"`
	Mappings map[string]interface{} `json:"mappings"`
	Settings map[string]interface{} `json:"settings"`
	Warmers  map[string]interface{} `json:"warmers"`
}

// http helper - check status and read the whole response body
func readRequest(client *http.Client, req *http.Request) (body []byte, err error) {
	resp, err := client.Do(req)
	if err != nil {
		return
	}
	defer resp.Body.Close()
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	if resp.StatusCode >= 300 {
		err = errors.New(fmt.Sprintf("Http error[%s]: %s", resp.Status, string(body)))
	}
	return
}

func ConnectES(url string) (EsConn, error) {
	if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
		url = "http://" + url
	}
	u, err := neturl.ParseRequestURI(url)
	if err != nil {
		return EsConn{}, err
	}
	hostPrefix := fmt.Sprintf("http://%s", u.Host)
	return EsConn{hostPrefix: hostPrefix, path: u.Path, client: http.DefaultClient}, nil
}

func (this EsConn) NewScroll(size int) (scroll Scroll, err error) {
	query := fmt.Sprintf(`
        {
            "query": {
            	"match_all": {}
            },
            "size": %d
        }
    `, size)
	url := fmt.Sprintf("%s%s/_search?search_type=scan&scroll=1m", this.hostPrefix, this.path)
	request, err := http.NewRequest("GET", url, bytes.NewBufferString(query))
	if err != nil {
		return Scroll{}, err
	}
	resp, err := readRequest(this.client, request)
	if err != nil {
		return
	}
	scrollID, err := getScrollId(resp)
	if err != nil {
		return
	}
	scroll = Scroll{id: scrollID, hostPrefix: this.hostPrefix, client: this.client}
	return
}

func getScrollId(data []byte) (string, error) {
	v := struct {
		ScrollID string `json:"_scroll_id"`
	}{}
	err := json.Unmarshal(data, &v)
	return v.ScrollID, err
}

func (this EsConn) Bulk(typeName string, bulk []string) (err error) {
	bodyStr := strings.Join(bulk, "")
	body := bytes.NewBufferString(bodyStr)
	url := fmt.Sprintf("%s%s/%s/_bulk", this.hostPrefix, this.path, typeName)
	request, err := http.NewRequest("PUT", url, body)
	if err != nil {
		return err
	}
	_, err = readRequest(this.client, request)
	return
}

// delete index of EsConn
func (this EsConn) DeleteIndex() (err error) {
	req, err := http.NewRequest("DELETE", this.hostPrefix+this.path, bytes.NewReader([]byte{}))
	if err != nil {
		return
	}
	_, err = readRequest(this.client, req)
	return
}

// Returns name of the first type in the mapping (that's what we use - FIXME for more general ways)
func (this EsConn) PutIndex(metaString string) (string, error) {
	// meta - all metadata including the old index name (top-level)
	meta := make(map[string]Index)
	err := json.Unmarshal([]byte(metaString), &meta)
	if err != nil {
		return "", err
	}
	var oldIndexName string
	for k := range meta { // FIXME: iterating over map of 1 item to get the only key ?
		oldIndexName = k
	}
	var typeName string
	for k := range meta[oldIndexName].Mappings {
		typeName = k
	}
	log.Printf("Transfering index `%s` type `%s`", oldIndexName, typeName)
	// metaBlob - all index meta data
	metaBlob, err := json.Marshal(meta[oldIndexName])
	if err != nil {
		return "", err
	}
	reqMapping, err := http.NewRequest("PUT", this.hostPrefix+this.path, bytes.NewBuffer(metaBlob))
	if err != nil {
		return "", err
	}
	_, err = readRequest(this.client, reqMapping)
	return typeName, err
}

func (this EsConn) GetIndex() (string, error) {
	resp, err := this.client.Get(this.hostPrefix + this.path)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	if resp.StatusCode >= 400 {
		log.Fatalf("Elastic error[%s]: %s", resp.Status, string(data))
	}
	return string(data), nil
}

func (this Scroll) Next() ([]map[string]interface{}, error) {
	resp, err := this.client.Get(this.hostPrefix + "/_search/scroll?scroll=1m&scroll_id=" + this.id)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	data, errRead := ioutil.ReadAll(resp.Body)
	if errRead != nil {
		return nil, errRead
	}

	hits := struct {
		Hits struct {
			Hits []map[string]interface{} `json:"hits"`
		} `json:"hits"`
	}{}
	json.Unmarshal(data, &hits)
	return hits.Hits.Hits, errRead
}

func exportTask(p Params) error {
	log.Printf("Export %s --> %s\n", p.in, p.out)
	if !p.force {
		_, err := os.Open(p.out)
		if err == nil {
			log.Fatalf("File `%s` already exits, use --force to overwrite.", p.out)
		}
	}
	sink, err := os.Create(p.out)
	if err != nil {
		return err
	}
	defer sink.Close()
	es, err := ConnectES(p.in)
	if err != nil {
		return err
	}
	index, err := es.GetIndex()
	if err != nil {
		return err
	}
	_, err = sink.WriteString(index)
	if err != nil {
		return err
	}
	_, err = sink.WriteString("\n")
	if err != nil {
		return err
	}

	scroll, err := es.NewScroll(p.window)
	if err != nil {
		return err
	}

	for hits, err := scroll.Next(); len(hits) != 0 && err == nil; hits, err = scroll.Next() {
		log.Printf("Got %d docs.\n", len(hits))
		for _, h := range hits {
			// create is faster then index but must avoid collision (index is empty at start)
			id := fmt.Sprintf("{ \"create\" : { \"_id\" : \"%v\" } }\n", h["_id"])
			io.WriteString(sink, id)
			bytes, err := json.Marshal(h["_source"])
			if err != nil {
				return err
			}
			_, err = sink.Write(bytes)
			io.WriteString(sink, "\n")
			if err != nil {
				return err
			}
		}
	}

	return err
}

func importTask(p Params) (err error) {
	log.Printf("Import %s --> %s", p.in, p.out)
	src, err := os.Open(p.in)
	if err != nil {
		return
	}
	defer src.Close()
	reader := bufio.NewReader(src)
	index, err := reader.ReadString('\n')
	es, err := ConnectES(p.out)
	if err != nil {
		return
	}
	if p.force {
		err = es.DeleteIndex()
		if err != nil {
			return
		}
	}
	typeName, err := es.PutIndex(index)
	if err != nil {
		return
	}
	log.Printf("Type: `%v`", typeName)
	batch := []string{}
	for {
		item, err := reader.ReadString('\n')
		if err == io.EOF {
			es.Bulk(typeName, batch)
			break
		}
		batch = append(batch, item)
		if len(batch) == p.window*2 {
			log.Printf("Imported %d", p.window)
			es.Bulk(typeName, batch)
			batch = batch[:0]
		}
	}
	return
}

func copyTask(p Params) (err error) {
	log.Printf("Copy %s --> %s\n", p.in, p.out)
	es1, err := ConnectES(p.in)
	if err != nil {
		return
	}
	es2, err := ConnectES(p.out)
	if err != nil {
		return
	}
	index, err := es1.GetIndex()
	if err != nil {
		return
	}
	if p.force {
		_ = es2.DeleteIndex()
	}
	typeName, err := es2.PutIndex(index)

	scroll, err := es1.NewScroll(p.window)
	if err != nil {
		return err
	}

	var batch []string
	for hits, err := scroll.Next(); len(hits) != 0 && err == nil; hits, err = scroll.Next() {
		log.Printf("Got %d docs.\n", len(hits))
		for _, h := range hits {
			id := fmt.Sprintf("{ \"create\" : { \"_id\" : \"%v\" } }\n", h["_id"])
			batch = append(batch, id)
			bytes, err := json.Marshal(h["_source"])
			if err != nil {
				return err
			}
			batch = append(batch, string(bytes)+"\n")
		}
		err = es2.Bulk(typeName, batch)
		batch = batch[:0]
		if err != nil {
			return err
		}
		log.Printf("Put %d docs.\n", len(hits))
	}
	return
}

type Params struct {
	in     string
	out    string
	window int
	force  bool
}

type Command func(Params) error

/**
Commands:
	./estool export --in <url-of-index> --out <path-to-store>
	./estool import --in <path-to-store> --out <url-to-new-index>
	./estool copy --in <url-of-src-index> --out <url-out-index>
*/
func main() {
	commandTab := map[string]Command{
		"export": exportTask,
		"import": importTask,
		"copy":   copyTask,
	}
	args := os.Args
	commands := flag.NewFlagSet("ES-tool", flag.ExitOnError)
	input := commands.String("in", "", "input path/URL")
	output := commands.String("out", "", "output path/URL")
	force := commands.Bool("force", false, "force overwrite of existing index/file")
	window := commands.Int("window", 10, "size of scroll/scan window")

	if len(args) == 1 {
		log.Printf("No command supplied, valid commands :\n")
		for c, _ := range commandTab {
			log.Printf("\t%s\n", c)
		}
		return
	}
	cmd := args[1]
	task, ok := commandTab[cmd]
	if !ok {
		log.Fatalf("Invalid command: %s\n", cmd)
	}
	commands.Parse(args[2:])
	if *input == "" {
		log.Fatalf("No in param provided")
	}
	if *output == "" {
		log.Fatalf("No out param provided")
	}
	err := task(Params{in: *input, out: *output, window: *window, force: *force})
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package gois

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"strings"
	"unicode/utf8"

	"github.com/jinzhu/now"
)

//go:generate go run cmd/generate-whois-servers/main.go
//go:generate go fmt servers.go

var timeFormats = []string{
	"02-Jan-2006",
	"2006.01.02",
	"02-Jan-2006 15:04:05 MST",
	"2006-01-02T15:04:05.0Z",
	"2006-01-02T15:04:05Z",
	"2006-01-02T15:04:05-07:00",
}

func init() {
	now.TimeFormats = append(now.TimeFormats, timeFormats...)
}

func longestTLDSuffix(domain string) string {
	longestTld := ""
	for tld := range TLDWhoisServers {
		if strings.HasSuffix(domain, "."+tld) && utf8.RuneCountInString(tld) > utf8.RuneCountInString(longestTld) {
			longestTld = tld
		}
	}
	return longestTld
}

func trimSubdomains(domain, tld string) (trimmedDomain string) {
	noTld := strings.TrimSuffix(domain, "."+tld)
	parts := strings.Split(noTld, ".")
	trimmedDomain = fmt.Sprintf("%s.%s", parts[len(parts)-1], tld)
	return trimmedDomain
}

// Whois returns the public whois information for a domain
func Whois(domain string) (record *Record, err error) {
	tld := longestTLDSuffix(domain)
	server := TLDWhoisServers[tld]

	trimmedDomain := trimSubdomains(domain, tld)
	requestDomain := trimmedDomain
	if server == "whois.verisign-grs.com" {
		requestDomain = "=" + trimmedDomain
	} else if server == "whois.denic.de" {
		requestDomain = "-T dn,ace " + trimmedDomain
	}

	response, err := QueryWhoisServer(requestDomain, server)
	if err != nil {
		return
	}

	record, err = parse(response)
	if err == nil {
		record.Domain = domain
		record.TrimmedDomain = trimmedDomain
	}

	return
}

// QueryWhoisServer queries a particular whois server for information about a domain
func QueryWhoisServer(domain, server string) (response string, err error) {
	conn, err := net.Dial("tcp", server+":43")
	if err != nil {
		return
	}
	defer conn.Close()

	fmt.Fprintf(conn, "%s\r\n", domain)
	if buf, err := ioutil.ReadAll(conn); err == nil {
		response = string(buf)
	}

	return
}

func parse(response string) (record *Record, err error) {
	for _, line := range strings.Split(response, "\n") {
		line = strings.TrimSpace(line)
		if line == "" || !strings.Contains(line, ":") {
			continue
		}
		parts := strings.SplitN(line, ":", 2)
		key, value := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
		if strings.ToLower(key) == "created" || strings.ToLower(key) == "creation date" || strings.ToLower(key) == "changed" || strings.ToLower(key) == "domain create date" {
			if parsedDate, parseErr := now.Parse(value); parseErr != nil {
				err = parseErr
			} else {
				record = &Record{CreatedOn: parsedDate, Registered: true}
			}
			return
		}
	}
	return nil, errors.New("Unable to parse whois record")
}
<commit_msg>Replace custom methods with publicsuffix calls<commit_after>package gois

import (
	"errors"
	"fmt"
	"github.com/jinzhu/now"
	"golang.org/x/net/publicsuffix"
	"io/ioutil"
	"net"
	"strings"
)

//go:generate go run cmd/generate-whois-servers/main.go
//go:generate go fmt servers.go

var timeFormats = []string{
	"02-Jan-2006",
	"2006.01.02",
	"02-Jan-2006 15:04:05 MST",
	"2006-01-02T15:04:05.0Z",
	"2006-01-02T15:04:05Z",
	"2006-01-02T15:04:05-07:00",
}

func init() {
	now.TimeFormats = append(now.TimeFormats, timeFormats...)
}

// Whois returns the public whois information for a domain
func Whois(domain string) (record *Record, err error) {
	tld, _ := publicsuffix.PublicSuffix(domain)
	whoisServer := TLDWhoisServers[tld]

	eTLDPlusOne, err := publicsuffix.EffectiveTLDPlusOne(domain)
	if err != nil {
		return nil, err
	}

	requestDomain := eTLDPlusOne
	if whoisServer == "whois.verisign-grs.com" {
		requestDomain = "=" + eTLDPlusOne
	} else if whoisServer == "whois.denic.de" {
		requestDomain = "-T dn,ace " + eTLDPlusOne
	}

	response, err := QueryWhoisServer(requestDomain, whoisServer)
	if err != nil {
		return
	}

	record, err = parse(response)
	if err == nil {
		record.Domain = domain
		record.TrimmedDomain = eTLDPlusOne
	}

	return
}

// QueryWhoisServer queries a particular whois server for information about a domain
func QueryWhoisServer(domain, server string) (response string, err error) {
	conn, err := net.Dial("tcp", server+":43")
	if err != nil {
		return
	}
	defer conn.Close()

	fmt.Fprintf(conn, "%s\r\n", domain)
	if buf, err := ioutil.ReadAll(conn); err == nil {
		response = string(buf)
	}

	return
}

func parse(response string) (record *Record, err error) {
	for _, line := range strings.Split(response, "\n") {
		line = strings.TrimSpace(line)
		if line == "" || !strings.Contains(line, ":") {
			continue
		}
		parts := strings.SplitN(line, ":", 2)
		key, value := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
		if strings.ToLower(key) == "created" || strings.ToLower(key) == "creation date" || strings.ToLower(key) == "changed" || strings.ToLower(key) == "domain create date" {
			if parsedDate, parseErr := now.Parse(value); parseErr != nil {
				err = parseErr
			} else {
				record = &Record{CreatedOn: parsedDate, Registered: true}
			}
			return
		}
	}
	return nil, errors.New("Unable to parse whois record")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net/http"
	"os"
)

// Declare all variables here
var (
	port string
	cwd  string
	err  error
)

// Log the request & return the handler
func logger(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Println(r.RemoteAddr, r.Method, r.URL)
		h.ServeHTTP(w, r)
	})
}

func main() {
	// Different port can be assigned with command line variable -port
	flag.StringVar(&port, "port", "8080", "Assign a port for the server to listen to. Defaults to 8080.")
	flag.StringVar(&cwd, "root", "", "Set a root directory for server. Defaults to current working directory.")
	flag.Parse()

	// Print the assigned port
	log.Println("Listening on port", port)

	// Get current working directory if not set with command line argument
	if len(cwd) == 0 {
		if cwd, err = os.Getwd(); err != nil {
			log.Fatal(err) // Panic if something goes wrong
		}
	}

	// Log a fatal error if a HTTP server fails
	// Server listens to %port% and servers the contents of the current working directory
	// If index.html (.htm not OK) is present, serve that on root request instead of directory listing
	log.Fatal(http.ListenAndServe(":" + port, logger(http.FileServer(http.Dir(cwd)))))
}
<commit_msg>Update gong.go<commit_after>package main

import (
	"flag"
	"log"
	"net/http"
	"os"
)

// Declare all variables here
var (
	port   string
	cwd    string
	err    error
	lop    string
	output *log.Logger
)

// Log the request & return the handler
func logger(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		output.Println(r.RemoteAddr, r.Method, r.URL)
		h.ServeHTTP(w, r)
	})
}

func main() {
	// Different port can be assigned with command line variable -port
	flag.StringVar(&port, "port", "8080", "Assign a port for the server to listen to. Defaults to 8080.")
	flag.StringVar(&cwd, "root", "", "Set a root directory for server. Defaults to current working directory.")
	flag.StringVar(&lop, "log", "", "Optional log output file.")
	flag.Parse()

	// Get current working directory if not set with command line argument
	if len(cwd) == 0 {
		if cwd, err = os.Getwd(); err != nil {
			log.Fatal(err) // Exit if something goes wrong
		}
	}

	// If log output file was set, open it and set it as the output.
	if lop != "" {
		// Open the file write only. Use append and create it if nonexistant.
		f, err := os.OpenFile(lop, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
		if err != nil {
			log.Fatal(err) // Something went wrong opening the log file.
		}
		defer f.Close() // Close the file when main exits.

		output = log.New(f, "", log.Ldate | log.Ltime) // Set logger.
		output.Println("Server started on port", port) // Log the start.
		output.Println("Document root:", cwd) // Log the document root.
	} else {
		// If -log was not used, use stdout.
		output = log.New(os.Stdout, "", log.Ldate | log.Ltime)
	}

	// Print the assigned port on screen.
	log.Println("Listening on port", port)

	// Print the document root path on screen.
	log.Println("Document root:", cwd)

	// Log a fatal error if a HTTP server fails
	// Server listens to %port% and servers the contents of the current working directory
	// If index.html (.htm not OK) is present, serve that on root request instead of directory listing
	output.Fatal(http.ListenAndServe(":" + port, logger(http.FileServer(http.Dir(cwd)))))
}
<|endoftext|>


<commit_before>/*
goqu is a library built with the following goals:

    1. Make the generation of SQL easy and enjoyable
    2. Provide a DSL that accounts for the common SQL expressions, NOT every nuance for each database.
    3. Allow users to use SQL when desired
    4. Provide a simple query API for scanning rows
    5. Allow the user to use the native sql.Db methods when desired

Features

goqu was comes with many features but here are a few of the more notable ones

    1. Query Builder
    2. Parameter interpolation (e.g SELECT * FROM "items" WHERE "id" = ? -> SELECT * FROM "items" WHERE "id" = 1)
    3. Built from the ground up with adapters in mind
    4. Insert, Multi Insert, Update, and Delete support
    5. Scanning of rows to struct[s] or primitive value[s]

While goqu may support the scanning of rows into structs it is not intended to be used as an ORM if you are looking for common ORM features like associations,
or hooks I would recommend looking at some of the great ORM libraries such as https://github.com/jinzhu/gorm

Basics

In order to start using goqu with your database you need to load an adapter. We have included some adapters by default.

    1. Postgres - github.com/doug-martin/goqu/adapters/postgres
    2. MySQL - github.com/doug-martin/goqu/adapters/mysql

Adapters in goqu work the same way as a driver with the database in that they register themselves with goqu once loaded.

    import (
          "database/sql"
          "github.com/doug-martin/goqu"
          _ "github.com/doug-martin/goqu/adapters/postgres"
          _ "github.com/lib/pq"
      )
Notice that we imported the adapter and driver for side effect only.

Once you have your adapter and driver loaded you can create a goqu.Database instance

      pgDb, err := sql.Open("postgres", "user=postgres dbname=goqupostgres sslmode=disable ")
      if err != nil {
          panic(err.Error())
      }
      db := goqu.New("postgres", pgDb)
Once you have your goqu.Database you can build your SQL and it will be formatted appropiately for the provided dialect.

     sql, _ := db.From("user").Where(gq.I("password").IsNotNull()).Sql()
     fmt.Println(sql) //SELECT * FROM "user" WHERE "password" IS NOT NULL

     sql, args, _ := db.From("user").Where(gq.I("id").Eq(10)).ToSql(true)
     fmt.Println(sql) //SELECT * FROM "user" WHERE "id" = $1

Expressions

goqu provides a DSL for generating the SQL however the Dataset only provides the the different clause methods (e.g. Where, From, Select), most of these clause methods accept Expressions(with a few exceptions) which are the building blocks for your SQL statement, you can think of them as fragments of SQL.

The entry points for expressions are:

* I() - An Identifier represents a schema, table, or column or any combination
        goqu.I("my_schema.table.col")
        goqu.I("table.col")
        goqu.I("col")
If you look at the IdentiferExpression it implements many of your common sql operations that you would perform.
    goqu.I("col").Eq(10)
    goqu.I("col").In([]int64{1,2,3,4})
    goqu.I("col").Like(regexp.MustCompile("(a|b)")
    goqu.I("col").IsNull()
Please see the exmaples for I() to see more in depth examples

* L() - An SQL literal. You may find yourself in a situation where an IdentifierExpression cannot expression an SQL fragment that your database supports. In that case you can use a LiteralExpression
        goqu.L(`"col"::TEXT = ""other_col"::text`)
You can also use placeholders in your literal. When using the LiteralExpressions placeholders are normalized to the ? character and will be transformed to the correct placeholder for your adapter (e.g. ? - $1 in postgres)
	    goqu.L("col IN (?, ?, ?)", "a", "b", "c")
Putting it together
   db.From("test").Where(
      goqu.I("col").Eq(10),
      goqu.L(`"col"::TEXT = ""other_col"::text`),
   )
Both the Identifier and Literal expressions will be ANDed together by default.
You may however want to have your expressions ORed together you can use the Or() function to create an ExpressionList
   db.From("test").Where(
      Or(
         goqu.I("col").Eq(10),
         goqu.L(`"col"::TEXT = ""other_col"::text`),
      ),
   )
You can also use Or and the And function in tandem which will give you control not only over how the Expressions are joined together, but also how they are grouped
   db.From("test").Where(
      Or(
         I("a").Gt(10),
         And(
            I("b").Eq(100),
            I("c").Neq("test"),
         ),
      ),
   ) //SELECT * FROM "test" WHERE (("a" > 10) OR (("b" = 100) AND ("c" != 'test')))

Adapters

Adapters in goqu are the foundation of building the correct SQL for each DB dialect.

When creating your adapters you must register your adapter with goqu.RegisterAdapter. This method requires 2 arguments.

   1. Dialect - The dialect for your adapter.
   2. DatasetAdapterFactory - This is a factory function that will return a new goqu.Adapter  used to create the the dialect specific SQL.

Between most dialects there is a large portion of shared syntax, for this reason we have a DefaultAdapter that can be used as a base for any new Dialect specific adapter.
In fact for most use cases you will not have to override any methods but instead just override the default values as documented for DefaultAdapter.

For example the code for the postgres adapter is fairly short.

    package postgres

    import (
	    "github.com/doug-martin/goqu"
    )

    //postgres requires a $ placeholder for prepared statements
    const placeholder_rune = '$'

    func newDatasetAdapter(ds *goqu.Dataset) goqu.Adapter {
	    ret := goqu.NewDefaultAdapter(ds).(*goqu.DefaultAdapter)

	    //override the settings required
	    ret.PlaceHolderRune = placeholder_rune
	    //postgres requires a paceholder number (e.g. $1)
	    ret.IncludePlaceholderNum = true

	    return ret
    }

    func init() {
        //register our adapter with goqu
	    goqu.RegisterAdapter("postgres", newDatasetAdapter)
    }

If you are looking to write your own adapter take a look at the postgres or mysql adapter located at https://github.com/doug-martin/goqu/tree/master/adapters.
*/
package goqu
<commit_msg>Updated synopsis<commit_after>/*
goqu is an expressive SQL builder

goqu was built with the following goals:

    1. Make the generation of SQL easy and enjoyable
    2. Provide a DSL that accounts for the common SQL expressions, NOT every nuance for each database.
    3. Allow users to use SQL when desired
    4. Provide a simple query API for scanning rows
    5. Allow the user to use the native sql.Db methods when desired

Features

goqu was comes with many features but here are a few of the more notable ones

    1. Query Builder
    2. Parameter interpolation (e.g SELECT * FROM "items" WHERE "id" = ? -> SELECT * FROM "items" WHERE "id" = 1)
    3. Built from the ground up with adapters in mind
    4. Insert, Multi Insert, Update, and Delete support
    5. Scanning of rows to struct[s] or primitive value[s]

While goqu may support the scanning of rows into structs it is not intended to be used as an ORM if you are looking for common ORM features like associations,
or hooks I would recommend looking at some of the great ORM libraries such as https://github.com/jinzhu/gorm

Basics

In order to start using goqu with your database you need to load an adapter. We have included some adapters by default.

    1. Postgres - github.com/doug-martin/goqu/adapters/postgres
    2. MySQL - github.com/doug-martin/goqu/adapters/mysql

Adapters in goqu work the same way as a driver with the database in that they register themselves with goqu once loaded.

    import (
          "database/sql"
          "github.com/doug-martin/goqu"
          _ "github.com/doug-martin/goqu/adapters/postgres"
          _ "github.com/lib/pq"
      )
Notice that we imported the adapter and driver for side effect only.

Once you have your adapter and driver loaded you can create a goqu.Database instance

      pgDb, err := sql.Open("postgres", "user=postgres dbname=goqupostgres sslmode=disable ")
      if err != nil {
          panic(err.Error())
      }
      db := goqu.New("postgres", pgDb)
Once you have your goqu.Database you can build your SQL and it will be formatted appropiately for the provided dialect.

     sql, _ := db.From("user").Where(gq.I("password").IsNotNull()).Sql()
     fmt.Println(sql) //SELECT * FROM "user" WHERE "password" IS NOT NULL

     sql, args, _ := db.From("user").Where(gq.I("id").Eq(10)).ToSql(true)
     fmt.Println(sql) //SELECT * FROM "user" WHERE "id" = $1

Expressions

goqu provides a DSL for generating the SQL however the Dataset only provides the the different clause methods (e.g. Where, From, Select), most of these clause methods accept Expressions(with a few exceptions) which are the building blocks for your SQL statement, you can think of them as fragments of SQL.

The entry points for expressions are:

* I() - An Identifier represents a schema, table, or column or any combination
        goqu.I("my_schema.table.col")
        goqu.I("table.col")
        goqu.I("col")
If you look at the IdentiferExpression it implements many of your common sql operations that you would perform.
    goqu.I("col").Eq(10)
    goqu.I("col").In([]int64{1,2,3,4})
    goqu.I("col").Like(regexp.MustCompile("(a|b)")
    goqu.I("col").IsNull()
Please see the exmaples for I() to see more in depth examples

* L() - An SQL literal. You may find yourself in a situation where an IdentifierExpression cannot expression an SQL fragment that your database supports. In that case you can use a LiteralExpression
        goqu.L(`"col"::TEXT = ""other_col"::text`)
You can also use placeholders in your literal. When using the LiteralExpressions placeholders are normalized to the ? character and will be transformed to the correct placeholder for your adapter (e.g. ? - $1 in postgres)
	    goqu.L("col IN (?, ?, ?)", "a", "b", "c")
Putting it together
   db.From("test").Where(
      goqu.I("col").Eq(10),
      goqu.L(`"col"::TEXT = ""other_col"::text`),
   )
Both the Identifier and Literal expressions will be ANDed together by default.
You may however want to have your expressions ORed together you can use the Or() function to create an ExpressionList
   db.From("test").Where(
      Or(
         goqu.I("col").Eq(10),
         goqu.L(`"col"::TEXT = ""other_col"::text`),
      ),
   )
You can also use Or and the And function in tandem which will give you control not only over how the Expressions are joined together, but also how they are grouped
   db.From("test").Where(
      Or(
         I("a").Gt(10),
         And(
            I("b").Eq(100),
            I("c").Neq("test"),
         ),
      ),
   ) //SELECT * FROM "test" WHERE (("a" > 10) OR (("b" = 100) AND ("c" != 'test')))

Adapters

Adapters in goqu are the foundation of building the correct SQL for each DB dialect.

When creating your adapters you must register your adapter with goqu.RegisterAdapter. This method requires 2 arguments.

   1. Dialect - The dialect for your adapter.
   2. DatasetAdapterFactory - This is a factory function that will return a new goqu.Adapter  used to create the the dialect specific SQL.

Between most dialects there is a large portion of shared syntax, for this reason we have a DefaultAdapter that can be used as a base for any new Dialect specific adapter.
In fact for most use cases you will not have to override any methods but instead just override the default values as documented for DefaultAdapter.

For example the code for the postgres adapter is fairly short.

    package postgres

    import (
	    "github.com/doug-martin/goqu"
    )

    //postgres requires a $ placeholder for prepared statements
    const placeholder_rune = '$'

    func newDatasetAdapter(ds *goqu.Dataset) goqu.Adapter {
	    ret := goqu.NewDefaultAdapter(ds).(*goqu.DefaultAdapter)

	    //override the settings required
	    ret.PlaceHolderRune = placeholder_rune
	    //postgres requires a paceholder number (e.g. $1)
	    ret.IncludePlaceholderNum = true

	    return ret
    }

    func init() {
        //register our adapter with goqu
	    goqu.RegisterAdapter("postgres", newDatasetAdapter)
    }

If you are looking to write your own adapter take a look at the postgres or mysql adapter located at https://github.com/doug-martin/goqu/tree/master/adapters.
*/
package goqu
<|endoftext|>


<commit_before>package htmlform

var FormTemplate = `
<!DOCTYPE html>

	<meta charset="utf-8"/>
	<title>{{.Title}}</title>

{{.Title}}

<form action="." method="post" enctype="multipart/form-data">
	{{range .Fields}}

			{{if eq .Type "checkbox"}}
				<input type="checkbox" id="{{.Name}}" name="{{.Name}}" value="true" {{if eq .Value "true"}}checked{{end}}/>
				{{.Label}}
			{{else if eq .Type "select"}}
				{{.Label}}:
				<select id="{{.Name}}" name="{{.Name}}" {{if .Required}}required{{end}}>
					{{$selectValue := .Value}}
					{{range .Options}}
						<option value="{{.Value}}" {{if eq (printf "%v" .Value) $selectValue}}selected{{end}}>{{.Label}}</option>
					{{end}}
				</select>
			{{else if eq .Type "textarea"}}
				{{.Label}}:
				<textarea id="{{.Name}}" name="{{.Name}}" cols="40" rows="5" {{if .Required}}required{{end}}>{{.Value}}</textarea>
			{{else}}
				{{.Label}}:
				<input type="{{.Type}}" id="{{.Name}}" name="{{.Name}}" value="{{.Value}}" size="40" {{if .Required}}required{{end}}/>
			{{end}}

	{{end}}
	<button>{{.SubmitButtonText}}</button>
</form>
`
<commit_msg>no htmlform action<commit_after>package htmlform

var FormTemplate = `
<!DOCTYPE html>

	<meta charset="utf-8"/>
	<title>{{.Title}}</title>

{{.Title}}

<form method="post" enctype="multipart/form-data">
	{{range .Fields}}

			{{if eq .Type "checkbox"}}
				<input type="checkbox" id="{{.Name}}" name="{{.Name}}" value="true" {{if eq .Value "true"}}checked{{end}}/>
				{{.Label}}
			{{else if eq .Type "select"}}
				{{.Label}}:
				<select id="{{.Name}}" name="{{.Name}}" {{if .Required}}required{{end}}>
					{{$selectValue := .Value}}
					{{range .Options}}
						<option value="{{.Value}}" {{if eq (printf "%v" .Value) $selectValue}}selected{{end}}>{{.Label}}</option>
					{{end}}
				</select>
			{{else if eq .Type "textarea"}}
				{{.Label}}:
				<textarea id="{{.Name}}" name="{{.Name}}" cols="40" rows="5" {{if .Required}}required{{end}}>{{.Value}}</textarea>
			{{else}}
				{{.Label}}:
				<input type="{{.Type}}" id="{{.Name}}" name="{{.Name}}" value="{{.Value}}" size="40" {{if .Required}}required{{end}}/>
			{{end}}

	{{end}}
	<button>{{.SubmitButtonText}}</button>
</form>
`
<|endoftext|>


<commit_before>package main

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

const (
	GitIgnore = ".gitignore"
)

var (
	GOPATH = os.Getenv("GOPATH")

	dir = ""
)

func main() {
	log.SetFlags(0)

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

func doinit() {
	var err error
	dir, err = os.Getwd()
	if err != nil {
		log.Fatalf("Unable to determine current directory: %s", err)
	}

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

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

	writeAndCommit(GitIgnore, DefaultGitIgnore, "Initialized repo with a .gitignore file")

	// Done
	log.Printf("Initialized git repo with a default README.md, please set your GOPATH to \"%s\", e.g.", dir)
	log.Printf("  export GOPATH=\"%s\"", dir)
	os.Setenv("GOPATH", dir)
}

func get() {
	requireGOPATH()

	flags := flag.NewFlagSet("get", flag.ExitOnError)
	flags.Parse(os.Args[2:])
	args := flags.Args()
	if len(args) < 1 {
		log.Fatal("Please specify a package")
	}

	pkg := args[0]
	if !isGithub(pkg) {
		log.Fatal("gost only supports packages on github.com")
	}

	branch := "master"
	if len(args) > 1 {
		branch = args[1]
	}

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

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

func fetchSubtree(pkg string, branch string, alreadyFetched map[string]bool) {
	// Take only the path up to the github repo
	pkgParts := strings.Split(pkg, "/")
	pkgRoot := path.Join(pkgParts[:3]...)
	if alreadyFetched[pkgRoot] {
		return
	}

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

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

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

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

func goGet(pkg string, alreadyFetched map[string]bool) {
	if alreadyFetched[pkg] {
		return
	}
	run("go", "get", pkg)
	alreadyFetched[pkg] = true
}

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

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

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

func requireGOPATH() {
	if GOPATH == "" {
		log.Fatal("Please set your GOPATH")
	}
}

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

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

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

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

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

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

const DefaultGitIgnore = `pkg
bin
`
<commit_msg>Ignoring imports of .<commit_after>package main

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

const (
	GitIgnore = ".gitignore"
)

var (
	GOPATH = os.Getenv("GOPATH")

	dir = ""
)

func main() {
	log.SetFlags(0)

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

func doinit() {
	var err error
	dir, err = os.Getwd()
	if err != nil {
		log.Fatalf("Unable to determine current directory: %s", err)
	}

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

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

	writeAndCommit(GitIgnore, DefaultGitIgnore, "Initialized repo with a .gitignore file")

	// Done
	log.Printf("Initialized git repo with a default README.md, please set your GOPATH to \"%s\", e.g.", dir)
	log.Printf("  export GOPATH=\"%s\"", dir)
	os.Setenv("GOPATH", dir)
}

func get() {
	requireGOPATH()

	flags := flag.NewFlagSet("get", flag.ExitOnError)
	flags.Parse(os.Args[2:])
	args := flags.Args()
	if len(args) < 1 {
		log.Fatal("Please specify a package")
	}

	pkg := args[0]
	if !isGithub(pkg) {
		log.Fatal("gost only supports packages on github.com")
	}

	branch := "master"
	if len(args) > 1 {
		branch = args[1]
	}

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

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

func fetchSubtree(pkg string, branch string, alreadyFetched map[string]bool) {
	// Take only the path up to the github repo
	pkgParts := strings.Split(pkg, "/")
	pkgRoot := path.Join(pkgParts[:3]...)
	if alreadyFetched[pkgRoot] {
		return
	}

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

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

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

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

func goGet(pkg string, alreadyFetched map[string]bool) {
	if alreadyFetched[pkg] {
		return
	}
	run("go", "get", pkg)
	alreadyFetched[pkg] = true
}

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

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

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

func requireGOPATH() {
	if GOPATH == "" {
		log.Fatal("Please set your GOPATH")
	}
}

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

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

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

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

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

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

const DefaultGitIgnore = `pkg
bin
`
<|endoftext|>


<commit_before>package gown

import (
	"fmt"
	"os"
	"strings"
)

type WN struct {
	senseIndex  senseIndex
	PosIndicies map[int]*dataIndex
	posData     map[int]*dataFile
	exceptions  []map[string]string
}

func GetWordNetDictDir() (string, error) {
	systemDefaults := []string{
		"/usr/WordNet-3.%d/dict",
		"/usr/share/WordNet-3.%d/dict",
		"/usr/local/WordNet-3.%d/dict",
		"/usr/local/share/WordNet-3.%d/dict",
		"/opt/WordNet-3.%d/dict",
		"/opt/share/WordNet-3.%d/dict",
		"/opt/local/WordNet-3.%d/dict",
		"/opt/local/share/WordNet-3.%d/dict",
	}
	// check environment variables
	dictname := os.Getenv("WNHOME") + "/dict"
	_, err := os.Stat(dictname)
	if err == nil {
		return dictname, nil
	}

	dictname = os.Getenv("WNSEARCHDIR")
	_, err = os.Stat(dictname)
	if err == nil {
		return dictname, nil
	}

	// check possible installation dirs
	for v := 0; v <= 1; v++ { // checks for WordNet 3.0 and 3.1
		for _, systemDefault := range systemDefaults {
			dictname = fmt.Sprintf(systemDefault, v)
			_, err = os.Stat(dictname)
			if err == nil {
				return dictname, nil
			}
		}
	}

	// tried everything
	return "", fmt.Errorf("Can't find WordNet dictionary")
}

func LoadWordNet(dictDirname string) (*WN, error) {
	wn := &WN{
		senseIndex:  nil,
		PosIndicies: map[int]*dataIndex{},
		posData:     map[int]*dataFile{},
	}

	var err error = nil
	pos_file_names := []string{"", "noun", "verb", "adj", "adv"}
	for i := 1; i < len(pos_file_names); i++ {
		wn.PosIndicies[i], err = readPosIndex(dictDirname + "/index." + pos_file_names[i])
		if err != nil {
			return nil, err
		}
		wn.posData[i], err = readPosData(dictDirname + "/data." + pos_file_names[i])
		if err != nil {
			return nil, err
		}
	}

	wn.senseIndex, err = loadSenseIndex(wn, dictDirname + "/index.sense")
	if err != nil {
		return nil, err
	}

	return wn, nil
}

func (wn *WN) LookupWithPartOfSpeech(lemma string, pos int) *DataIndexEntry {
	posIndexPtr, exists := wn.PosIndicies[pos]
	if !exists {
		return nil
	}
	sn, exists := (*posIndexPtr)[strings.ToLower(lemma)]
	if exists {
		return &sn
	} else {
		return nil
	}
}

func (wn *WN) LookupSensesWithPartOfSpeech(lemma string, pos int) []*SenseIndexEntry {
	senses, _ := wn.senseIndex[lemma]
	ret := make([]*SenseIndexEntry, 0, len(senses))
	for i, _ := range senses {
		if senses[i].PartOfSpeech == pos {
			ret = append(ret, &senses[i])
		}
	}
	return ret
}

func (wn *WN) LookupWithPartOfSpeechAndSense(lemma string, pos int, senseId int) *SenseIndexEntry {
	senses, _ := wn.senseIndex[lemma]
	var z *SenseIndexEntry
	for i, sense := range senses {
		fmt.Printf("%q %d: %s\n", lemma, i, sense.ToString())
		if (sense.PartOfSpeech == pos) && (sense.SenseNumber == senseId) {
			z = &sense
		}
	}
	return z
}

func (wn *WN) Lookup(lemma string) []*SenseIndexEntry {
	senseEntries, exists := wn.senseIndex[strings.ToLower(lemma)]
	if !exists {
		return []*SenseIndexEntry{}
	}
	ret := make([]*SenseIndexEntry, len(senseEntries))
	for i, _ := range senseEntries {
		ret[i] = &senseEntries[i]
	}
	return ret
}

func (wn *WN) GetSynset(pos int, synsetOffset int) *Synset {
	if pos == POS_ADJECTIVE_SATELLITE {
		pos = POS_ADJECTIVE
	}
	idxPtr, exists := wn.posData[pos]
	if !exists || idxPtr == nil {
		return nil
	}
	s, exists := (*idxPtr)[synsetOffset]
	if !exists {
		return nil
	}
	return &s
}

func (wn *WN) TraverseDataIndex(pos int) <-chan DataIndexPair {
	table, ok := wn.PosIndicies[pos]
	if !ok {
		return nil
	}
	out := make(chan DataIndexPair)
	go func() {
		for k, v := range *table {
			out <- DataIndexPair{k, v}
		}
		close(out)
	}()
	return out
}

func (wn *WN) Iter() <-chan *Synset {
	outChan := make(chan *Synset)
	go func() {
		for _, datFile := range wn.posData {
			for _, synset := range *datFile {
				words := make([]string, len(synset.Words))
				for i, w := range synset.Words {
					words[i] = w
				}
				lexids := make([]int, len(synset.LexIds))
				for i, w := range synset.LexIds {
					lexids[i] = w
				}
				edges := make([]RelationshipEdge, len(synset.Relationships))
				for i, w := range synset.Relationships {
					edges[i] = w
				}
				outChan <- &Synset{
					SynsetOffset:       synset.SynsetOffset,
					LexographerFilenum: synset.LexographerFilenum,
					PartOfSpeech:       synset.PartOfSpeech,
					Words:              words,
					LexIds:             lexids,
					Relationships:      edges,
					Gloss:              synset.Gloss,
				}
			}
		}
		close(outChan)
	}()
	return outChan
}

func (wn *WN) IterSenses() <-chan *SenseIndexEntry {
	outchan := make(chan *SenseIndexEntry)
	go func() {
		for _, senses := range wn.senseIndex {
			for i, _ := range senses {
				outchan <- &senses[i]
			}
		}
		close(outchan)
	}()
	return outchan
}
<commit_msg>kill debug<commit_after>package gown

import (
	"fmt"
	"os"
	"strings"
)

type WN struct {
	senseIndex  senseIndex
	PosIndicies map[int]*dataIndex
	posData     map[int]*dataFile
	exceptions  []map[string]string
}

func GetWordNetDictDir() (string, error) {
	systemDefaults := []string{
		"/usr/WordNet-3.%d/dict",
		"/usr/share/WordNet-3.%d/dict",
		"/usr/local/WordNet-3.%d/dict",
		"/usr/local/share/WordNet-3.%d/dict",
		"/opt/WordNet-3.%d/dict",
		"/opt/share/WordNet-3.%d/dict",
		"/opt/local/WordNet-3.%d/dict",
		"/opt/local/share/WordNet-3.%d/dict",
	}
	// check environment variables
	dictname := os.Getenv("WNHOME") + "/dict"
	_, err := os.Stat(dictname)
	if err == nil {
		return dictname, nil
	}

	dictname = os.Getenv("WNSEARCHDIR")
	_, err = os.Stat(dictname)
	if err == nil {
		return dictname, nil
	}

	// check possible installation dirs
	for v := 0; v <= 1; v++ { // checks for WordNet 3.0 and 3.1
		for _, systemDefault := range systemDefaults {
			dictname = fmt.Sprintf(systemDefault, v)
			_, err = os.Stat(dictname)
			if err == nil {
				return dictname, nil
			}
		}
	}

	// tried everything
	return "", fmt.Errorf("Can't find WordNet dictionary")
}

func LoadWordNet(dictDirname string) (*WN, error) {
	wn := &WN{
		senseIndex:  nil,
		PosIndicies: map[int]*dataIndex{},
		posData:     map[int]*dataFile{},
	}

	var err error = nil
	pos_file_names := []string{"", "noun", "verb", "adj", "adv"}
	for i := 1; i < len(pos_file_names); i++ {
		wn.PosIndicies[i], err = readPosIndex(dictDirname + "/index." + pos_file_names[i])
		if err != nil {
			return nil, err
		}
		wn.posData[i], err = readPosData(dictDirname + "/data." + pos_file_names[i])
		if err != nil {
			return nil, err
		}
	}

	wn.senseIndex, err = loadSenseIndex(wn, dictDirname + "/index.sense")
	if err != nil {
		return nil, err
	}

	return wn, nil
}

func (wn *WN) LookupWithPartOfSpeech(lemma string, pos int) *DataIndexEntry {
	posIndexPtr, exists := wn.PosIndicies[pos]
	if !exists {
		return nil
	}
	sn, exists := (*posIndexPtr)[strings.ToLower(lemma)]
	if exists {
		return &sn
	} else {
		return nil
	}
}

func (wn *WN) LookupSensesWithPartOfSpeech(lemma string, pos int) []*SenseIndexEntry {
	senses, _ := wn.senseIndex[lemma]
	ret := make([]*SenseIndexEntry, 0, len(senses))
	for i, _ := range senses {
		if senses[i].PartOfSpeech == pos {
			ret = append(ret, &senses[i])
		}
	}
	return ret
}

func (wn *WN) LookupWithPartOfSpeechAndSense(lemma string, pos int, senseId int) *SenseIndexEntry {
	senses, _ := wn.senseIndex[lemma]
	for i, sense := range senses {
		if (sense.PartOfSpeech == pos) && (sense.SenseNumber == senseId) {
			return &sense
		}
	}
	return nil
}

func (wn *WN) Lookup(lemma string) []*SenseIndexEntry {
	senseEntries, exists := wn.senseIndex[strings.ToLower(lemma)]
	if !exists {
		return []*SenseIndexEntry{}
	}
	ret := make([]*SenseIndexEntry, len(senseEntries))
	for i, _ := range senseEntries {
		ret[i] = &senseEntries[i]
	}
	return ret
}

func (wn *WN) GetSynset(pos int, synsetOffset int) *Synset {
	if pos == POS_ADJECTIVE_SATELLITE {
		pos = POS_ADJECTIVE
	}
	idxPtr, exists := wn.posData[pos]
	if !exists || idxPtr == nil {
		return nil
	}
	s, exists := (*idxPtr)[synsetOffset]
	if !exists {
		return nil
	}
	return &s
}

func (wn *WN) TraverseDataIndex(pos int) <-chan DataIndexPair {
	table, ok := wn.PosIndicies[pos]
	if !ok {
		return nil
	}
	out := make(chan DataIndexPair)
	go func() {
		for k, v := range *table {
			out <- DataIndexPair{k, v}
		}
		close(out)
	}()
	return out
}

func (wn *WN) Iter() <-chan *Synset {
	outChan := make(chan *Synset)
	go func() {
		for _, datFile := range wn.posData {
			for _, synset := range *datFile {
				words := make([]string, len(synset.Words))
				for i, w := range synset.Words {
					words[i] = w
				}
				lexids := make([]int, len(synset.LexIds))
				for i, w := range synset.LexIds {
					lexids[i] = w
				}
				edges := make([]RelationshipEdge, len(synset.Relationships))
				for i, w := range synset.Relationships {
					edges[i] = w
				}
				outChan <- &Synset{
					SynsetOffset:       synset.SynsetOffset,
					LexographerFilenum: synset.LexographerFilenum,
					PartOfSpeech:       synset.PartOfSpeech,
					Words:              words,
					LexIds:             lexids,
					Relationships:      edges,
					Gloss:              synset.Gloss,
				}
			}
		}
		close(outChan)
	}()
	return outChan
}

func (wn *WN) IterSenses() <-chan *SenseIndexEntry {
	outchan := make(chan *SenseIndexEntry)
	go func() {
		for _, senses := range wn.senseIndex {
			for i, _ := range senses {
				outchan <- &senses[i]
			}
		}
		close(outchan)
	}()
	return outchan
}
<|endoftext|>


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

package goyo

import (
	"bytes"
	"io/ioutil"
	"log"
	"net/mail"
	"regexp"
	"time"
)

//TODO this needs to be set in an init() function
var ROOT_DIRECTORY = ""

var TIME_REGEX = regexp.MustCompile(`\+([0-9]+)\.([A-Za-z]+)@`)

var UNIQ_FILENAME_REGEX = regexp.MustCompile(`(.+):`)

//processMessage processes each new message that appears in /new
func processMessage(filename string) error {
	//Parse message and determine when the message should be yo-yoed

	bts, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}

	message, err := mail.ReadMessage(bytes.NewBuffer(bts))

	if err != nil {
		return err
	}

	//Assume that there is only one recipient - the one we care about
	addresses, err := message.Header.AddressList("To")
	if err != nil {
		return err
	}

	to_address := addresses[0].Address

	t, err := extractTimeFromAddress(to_address)
	if err != nil {
		return err
	}

	//Schedule future message for that yo-yoed time

	if err := scheduleFutureMessage(filename, t); err != nil {
		return err
	}

	//Move message from /new to /cur, setting Maildir info flag to S (seen)
	err = os.Rename(filepath.Join(ROOT_DIRECTORY, "new", filename), filepath.Join(ROOT_DIRECTORY, "cur", filename))

	return err
}

//Parse an email address and return the future time at which to bounce the email
func extractTimeFromAddress(to_address string) (time.Time, error) {

	matches := TIME_REGEX.FindStringSubmatch(to_address)

	number_s := matches[1]
	time_unit_s := matches[2]

	number, err := strconv.Atoi(number_s)
	if err != nil {
		panic(err)
	}

	//For now, we'll support minutes, hours, days, weeks, and months

	var time_unit time.Duration

	switch strings.ToLower(time_unit) {
	case "minute", "minutes":
		{
			time_unit = time.Minute
		}

	case "hour", "hours":
		{
			time_unit = time.Hour
		}

	case "day", "days":
		{
			time_unit = time.Day
		}

	case "week", "weeks":
		{
			time_unit = 7 * time.Day
		}

	case "month", "months":
		{
			time_unit = 30 * time.Day
		}
	}

	delay := number * time_unit
	future_time := time.Now().Add(number * time_unit)
	return future_time, nil

}

//scheduleFutureMessage schedules a future email delivery
func scheduleFutureMessage(filename string, t time.Time) (err error) {
	//TODO actually implement this
	uniq := uniqueFromFilename(filename)
	log.Print(uniq)

	return nil
}

//uniqueFromFilename extracts the unique part of a Maildir filename
func uniqueFromFilename(filename string) (uniq string) {
    //TODO the real input set may actually be larger/more complicated than this
    matches := UNIQ_FILENAME_REGEX.FindStringSubmatch(filename)
    uniq = matches[1]
}
<commit_msg>Fix compilation error by casting int to time.Duration<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goyo

import (
	"bytes"
	"io/ioutil"
	"log"
	"net/mail"
	"regexp"
	"time"
    "os"
    "strings"
    "strconv"
    "path/filepath"
)

//TODO this needs to be set in an init() function
var ROOT_DIRECTORY = ""

var TIME_REGEX = regexp.MustCompile(`\+([0-9]+)\.([A-Za-z]+)@`)

var UNIQ_FILENAME_REGEX = regexp.MustCompile(`(.+):`)

//processMessage processes each new message that appears in /new
func processMessage(filename string) error {
	//Parse message and determine when the message should be yo-yoed

	bts, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}

	message, err := mail.ReadMessage(bytes.NewBuffer(bts))

	if err != nil {
		return err
	}

	//Assume that there is only one recipient - the one we care about
	addresses, err := message.Header.AddressList("To")
	if err != nil {
		return err
	}

	to_address := addresses[0].Address

	t, err := extractTimeFromAddress(to_address)
	if err != nil {
		return err
	}

	//Schedule future message for that yo-yoed time

	if err := scheduleFutureMessage(filename, t); err != nil {
		return err
	}

	//Move message from /new to /cur, setting Maildir info flag to S (seen)
    err = os.Rename(filepath.Join(ROOT_DIRECTORY, "new", filename), filepath.Join(ROOT_DIRECTORY, "cur", uniqueFromFilename(filename) + ":2,S"))

	return err
}

//Parse an email address and return the future time at which to bounce the email
func extractTimeFromAddress(to_address string) (time.Time, error) {

	matches := TIME_REGEX.FindStringSubmatch(to_address)

	number_s := matches[1]
	time_unit_s := matches[2]

	number, err := strconv.Atoi(number_s)
	if err != nil {
		panic(err)
	}

	//For now, we'll support minutes, hours, days, weeks, and months

	var time_unit time.Duration

	switch strings.ToLower(time_unit_s) {
	case "minute", "minutes":
		{
			time_unit = time.Minute
		}

	case "hour", "hours":
		{
			time_unit = time.Hour
		}

	case "day", "days":
		{
			time_unit = 24 * time.Hour
		}

	case "week", "weeks":
		{
			time_unit = 7 * 24 * time.Hour
		}

	case "month", "months":
		{
			time_unit = 30 * 7 * 24 * time.Hour
		}
	}

	delay := time.Duration(number) * time_unit
	future_time := time.Now().Add(delay)
	return future_time, nil

}

//scheduleFutureMessage schedules a future email delivery
func scheduleFutureMessage(filename string, t time.Time) (err error) {
	//TODO actually implement this
	uniq := uniqueFromFilename(filename)
	log.Print(uniq)

	return nil
}

//uniqueFromFilename extracts the unique part of a Maildir filename
func uniqueFromFilename(filename string) (uniq string) {
    //The real input set may actually be larger/more complicated than this
    //But this works for now
    matches := UNIQ_FILENAME_REGEX.FindStringSubmatch(filename)
    uniq = matches[1]
    return
}
<|endoftext|>


<commit_before>package kite

import (
	"archive/tar"
	"compress/gzip"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"koding/newKite/kd/util"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

type Install struct{}

func NewInstall() *Install {
	return &Install{}
}

func (*Install) Definition() string {
	return "Install kite from Koding repository"
}

const S3URL = "http://koding-kites.s3.amazonaws.com/"

func (*Install) Exec() error {
	// Parse kite name
	flag.Parse()
	if flag.NArg() != 1 {
		return errors.New("You should give a kite name")
	}

	kiteFullName := flag.Arg(0)
	kiteName, kiteVersion, err := splitVersion(kiteFullName, true)
	if err != nil {
		kiteName, kiteVersion = kiteFullName, "latest"
	}

	// Make download request
	fmt.Println("Downloading...")
	targz, err := requestPackage(kiteName, kiteVersion)
	if err != nil {
		return err
	}
	defer targz.Close()

	// Extract gzip
	gz, err := gzip.NewReader(targz)
	if err != nil {
		return err
	}
	defer gz.Close()

	// Extract tar
	tempKitePath, err := ioutil.TempDir("", "kd-kite-install-")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tempKitePath)

	err = extractTar(gz, tempKitePath)
	if err != nil {
		return err
	}

	foundName, _, bundlePath, err := validatePackage(tempKitePath)
	if err != nil {
		return err
	}

	if foundName != kiteName {
		return fmt.Errorf("Invalid package: Bundle name does not match with package name: %s != %s",
			foundName, kiteName)
	}

	err = installBundle(bundlePath)
	if err != nil {
		return err
	}

	fmt.Println("Installed successfully:", kiteFullName)
	return nil
}

// requestPackage makes a request to the kite repository and returns
// a io.ReadCloser. The caller must close the returned io.ReadCloser.
func requestPackage(kiteName, kiteVersion string) (io.ReadCloser, error) {
	kiteURL := S3URL + kiteName + "-" + kiteVersion + ".kite.tar.gz"

	res, err := http.Get(kiteURL)
	if err != nil {
		return nil, err
	}

	if res.StatusCode == 404 {
		res.Body.Close()
		return nil, errors.New("Package is not found on the server.")
	}

	if res.StatusCode != 200 {
		res.Body.Close()
		return nil, fmt.Errorf("Unexpected response from server: %d", res.StatusCode)
	}

	return res.Body, nil
}

// extractTar reads from the io.Reader and writes the files into the directory.
func extractTar(r io.Reader, dir string) error {
	tr := tar.NewReader(r)
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			// end of tar archive
			break
		}
		if err != nil {
			return err
		}

		path := filepath.Join(dir, hdr.Name)

		// TODO make the binary under /bin executable

		if hdr.FileInfo().IsDir() {
			os.MkdirAll(path, 0700)
		} else {
			f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
			if err != nil {
				return err
			}

			if _, err := io.Copy(f, tr); err != nil {
				return err
			}
		}
	}
	return nil
}

// validatePackage returns the package name, version and bundle path.
func validatePackage(tempKitePath string) (string, string, string, error) {
	dirs, err := ioutil.ReadDir(tempKitePath)
	if err != nil {
		return "", "", "", err
	}

	if len(dirs) != 1 {
		return "", "", "", errors.New("Invalid package: Package must contain only one directory.")
	}

	bundleName := dirs[0].Name() // Example: asdf-1.2.3.kite
	if !strings.HasSuffix(bundleName, ".kite") {
		return "", "", "", errors.New("Invalid package: Direcory name must end with \".kite\".")
	}

	fullName := strings.TrimSuffix(bundleName, ".kite") // Example: asdf-1.2.3
	kiteName, version, err := splitVersion(fullName, false)
	if err != nil {
		return "", "", "", errors.New("Invalid package: No version number in Kite bundle")
	}

	return kiteName, version, filepath.Join(tempKitePath, bundleName), nil
}

// installBundle moves the .kite bundle into ~/kd/kites.
func installBundle(bundlePath string) error {
	kitesPath := filepath.Join(util.GetKdPath(), "kites")
	os.MkdirAll(kitesPath, 0700)

	bundleName := filepath.Base(bundlePath)
	kitePath := filepath.Join(kitesPath, bundleName)
	return os.Rename(bundlePath, kitePath)
}

// splitVersion takes a name like "asdf-1.2.3" and
// returns the name "asdf" and version "1.2.3" seperately.
// If allowLatest is true, then the version must not be numeric and can be "latest".
func splitVersion(fullname string, allowLatest bool) (name, version string, err error) {
	notFound := errors.New("name does not contain a version number")

	parts := strings.Split(fullname, "-")
	n := len(parts)
	if n < 2 {
		return "", "", notFound
	}

	name = strings.Join(parts[:n-1], "-")
	version = parts[n-1]

	if allowLatest && version == "latest" {
		return name, version, nil
	}

	versionParts := strings.Split(version, ".")
	for _, v := range versionParts {
		if _, err := strconv.Atoi(v); err != nil {
			return "", "", notFound
		}
	}

	return name, version, nil
}
<commit_msg>do not download again if alreay installed<commit_after>package kite

import (
	"archive/tar"
	"compress/gzip"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"koding/newKite/kd/util"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

type Install struct{}

func NewInstall() *Install {
	return &Install{}
}

func (*Install) Definition() string {
	return "Install kite from Koding repository"
}

const S3URL = "http://koding-kites.s3.amazonaws.com/"

func (*Install) Exec() error {
	// Parse kite name
	flag.Parse()
	if flag.NArg() != 1 {
		return errors.New("You should give a kite name")
	}

	kiteFullName := flag.Arg(0)
	kiteName, kiteVersion, err := splitVersion(kiteFullName, true)
	if err != nil {
		kiteName, kiteVersion = kiteFullName, "latest"
	}

	// Make download request
	fmt.Println("Downloading...")
	targz, err := requestPackage(kiteName, kiteVersion)
	if err != nil {
		return err
	}
	defer targz.Close()

	// Extract gzip
	gz, err := gzip.NewReader(targz)
	if err != nil {
		return err
	}
	defer gz.Close()

	// Extract tar
	tempKitePath, err := ioutil.TempDir("", "kd-kite-install-")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tempKitePath)

	err = extractTar(gz, tempKitePath)
	if err != nil {
		return err
	}

	foundName, _, bundlePath, err := validatePackage(tempKitePath)
	if err != nil {
		return err
	}

	if foundName != kiteName {
		return fmt.Errorf("Invalid package: Bundle name does not match with package name: %s != %s",
			foundName, kiteName)
	}

	err = installBundle(bundlePath)
	if err != nil {
		return err
	}

	fmt.Println("Installed successfully:", kiteFullName)
	return nil
}

// requestPackage makes a request to the kite repository and returns
// a io.ReadCloser. The caller must close the returned io.ReadCloser.
func requestPackage(kiteName, kiteVersion string) (io.ReadCloser, error) {
	kiteURL := S3URL + kiteName + "-" + kiteVersion + ".kite.tar.gz"

	res, err := http.Get(kiteURL)
	if err != nil {
		return nil, err
	}

	if res.StatusCode == 404 {
		res.Body.Close()
		return nil, errors.New("Package is not found on the server.")
	}

	if res.StatusCode != 200 {
		res.Body.Close()
		return nil, fmt.Errorf("Unexpected response from server: %d", res.StatusCode)
	}

	return res.Body, nil
}

// extractTar reads from the io.Reader and writes the files into the directory.
func extractTar(r io.Reader, dir string) error {
	first := true // true if we are on the first entry of tarball
	tr := tar.NewReader(r)
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			// end of tar archive
			break
		}
		if err != nil {
			return err
		}

		if first {
			first = false
			kiteName := strings.TrimSuffix(hdr.Name, ".kite/")

			installed, err := isInstalled(kiteName)
			if err != nil {
				return err
			}

			if installed {
				return fmt.Errorf("Already installed: %s", kiteName)
			}
		}

		path := filepath.Join(dir, hdr.Name)

		// TODO make the binary under /bin executable

		if hdr.FileInfo().IsDir() {
			os.MkdirAll(path, 0700)
		} else {
			f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
			if err != nil {
				return err
			}

			if _, err := io.Copy(f, tr); err != nil {
				return err
			}
		}
	}
	return nil
}

// validatePackage returns the package name, version and bundle path.
func validatePackage(tempKitePath string) (string, string, string, error) {
	dirs, err := ioutil.ReadDir(tempKitePath)
	if err != nil {
		return "", "", "", err
	}

	if len(dirs) != 1 {
		return "", "", "", errors.New("Invalid package: Package must contain only one directory.")
	}

	bundleName := dirs[0].Name() // Example: asdf-1.2.3.kite
	if !strings.HasSuffix(bundleName, ".kite") {
		return "", "", "", errors.New("Invalid package: Direcory name must end with \".kite\".")
	}

	fullName := strings.TrimSuffix(bundleName, ".kite") // Example: asdf-1.2.3
	kiteName, version, err := splitVersion(fullName, false)
	if err != nil {
		return "", "", "", errors.New("Invalid package: No version number in Kite bundle")
	}

	return kiteName, version, filepath.Join(tempKitePath, bundleName), nil
}

// installBundle moves the .kite bundle into ~/kd/kites.
func installBundle(bundlePath string) error {
	kitesPath := filepath.Join(util.GetKdPath(), "kites")
	os.MkdirAll(kitesPath, 0700)

	bundleName := filepath.Base(bundlePath)
	kitePath := filepath.Join(kitesPath, bundleName)
	return os.Rename(bundlePath, kitePath)
}

// splitVersion takes a name like "asdf-1.2.3" and
// returns the name "asdf" and version "1.2.3" seperately.
// If allowLatest is true, then the version must not be numeric and can be "latest".
func splitVersion(fullname string, allowLatest bool) (name, version string, err error) {
	notFound := errors.New("name does not contain a version number")

	parts := strings.Split(fullname, "-")
	n := len(parts)
	if n < 2 {
		return "", "", notFound
	}

	name = strings.Join(parts[:n-1], "-")
	version = parts[n-1]

	if allowLatest && version == "latest" {
		return name, version, nil
	}

	versionParts := strings.Split(version, ".")
	for _, v := range versionParts {
		if _, err := strconv.Atoi(v); err != nil {
			return "", "", notFound
		}
	}

	return name, version, nil
}
<|endoftext|>


<commit_before>// 31 august 2014

package ui

import (
	"fmt"
)

// Grid is a Control that arranges other Controls in a grid.
// Grid is a very powerful container: it can position and size each Control in several ways and can (and must) have Controls added to it at any time, in any direction.
// it can also have Controls spanning multiple rows and columns.
//
// Each Control in a Grid has associated "expansion" and "alignment" values in both the X and Y direction.
// Expansion determines whether all cells in the same row/column are given whatever space is left over after figuring out how big the rest of the Grid should be.
// Alignment determines the position of a Control relative to its cell after computing the above.
// The special alignment Fill can be used to grow a Control to fit its cell.
// Note that expansion and alignment are independent variables.
// For more information on expansion and alignment, read https://developer.gnome.org/gtk3/unstable/ch28s02.html.
type Grid interface {
	Control

	// Add adds a Control to the Grid.
	// If this is the first Control in the Grid, it is merely added; nextTo should be nil.
	// Otherwise, it is placed relative to nextTo.
	// If nextTo is nil, it is placed next to the previously added Control.
	// The effect of adding the same Control multiple times is undefined, as is the effect of adding a Control next to one not present in the Grid.
	// The effect of overlapping spanning Controls is also undefined.
	// Add panics if either xspan or yspan are zero or negative.
	Add(control Control, nextTo Control, side Side, xexpand bool, xalign Align, yexpand bool, yalign Align, xspan int, yspan int)
}

// Align represents the alignment of a Control in its cell of a Grid.
type Align uint
const (
	LeftTop Align = iota
	Center
	RightBottom
	Fill
)

// Side represents a side of a Control to add other Controls to a Grid to.
type Side uint
const (
	West Side = iota
	East
	North
	South
	nSides
)

type grid struct {
	controls		[]gridCell
	indexof		map[Control]int
	prev			int
	parent		*controlParent

	xmax		int
	ymax		int
}

type gridCell struct {
	control		Control
	xexpand		bool
	xalign		Align
	yexpand		bool
	yalign		Align
	xspan		int
	yspan		int

	x			int
	y			int

	finalx		int
	finaly		int
	finalwidth		int
	finalheight	int
	prefwidth		int
	prefheight	int
}

// NewGrid creates a new Grid with no Controls.
func NewGrid() Grid {
	return &grid{
		indexof:		map[Control]int{},
	}
}

// ensures that all (x, y) pairs are 0-based
// also computes g.xmax/g.ymax
func (g *grid) reorigin() {
	xmin := 0
	ymin := 0
	for i := range g.controls {
		if g.controls[i].x < xmin {
			xmin = g.controls[i].x
		}
		if g.controls[i].y < ymin {
			ymin = g.controls[i].y
		}
	}
	xmin = -xmin
	ymin = -ymin
	g.xmax = 0
	g.ymax = 0
	for i := range g.controls {
		g.controls[i].x += xmin
		g.controls[i].y += ymin
		if g.xmax < g.controls[i].x + g.controls[i].xspan {
			g.xmax = g.controls[i].x + g.controls[i].xspan
		}
		if g.ymax < g.controls[i].y + g.controls[i].yspan {
			g.ymax = g.controls[i].y + g.controls[i].yspan
		}
	}
}

func (g *grid) Add(control Control, nextTo Control, side Side, xexpand bool, xalign Align, yexpand bool, yalign Align, xspan int, yspan int) {
	if xspan <= 0 || yspan <= 0 {
		panic(fmt.Errorf("invalid span %dx%d given to Grid.Add()", xspan, yspan))
	}
	cell := gridCell{
		control:		control,
		xexpand:		xexpand,
		xalign:		xalign,
		yexpand:		yexpand,
		yalign:		yalign,
		xspan:		xspan,
		yspan:		yspan,
	}
	if g.parent != nil {
		control.setParent(g.parent)
	}
	// if this is the first control, just add it in directly
	if len(g.controls) != 0 {
		next := g.prev
		if nextTo != nil {
			next = g.indexof[nextTo]
		}
		switch side {
		case West:
			cell.x = g.controls[next].x - cell.xspan
			cell.y = g.controls[next].y
		case North:
			cell.x = g.controls[next].x
			cell.y = g.controls[next].y - cell.yspan
		case East:
			cell.x = g.controls[next].x + g.controls[next].xspan
			cell.y = g.controls[next].y
		case South:
			cell.x = g.controls[next].x
			cell.y = g.controls[next].y + g.controls[next].yspan
		default:
			panic(fmt.Errorf("invalid side %d in Grid.Add()", side))
		}
	}
	g.controls = append(g.controls, cell)
	g.prev = len(g.controls) - 1
	g.indexof[control] = g.prev
	g.reorigin()
}

func (g *grid) setParent(p *controlParent) {
	g.parent = p
	for i := range g.controls {
		g.controls[i].control.setParent(g.parent)
	}
}

// builds the topological cell grid; also makes colwidths and rowheights
func (g *grid) mkgrid() (gg [][]int, colwidths []int, rowheights []int) {
	gg = make([][]int, g.ymax)
	for y := 0; y < g.ymax; y++ {
		gg[y] = make([]int, g.xmax)
		for x := 0; x < g.xmax; x++ {
			gg[y][x] = -1
		}
	}
	for i := range g.controls {
		for y := g.controls[i].y; y < g.controls[i].y + g.controls[i].yspan; y++ {
			for x := g.controls[i].x; x < g.controls[i].x + g.controls[i].xspan; x++ {
				gg[y][x] = i
			}
		}
	}
	return gg, make([]int, g.xmax), make([]int, g.ymax)
}

func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocations []*allocation) {
	if len(g.controls) == 0 {
		// nothing to do
		return nil
	}

	// -1) discount padding from width/height
	width -= (g.xmax - 1) * d.xpadding
	height -= (g.ymax - 1) * d.ypadding

	// 0) build necessary data structures
	gg, colwidths, rowheights := g.mkgrid()
	xexpand := make([]bool, g.xmax)
	yexpand := make([]bool, g.ymax)

	// 1) compute colwidths and rowheights before handling expansion
	for y := 0; y < len(gg); y++ {
		for x := 0; x < len(gg[y]); x++ {
			i := gg[y][x]
			if i == -1 {
				continue
			}
			w, h := g.controls[i].control.preferredSize(d)
			// allot equal space in the presence of spanning to keep things sane
			if colwidths[x] < w / g.controls[i].xspan {
				colwidths[x] = w / g.controls[i].xspan
			}
			if rowheights[y] < h / g.controls[i].yspan {
				rowheights[y] = h / g.controls[i].yspan
			}
			// save these for step 6
			g.controls[i].prefwidth = w
			g.controls[i].prefheight = h
		}
	}

	// 2) figure out which columns expand
	// we only mark the first row/column of a spanning cell as expanding to prevent unexpected behavior
	for i := range g.controls {
		if g.controls[i].xexpand {
			xexpand[g.controls[i].x] = true
		}
		if g.controls[i].yexpand {
			yexpand[g.controls[i].y] = true
		}
	}

	// 3) compute and assign expanded widths/heights
	nxexpand := 0
	nyexpand := 0
	for x, expand := range xexpand {
		if expand {
			nxexpand++
		} else {
			width -= colwidths[x]
		}
	}
	for y, expand := range yexpand {
		if expand {
			nyexpand++
		} else {
			height -= rowheights[y]
		}
	}
	for x, expand := range xexpand {
		if expand {
			colwidths[x] = width / nxexpand
		}
	}
	for y, expand := range yexpand {
		if expand {
			rowheights[y] = height / nyexpand
		}
	}

	// 4) reset the final coordinates for the next step
	for i := range g.controls {
		g.controls[i].finalx = 0
		g.controls[i].finaly = 0
		g.controls[i].finalwidth = 0
		g.controls[i].finalheight = 0
	}

	// 5) compute cell positions and widths
	for y := 0; y < g.ymax; y++ {
		curx := 0
		prev := -1
		for x := 0; x < g.xmax; x++ {
			i := gg[y][x]
			if i != -1 {
				if i != prev {
					g.controls[i].finalx = curx
				} else {
					g.controls[i].finalwidth += d.xpadding
				}
				g.controls[i].finalwidth += colwidths[x]
			}
			curx += colwidths[x] + d.xpadding
			prev = i
		}
	}
	for x := 0; x < g.xmax; x++ {
		cury := 0
		prev := -1
		for y := 0; y < g.ymax; y++ {
			i := gg[y][x]
			if i != -1 {
				if i != prev {
					g.controls[i].finaly = cury
				} else {
					g.controls[i].finalheight += d.ypadding
				}
				g.controls[i].finalheight += rowheights[y]
			}
			cury += rowheights[y] + d.ypadding
			prev = i
		}
	}

	// 6) everything as it stands now is set for xalign == Fill yalign == Fill; set the correct alignments
	// this is why we saved prefwidth/prefheight above
	for i := range g.controls {
		if g.controls[i].xalign != Fill {
			switch g.controls[i].xalign {
			case RightBottom:
				g.controls[i].finalx += g.controls[i].finalwidth - g.controls[i].prefwidth
			case Center:
				g.controls[i].finalx += (g.controls[i].finalwidth - g.controls[i].prefwidth) / 2
			}
			g.controls[i].finalwidth = g.controls[i].prefwidth	// for all three
		}
		if g.controls[i].yalign != Fill {
			switch g.controls[i].yalign {
			case RightBottom:
				g.controls[i].finaly += g.controls[i].finalheight - g.controls[i].prefheight
			case Center:
				g.controls[i].finaly += (g.controls[i].finalheight - g.controls[i].prefheight) / 2
			}
			g.controls[i].finalheight = g.controls[i].prefheight	// for all three
		}
	}

	// 7) and FINALLY we draw
	var current *allocation

	for _, ycol := range gg {
		current = nil
		for _, i := range ycol {
			if i != -1 {						// treat empty cells like spaces
				as := g.controls[i].control.allocate(
					g.controls[i].finalx + x, g.controls[i].finaly + y,
					g.controls[i].finalwidth, g.controls[i].finalheight, d)
				if current != nil {			// connect first left to first right
					current.neighbor = g.controls[i].control
				}
				if len(as) != 0 {
					current = as[0]			// next left is first subwidget
				} else {
					current = nil			// spaces don't have allocation data
				}
				allocations = append(allocations, as...)
			}
		}
	}

	return allocations
}

func (g *grid) preferredSize(d *sizing) (width, height int) {
	if len(g.controls) == 0 {
		// nothing to do
		return 0, 0
	}

	// 0) build necessary data structures
	gg, colwidths, rowheights := g.mkgrid()

	// 1) compute colwidths and rowheights before handling expansion
	// TODO put this in its own function
	for y := 0; y < len(gg); y++ {
		for x := 0; x < len(gg[y]); x++ {
			i := gg[y][x]
			if i == -1 {
				continue
			}
			w, h := g.controls[i].control.preferredSize(d)
			// allot equal space in the presence of spanning to keep things sane
			if colwidths[x] < w / g.controls[i].xspan {
				colwidths[x] = w / g.controls[i].xspan
			}
			if rowheights[y] < h / g.controls[i].yspan {
				rowheights[y] = h / g.controls[i].yspan
			}
			// save these for step 6
			g.controls[i].prefwidth = w
			g.controls[i].prefheight = h
		}
	}

	// 2) compute total column width/row height
	colwidth := 0
	rowheight := 0
	for _, w := range colwidths {
		colwidth += w
	}
	for _, h := range rowheights {
		rowheight += h
	}

	// and that's it; just account for padding
	return colwidth + (g.xmax - 1) * d.xpadding,
		rowheight + (g.ymax - 1) * d.ypadding
}

func (g *grid) commitResize(a *allocation, d *sizing) {
	// do nothing; needed to satisfy Control
}

func (g *grid) getAuxResizeInfo(d *sizing) {
	// do nothing; needed to satisfy Control
}
<commit_msg>Mostly fixed Grid behavior.<commit_after>// 31 august 2014

package ui

import (
	"fmt"
)

// Grid is a Control that arranges other Controls in a grid.
// Grid is a very powerful container: it can position and size each Control in several ways and can (and must) have Controls added to it at any time, in any direction.
// it can also have Controls spanning multiple rows and columns.
//
// Each Control in a Grid has associated "expansion" and "alignment" values in both the X and Y direction.
// Expansion determines whether all cells in the same row/column are given whatever space is left over after figuring out how big the rest of the Grid should be.
// Alignment determines the position of a Control relative to its cell after computing the above.
// The special alignment Fill can be used to grow a Control to fit its cell.
// Note that expansion and alignment are independent variables.
// For more information on expansion and alignment, read https://developer.gnome.org/gtk3/unstable/ch28s02.html.
type Grid interface {
	Control

	// Add adds a Control to the Grid.
	// If this is the first Control in the Grid, it is merely added; nextTo should be nil.
	// Otherwise, it is placed relative to nextTo.
	// If nextTo is nil, it is placed next to the previously added Control.
	// The effect of adding the same Control multiple times is undefined, as is the effect of adding a Control next to one not present in the Grid.
	// The effect of overlapping spanning Controls is also undefined.
	// Add panics if either xspan or yspan are zero or negative.
	Add(control Control, nextTo Control, side Side, xexpand bool, xalign Align, yexpand bool, yalign Align, xspan int, yspan int)
}

// Align represents the alignment of a Control in its cell of a Grid.
type Align uint
const (
	LeftTop Align = iota
	Center
	RightBottom
	Fill
)

// Side represents a side of a Control to add other Controls to a Grid to.
type Side uint
const (
	West Side = iota
	East
	North
	South
	nSides
)

type grid struct {
	controls		[]gridCell
	indexof		map[Control]int
	prev			int
	parent		*controlParent

	xmax		int
	ymax		int
}

type gridCell struct {
	control		Control
	xexpand		bool
	xalign		Align
	yexpand		bool
	yalign		Align
	xspan		int
	yspan		int

	x			int
	y			int

	finalx		int
	finaly		int
	finalwidth		int
	finalheight	int
	prefwidth		int
	prefheight	int
}

// NewGrid creates a new Grid with no Controls.
func NewGrid() Grid {
	return &grid{
		indexof:		map[Control]int{},
	}
}

// ensures that all (x, y) pairs are 0-based
// also computes g.xmax/g.ymax
func (g *grid) reorigin() {
	xmin := 0
	ymin := 0
	for i := range g.controls {
		if g.controls[i].x < xmin {
			xmin = g.controls[i].x
		}
		if g.controls[i].y < ymin {
			ymin = g.controls[i].y
		}
	}
	xmin = -xmin
	ymin = -ymin
	g.xmax = 0
	g.ymax = 0
	for i := range g.controls {
		g.controls[i].x += xmin
		g.controls[i].y += ymin
		if g.xmax < g.controls[i].x + g.controls[i].xspan {
			g.xmax = g.controls[i].x + g.controls[i].xspan
		}
		if g.ymax < g.controls[i].y + g.controls[i].yspan {
			g.ymax = g.controls[i].y + g.controls[i].yspan
		}
	}
}

func (g *grid) Add(control Control, nextTo Control, side Side, xexpand bool, xalign Align, yexpand bool, yalign Align, xspan int, yspan int) {
	if xspan <= 0 || yspan <= 0 {
		panic(fmt.Errorf("invalid span %dx%d given to Grid.Add()", xspan, yspan))
	}
	cell := gridCell{
		control:		control,
		xexpand:		xexpand,
		xalign:		xalign,
		yexpand:		yexpand,
		yalign:		yalign,
		xspan:		xspan,
		yspan:		yspan,
	}
	if g.parent != nil {
		control.setParent(g.parent)
	}
	// if this is the first control, just add it in directly
	if len(g.controls) != 0 {
		next := g.prev
		if nextTo != nil {
			next = g.indexof[nextTo]
		}
		switch side {
		case West:
			cell.x = g.controls[next].x - cell.xspan
			cell.y = g.controls[next].y
		case North:
			cell.x = g.controls[next].x
			cell.y = g.controls[next].y - cell.yspan
		case East:
			cell.x = g.controls[next].x + g.controls[next].xspan
			cell.y = g.controls[next].y
		case South:
			cell.x = g.controls[next].x
			cell.y = g.controls[next].y + g.controls[next].yspan
		default:
			panic(fmt.Errorf("invalid side %d in Grid.Add()", side))
		}
	}
	g.controls = append(g.controls, cell)
	g.prev = len(g.controls) - 1
	g.indexof[control] = g.prev
	g.reorigin()
}

func (g *grid) setParent(p *controlParent) {
	g.parent = p
	for i := range g.controls {
		g.controls[i].control.setParent(g.parent)
	}
}

// builds the topological cell grid; also makes colwidths and rowheights
func (g *grid) mkgrid() (gg [][]int, colwidths []int, rowheights []int) {
	gg = make([][]int, g.ymax)
	for y := 0; y < g.ymax; y++ {
		gg[y] = make([]int, g.xmax)
		for x := 0; x < g.xmax; x++ {
			gg[y][x] = -1
		}
	}
	for i := range g.controls {
		for y := g.controls[i].y; y < g.controls[i].y + g.controls[i].yspan; y++ {
			for x := g.controls[i].x; x < g.controls[i].x + g.controls[i].xspan; x++ {
				gg[y][x] = i
			}
		}
	}
	return gg, make([]int, g.xmax), make([]int, g.ymax)
}

func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocations []*allocation) {
	if len(g.controls) == 0 {
		// nothing to do
		return nil
	}

	// -1) discount padding from width/height
	width -= (g.xmax - 1) * d.xpadding
	height -= (g.ymax - 1) * d.ypadding

	// 0) build necessary data structures
	gg, colwidths, rowheights := g.mkgrid()
	xexpand := make([]bool, g.xmax)
	yexpand := make([]bool, g.ymax)

	// 1) compute colwidths and rowheights before handling expansion
	// we only count non-spanning controls to avoid weirdness
	for y := 0; y < len(gg); y++ {
		for x := 0; x < len(gg[y]); x++ {
			i := gg[y][x]
			if i == -1 {
				continue
			}
			w, h := g.controls[i].control.preferredSize(d)
			if g.controls[i].xspan == 1 {
				if colwidths[x] < w {
					colwidths[x] = w
				}
			}
			if g.controls[i].yspan == 1 {
				if rowheights[y] < h {
					rowheights[y] = h
				}
			}
			// save these for step 6
			g.controls[i].prefwidth = w
			g.controls[i].prefheight = h
		}
	}

	// 2) figure out which rows/columns expand but not span
	// we need to know which expanding rows/columns don't span before we can handle the ones that do
	for i := range g.controls {
		if g.controls[i].xexpand && g.controls[i].xspan == 1 {
			xexpand[g.controls[i].x] = true
		}
		if g.controls[i].yexpand && g.controls[i].yspan == 1 {
			yexpand[g.controls[i].y] = true
		}
	}

	// 2) figure out which rows/columns expand that do span
	// the way we handle this is simple: if none of the spanned rows/columns expand, make all rows/columns expand
	for i := range g.controls {
		if g.controls[i].xexpand && g.controls[i].xspan != 1 {
			do := true
			for x := g.controls[i].x; x < g.controls[i].x + g.controls[i].xspan; x++ {
				if xexpand[x] {
					do = false
					break
				}
			}
			if do {
				for x := g.controls[i].x; x < g.controls[i].x + g.controls[i].xspan; x++ {
					xexpand[x] = true
				}
			}
		}
		if g.controls[i].yexpand && g.controls[i].yspan != 1 {
			do := true
			for y := g.controls[i].y; y < g.controls[i].y + g.controls[i].yspan; y++ {
				if yexpand[y] {
					do = false
					break
				}
			}
			if do {
				for y := g.controls[i].y; y < g.controls[i].y + g.controls[i].yspan; y++ {
					yexpand[y] = true
				}
			}
		}
	}

	// 3) compute and assign expanded widths/heights
	nxexpand := 0
	nyexpand := 0
	for x, expand := range xexpand {
		if expand {
			nxexpand++
		} else {
			width -= colwidths[x]
		}
	}
	for y, expand := range yexpand {
		if expand {
			nyexpand++
		} else {
			height -= rowheights[y]
		}
	}
	for x, expand := range xexpand {
		if expand {
			colwidths[x] = width / nxexpand
		}
	}
	for y, expand := range yexpand {
		if expand {
			rowheights[y] = height / nyexpand
		}
	}

	// 4) reset the final coordinates for the next step
	for i := range g.controls {
		g.controls[i].finalx = 0
		g.controls[i].finaly = 0
		g.controls[i].finalwidth = 0
		g.controls[i].finalheight = 0
	}

	// 5) compute cell positions and widths
	for y := 0; y < g.ymax; y++ {
		curx := 0
		prev := -1
		for x := 0; x < g.xmax; x++ {
			i := gg[y][x]
			if i != -1 {
				if i != prev {
					g.controls[i].finalx = curx
				} else {
					g.controls[i].finalwidth += d.xpadding
				}
				g.controls[i].finalwidth += colwidths[x]
			}
			curx += colwidths[x] + d.xpadding
			prev = i
		}
	}
	for x := 0; x < g.xmax; x++ {
		cury := 0
		prev := -1
		for y := 0; y < g.ymax; y++ {
			i := gg[y][x]
			if i != -1 {
				if i != prev {
					g.controls[i].finaly = cury
				} else {
					g.controls[i].finalheight += d.ypadding
				}
				g.controls[i].finalheight += rowheights[y]
			}
			cury += rowheights[y] + d.ypadding
			prev = i
		}
	}

	// 6) everything as it stands now is set for xalign == Fill yalign == Fill; set the correct alignments
	// this is why we saved prefwidth/prefheight above
	for i := range g.controls {
		if g.controls[i].xalign != Fill {
			switch g.controls[i].xalign {
			case RightBottom:
				g.controls[i].finalx += g.controls[i].finalwidth - g.controls[i].prefwidth
			case Center:
				g.controls[i].finalx += (g.controls[i].finalwidth - g.controls[i].prefwidth) / 2
			}
			g.controls[i].finalwidth = g.controls[i].prefwidth	// for all three
		}
		if g.controls[i].yalign != Fill {
			switch g.controls[i].yalign {
			case RightBottom:
				g.controls[i].finaly += g.controls[i].finalheight - g.controls[i].prefheight
			case Center:
				g.controls[i].finaly += (g.controls[i].finalheight - g.controls[i].prefheight) / 2
			}
			g.controls[i].finalheight = g.controls[i].prefheight	// for all three
		}
	}

	// 7) and FINALLY we draw
	var current *allocation

	for _, ycol := range gg {
		current = nil
		for _, i := range ycol {
			if i != -1 {						// treat empty cells like spaces
				as := g.controls[i].control.allocate(
					g.controls[i].finalx + x, g.controls[i].finaly + y,
					g.controls[i].finalwidth, g.controls[i].finalheight, d)
				if current != nil {			// connect first left to first right
					current.neighbor = g.controls[i].control
				}
				if len(as) != 0 {
					current = as[0]			// next left is first subwidget
				} else {
					current = nil			// spaces don't have allocation data
				}
				allocations = append(allocations, as...)
			}
		}
	}

	return allocations
}

func (g *grid) preferredSize(d *sizing) (width, height int) {
	if len(g.controls) == 0 {
		// nothing to do
		return 0, 0
	}

	// 0) build necessary data structures
	gg, colwidths, rowheights := g.mkgrid()

	// 1) compute colwidths and rowheights before handling expansion
	// TODO put this in its own function
	for y := 0; y < len(gg); y++ {
		for x := 0; x < len(gg[y]); x++ {
			i := gg[y][x]
			if i == -1 {
				continue
			}
			w, h := g.controls[i].control.preferredSize(d)
			// allot equal space in the presence of spanning to keep things sane
			if colwidths[x] < w / g.controls[i].xspan {
				colwidths[x] = w / g.controls[i].xspan
			}
			if rowheights[y] < h / g.controls[i].yspan {
				rowheights[y] = h / g.controls[i].yspan
			}
			// save these for step 6
			g.controls[i].prefwidth = w
			g.controls[i].prefheight = h
		}
	}

	// 2) compute total column width/row height
	colwidth := 0
	rowheight := 0
	for _, w := range colwidths {
		colwidth += w
	}
	for _, h := range rowheights {
		rowheight += h
	}

	// and that's it; just account for padding
	return colwidth + (g.xmax - 1) * d.xpadding,
		rowheight + (g.ymax - 1) * d.ypadding
}

func (g *grid) commitResize(a *allocation, d *sizing) {
	// do nothing; needed to satisfy Control
}

func (g *grid) getAuxResizeInfo(d *sizing) {
	// do nothing; needed to satisfy Control
}
<|endoftext|>