<commit_before>/*
 * Copyright 2013 Brett Vickers. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package etree

import (
	"errors"
	"strconv"
	"strings"
)

var (
	errPath = errors.New("etree: invalid path")
)

// A Path is an object that represents an optimized version of an
// XPath-like string.
type Path struct {
	segments []segment
}

// NewPath creates an optimized version of an XPath-like string that
// can be used to query elements in an element tree.  Panics if an
// invalid path string is supplied.
func NewPath(path string) Path {
	return Path{parsePath(path)}
}

// A segment is a portion of a path between "/" characters.
// It contains one selector and zero or more [filters].
type segment struct {
	sel     selector
	filters []filter
}

func (seg *segment) apply(e *Element, p *pather) {
	seg.sel.apply(e, p)
	for _, f := range seg.filters {
		f.apply(p)
	}
}

// A selector selects XML elements for consideration by the
// path traversal.
type selector interface {
	apply(e *Element, p *pather)
}

// A filter pares down a list of candidate XML elements based
// on a path filter in [brackets].
type filter interface {
	apply(p *pather)
}

// A pather is helper object used to traverse an element tree with
// a Path object.  It collects and deduplicates elements matching
// the path query.
type pather struct {
	queue      []node
	qindex     int
	results    []*Element
	inResults  map[*Element]bool
	candidates []*Element
	scratch    []*Element // used by filters
}

// A node represents an element and the remaining path segments that
// should be applied against it by the pather.
type node struct {
	e        *Element
	segments []segment
}

func newPather() *pather {
	return &pather{
		queue:      make([]node, 0),
		results:    make([]*Element, 0),
		inResults:  make(map[*Element]bool),
		candidates: make([]*Element, 0),
		scratch:    make([]*Element, 0),
	}
}

func (p *pather) add(n node) {
	p.queue = append(p.queue, n)
}

func (p *pather) remove() node {
	n := p.queue[p.qindex]
	p.qindex++
	if p.qindex > len(p.queue)/2 && p.qindex > 32 {
		p.rebalance()
	}
	return n
}

func (p *pather) rebalance() {
	count := len(p.queue) - p.qindex
	copy(p.queue[:count], p.queue[p.qindex:])
	p.queue, p.qindex = p.queue[:count], 0
}

func (p *pather) empty() bool {
	return len(p.queue) == p.qindex
}

// traverse follows the path from the element e, collecting
// and then returning all elements that match the path's selectors
// and filters.
func (p *pather) traverse(e *Element, path Path) []*Element {
	for p.add(node{e, path.segments}); !p.empty(); {
		p.eval(p.remove())
	}
	return p.results
}

// eval evalutes the current path node by applying the remaining
// path's selector rules against the node's element.
func (p *pather) eval(n node) {
	p.candidates = p.candidates[0:0]
	seg, remain := n.segments[0], n.segments[1:]
	seg.apply(n.e, p)

	if len(remain) == 0 {
		for _, c := range p.candidates {
			if in := p.inResults[c]; !in {
				p.inResults[c] = true
				p.results = append(p.results, c)
			}
		}
	} else {
		for _, c := range p.candidates {
			p.add(node{c, remain})
		}
	}
}

// parsePath parses an XPath-like string describing a path
// through an element tree and returns a slice of segment
// descriptors.
func parsePath(path string) []segment {
	// Path can start with //, but not /
	if strings.HasPrefix(path, "//") {
		path = path[1:]
	} else if strings.HasPrefix(path, "/") {
		panic(errPath)
	}

	segments := make([]segment, 0)
	for _, s := range strings.Split(path, "/") {
		segments = append(segments, parseSegment(s))
	}
	return segments
}

// parseSegment parses a path segment between / characters.
func parseSegment(path string) segment {
	pieces := strings.Split(path, "[")
	seg := segment{
		sel:     parseSelector(pieces[0]),
		filters: make([]filter, 0),
	}
	for i := 1; i < len(pieces); i++ {
		fpath := pieces[i]
		if fpath[len(fpath)-1] != ']' {
			panic(errPath)
		}
		seg.filters = append(seg.filters, parseFilter(fpath[:len(fpath)-1]))
	}
	return seg
}

// parseSelector parses a selector at the start of a path segment.
func parseSelector(path string) selector {
	switch path {
	case ".":
		return new(selectSelf)
	case "..":
		return new(selectParent)
	case "*":
		return new(selectChildren)
	case "":
		return new(selectDescendants)
	default:
		return &selectChildrenTag{path}
	}
}

// parseFilter parses a path filter contained within [brackets].
func parseFilter(path string) filter {
	if len(path) == 0 {
		panic(errPath)
	}
	eqindex := strings.Index(path, "='")
	if eqindex == -1 {
		switch {
		case path[0] == '@':
			return &filterAttr{path[1:]}
		case isInteger(path):
			pos, _ := strconv.Atoi(path)
			if pos == 0 {
				pos = 1 // force to 1-based
			}
			return &filterPos{pos - 1}
		default:
			return &filterChild{path}
		}
	} else {
		rindex := nextIndex(path, "'", eqindex+2)
		if rindex != len(path)-1 {
			panic(errPath)
		}
		switch {
		case path[0] == '@':
			return &filterAttrVal{path[1:eqindex], path[eqindex+2 : rindex]}
		default:
			return &filterChildText{path[:eqindex], path[eqindex+2 : rindex]}
		}
	}
}

// selectSelf selects the current element into the candidate list.
type selectSelf struct{}

func (s *selectSelf) apply(e *Element, p *pather) {
	p.candidates = append(p.candidates, e)
}

// selectParent selects the element's parent into the candidate list.
type selectParent struct{}

func (s *selectParent) apply(e *Element, p *pather) {
	if e.Parent != nil {
		p.candidates = append(p.candidates, e.Parent)
	}
}

// selectChildren selects the element's child elements into the
// candidate list.
type selectChildren struct{}

func (s *selectChildren) apply(e *Element, p *pather) {
	for _, c := range e.Child {
		if c, ok := c.(*Element); ok {
			p.candidates = append(p.candidates, c)
		}
	}
}

// selectDescendants selects all descendant child elements
// of the element into the candidate list.
type selectDescendants struct{}

func (s *selectDescendants) apply(e *Element, p *pather) {
	stack := elementStack{e}
	for !stack.empty() {
		e := stack.pop()
		p.candidates = append(p.candidates, e)
		for _, c := range e.Child {
			if c, ok := c.(*Element); ok {
				p.candidates = append(p.candidates, c)
				stack.push(c)
			}
		}
	}
}

// selectChildrenTag selects into the candidate list all child
// elements of the element having the specified tag.
type selectChildrenTag struct {
	tag string
}

func (s *selectChildrenTag) apply(e *Element, p *pather) {
	for _, c := range e.Child {
		if c, ok := c.(*Element); ok && c.Tag == s.tag {
			p.candidates = append(p.candidates, c)
		}
	}
}

// filterPos filters the candidate list, keeping only the
// candidate at the specified index.
type filterPos struct {
	index int
}

func (f *filterPos) apply(p *pather) {
	p.scratch = p.scratch[:0]
	if f.index < len(p.candidates) {
		p.scratch = append(p.scratch, p.candidates[f.index])
	}
	p.candidates = p.scratch
}

// filterAttr filters the candidate list for elements having
// the specified attribute.
type filterAttr struct {
	attr string
}

func (f *filterAttr) apply(p *pather) {
	p.scratch = p.scratch[:0]
	for _, c := range p.candidates {
		if a := c.SelectAttr(f.attr); a != nil {
			p.scratch = append(p.scratch, c)
		}
	}
	p.candidates = p.scratch
}

// filterAttrVal filters the candidate list for elements having
// the specified attribute with the specified value.
type filterAttrVal struct {
	attr, val string
}

func (f *filterAttrVal) apply(p *pather) {
	p.scratch = p.scratch[:0]
	for _, c := range p.candidates {
		if a := c.SelectAttr(f.attr); a != nil && a.Value == f.val {
			p.scratch = append(p.scratch, c)
		}
	}
	p.candidates = p.scratch
}

// filterChild filters the candidate list for elements having
// a child element with the specified tag.
type filterChild struct {
	tag string
}

func (f *filterChild) apply(p *pather) {
	p.scratch = p.scratch[:0]
	for _, c := range p.candidates {
		for _, cc := range c.Child {
			if cc, ok := cc.(*Element); ok && cc.Tag == f.tag {
				p.scratch = append(p.scratch, c)
			}
		}
	}
	p.candidates = p.scratch
}

// filterChildText filters the candidate list for elements having
// a child element with the specified tag and text.
type filterChildText struct {
	tag, text string
}

func (f *filterChildText) apply(p *pather) {
	p.scratch = p.scratch[:0]
	for _, c := range p.candidates {
		for _, cc := range c.Child {
			if cc, ok := cc.(*Element); ok && cc.Tag == f.tag && cc.Text() == f.text {
				p.scratch = append(p.scratch, c)
			}
		}
	}
	p.candidates = p.scratch
}
<commit_msg>Fix bugs with descendant path selector.<commit_after>/*
 * Copyright 2013 Brett Vickers. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package etree

import (
	"errors"
	"strconv"
	"strings"
)

var (
	errPath = errors.New("etree: invalid path")
)

// A Path is an object that represents an optimized version of an
// XPath-like string.
type Path struct {
	segments []segment
}

// NewPath creates an optimized version of an XPath-like string that
// can be used to query elements in an element tree.  Panics if an
// invalid path string is supplied.
func NewPath(path string) Path {
	return Path{parsePath(path)}
}

// A segment is a portion of a path between "/" characters.
// It contains one selector and zero or more [filters].
type segment struct {
	sel     selector
	filters []filter
}

func (seg *segment) apply(e *Element, p *pather) {
	seg.sel.apply(e, p)
	for _, f := range seg.filters {
		f.apply(p)
	}
}

// A selector selects XML elements for consideration by the
// path traversal.
type selector interface {
	apply(e *Element, p *pather)
}

// A filter pares down a list of candidate XML elements based
// on a path filter in [brackets].
type filter interface {
	apply(p *pather)
}

// A pather is helper object used to traverse an element tree with
// a Path object.  It collects and deduplicates elements matching
// the path query.
type pather struct {
	queue      []node
	qindex     int
	results    []*Element
	inResults  map[*Element]bool
	candidates []*Element
	scratch    []*Element // used by filters
}

// A node represents an element and the remaining path segments that
// should be applied against it by the pather.
type node struct {
	e        *Element
	segments []segment
}

func newPather() *pather {
	return &pather{
		queue:      make([]node, 0),
		results:    make([]*Element, 0),
		inResults:  make(map[*Element]bool),
		candidates: make([]*Element, 0),
		scratch:    make([]*Element, 0),
	}
}

func (p *pather) add(n node) {
	p.queue = append(p.queue, n)
}

func (p *pather) remove() node {
	n := p.queue[p.qindex]
	p.qindex++
	if p.qindex > len(p.queue)/2 && p.qindex > 32 {
		p.rebalance()
	}
	return n
}

func (p *pather) rebalance() {
	count := len(p.queue) - p.qindex
	copy(p.queue[:count], p.queue[p.qindex:])
	p.queue, p.qindex = p.queue[:count], 0
}

func (p *pather) empty() bool {
	return len(p.queue) == p.qindex
}

// traverse follows the path from the element e, collecting
// and then returning all elements that match the path's selectors
// and filters.
func (p *pather) traverse(e *Element, path Path) []*Element {
	for p.add(node{e, path.segments}); !p.empty(); {
		p.eval(p.remove())
	}
	return p.results
}

// eval evalutes the current path node by applying the remaining
// path's selector rules against the node's element.
func (p *pather) eval(n node) {
	p.candidates = p.candidates[0:0]
	seg, remain := n.segments[0], n.segments[1:]
	seg.apply(n.e, p)

	if len(remain) == 0 {
		for _, c := range p.candidates {
			if in := p.inResults[c]; !in {
				p.inResults[c] = true
				p.results = append(p.results, c)
			}
		}
	} else {
		for _, c := range p.candidates {
			p.add(node{c, remain})
		}
	}
}

// parsePath parses an XPath-like string describing a path
// through an element tree and returns a slice of segment
// descriptors.
func parsePath(path string) []segment {
	// If path starts or ends with //, add a .
	if strings.HasPrefix(path, "//") {
		path = "." + path
	}
	if strings.HasSuffix(path, "//") {
		path = path + "."
	}

	// Paths cannot be absolute
	if strings.HasPrefix(path, "/") {
		panic(errPath)
	}

	segments := make([]segment, 0)
	for _, s := range strings.Split(path, "/") {
		segments = append(segments, parseSegment(s))
	}
	return segments
}

// parseSegment parses a path segment between / characters.
func parseSegment(path string) segment {
	pieces := strings.Split(path, "[")
	seg := segment{
		sel:     parseSelector(pieces[0]),
		filters: make([]filter, 0),
	}
	for i := 1; i < len(pieces); i++ {
		fpath := pieces[i]
		if fpath[len(fpath)-1] != ']' {
			panic(errPath)
		}
		seg.filters = append(seg.filters, parseFilter(fpath[:len(fpath)-1]))
	}
	return seg
}

// parseSelector parses a selector at the start of a path segment.
func parseSelector(path string) selector {
	switch path {
	case ".":
		return new(selectSelf)
	case "..":
		return new(selectParent)
	case "*":
		return new(selectChildren)
	case "":
		return new(selectDescendants)
	default:
		return &selectChildrenTag{path}
	}
}

// parseFilter parses a path filter contained within [brackets].
func parseFilter(path string) filter {
	if len(path) == 0 {
		panic(errPath)
	}

	// Filter contains [@attr='val'] or [tag='val']?
	eqindex := strings.Index(path, "='")
	if eqindex >= 0 {
		rindex := nextIndex(path, "'", eqindex+2)
		if rindex != len(path)-1 {
			panic(errPath)
		}
		switch {
		case path[0] == '@':
			return &filterAttrVal{path[1:eqindex], path[eqindex+2 : rindex]}
		default:
			return &filterChildText{path[:eqindex], path[eqindex+2 : rindex]}
		}
	}

	// Filter contains [@attr], [N] or [tag]
	switch {
	case path[0] == '@':
		return &filterAttr{path[1:]}
	case isInteger(path):
		pos, _ := strconv.Atoi(path)
		if pos == 0 {
			pos = 1 // force to 1-based
		}
		return &filterPos{pos - 1}
	default:
		return &filterChild{path}
	}
}

// selectSelf selects the current element into the candidate list.
type selectSelf struct{}

func (s *selectSelf) apply(e *Element, p *pather) {
	p.candidates = append(p.candidates, e)
}

// selectParent selects the element's parent into the candidate list.
type selectParent struct{}

func (s *selectParent) apply(e *Element, p *pather) {
	if e.Parent != nil {
		p.candidates = append(p.candidates, e.Parent)
	}
}

// selectChildren selects the element's child elements into the
// candidate list.
type selectChildren struct{}

func (s *selectChildren) apply(e *Element, p *pather) {
	for _, c := range e.Child {
		if c, ok := c.(*Element); ok {
			p.candidates = append(p.candidates, c)
		}
	}
}

// selectDescendants selects all descendant child elements
// of the element into the candidate list.
type selectDescendants struct{}

func (s *selectDescendants) apply(e *Element, p *pather) {
	stack := elementStack{}
	for _, c := range e.Child {
		if c, ok := c.(*Element); ok {
			stack.push(c)
		}
	}
	for !stack.empty() {
		e := stack.pop()
		p.candidates = append(p.candidates, e)
		for _, c := range e.Child {
			if c, ok := c.(*Element); ok {
				stack.push(c)
			}
		}
	}
}

// selectChildrenTag selects into the candidate list all child
// elements of the element having the specified tag.
type selectChildrenTag struct {
	tag string
}

func (s *selectChildrenTag) apply(e *Element, p *pather) {
	for _, c := range e.Child {
		if c, ok := c.(*Element); ok && c.Tag == s.tag {
			p.candidates = append(p.candidates, c)
		}
	}
}

// filterPos filters the candidate list, keeping only the
// candidate at the specified index.
type filterPos struct {
	index int
}

func (f *filterPos) apply(p *pather) {
	p.scratch = p.scratch[:0]
	if f.index < len(p.candidates) {
		p.scratch = append(p.scratch, p.candidates[f.index])
	}
	p.candidates = p.scratch
}

// filterAttr filters the candidate list for elements having
// the specified attribute.
type filterAttr struct {
	attr string
}

func (f *filterAttr) apply(p *pather) {
	p.scratch = p.scratch[:0]
	for _, c := range p.candidates {
		if a := c.SelectAttr(f.attr); a != nil {
			p.scratch = append(p.scratch, c)
		}
	}
	p.candidates = p.scratch
}

// filterAttrVal filters the candidate list for elements having
// the specified attribute with the specified value.
type filterAttrVal struct {
	attr, val string
}

func (f *filterAttrVal) apply(p *pather) {
	p.scratch = p.scratch[:0]
	for _, c := range p.candidates {
		if a := c.SelectAttr(f.attr); a != nil && a.Value == f.val {
			p.scratch = append(p.scratch, c)
		}
	}
	p.candidates = p.scratch
}

// filterChild filters the candidate list for elements having
// a child element with the specified tag.
type filterChild struct {
	tag string
}

func (f *filterChild) apply(p *pather) {
	p.scratch = p.scratch[:0]
	for _, c := range p.candidates {
		for _, cc := range c.Child {
			if cc, ok := cc.(*Element); ok && cc.Tag == f.tag {
				p.scratch = append(p.scratch, c)
			}
		}
	}
	p.candidates = p.scratch
}

// filterChildText filters the candidate list for elements having
// a child element with the specified tag and text.
type filterChildText struct {
	tag, text string
}

func (f *filterChildText) apply(p *pather) {
	p.scratch = p.scratch[:0]
	for _, c := range p.candidates {
		for _, cc := range c.Child {
			if cc, ok := cc.(*Element); ok && cc.Tag == f.tag && cc.Text() == f.text {
				p.scratch = append(p.scratch, c)
			}
		}
	}
	p.candidates = p.scratch
}
<|endoftext|>


<commit_before>package main

import (
	"regexp"
	"io/ioutil"
	"net/http"
	"time"
)

const templateInterval = 30 * time.Second // Template interval

// Hold the page information
type Page struct {
	Title string
	Body []byte
}

// Save a page to a destination based on the page Title
func (p *Page) save() error {
	filename := p.Title + ".txt"
	return ioutil.WriteFile(filename, p.Body, 0600)
}

// Load a page based on the title
func loadPage(title string) (*Page, error) {
	filename := title + ".txt"
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return &Page{Title: title, Body: body}, nil
}

var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$")

func makeHandler(fn func (http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		m := validPath.FindStringSubmatch(r.URL.Path)
		if m == nil {
			http.NotFound(w, r)
			return
		}
		fn(w, r, m[2])
	}
}

func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
	p, err := loadPage(title)
	if err != nil {
		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
		return
	}
	renderTemplate(w, "view", p)
}

func editHandler(w http.ResponseWriter, r *http.Request, title string) {
	p, err := loadPage(title)
	if err != nil {
		p = &Page{Title: title}
	}
	renderTemplate(w, "edit", p)
}

func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
	body := r.FormValue("body")
	p := &Page{Title: title, Body: []byte(body)}
	err := p.save()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/view/"+title, http.StatusFound)
}

func main() {
	LoadTemplates("tmpl", templateInterval)
	http.HandleFunc("/view/", makeHandler(viewHandler))
	http.HandleFunc("/edit/", makeHandler(editHandler))
	http.HandleFunc("/save/", makeHandler(saveHandler))
	http.ListenAndServe(":8080", nil)
}
<commit_msg>Redirect / to /view/FrontPage<commit_after>package main

import (
	"regexp"
	"io/ioutil"
	"net/http"
	"time"
)

const templateInterval = 30 * time.Second // Template interval

// Hold the page information
type Page struct {
	Title string
	Body []byte
}

// Save a page to a destination based on the page Title
func (p *Page) save() error {
	filename := p.Title + ".txt"
	return ioutil.WriteFile(filename, p.Body, 0600)
}

// Load a page based on the title
func loadPage(title string) (*Page, error) {
	filename := title + ".txt"
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return &Page{Title: title, Body: body}, nil
}

var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$")

func makeHandler(fn func (http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		m := validPath.FindStringSubmatch(r.URL.Path)
		if m == nil {
			http.NotFound(w, r)
			return
		}
		fn(w, r, m[2])
	}
}

func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
	p, err := loadPage(title)
	if err != nil {
		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
		return
	}
	renderTemplate(w, "view", p)
}

func editHandler(w http.ResponseWriter, r *http.Request, title string) {
	p, err := loadPage(title)
	if err != nil {
		p = &Page{Title: title}
	}
	renderTemplate(w, "edit", p)
}

func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
	body := r.FormValue("body")
	p := &Page{Title: title, Body: []byte(body)}
	err := p.save()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/view/"+title, http.StatusFound)
}

func main() {
	LoadTemplates("tmpl", templateInterval)
	http.Handle("/", http.RedirectHandler("/view/FrontPage", 301))
	http.HandleFunc("/view/", makeHandler(viewHandler))
	http.HandleFunc("/edit/", makeHandler(editHandler))
	http.HandleFunc("/save/", makeHandler(saveHandler))
	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/inhies/go-bytesize" // Do we really need this?
	"io/ioutil"
	"net/url"
	"os"
	"os/exec"
	upath "path"
	fpath "path/filepath"
	"regexp"
	"strconv"
	"strings"
	"text/template"
)

type WikiTrackData struct {
	Duration                 string
	FolderName               string
	Index                    int
	HasAlternateLeadVocalist bool
	Name                     string
	CD                       int
	LinePrefix               string
}

type WikiAlbumData struct {
	Notes      string
	FolderName string
	Tracks     []WikiTrackData
	Duration   string
	Lineage    string
	Size       string
	SampleRate string
	BPS        string
}

func generateWikifiles(c *cli.Context) {
	fileInfo, filepath := checkFilepathArgument(c)
	if fileInfo == nil {
		return
	}

	mode := "batch"
	if c.GlobalBool("single") {
		mode = "single"
	}

	fmt.Printf("The following filepath (%s mode) will be processed: %s\n", mode, filepath)
	notifyDeleteMode(c)

	if !shouldContinue(c) {
		return
	}

	regex, err := regexp.Compile(wikiRegex)
	if err != nil {
		fmt.Println("Internal error - wiki regex could not be compiled!")
		fmt.Println(err.Error())
		os.Exit(1)
	}

	wikiTemplate, err := template.New("wiki").Parse(
		// Stupid windows
		strings.Replace(wikiTemplate, "\n", "\r\n", -1),
	)
	if err != nil {
		fmt.Println("Internal error - wiki template could not be parsed!")
		fmt.Println(err.Error())
		os.Exit(1)
	}

	if mode == "single" {
		generateWikifile(filepath, fileInfo.Name(), regex, wikiTemplate, c.GlobalBool("delete"), "")
		return
	}

	// Create the wikifiles folder path
	wikifiles := fpath.Join(filepath, "__wikifiles")

	// MkdirAll is used instead of Mkdir because this function
	// doesn't error if the folder already exists
	err = os.MkdirAll(wikifiles, os.ModeDir)
	if err != nil {
		fmt.Println("Internal error creating __wikifiles folder")
		fmt.Println(err.Error())
		os.Exit(1)
	}

	files, _ := ioutil.ReadDir(filepath)
	for _, file := range files {
		if file.IsDir() {
			name := file.Name()
			if name != "__wikifiles" {
				generateWikifile(fpath.Join(filepath, name), name, regex, wikiTemplate, c.GlobalBool("delete"), wikifiles)
			}
		}
	}
}

func wikiGetInfoFromFlac(filepath string, parsedData *WikiAlbumData) bool {
	directoryContents, err := ioutil.ReadDir(filepath)
	if err != nil {
		fmt.Println("failed to get directory contents")
		fmt.Println(err)
		return false
	}

	for _, file := range directoryContents {
		if file.IsDir() {
			if wikiGetInfoFromFlac(fpath.Join(filepath, file.Name()), parsedData) {
				return true
			}
		} else if fpath.Ext(file.Name()) == ".flac" {
			data, err := exec.Command(
				"metaflac",
				"--show-sample-rate",
				"--show-bps", // update numbers below
				fpath.Join(filepath, file.Name()),
			).Output()

			if err != nil {
				fmt.Println("metaflac returned an invalid response for sample-rate/bps")
				if data != nil {
					fmt.Println(data)
				}
				panic(err)
			}

			lines := strings.Split(string(data), "\n")
			if len(lines) != 1+2 {
				// Update '2' below and above
				fmt.Println("expected 2 metaflac lines, got", len(lines)-1)
				continue
			}

			failure := false
			for i, line := range lines {
				line := strings.TrimSpace(line)

				if i == 0 {
					rateFloat, err := strconv.ParseFloat(line, 32)
					if err != nil {
						fmt.Print("could not convert sample-rate to float")
						fmt.Println(err)
						failure = true
						break
					}
					parsedData.SampleRate = strconv.FormatFloat(rateFloat/1000, 'f', -1, 32) + "KHz"
				} else if i == 1 {
					parsedData.BPS = line
				}
			}

			if !failure {
				return true
			}
		}
	}

	fmt.Println("failed to find file for sampling info")
	return false
}

func generateWikifile(filepath string, foldername string, regex *regexp.Regexp, wikiTemplate *template.Template, deleteMode bool, outBasepath string) {
	basepath := fpath.Join(filepath, foldername)
	infofile := basepath + ".txt"
	wikifile := filepath
	if outBasepath != "" {
		wikifile = outBasepath
	}

	if deleteMode {
		fmt.Printf("Deleting wiki from %s...", infofile)
	} else {
		fmt.Printf("Generating from %s... ", infofile)
	}

	infobytes, err := ioutil.ReadFile(infofile)
	if err != nil {
		if os.IsNotExist(err) {
			fmt.Println("infofile doesn't exist")
		} else {
			fmt.Printf("error (%s)\n", err.Error())
		}
		return
	}

	matches := regex.FindSubmatch(infobytes)
	if len(matches) != 1+regex.NumSubexp() {
		// (entire string itself)+(capture groups)
		fmt.Printf("parse failure, expected %d capturing groups!\n", 1+regex.NumSubexp())
		return
	}

	var parsedData WikiAlbumData
	parsedData.FolderName = foldername

	size, err := getDirectorySize(filepath)
	if err != nil {
		fmt.Println("failed to get directory size")
		fmt.Println(err)
		return
	}
	b := bytesize.New(size)
	parsedData.Size = b.String()

	if !wikiGetInfoFromFlac(filepath, &parsedData) {
		return
	}

	tracks := make([]WikiTrackData, 0)

	var lastTrack WikiTrackData
	var currentTrackNumber int = 0

	for i, field := range matches {
		if i == 0 {
			// The first one is just itself
			continue
		}

		field := string(bytes.TrimSpace(field))

		switch i {
		case 1:
			if field != "" {
				parsedData.Lineage = "*" + field + "\n"
			}
		case 2:
			lineage := ""
			for _, item := range strings.Split(field, "\n") {
				lineage += "*" + strings.TrimSpace(item) + "\r\n"
			}
			parsedData.Lineage += lineage
		case 3:
			parsedData.Notes = field
		case 4:
			str, err := url.QueryUnescape(field)
			if err != nil {
				fmt.Println("error unescaping query from url")
				fmt.Println(err.Error())
				return
			}

			str = strings.Replace(str, "_", " ", -1) // make spaces in wikiformat real spaces
			str = strings.Replace(str, "/", "_", -1) // make slashes fileurl compliant by making it a "_"
			// str = strings.Trim(strconv.QuoteToASCII(str), "\"") // make it ascii escaped, and trim "s
			wikifile = fpath.Join(wikifile, str+".wiki")
			fmt.Printf("\n - %s... ", wikifile)
		case 5:
			// parse tracks
			for _, track := range strings.Split(field, "\n") {
				var trackData WikiTrackData
				trackData.FolderName = foldername
				trackData.LinePrefix = "#"

				str := strings.TrimSpace(track)
				f := strings.Index(str, "[")
				l := strings.Index(str, "]")
				trackData.Duration = str[f+1 : l]

				number := str[:f-2]
				separator := strings.Index(number, ".")
				if separator != -1 {
					// This bit only uses the "path" library
					// because URL's only use forward slash
					cdStr := number[:separator]

					cdNumber, err := strconv.Atoi(cdStr)
					if err != nil {
						panic(err.Error())
					}
					trackData.FolderName = upath.Join(foldername, "CD"+cdStr)
					trackData.CD = cdNumber

					if lastTrack.CD != cdNumber {
						currentTrackNumber = 0
						trackData.LinePrefix = fmt.Sprintf("\r\nCD%d:\r\n%s", cdNumber, trackData.LinePrefix)
					}
				} else {
					// Here lies incomplete support for "Bonus." tracks
					// you need to add support for using filenames instead
					// of tracknumbers for bonus tracks

					// _, err := strconv.Atoi(number)
					// if err != nil {
					// 	trackData.FolderName = number
					// 	trackData.CD = -1

					// 	if lastTrack.FolderName != number {
					// 		currentTrackNumber = 0
					// 	}
					// }
					// continue
				}

				name := strings.TrimSpace(str[l+1:])
				nameWithoutSuffix := strings.TrimSuffix(name, " (*)")
				trackData.HasAlternateLeadVocalist = name != nameWithoutSuffix
				trackData.Name = nameWithoutSuffix

				currentTrackNumber += 1
				trackData.Index = currentTrackNumber

				lastTrack = trackData
				tracks = append(tracks, trackData)
			}
		case 6:
			parsedData.Duration = field
		}
	}
	parsedData.Tracks = tracks

	success := removeFile(wikifile, false)
	if deleteMode {
		message := "success!"
		if !success {
			message = "couldn't delete!"
		}
		fmt.Println(message)
		return
	} else if success {
		fmt.Print("overwritten... ")
	}

	wikiout := createFile(wikifile)
	defer wikiout.Close()

	if wikiout != nil {
		err = wikiTemplate.Execute(wikiout, parsedData)
		if err != nil {
			fmt.Println("could not insert data into template!")
			fmt.Println(err)
			return
		}

		fmt.Println("success!")
	}
}
<commit_msg>Workaround for wiki filenames with non-ascii chars<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/inhies/go-bytesize" // Do we really need this?
	"io/ioutil"
	"net/url"
	"os"
	"os/exec"
	upath "path"
	fpath "path/filepath"
	"regexp"
	"strconv"
	"strings"
	"text/template"
)

type WikiTrackData struct {
	Duration                 string
	FolderName               string
	Index                    int
	HasAlternateLeadVocalist bool
	Name                     string
	CD                       int
	LinePrefix               string
}

type WikiAlbumData struct {
	Notes      string
	FolderName string
	Tracks     []WikiTrackData
	Duration   string
	Lineage    string
	Size       string
	SampleRate string
	BPS        string
}

func generateWikifiles(c *cli.Context) {
	fileInfo, filepath := checkFilepathArgument(c)
	if fileInfo == nil {
		return
	}

	mode := "batch"
	if c.GlobalBool("single") {
		mode = "single"
	}

	fmt.Printf("The following filepath (%s mode) will be processed: %s\n", mode, filepath)
	notifyDeleteMode(c)

	if !shouldContinue(c) {
		return
	}

	regex, err := regexp.Compile(wikiRegex)
	if err != nil {
		fmt.Println("Internal error - wiki regex could not be compiled!")
		fmt.Println(err.Error())
		os.Exit(1)
	}

	wikiTemplate, err := template.New("wiki").Parse(
		// Stupid windows
		strings.Replace(wikiTemplate, "\n", "\r\n", -1),
	)
	if err != nil {
		fmt.Println("Internal error - wiki template could not be parsed!")
		fmt.Println(err.Error())
		os.Exit(1)
	}

	if mode == "single" {
		generateWikifile(filepath, fileInfo.Name(), regex, wikiTemplate, c.GlobalBool("delete"), "")
		return
	}

	// Create the wikifiles folder path
	wikifiles := fpath.Join(filepath, "__wikifiles")

	// MkdirAll is used instead of Mkdir because this function
	// doesn't error if the folder already exists
	err = os.MkdirAll(wikifiles, os.ModeDir)
	if err != nil {
		fmt.Println("Internal error creating __wikifiles folder")
		fmt.Println(err.Error())
		os.Exit(1)
	}

	files, _ := ioutil.ReadDir(filepath)
	for _, file := range files {
		if file.IsDir() {
			name := file.Name()
			if name != "__wikifiles" {
				generateWikifile(fpath.Join(filepath, name), name, regex, wikiTemplate, c.GlobalBool("delete"), wikifiles)
			}
		}
	}
}

func wikiGetInfoFromFlac(filepath string, parsedData *WikiAlbumData) bool {
	directoryContents, err := ioutil.ReadDir(filepath)
	if err != nil {
		fmt.Println("failed to get directory contents")
		fmt.Println(err)
		return false
	}

	for _, file := range directoryContents {
		if file.IsDir() {
			if wikiGetInfoFromFlac(fpath.Join(filepath, file.Name()), parsedData) {
				return true
			}
		} else if fpath.Ext(file.Name()) == ".flac" {
			data, err := exec.Command(
				"metaflac",
				"--show-sample-rate",
				"--show-bps", // update numbers below
				fpath.Join(filepath, file.Name()),
			).Output()

			if err != nil {
				fmt.Println("metaflac returned an invalid response for sample-rate/bps")
				if data != nil {
					fmt.Println(data)
				}
				panic(err)
			}

			lines := strings.Split(string(data), "\n")
			if len(lines) != 1+2 {
				// Update '2' below and above
				fmt.Println("expected 2 metaflac lines, got", len(lines)-1)
				continue
			}

			failure := false
			for i, line := range lines {
				line := strings.TrimSpace(line)

				if i == 0 {
					rateFloat, err := strconv.ParseFloat(line, 32)
					if err != nil {
						fmt.Print("could not convert sample-rate to float")
						fmt.Println(err)
						failure = true
						break
					}
					parsedData.SampleRate = strconv.FormatFloat(rateFloat/1000, 'f', -1, 32) + "KHz"
				} else if i == 1 {
					parsedData.BPS = line
				}
			}

			if !failure {
				return true
			}
		}
	}

	fmt.Println("failed to find file for sampling info")
	return false
}

func generateWikifile(filepath string, foldername string, regex *regexp.Regexp, wikiTemplate *template.Template, deleteMode bool, outBasepath string) {
	basepath := fpath.Join(filepath, foldername)
	infofile := basepath + ".txt"
	wikifile := filepath
	if outBasepath != "" {
		wikifile = outBasepath
	}

	if deleteMode {
		fmt.Printf("Deleting wiki from %s...", infofile)
	} else {
		fmt.Printf("Generating from %s... ", infofile)
	}

	infobytes, err := ioutil.ReadFile(infofile)
	if err != nil {
		if os.IsNotExist(err) {
			fmt.Println("infofile doesn't exist")
		} else {
			fmt.Printf("error (%s)\n", err.Error())
		}
		return
	}

	matches := regex.FindSubmatch(infobytes)
	if len(matches) != 1+regex.NumSubexp() {
		// (entire string itself)+(capture groups)
		fmt.Printf("parse failure, expected %d capturing groups!\n", 1+regex.NumSubexp())
		return
	}

	var parsedData WikiAlbumData
	parsedData.FolderName = foldername

	size, err := getDirectorySize(filepath)
	if err != nil {
		fmt.Println("failed to get directory size")
		fmt.Println(err)
		return
	}
	b := bytesize.New(size)
	parsedData.Size = b.String()

	if !wikiGetInfoFromFlac(filepath, &parsedData) {
		return
	}

	tracks := make([]WikiTrackData, 0)

	var lastTrack WikiTrackData
	var currentTrackNumber int = 0

	for i, field := range matches {
		if i == 0 {
			// The first one is just itself
			continue
		}

		field := string(bytes.TrimSpace(field))

		switch i {
		case 1:
			if field != "" {
				parsedData.Lineage = "*" + field + "\n"
			}
		case 2:
			lineage := ""
			for _, item := range strings.Split(field, "\n") {
				lineage += "*" + strings.TrimSpace(item) + "\r\n"
			}
			parsedData.Lineage += lineage
		case 3:
			parsedData.Notes = field
		case 4:
			str, err := url.QueryUnescape(field)
			if err != nil {
				fmt.Println("error unescaping query from url")
				fmt.Println(err.Error())
				return
			}

			str = strings.Replace(str, "_", " ", -1) // make spaces in wikiformat real spaces
			str = strings.Replace(str, "/", "_", -1) // make slashes fileurl compliant by making it a "_"

			str = strings.Trim(strconv.QuoteToASCII(str), "\"") // make it ascii escaped, and trim "s
			str = strings.Replace(str, "\\", "^", -1)           // escape "\" with "^" so that the bash script can make it a codepoint again

			wikifile = fpath.Join(wikifile, str+".wiki")
			fmt.Printf("\n - %s... ", wikifile)
		case 5:
			// parse tracks
			for _, track := range strings.Split(field, "\n") {
				var trackData WikiTrackData
				trackData.FolderName = foldername
				trackData.LinePrefix = "#"

				str := strings.TrimSpace(track)
				f := strings.Index(str, "[")
				l := strings.Index(str, "]")
				trackData.Duration = str[f+1 : l]

				number := str[:f-2]
				separator := strings.Index(number, ".")
				if separator != -1 {
					// This bit only uses the "path" library
					// because URL's only use forward slash
					cdStr := number[:separator]

					cdNumber, err := strconv.Atoi(cdStr)
					if err != nil {
						panic(err.Error())
					}
					trackData.FolderName = upath.Join(foldername, "CD"+cdStr)
					trackData.CD = cdNumber

					if lastTrack.CD != cdNumber {
						currentTrackNumber = 0
						trackData.LinePrefix = fmt.Sprintf("\r\nCD%d:\r\n%s", cdNumber, trackData.LinePrefix)
					}
				} else {
					// Here lies incomplete support for "Bonus." tracks
					// you need to add support for using filenames instead
					// of tracknumbers for bonus tracks

					// _, err := strconv.Atoi(number)
					// if err != nil {
					// 	trackData.FolderName = number
					// 	trackData.CD = -1

					// 	if lastTrack.FolderName != number {
					// 		currentTrackNumber = 0
					// 	}
					// }
					// continue
				}

				name := strings.TrimSpace(str[l+1:])
				nameWithoutSuffix := strings.TrimSuffix(name, " (*)")
				trackData.HasAlternateLeadVocalist = name != nameWithoutSuffix
				trackData.Name = nameWithoutSuffix

				currentTrackNumber += 1
				trackData.Index = currentTrackNumber

				lastTrack = trackData
				tracks = append(tracks, trackData)
			}
		case 6:
			parsedData.Duration = field
		}
	}
	parsedData.Tracks = tracks

	success := removeFile(wikifile, false)
	if deleteMode {
		message := "success!"
		if !success {
			message = "couldn't delete!"
		}
		fmt.Println(message)
		return
	} else if success {
		fmt.Print("overwritten... ")
	}

	wikiout := createFile(wikifile)
	defer wikiout.Close()

	if wikiout != nil {
		err = wikiTemplate.Execute(wikiout, parsedData)
		if err != nil {
			fmt.Println("could not insert data into template!")
			fmt.Println(err)
			return
		}

		fmt.Println("success!")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math"
	"time"
)

type Line struct {
	Moves []Move
}

const INFINITY int = int(math.MaxInt32) // I win!
const MATE int = INFINITY - 10          // Value of a checkmate in 1

var nodecount uint64
var abort bool

func Evaluate(board *Board) int {
	phase := calcphase(board)
	opening := MaterialCount(board, false)
	endgame := MaterialCount(board, true)
	return ((opening * (256 - phase)) + (endgame * phase)) / 256
}

func calcphase(board *Board) int {
	TotalPhase := 24 // PawnPhase*16 + KnightPhase*4 + BishopPhase*4 + RookPhase*4 + QueenPhase*2
	phase := TotalPhase
	for _, i := range board.PieceList {
		phase -= PValue[GetPiece(board.Data[i])]
	}
	return (phase*256 + (TotalPhase / 2)) / TotalPhase
}

func MaterialCount(b *Board, endgame bool) int {
	var retval int
	for _, i := range b.PieceList {
		if OnBoard(i) && GetPiece(b.Data[i]) != EMPTY {
			if GetSide(b.Data[i]) == b.ToMove {
				retval += Value[GetPiece(b.Data[i])] + Pst(GetPiece(b.Data[i]), b.ToMove, i, endgame)
			} else {
				retval -= (Value[GetPiece(b.Data[i])] + Pst(GetPiece(b.Data[i]), b.ToMove, i, endgame))
			}
		}
	}
	return retval
}

func Pst(piece, side, index byte, endgame bool) int {
	switch piece {
	case PAWN:
		return GetPst(index, side, pstPawnMg, pstPawnEg, endgame)
	case KNIGHT:
		return GetPst(index, side, pstKnightMg, pstKnightEg, endgame)
	case BISHOP:
		return GetPst(index, side, pstBishopMg, pstBishopEg, endgame)
	case ROOK:
		return GetPst(index, side, pstRookMg, pstRookEg, endgame)
	case QUEEN:
		return GetPst(index, side, pstQueenMg, pstQueenMg, endgame)
	case KING:
		return GetPst(index, side, pstKingMg, pstKingEg, endgame)
	default:
		return 0
	}
}

func GetPst(index, side byte, tableMg [64]int, tableEg [64]int, endgame bool) int {
	var table []int
	if endgame {
		table = tableEg[:]
	} else {
		table = tableMg[:]
	}
	file, rank := IndexToCartesian(index)
	var tableindex byte
	if side == BLACK {
		tableindex = (8 * (7 - rank)) + file
	} else {
		tableindex = (8 * rank) + file
	}
	return table[tableindex]
}

func Quies(board *Board, alpha, beta int) int {
	nodecount++
	if abort {
		return 0
	}
	eval := Evaluate(board)
	if eval >= beta {
		return beta
	}
	if eval > alpha {
		alpha = eval
	}
	moves := FilterCaptures(MoveGen(board))
	for _, move := range moves {
		undo := MakeMove(board, &move)
		if Illegal(board) {
			UnmakeMove(board, &move, undo)
			continue
		}
		val := -Quies(board, -beta, -alpha)
		UnmakeMove(board, &move, undo)
		if abort {
			return 0
		}
		if val >= beta {
			return beta
		}
		if val > alpha {
			alpha = val
		}
	}
	return alpha
}

func AlphaBeta(board *Board, depth, alpha, beta, mate int, pline *Line) int {
	nodecount++
	if abort {
		return 0
	}
	legal := 0
	if depth <= 0 {
		return Quies(board, alpha, beta)
	}
	line := new(Line)
	moves := MoveGen(board)
	for _, move := range moves {
		undo := MakeMove(board, &move)
		if Illegal(board) {
			UnmakeMove(board, &move, undo)
			continue
		}
		val := -AlphaBeta(board, depth-1, -beta, -alpha, mate-1, line)
		UnmakeMove(board, &move, undo)
		if abort {
			return 0
		}
		if val >= beta {
			return beta
		}
		if val > alpha {
			alpha = val
			pline.Moves = append([]Move{move}, pline.Moves...)
			line.Moves = append(pline.Moves, move)
		}
		legal++
	}
	if legal == 0 {
		if !InCheck(board) {
			return 0
		}
		return -mate
	}
	return alpha
}

func AllotTime(board *Board) time.Duration {
	repeat := TimeRepeat
	if repeat <= 0 {
		repeat = 40
	}
	moves := repeat - board.Moves
	for moves <= 0 {
		moves += repeat
	}
	return (Clock/time.Duration(moves+1) - 20*time.Millisecond)
}

func ThinkingOutput(depth, score int, start time.Time, pv *Line) {
	fmt.Println(depth, score, int64(time.Since(start)/time.Millisecond)/10, nodecount, pv)
}

func SleepThread(board *Board, start time.Time) {
	bedoneby := AllotTime(board)
	fmt.Println("# ", Clock, ": allocated ", bedoneby)
	time.Sleep(bedoneby)
	abort = true
}

func FindMove(board *Board) *Move {
	start := time.Now()
	abort = false
	nodecount = 0
	retval := new(Move)
	for depth := 1; ; depth++ {
		line := new(Line)
		score := AlphaBeta(board, depth, -INFINITY, INFINITY, MATE, line)
		ThinkingOutput(depth, score, start, line)
		if !abort {
			retval = &line.Moves[0]
		} else {
			break
		}

                if depth == 1 {
                    Clock -= time.Since(start)
                    go SleepThread(board, start)
                }
	}
	return retval
}
<commit_msg>Make evaluation absolute<commit_after>package main

import (
	"fmt"
	"math"
	"time"
)

type Line struct {
	Moves []Move
}

const INFINITY int = int(math.MaxInt32) // I win!
const MATE int = INFINITY - 10          // Value of a checkmate in 1

var nodecount uint64
var abort bool

func Evaluate(board *Board) int {
	phase := calcphase(board)
	opening := MaterialCount(board, false)
	endgame := MaterialCount(board, true)
	score := ((opening * (256 - phase)) + (endgame * phase)) / 256
	if board.ToMove == BLACK {
		score = -score
	}
	return score
}

func calcphase(board *Board) int {
	TotalPhase := 24 // PawnPhase*16 + KnightPhase*4 + BishopPhase*4 + RookPhase*4 + QueenPhase*2
	phase := TotalPhase
	for _, i := range board.PieceList {
		phase -= PValue[GetPiece(board.Data[i])]
	}
	return (phase*256 + (TotalPhase / 2)) / TotalPhase
}

func MaterialCount(b *Board, endgame bool) int {
	var retval int
	for _, i := range b.PieceList {
		if OnBoard(i) && GetPiece(b.Data[i]) != EMPTY {
			if GetSide(b.Data[i]) == WHITE {
				retval += Value[GetPiece(b.Data[i])] + Pst(GetPiece(b.Data[i]), WHITE, i, endgame)
			} else {
				retval -= (Value[GetPiece(b.Data[i])] + Pst(GetPiece(b.Data[i]), BLACK, i, endgame))
			}
		}
	}
	return retval
}

func Pst(piece, side, index byte, endgame bool) int {
	switch piece {
	case PAWN:
		return GetPst(index, side, pstPawnMg, pstPawnEg, endgame)
	case KNIGHT:
		return GetPst(index, side, pstKnightMg, pstKnightEg, endgame)
	case BISHOP:
		return GetPst(index, side, pstBishopMg, pstBishopEg, endgame)
	case ROOK:
		return GetPst(index, side, pstRookMg, pstRookEg, endgame)
	case QUEEN:
		return GetPst(index, side, pstQueenMg, pstQueenMg, endgame)
	case KING:
		return GetPst(index, side, pstKingMg, pstKingEg, endgame)
	default:
		return 0
	}
}

func GetPst(index, side byte, tableMg [64]int, tableEg [64]int, endgame bool) int {
	var table []int
	if endgame {
		table = tableEg[:]
	} else {
		table = tableMg[:]
	}
	file, rank := IndexToCartesian(index)
	var tableindex byte
	if side == BLACK {
		tableindex = (8 * (7 - rank)) + file
	} else {
		tableindex = (8 * rank) + file
	}
	return table[tableindex]
}

func Quies(board *Board, alpha, beta int) int {
	nodecount++
	if abort {
		return 0
	}
	eval := Evaluate(board)
	if eval >= beta {
		return beta
	}
	if eval > alpha {
		alpha = eval
	}
	moves := FilterCaptures(MoveGen(board))
	for _, move := range moves {
		undo := MakeMove(board, &move)
		if Illegal(board) {
			UnmakeMove(board, &move, undo)
			continue
		}
		val := -Quies(board, -beta, -alpha)
		UnmakeMove(board, &move, undo)
		if abort {
			return 0
		}
		if val >= beta {
			return beta
		}
		if val > alpha {
			alpha = val
		}
	}
	return alpha
}

func AlphaBeta(board *Board, depth, alpha, beta, mate int, pline *Line) int {
	nodecount++
	if abort {
		return 0
	}
	legal := 0
	if depth <= 0 {
		return Quies(board, alpha, beta)
	}
	line := new(Line)
	moves := MoveGen(board)
	for _, move := range moves {
		undo := MakeMove(board, &move)
		if Illegal(board) {
			UnmakeMove(board, &move, undo)
			continue
		}
		val := -AlphaBeta(board, depth-1, -beta, -alpha, mate-1, line)
		UnmakeMove(board, &move, undo)
		if abort {
			return 0
		}
		if val >= beta {
			return beta
		}
		if val > alpha {
			alpha = val
			pline.Moves = append([]Move{move}, pline.Moves...)
			line.Moves = append(pline.Moves, move)
		}
		legal++
	}
	if legal == 0 {
		if !InCheck(board) {
			return 0
		}
		return -mate
	}
	return alpha
}

func AllotTime(board *Board) time.Duration {
	repeat := TimeRepeat
	if repeat <= 0 {
		repeat = 40
	}
	moves := repeat - board.Moves
	for moves <= 0 {
		moves += repeat
	}
	return (Clock/time.Duration(moves+1) - 20*time.Millisecond)
}

func ThinkingOutput(depth, score int, start time.Time, pv *Line) {
	fmt.Println(depth, score, int64(time.Since(start)/time.Millisecond)/10, nodecount, pv)
}

func SleepThread(board *Board, start time.Time) {
	bedoneby := AllotTime(board)
	fmt.Println("# ", Clock, ": allocated ", bedoneby)
	time.Sleep(bedoneby)
	abort = true
}

func FindMove(board *Board) *Move {
	start := time.Now()
	abort = false
	nodecount = 0
	retval := new(Move)
	for depth := 1; ; depth++ {
		line := new(Line)
		score := AlphaBeta(board, depth, -INFINITY, INFINITY, MATE, line)
		ThinkingOutput(depth, score, start, line)
		if !abort {
			retval = &line.Moves[0]
		} else {
			break
		}

		if depth == 1 {
			Clock -= time.Since(start)
			go SleepThread(board, start)
		}
	}
	return retval
}
<|endoftext|>


<commit_before>package timetoken

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TokenManager(periodicFuncTimesCalled *int) *tokenManager {
	return NewTokenManager(1*time.Millisecond, 3*time.Millisecond, func(id string) {
		*periodicFuncTimesCalled += 1
	})
}

func TestPeriodicFunctionCalledUntilTTL(t *testing.T) {
	t.Parallel()
	periodicFuncTimesCalled := 0
	tm := TokenManager(&periodicFuncTimesCalled)
	tm.Add("id", 3*time.Millisecond)
	time.Sleep(2 * time.Millisecond)
	assert.Equal(t, 2, periodicFuncTimesCalled)
}

func TestPeriodicFunctionCalledEveryPeriodeUntilTTL(t *testing.T) {
	t.Parallel()
	periodicFuncTimesCalled := 0
	tm := TokenManager(&periodicFuncTimesCalled)
	tm.Add("id", 3*time.Millisecond)

	time.Sleep(1 * time.Millisecond)
	assert.Equal(t, 1, periodicFuncTimesCalled)

	time.Sleep(1 * time.Millisecond)
	assert.Equal(t, 2, periodicFuncTimesCalled)
}
<commit_msg>More tests for the timetoken<commit_after>package timetoken

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TokenManager(periodicFuncTimesCalled *int) *tokenManager {
	return NewTokenManager(1*time.Millisecond, 3*time.Millisecond, func(id string) {
		*periodicFuncTimesCalled += 1
	})
}

func TestPeriodicFunctionCalledUntilTTL(t *testing.T) {
	t.Parallel()
	periodicFuncTimesCalled := 0
	tm := TokenManager(&periodicFuncTimesCalled)
	tm.Add("id", 3*time.Millisecond)
	time.Sleep(2 * time.Millisecond)
	assert.Equal(t, 2, periodicFuncTimesCalled)
}

func TestPeriodicFunctionCalledEveryPeriodeUntilTTL(t *testing.T) {
	t.Parallel()
	periodicFuncTimesCalled := 0
	tm := TokenManager(&periodicFuncTimesCalled)
	tm.Add("id", 3*time.Millisecond)

	time.Sleep(1 * time.Millisecond)
	assert.Equal(t, 1, periodicFuncTimesCalled)

	time.Sleep(1 * time.Millisecond)
	assert.Equal(t, 2, periodicFuncTimesCalled)
}

func TestPeriodicFunctionNotCalledAfterTTL(t *testing.T) {
	t.Parallel()
	periodicFuncTimesCalled := 0
	tm := TokenManager(&periodicFuncTimesCalled)
	tm.Add("id", 2*time.Millisecond)

	time.Sleep(3 * time.Millisecond)
	assert.Equal(t, 2, periodicFuncTimesCalled)
}
<|endoftext|>


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

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/golang/dep"
	"github.com/pkg/errors"
	"github.com/sdboyer/gps"
)

const initShortHelp = `Initialize a new project with manifest and lock files`
const initLongHelp = `
Initialize the project at filepath root by parsing its dependencies and writing
manifest and lock files. If root isn't specified, use the current directory.

The version of each dependency will reflect the current state of the GOPATH. If
a dependency doesn't exist in the GOPATH, it won't be written to the manifest,
but it will be solved-for, and will appear in the lock.

Note: init may use the network to solve the dependency graph.

Note: init does NOT vendor dependencies at the moment. See dep ensure.
`

func (cmd *initCommand) Name() string      { return "init" }
func (cmd *initCommand) Args() string      { return "[root]" }
func (cmd *initCommand) ShortHelp() string { return initShortHelp }
func (cmd *initCommand) LongHelp() string  { return initLongHelp }
func (cmd *initCommand) Hidden() bool      { return false }

func (cmd *initCommand) Register(fs *flag.FlagSet) {}

type initCommand struct{}

func trimPathPrefix(p1, p2 string) string {
	if filepath.HasPrefix(p1, p2) {
		return p1[len(p2):]
	}
	return p1
}

func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
	if len(args) > 1 {
		return errors.Errorf("too many args (%d)", len(args))
	}

	var root string
	if len(args) <= 0 {
		wd, err := os.Getwd()
		if err != nil {
			return err
		}
		root = wd
	} else {
		root = args[0]
	}

	mf := filepath.Join(root, dep.ManifestName)
	lf := filepath.Join(root, dep.LockName)

	mok, err := dep.IsRegular(mf)
	if err != nil {
		return err
	}
	if mok {
		return fmt.Errorf("manifest file %q already exists", mf)
	}
	// Manifest file does not exist.

	lok, err := dep.IsRegular(lf)
	if err != nil {
		return err
	}
	if lok {
		return fmt.Errorf("invalid state: manifest %q does not exist, but lock %q does", mf, lf)
	}

	cpr, err := ctx.SplitAbsoluteProjectRoot(root)
	if err != nil {
		return errors.Wrap(err, "determineProjectRoot")
	}
	vlogf("Finding dependencies for %q...", cpr)
	pkgT, err := gps.ListPackages(root, cpr)
	if err != nil {
		return errors.Wrap(err, "gps.ListPackages")
	}
	vlogf("Found %d dependencies.", len(pkgT.Packages))
	sm, err := ctx.SourceManager()
	if err != nil {
		return errors.Wrap(err, "getSourceManager")
	}
	sm.UseDefaultSignalHandling()
	defer sm.Release()

	pd, err := getProjectData(ctx, pkgT, cpr, sm)
	if err != nil {
		return err
	}
	m := dep.Manifest{
		Dependencies: pd.constraints,
	}

	// Make an initial lock from what knowledge we've collected about the
	// versions on disk
	l := dep.Lock{
		P: make([]gps.LockedProject, 0, len(pd.ondisk)),
	}

	for pr, v := range pd.ondisk {
		// That we have to chop off these path prefixes is a symptom of
		// a problem in gps itself
		pkgs := make([]string, 0, len(pd.dependencies[pr]))
		prslash := string(pr) + "/"
		for _, pkg := range pd.dependencies[pr] {
			if pkg == string(pr) {
				pkgs = append(pkgs, ".")
			} else {
				pkgs = append(pkgs, trimPathPrefix(pkg, prslash))
			}
		}

		l.P = append(l.P, gps.NewLockedProject(
			gps.ProjectIdentifier{ProjectRoot: pr}, v, pkgs),
		)
	}

	sw := dep.SafeWriter{
		Root:          root,
		SourceManager: sm,
		Manifest:      &m,
	}

	if len(pd.notondisk) > 0 {
		vlogf("Solving...")
		params := gps.SolveParameters{
			RootDir:         root,
			RootPackageTree: pkgT,
			Manifest:        &m,
			Lock:            &l,
		}

		if *verbose {
			params.Trace = true
			params.TraceLogger = log.New(os.Stderr, "", 0)
		}
		s, err := gps.Prepare(params, sm)
		if err != nil {
			return errors.Wrap(err, "prepare solver")
		}

		soln, err := s.Solve()
		if err != nil {
			handleAllTheFailuresOfTheWorld(err)
			return err
		}
		sw.Lock = dep.LockFromInterface(soln)
	} else {
		sw.Lock = &l
	}

	vlogf("Writing manifest and lock files.")

	if err := sw.WriteAllSafe(true); err != nil {
		return errors.Wrap(err, "safe write of manifest and lock")
	}

	return nil
}

// contains checks if a array of strings contains a value
func contains(a []string, b string) bool {
	for _, v := range a {
		if b == v {
			return true
		}
	}
	return false
}

// isStdLib reports whether $GOROOT/src/path should be considered
// part of the standard distribution. For historical reasons we allow people to add
// their own code to $GOROOT instead of using $GOPATH, but we assume that
// code will start with a domain name (dot in the first element).
// This was loving taken from src/cmd/go/pkg.go in Go's code (isStandardImportPath).
func isStdLib(path string) bool {
	i := strings.Index(path, "/")
	if i < 0 {
		i = len(path)
	}
	elem := path[:i]
	return !strings.Contains(elem, ".")
}

// TODO solve failures can be really creative - we need to be similarly creative
// in handling them and informing the user appropriately
func handleAllTheFailuresOfTheWorld(err error) {
	fmt.Printf("solve error: %s", err)
}

func writeFile(path string, in json.Marshaler) error {
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	defer f.Close()

	b, err := in.MarshalJSON()
	if err != nil {
		return err
	}

	_, err = f.Write(b)
	return err
}

func hasImportPathPrefix(s, prefix string) bool {
	if s == prefix {
		return true
	}
	return strings.HasPrefix(s, prefix+"/")
}

type projectData struct {
	constraints  gps.ProjectConstraints          // constraints that could be found
	dependencies map[gps.ProjectRoot][]string    // all dependencies (imports) found by project root
	notondisk    map[gps.ProjectRoot]bool        // projects that were not found on disk
	ondisk       map[gps.ProjectRoot]gps.Version // projects that were found on disk
}

func getProjectData(ctx *dep.Ctx, pkgT gps.PackageTree, cpr string, sm *gps.SourceMgr) (projectData, error) {
	vlogf("Building dependency graph...")

	constraints := make(gps.ProjectConstraints)
	dependencies := make(map[gps.ProjectRoot][]string)
	packages := make(map[string]bool)
	notondisk := make(map[gps.ProjectRoot]bool)
	ondisk := make(map[gps.ProjectRoot]gps.Version)

	syncDep := func(pr gps.ProjectRoot, sm *gps.SourceMgr) {
		message := "Cached"
		if err := sm.SyncSourceFor(gps.ProjectIdentifier{ProjectRoot: pr}); err != nil {
			message = "Unable to cache"
		}
		fmt.Fprintf(os.Stderr, "%s %s\n", message, pr)
	}

	for _, v := range pkgT.Packages {
		// TODO: Some errors maybe should not be skipped ;-)
		if v.Err != nil {
			vlogf("%v", v.Err)
			continue
		}
		vlogf("Package %q, analyzing...", v.P.ImportPath)

		for _, ip := range v.P.Imports {
			if isStdLib(ip) {
				continue
			}
			if hasImportPathPrefix(ip, cpr) {
				// Don't analyze imports from the current project.
				continue
			}
			pr, err := sm.DeduceProjectRoot(ip)
			if err != nil {
				return projectData{}, errors.Wrap(err, "sm.DeduceProjectRoot") // TODO: Skip and report ?
			}

			packages[ip] = true
			if _, ok := dependencies[pr]; ok {
				if !contains(dependencies[pr], ip) {
					dependencies[pr] = append(dependencies[pr], ip)
				}

				continue
			}
			go syncDep(pr, sm)

			vlogf("Package %q has import %q, analyzing...", v.P.ImportPath, ip)

			dependencies[pr] = []string{ip}
			v, err := ctx.VersionInWorkspace(pr)
			if err != nil {
				notondisk[pr] = true
				vlogf("Could not determine version for %q, omitting from generated manifest", pr)
				continue
			}

			ondisk[pr] = v
			pp := gps.ProjectProperties{}
			switch v.Type() {
			case gps.IsBranch, gps.IsVersion, gps.IsRevision:
				pp.Constraint = v
			case gps.IsSemver:
				c, _ := gps.NewSemverConstraint("^" + v.String())
				pp.Constraint = c
			}

			constraints[pr] = pp
		}
	}

	vlogf("Analyzing transitive imports...")
	// Explore the packages we've found for transitive deps, either
	// completing the lock or identifying (more) missing projects that we'll
	// need to ask gps to solve for us.
	colors := make(map[string]uint8)
	const (
		white uint8 = iota
		grey
		black
	)

	// cache of PackageTrees, so we don't parse projects more than once
	ptrees := make(map[gps.ProjectRoot]gps.PackageTree)

	// depth-first traverser
	var dft func(string) error
	dft = func(pkg string) error {
		switch colors[pkg] {
		case white:
			vlogf("Analyzing %q...", pkg)
			colors[pkg] = grey

			pr, err := sm.DeduceProjectRoot(pkg)
			if err != nil {
				return errors.Wrap(err, "could not deduce project root for "+pkg)
			}

			// We already visited this project root earlier via some other
			// pkg within it, and made the decision that it's not on disk.
			// Respect that decision, and pop the stack.
			if notondisk[pr] {
				colors[pkg] = black
				return nil
			}

			ptree, has := ptrees[pr]
			if !has {
				// It's fine if the root does not exist - it indicates that this
				// project is not present in the workspace, and so we need to
				// solve to deal with this dep.
				r := filepath.Join(ctx.GOPATH, "src", string(pr))
				_, err := os.Lstat(r)
				if os.IsNotExist(err) {
					colors[pkg] = black
					notondisk[pr] = true
					return nil
				}

				ptree, err = gps.ListPackages(r, string(pr))
				if err != nil {
					// Any error here other than an a nonexistent dir (which
					// can't happen because we covered that case above) is
					// probably critical, so bail out.
					return errors.Wrap(err, "gps.ListPackages")
				}
				ptrees[pr] = ptree
			}

			// Get a reachmap that includes main pkgs (even though importing
			// them is an error, what we're checking right now is simply whether
			// there's a package with go code present on disk), and does not
			// backpropagate errors (again, because our only concern right now
			// is package existence).
			rm, errmap := ptree.ToReachMap(true, false, false, nil)
			reached, ok := rm[pkg]
			if !ok {
				colors[pkg] = black
				// not on disk...
				notondisk[pr] = true
				return nil
			} else if _, ok := errmap[pkg]; ok {
				// The package is on disk, but it itself contains some errors.
				colors[pkg] = black
				return nil
			}

			if _, ok := dependencies[pr]; ok {
				if !contains(dependencies[pr], pkg) {
					dependencies[pr] = append(dependencies[pr], pkg)
				}
			} else {
				dependencies[pr] = []string{pkg}
				go syncDep(pr, sm)
			}

			// project must be on disk at this point; question is
			// whether we're first seeing it here, in the transitive
			// exploration, or if it arose in the direct dep parts
			if _, in := ondisk[pr]; !in {
				v, err := ctx.VersionInWorkspace(pr)
				if err != nil {
					colors[pkg] = black
					notondisk[pr] = true
					return nil
				}
				ondisk[pr] = v
			}

			// recurse
			for _, rpkg := range reached.External {
				if isStdLib(rpkg) {
					continue
				}

				err := dft(rpkg)
				if err != nil {
					return err
				}
			}

			colors[pkg] = black
		case grey:
			return fmt.Errorf("Import cycle detected on %s", pkg)
		}
		return nil
	}

	// run the depth-first traversal from the set of immediate external
	// package imports we found in the current project
	for pkg := range packages {
		err := dft(pkg)
		if err != nil {
			return projectData{}, err // already errors.Wrap()'d internally
		}
	}

	pd := projectData{
		constraints:  constraints,
		dependencies: dependencies,
		notondisk:    notondisk,
		ondisk:       ondisk,
	}
	return pd, nil
}
<commit_msg>Update based on review<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/golang/dep"
	"github.com/pkg/errors"
	"github.com/sdboyer/gps"
)

const initShortHelp = `Initialize a new project with manifest and lock files`
const initLongHelp = `
Initialize the project at filepath root by parsing its dependencies and writing
manifest and lock files. If root isn't specified, use the current directory.

The version of each dependency will reflect the current state of the GOPATH. If
a dependency doesn't exist in the GOPATH, it won't be written to the manifest,
but it will be solved-for, and will appear in the lock.

Note: init may use the network to solve the dependency graph.

Note: init does NOT vendor dependencies at the moment. See dep ensure.
`

func (cmd *initCommand) Name() string      { return "init" }
func (cmd *initCommand) Args() string      { return "[root]" }
func (cmd *initCommand) ShortHelp() string { return initShortHelp }
func (cmd *initCommand) LongHelp() string  { return initLongHelp }
func (cmd *initCommand) Hidden() bool      { return false }

func (cmd *initCommand) Register(fs *flag.FlagSet) {}

type initCommand struct{}

func trimPathPrefix(p1, p2 string) string {
	if filepath.HasPrefix(p1, p2) {
		return p1[len(p2):]
	}
	return p1
}

func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
	if len(args) > 1 {
		return errors.Errorf("too many args (%d)", len(args))
	}

	var root string
	if len(args) <= 0 {
		wd, err := os.Getwd()
		if err != nil {
			return err
		}
		root = wd
	} else {
		root = args[0]
	}

	mf := filepath.Join(root, dep.ManifestName)
	lf := filepath.Join(root, dep.LockName)

	mok, err := dep.IsRegular(mf)
	if err != nil {
		return err
	}
	if mok {
		return fmt.Errorf("manifest file %q already exists", mf)
	}
	// Manifest file does not exist.

	lok, err := dep.IsRegular(lf)
	if err != nil {
		return err
	}
	if lok {
		return fmt.Errorf("invalid state: manifest %q does not exist, but lock %q does", mf, lf)
	}

	cpr, err := ctx.SplitAbsoluteProjectRoot(root)
	if err != nil {
		return errors.Wrap(err, "determineProjectRoot")
	}
	vlogf("Finding dependencies for %q...", cpr)
	pkgT, err := gps.ListPackages(root, cpr)
	if err != nil {
		return errors.Wrap(err, "gps.ListPackages")
	}
	vlogf("Found %d dependencies.", len(pkgT.Packages))
	sm, err := ctx.SourceManager()
	if err != nil {
		return errors.Wrap(err, "getSourceManager")
	}
	sm.UseDefaultSignalHandling()
	defer sm.Release()

	pd, err := getProjectData(ctx, pkgT, cpr, sm)
	if err != nil {
		return err
	}
	m := dep.Manifest{
		Dependencies: pd.constraints,
	}

	// Make an initial lock from what knowledge we've collected about the
	// versions on disk
	l := dep.Lock{
		P: make([]gps.LockedProject, 0, len(pd.ondisk)),
	}

	for pr, v := range pd.ondisk {
		// That we have to chop off these path prefixes is a symptom of
		// a problem in gps itself
		pkgs := make([]string, 0, len(pd.dependencies[pr]))
		prslash := string(pr) + "/"
		for _, pkg := range pd.dependencies[pr] {
			if pkg == string(pr) {
				pkgs = append(pkgs, ".")
			} else {
				pkgs = append(pkgs, trimPathPrefix(pkg, prslash))
			}
		}

		l.P = append(l.P, gps.NewLockedProject(
			gps.ProjectIdentifier{ProjectRoot: pr}, v, pkgs),
		)
	}

	sw := dep.SafeWriter{
		Root:          root,
		SourceManager: sm,
		Manifest:      &m,
	}

	if len(pd.notondisk) > 0 {
		vlogf("Solving...")
		params := gps.SolveParameters{
			RootDir:         root,
			RootPackageTree: pkgT,
			Manifest:        &m,
			Lock:            &l,
		}

		if *verbose {
			params.Trace = true
			params.TraceLogger = log.New(os.Stderr, "", 0)
		}
		s, err := gps.Prepare(params, sm)
		if err != nil {
			return errors.Wrap(err, "prepare solver")
		}

		soln, err := s.Solve()
		if err != nil {
			handleAllTheFailuresOfTheWorld(err)
			return err
		}
		sw.Lock = dep.LockFromInterface(soln)
	} else {
		sw.Lock = &l
	}

	vlogf("Writing manifest and lock files.")

	if err := sw.WriteAllSafe(true); err != nil {
		return errors.Wrap(err, "safe write of manifest and lock")
	}

	return nil
}

// contains checks if a array of strings contains a value
func contains(a []string, b string) bool {
	for _, v := range a {
		if b == v {
			return true
		}
	}
	return false
}

// isStdLib reports whether $GOROOT/src/path should be considered
// part of the standard distribution. For historical reasons we allow people to add
// their own code to $GOROOT instead of using $GOPATH, but we assume that
// code will start with a domain name (dot in the first element).
// This was loving taken from src/cmd/go/pkg.go in Go's code (isStandardImportPath).
func isStdLib(path string) bool {
	i := strings.Index(path, "/")
	if i < 0 {
		i = len(path)
	}
	elem := path[:i]
	return !strings.Contains(elem, ".")
}

// TODO solve failures can be really creative - we need to be similarly creative
// in handling them and informing the user appropriately
func handleAllTheFailuresOfTheWorld(err error) {
	fmt.Printf("solve error: %s", err)
}

func writeFile(path string, in json.Marshaler) error {
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	defer f.Close()

	b, err := in.MarshalJSON()
	if err != nil {
		return err
	}

	_, err = f.Write(b)
	return err
}

func hasImportPathPrefix(s, prefix string) bool {
	if s == prefix {
		return true
	}
	return strings.HasPrefix(s, prefix+"/")
}

type projectData struct {
	constraints  gps.ProjectConstraints          // constraints that could be found
	dependencies map[gps.ProjectRoot][]string    // all dependencies (imports) found by project root
	notondisk    map[gps.ProjectRoot]bool        // projects that were not found on disk
	ondisk       map[gps.ProjectRoot]gps.Version // projects that were found on disk
}

func getProjectData(ctx *dep.Ctx, pkgT gps.PackageTree, cpr string, sm *gps.SourceMgr) (projectData, error) {
	vlogf("Building dependency graph...")

	constraints := make(gps.ProjectConstraints)
	dependencies := make(map[gps.ProjectRoot][]string)
	packages := make(map[string]bool)
	notondisk := make(map[gps.ProjectRoot]bool)
	ondisk := make(map[gps.ProjectRoot]gps.Version)

	syncDep := func(pr gps.ProjectRoot, sm *gps.SourceMgr) {
		message := "Cached"
		if err := sm.SyncSourceFor(gps.ProjectIdentifier{ProjectRoot: pr}); err != nil {
			message = "Unable to cache"
		}
		fmt.Fprintf(os.Stderr, "%s %s\n", message, pr)
	}

	for _, v := range pkgT.Packages {
		// TODO: Some errors maybe should not be skipped ;-)
		if v.Err != nil {
			vlogf("%v", v.Err)
			continue
		}
		vlogf("Package %q, analyzing...", v.P.ImportPath)

		for _, ip := range v.P.Imports {
			if isStdLib(ip) {
				continue
			}
			if hasImportPathPrefix(ip, cpr) {
				// Don't analyze imports from the current project.
				continue
			}
			pr, err := sm.DeduceProjectRoot(ip)
			if err != nil {
				return projectData{}, errors.Wrap(err, "sm.DeduceProjectRoot") // TODO: Skip and report ?
			}

			packages[ip] = true
			if _, ok := dependencies[pr]; ok {
				if !contains(dependencies[pr], ip) {
					dependencies[pr] = append(dependencies[pr], ip)
				}

				continue
			}
			go syncDep(pr, sm)

			vlogf("Package %q has import %q, analyzing...", v.P.ImportPath, ip)

			dependencies[pr] = []string{ip}
			v, err := ctx.VersionInWorkspace(pr)
			if err != nil {
				notondisk[pr] = true
				vlogf("Could not determine version for %q, omitting from generated manifest", pr)
				continue
			}

			ondisk[pr] = v
			pp := gps.ProjectProperties{}
			switch v.Type() {
			case gps.IsBranch, gps.IsVersion, gps.IsRevision:
				pp.Constraint = v
			case gps.IsSemver:
				c, _ := gps.NewSemverConstraint("^" + v.String())
				pp.Constraint = c
			}

			constraints[pr] = pp
		}
	}

	vlogf("Analyzing transitive imports...")
	// Explore the packages we've found for transitive deps, either
	// completing the lock or identifying (more) missing projects that we'll
	// need to ask gps to solve for us.
	colors := make(map[string]uint8)
	const (
		white uint8 = iota
		grey
		black
	)

	// cache of PackageTrees, so we don't parse projects more than once
	ptrees := make(map[gps.ProjectRoot]gps.PackageTree)

	// depth-first traverser
	var dft func(string) error
	dft = func(pkg string) error {
		switch colors[pkg] {
		case white:
			vlogf("Analyzing %q...", pkg)
			colors[pkg] = grey

			pr, err := sm.DeduceProjectRoot(pkg)
			if err != nil {
				return errors.Wrap(err, "could not deduce project root for "+pkg)
			}

			// We already visited this project root earlier via some other
			// pkg within it, and made the decision that it's not on disk.
			// Respect that decision, and pop the stack.
			if notondisk[pr] {
				colors[pkg] = black
				return nil
			}

			ptree, has := ptrees[pr]
			if !has {
				// It's fine if the root does not exist - it indicates that this
				// project is not present in the workspace, and so we need to
				// solve to deal with this dep.
				r := filepath.Join(ctx.GOPATH, "src", string(pr))
				_, err := os.Lstat(r)
				if os.IsNotExist(err) {
					colors[pkg] = black
					notondisk[pr] = true
					return nil
				}

				ptree, err = gps.ListPackages(r, string(pr))
				if err != nil {
					// Any error here other than an a nonexistent dir (which
					// can't happen because we covered that case above) is
					// probably critical, so bail out.
					return errors.Wrap(err, "gps.ListPackages")
				}
				ptrees[pr] = ptree
			}

			// Get a reachmap that includes main pkgs (even though importing
			// them is an error, what we're checking right now is simply whether
			// there's a package with go code present on disk), and does not
			// backpropagate errors (again, because our only concern right now
			// is package existence).
			rm, errmap := ptree.ToReachMap(true, false, false, nil)
			reached, ok := rm[pkg]
			if !ok {
				colors[pkg] = black
				// not on disk...
				notondisk[pr] = true
				return nil
			}
			if _, ok := errmap[pkg]; ok {
				// The package is on disk, but contains some errors.
				colors[pkg] = black
				return nil
			}

			if _, ok := dependencies[pr]; ok {
				if !contains(dependencies[pr], pkg) {
					dependencies[pr] = append(dependencies[pr], pkg)
				}
			} else {
				dependencies[pr] = []string{pkg}
				go syncDep(pr, sm)
			}

			// project must be on disk at this point; question is
			// whether we're first seeing it here, in the transitive
			// exploration, or if it arose in the direct dep parts
			if _, in := ondisk[pr]; !in {
				v, err := ctx.VersionInWorkspace(pr)
				if err != nil {
					colors[pkg] = black
					notondisk[pr] = true
					return nil
				}
				ondisk[pr] = v
			}

			// recurse
			for _, rpkg := range reached.External {
				if isStdLib(rpkg) {
					continue
				}

				err := dft(rpkg)
				if err != nil {
					return err
				}
			}

			colors[pkg] = black
		case grey:
			return fmt.Errorf("Import cycle detected on %s", pkg)
		}
		return nil
	}

	// run the depth-first traversal from the set of immediate external
	// package imports we found in the current project
	for pkg := range packages {
		err := dft(pkg)
		if err != nil {
			return projectData{}, err // already errors.Wrap()'d internally
		}
	}

	pd := projectData{
		constraints:  constraints,
		dependencies: dependencies,
		notondisk:    notondisk,
		ondisk:       ondisk,
	}
	return pd, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package main

import (
	"errors"
	"fmt"
	"os"
	"time"

	"github.com/spf13/cobra"

	"k8s.io/helm/pkg/chartutil"
	"k8s.io/helm/pkg/helm"
	"k8s.io/helm/pkg/timeconv"
)

var getHelp = `
This command shows the details of a named release.

It can be used to get extended information about the release, including:

  - The values used to generate the release
  - The chart used to generate the release
  - The generated manifest file

By default, this prints a human readable collection of information about the
chart, the supplied values, and the generated manifest file.
`

var getValuesHelp = `
This command downloads a values file for a given release.

To save the output to a file, use the -f flag.
`

var getManifestHelp = `
This command fetches the generated manifest for a given release.

A manifest is a YAML-encoded representation of the Kubernetes resources that
were generated from this release's chart(s). If a chart is dependent on other
charts, those resources will also be included in the manifest.
`

// getOut is the filename to direct output.
//
// If it is blank, output is sent to os.Stdout.
var getOut = ""

var allValues = false

var errReleaseRequired = errors.New("release name is required")

var getCommand = &cobra.Command{
	Use:               "get [flags] RELEASE_NAME",
	Short:             "download a named release",
	Long:              getHelp,
	RunE:              getCmd,
	PersistentPreRunE: setupConnection,
}

var getValuesCommand = &cobra.Command{
	Use:   "values [flags] RELEASE_NAME",
	Short: "download the values file for a named release",
	Long:  getValuesHelp,
	RunE:  getValues,
}

var getManifestCommand = &cobra.Command{
	Use:   "manifest [flags] RELEASE_NAME",
	Short: "download the manifest for a named release",
	Long:  getManifestHelp,
	RunE:  getManifest,
}

func init() {
	// 'get' command flags.
	getCommand.PersistentFlags().StringVarP(&getOut, "file", "f", "", "output file")

	// 'get values' flags.
	getValuesCommand.PersistentFlags().BoolVarP(&allValues, "all", "a", false, "dump all (computed) values")

	getCommand.AddCommand(getValuesCommand)
	getCommand.AddCommand(getManifestCommand)
	RootCommand.AddCommand(getCommand)
}

// getCmd is the command that implements 'helm get'
func getCmd(cmd *cobra.Command, args []string) error {
	if len(args) == 0 {
		return errReleaseRequired
	}

	res, err := helm.GetReleaseContent(args[0])
	if err != nil {
		return prettyError(err)
	}

	cfg, err := chartutil.CoalesceValues(res.Release.Chart, res.Release.Config, nil)
	if err != nil {
		return err
	}
	cfgStr, err := cfg.YAML()
	if err != nil {
		return err
	}

	fmt.Printf("CHART: %s-%s\n", res.Release.Chart.Metadata.Name, res.Release.Chart.Metadata.Version)
	fmt.Printf("RELEASED: %s\n", timeconv.Format(res.Release.Info.LastDeployed, time.ANSIC))
	fmt.Println("USER-SUPPLIED VALUES:")
	fmt.Println(res.Release.Config.Raw)
	fmt.Println("COMPUTED VALUES:")
	fmt.Println(cfgStr)
	fmt.Println("MANIFEST:")
	fmt.Println(res.Release.Manifest)
	return nil
}

// getValues implements 'helm get values'
func getValues(cmd *cobra.Command, args []string) error {
	if len(args) == 0 {
		return errReleaseRequired
	}

	res, err := helm.GetReleaseContent(args[0])
	if err != nil {
		return prettyError(err)
	}

	// If the user wants all values, compute the values and return.
	if allValues {
		cfg, err := chartutil.CoalesceValues(res.Release.Chart, res.Release.Config, nil)
		if err != nil {
			return err
		}
		cfgStr, err := cfg.YAML()
		if err != nil {
			return err
		}
		return getToFile(cfgStr)
	}

	return getToFile(res.Release.Config.Raw)
}

// getManifest implements 'helm get manifest'
func getManifest(cmd *cobra.Command, args []string) error {
	if len(args) == 0 {
		return errReleaseRequired
	}

	res, err := helm.GetReleaseContent(args[0])
	if err != nil {
		return prettyError(err)
	}
	return getToFile(res.Release.Manifest)
}

func getToFile(v interface{}) error {
	out := os.Stdout
	if len(getOut) > 0 {
		t, err := os.Create(getOut)
		if err != nil {
			return fmt.Errorf("failed to create %s: %s", getOut, err)
		}
		defer t.Close()
		out = t
	}
	fmt.Fprintln(out, v)
	return nil
}
<commit_msg>fix(cmd): remove --file flag for helm get<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package main

import (
	"errors"
	"fmt"
	"time"

	"github.com/spf13/cobra"

	"k8s.io/helm/pkg/chartutil"
	"k8s.io/helm/pkg/helm"
	"k8s.io/helm/pkg/timeconv"
)

var getHelp = `
This command shows the details of a named release.

It can be used to get extended information about the release, including:

  - The values used to generate the release
  - The chart used to generate the release
  - The generated manifest file

By default, this prints a human readable collection of information about the
chart, the supplied values, and the generated manifest file.
`

var getValuesHelp = `
This command downloads a values file for a given release.

To save the output to a file, use the -f flag.
`

var getManifestHelp = `
This command fetches the generated manifest for a given release.

A manifest is a YAML-encoded representation of the Kubernetes resources that
were generated from this release's chart(s). If a chart is dependent on other
charts, those resources will also be included in the manifest.
`

var allValues = false

var errReleaseRequired = errors.New("release name is required")

var getCommand = &cobra.Command{
	Use:               "get [flags] RELEASE_NAME",
	Short:             "download a named release",
	Long:              getHelp,
	RunE:              getCmd,
	PersistentPreRunE: setupConnection,
}

var getValuesCommand = &cobra.Command{
	Use:   "values [flags] RELEASE_NAME",
	Short: "download the values file for a named release",
	Long:  getValuesHelp,
	RunE:  getValues,
}

var getManifestCommand = &cobra.Command{
	Use:   "manifest [flags] RELEASE_NAME",
	Short: "download the manifest for a named release",
	Long:  getManifestHelp,
	RunE:  getManifest,
}

func init() {
	// 'get values' flags.
	getValuesCommand.PersistentFlags().BoolVarP(&allValues, "all", "a", false, "dump all (computed) values")

	getCommand.AddCommand(getValuesCommand)
	getCommand.AddCommand(getManifestCommand)
	RootCommand.AddCommand(getCommand)
}

// getCmd is the command that implements 'helm get'
func getCmd(cmd *cobra.Command, args []string) error {
	if len(args) == 0 {
		return errReleaseRequired
	}

	res, err := helm.GetReleaseContent(args[0])
	if err != nil {
		return prettyError(err)
	}

	cfg, err := chartutil.CoalesceValues(res.Release.Chart, res.Release.Config, nil)
	if err != nil {
		return err
	}
	cfgStr, err := cfg.YAML()
	if err != nil {
		return err
	}

	fmt.Printf("CHART: %s-%s\n", res.Release.Chart.Metadata.Name, res.Release.Chart.Metadata.Version)
	fmt.Printf("RELEASED: %s\n", timeconv.Format(res.Release.Info.LastDeployed, time.ANSIC))
	fmt.Println("USER-SUPPLIED VALUES:")
	fmt.Println(res.Release.Config.Raw)
	fmt.Println("COMPUTED VALUES:")
	fmt.Println(cfgStr)
	fmt.Println("MANIFEST:")
	fmt.Println(res.Release.Manifest)
	return nil
}

// getValues implements 'helm get values'
func getValues(cmd *cobra.Command, args []string) error {
	if len(args) == 0 {
		return errReleaseRequired
	}

	res, err := helm.GetReleaseContent(args[0])
	if err != nil {
		return prettyError(err)
	}

	// If the user wants all values, compute the values and return.
	if allValues {
		cfg, err := chartutil.CoalesceValues(res.Release.Chart, res.Release.Config, nil)
		if err != nil {
			return err
		}
		cfgStr, err := cfg.YAML()
		if err != nil {
			return err
		}
		fmt.Println(cfgStr)
		return nil
	}

	fmt.Println(res.Release.Config.Raw)
	return nil
}

// getManifest implements 'helm get manifest'
func getManifest(cmd *cobra.Command, args []string) error {
	if len(args) == 0 {
		return errReleaseRequired
	}

	res, err := helm.GetReleaseContent(args[0])
	if err != nil {
		return prettyError(err)
	}
	fmt.Println(res.Release.Manifest)
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/juju/cmd"
	"launchpad.net/gnuflag"

	"github.com/juju/juju/cmd/envcmd"
	"github.com/juju/juju/juju"
)

// SetCommand updates the configuration of a service.
type SetCommand struct {
	envcmd.EnvCommandBase
	ServiceName     string
	SettingsStrings map[string]string
	SettingsYAML    cmd.FileVar
}

const setDoc = `
Set one or more configuration options for the specified service. See also the
unset command which sets one or more configuration options for a specified
service to their default value.
`

func (c *SetCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "set",
		Args:    "<service> name=value ...",
		Purpose: "set service config options",
		Doc:     "Set one or more configuration options for the specified service.",
	}
}

func (c *SetCommand) SetFlags(f *gnuflag.FlagSet) {
	f.Var(&c.SettingsYAML, "config", "path to yaml-formatted service config")
}

func (c *SetCommand) Init(args []string) error {
	if len(args) == 0 || len(strings.Split(args[0], "=")) > 1 {
		return errors.New("no service name specified")
	}
	if c.SettingsYAML.Path != "" && len(args) > 1 {
		return errors.New("cannot specify --config when using key=value arguments")
	}
	c.ServiceName = args[0]
	settings, err := parse(args[1:])
	if err != nil {
		return err
	}
	c.SettingsStrings = settings
	return nil
}

// Run updates the configuration of a service.
func (c *SetCommand) Run(ctx *cmd.Context) error {
	api, err := juju.NewAPIClientFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer api.Close()

	if c.SettingsYAML.Path != "" {
		b, err := c.SettingsYAML.Read(ctx)
		if err != nil {
			return err
		}
		return api.ServiceSetYAML(c.ServiceName, string(b))
	} else if len(c.SettingsStrings) == 0 {
		return nil
	}
	settings := map[string]string{}
	for k, v := range c.SettingsStrings {
		if v[0] != '@' {
			settings[k] = v
			continue
		}
		nv, err := readValue(ctx, v[1:])
		if err != nil {
			return err
		}
		settings[k] = nv
	}
	return api.ServiceSet(c.ServiceName, settings)
}

// parse parses the option k=v strings into a map of options to be
// updated in the config. Keys with empty values are returned separately
// and should be removed.
func parse(options []string) (map[string]string, error) {
	kv := make(map[string]string)
	for _, o := range options {
		s := strings.SplitN(o, "=", 2)
		if len(s) != 2 || s[0] == "" {
			return nil, fmt.Errorf("invalid option: %q", o)
		}
		kv[s[0]] = s[1]
	}
	return kv, nil
}

// readValue reads the value of an option out of the named file.
// An empty content is valid, like in parsing the options.
func readValue(ctx *cmd.Context, filename string) (string, error) {
	content, err := ioutil.ReadFile(ctx.AbsPath(filename))
	if err != nil {
		return "", fmt.Errorf("cannot read option from file %q: %v", filename, err)
	}
	return string(content), nil
}
<commit_msg>Added some help information.<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/juju/cmd"
	"launchpad.net/gnuflag"

	"github.com/juju/juju/cmd/envcmd"
	"github.com/juju/juju/juju"
)

// SetCommand updates the configuration of a service.
type SetCommand struct {
	envcmd.EnvCommandBase
	ServiceName     string
	SettingsStrings map[string]string
	SettingsYAML    cmd.FileVar
}

const setDoc = `
Set one or more configuration options for the specified service. See also the
unset command which sets one or more configuration options for a specified
service to their default value.

In case a value starts with an at sign (@) the rest of the value is interpreted
as a filename. The value itself is then read out of the named file.
`

func (c *SetCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "set",
		Args:    "<service> name=value ...",
		Purpose: "set service config options",
		Doc:     "Set one or more configuration options for the specified service.",
	}
}

func (c *SetCommand) SetFlags(f *gnuflag.FlagSet) {
	f.Var(&c.SettingsYAML, "config", "path to yaml-formatted service config")
}

func (c *SetCommand) Init(args []string) error {
	if len(args) == 0 || len(strings.Split(args[0], "=")) > 1 {
		return errors.New("no service name specified")
	}
	if c.SettingsYAML.Path != "" && len(args) > 1 {
		return errors.New("cannot specify --config when using key=value arguments")
	}
	c.ServiceName = args[0]
	settings, err := parse(args[1:])
	if err != nil {
		return err
	}
	c.SettingsStrings = settings
	return nil
}

// Run updates the configuration of a service.
func (c *SetCommand) Run(ctx *cmd.Context) error {
	api, err := juju.NewAPIClientFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer api.Close()

	if c.SettingsYAML.Path != "" {
		b, err := c.SettingsYAML.Read(ctx)
		if err != nil {
			return err
		}
		return api.ServiceSetYAML(c.ServiceName, string(b))
	} else if len(c.SettingsStrings) == 0 {
		return nil
	}
	settings := map[string]string{}
	for k, v := range c.SettingsStrings {
		if v[0] != '@' {
			settings[k] = v
			continue
		}
		nv, err := readValue(ctx, v[1:])
		if err != nil {
			return err
		}
		settings[k] = nv
	}
	return api.ServiceSet(c.ServiceName, settings)
}

// parse parses the option k=v strings into a map of options to be
// updated in the config. Keys with empty values are returned separately
// and should be removed.
func parse(options []string) (map[string]string, error) {
	kv := make(map[string]string)
	for _, o := range options {
		s := strings.SplitN(o, "=", 2)
		if len(s) != 2 || s[0] == "" {
			return nil, fmt.Errorf("invalid option: %q", o)
		}
		kv[s[0]] = s[1]
	}
	return kv, nil
}

// readValue reads the value of an option out of the named file.
// An empty content is valid, like in parsing the options.
func readValue(ctx *cmd.Context, filename string) (string, error) {
	content, err := ioutil.ReadFile(ctx.AbsPath(filename))
	if err != nil {
		return "", fmt.Errorf("cannot read option from file %q: %v", filename, err)
	}
	return string(content), nil
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"os"

	_ "github.com/go-sql-driver/mysql"
	_ "github.com/mattn/go-sqlite3"
	"github.com/morikuni/mdq"
	"github.com/spf13/pflag"
)

func main() {
	os.Exit(Run(os.Args, os.Stdin, os.Stdout, os.Stderr))
}

func Run(args []string, in io.Reader, out io.Writer, errW io.Writer) int {
	home := os.Getenv("HOME")

	flag := pflag.NewFlagSet("mdq", pflag.ContinueOnError)
	tag := flag.String("tag", "", "database tag")
	format := flag.String("format", "", "golang template string")
	query := flag.StringP("query", "q", "", "SQL")
	config := flag.String("config", home+"/.config/mdq/config.yaml", "path to config file")
	silent := flag.Bool("silent", false, "ignore errors from databases")

	flag.Parse(os.Args[1:])

	if *query == "" {
		panic("query is empty")
	}

	if *config == "" {
		panic("config is empty")
	}

	reporter := mdq.DefaultReporter
	if *silent {
		reporter = mdq.SilentReporter
	}

	f, err := os.Open(*config)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	dbs, err := mdq.CreateDBsFromConfig(f, *tag)
	if err != nil {
		panic(err)
	}

	cluster := mdq.NewCluster(dbs, reporter)

	results := cluster.Query(*query)

	var printer mdq.Printer
	if *format != "" {
		printer, err = mdq.NewTemplatePrinter(os.Stdout, *format)
		if err != nil {
			panic(err)
		}
	} else {
		printer = mdq.NewJsonPrinter(os.Stdout)
	}
	printer.Print(results)

	return 0
}
<commit_msg>Remove panic<commit_after>package main

import (
	"fmt"
	"io"
	"os"

	_ "github.com/go-sql-driver/mysql"
	_ "github.com/mattn/go-sqlite3"
	"github.com/morikuni/mdq"
	"github.com/spf13/pflag"
)

func main() {
	os.Exit(Run(os.Args, os.Stdin, os.Stdout, os.Stderr))
}

func Run(args []string, in io.Reader, out io.Writer, errW io.Writer) int {
	home := os.Getenv("HOME")

	flag := pflag.NewFlagSet("mdq", pflag.ContinueOnError)
	tag := flag.String("tag", "", "database tag")
	format := flag.String("format", "", "golang template string")
	query := flag.StringP("query", "q", "", "SQL")
	config := flag.String("config", home+"/.config/mdq/config.yaml", "path to config file")
	silent := flag.Bool("silent", false, "ignore errors from databases")

	flag.Parse(args[1:])

	if *query == "" {
		fmt.Fprintln(errW, "query is required")
		return 1
	}

	reporter := mdq.DefaultReporter
	if *silent {
		reporter = mdq.SilentReporter
	}

	f, err := os.Open(*config)
	if err != nil {
		fmt.Fprintln(errW, "cannot open config file:", *config)
		return 1
	}
	defer f.Close()

	dbs, err := mdq.CreateDBsFromConfig(f, *tag)
	if err != nil {
		fmt.Fprintln(errW, err)
		return 1
	}

	cluster := mdq.NewCluster(dbs, reporter)

	results := cluster.Query(*query)

	var printer mdq.Printer
	if *format != "" {
		printer, err = mdq.NewTemplatePrinter(os.Stdout, *format)
		if err != nil {
			fmt.Fprintln(errW, err)
			return 1
		}
	} else {
		printer = mdq.NewJsonPrinter(os.Stdout)
	}
	printer.Print(results)

	return 0
}
<|endoftext|>


<commit_before>package main

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

	"github.com/lucapette/testdata/pkg/testdata"
	flag "github.com/spf13/pflag"
)

var usage = `
  Usage: testdata field1 [[field2] [field3]]
    [--tick d]
    [--max n]
    [--generators]
    [--format]

    testdata --help

  Options:
    --generators    list all available generators
    --max n         generate data up to n [default: 10]
    --tick d        generate data every d [default: 10ms]
    --help          show help information
    --format f      generate data in f format [options: csv|tab, default: " "]
`

var generatorsFlag = flag.Bool("generators", false, "list all the generators")
var tickFlag = flag.Duration("tick", 10*time.Millisecond, "generate data every d milliseconds")
var maxFlag = flag.Int("max", 10, "generate up to n rows")
var helpFlag = flag.Bool("help", false, "print usage")
var formatFlag = flag.String("format", "", "Output format")

func main() {
	if *helpFlag {
		fmt.Printf(usage)
		os.Exit(0)
	}

	if *generatorsFlag {
		generators := testdata.List()
		sort.Strings(generators)

		for _, name := range generators {
			fmt.Printf("%s\n", name)
		}
		os.Exit(0)
	}

	if len(flag.Args()) == 0 {
		fmt.Printf(usage)
		os.Exit(0)
	}

	rand.Seed(time.Now().UnixNano())

	tick := time.Tick(*tickFlag)

	total := 0

	for _ = range tick {
		fmt.Print(testdata.GenerateRow(flag.Args(), *formatFlag))

		if total++; total == *maxFlag {
			return
		}
	}
}

func init() {
	flag.Parse()
}
<commit_msg>Simplify code<commit_after>package main

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

	"github.com/lucapette/testdata/pkg/testdata"
	flag "github.com/spf13/pflag"
)

var usage = `
  Usage: testdata field1 [[field2] [field3]]
    [--tick d]
    [--max n]
    [--generators]
    [--format]

    testdata --help

  Options:
    --generators    list all available generators
    --max n         generate data up to n [default: 10]
    --tick d        generate data every d [default: 10ms]
    --help          show help information
    --format f      generate data in f format [options: csv|tab, default: " "]
`

var generatorsFlag = flag.Bool("generators", false, "list all the generators")
var tickFlag = flag.Duration("tick", 10*time.Millisecond, "generate data every d milliseconds")
var maxFlag = flag.Int("max", 10, "generate up to n rows")
var helpFlag = flag.Bool("help", false, "print usage")
var formatFlag = flag.String("format", "", "Output format")

func main() {
	if *helpFlag {
		fmt.Printf(usage)
		os.Exit(0)
	}

	if *generatorsFlag {
		generators := testdata.List()
		sort.Strings(generators)

		for _, name := range generators {
			fmt.Printf("%s\n", name)
		}
		os.Exit(0)
	}

	if len(flag.Args()) == 0 {
		fmt.Printf(usage)
		os.Exit(0)
	}

	rand.Seed(time.Now().UnixNano())

	tick := time.Tick(*tickFlag)

	total := 0

	for range tick {
		fmt.Print(testdata.GenerateRow(flag.Args(), *formatFlag))

		if total++; total == *maxFlag {
			return
		}
	}
}

func init() {
	flag.Parse()
}
<|endoftext|>