<commit_before>package gopool

import (
	"errors"
	"strings"
)

// ChanPool implements the Pool interface, maintaining a pool of resources.
type ChanPool struct {
	ch chan interface{}
	pc config
}

// New creates a new Pool. The factory method used to create new items for the Pool must be
// specified using the gopool.Factory method. Optionally, the pool size and a reset function can be
// specified.
//
//	package main
//
//	import (
//		"bytes"
//		"errors"
//		"fmt"
//		"log"
//		"math/rand"
//		"sync"
//
//		"github.com/karrick/gopool"
//	)
//
//	const (
//		bufSize  = 64 * 1024
//		poolSize = 25
//	)
//
//	func main() {
//		const iterationCount = 1000
//		const parallelCount = 100
//
//		makeBuffer := func() (interface{}, error) {
//			return bytes.NewBuffer(make([]byte, 0, bufSize)), nil
//		}
//
//		resetBuffer := func(item interface{}) {
//			item.(*bytes.Buffer).Reset()
//		}
//
//		bp, err := gopool.New(gopool.Size(poolSize), gopool.Factory(makeBuffer), gopool.Reset(resetBuffer))
//		if err != nil {
//			log.Fatal(err)
//		}
//
//		var wg sync.WaitGroup
//		wg.Add(parallelCount)
//
//		for i := 0; i < parallelCount; i++ {
//			go func() {
//				defer wg.Done()
//
//				for j := 0; j < iterationCount; j++ {
//					if err := grabBufferAndUseIt(bp); err != nil {
//						fmt.Println(err)
//						return
//					}
//				}
//			}()
//		}
//		wg.Wait()
//	}
//
//	func grabBufferAndUseIt(pool gopool.Pool) error {
//		// WARNING: Must ensure resource returns to pool otherwise gopool will deadlock once all
//		// resources used.
//		bb := pool.Get().(*bytes.Buffer)
//		defer pool.Put(bb) // IMPORTANT: defer here to ensure invoked even when subsequent code bails
//
//		for k := 0; k < bufSize; k++ {
//			if rand.Intn(100000000) == 1 {
//				return errors.New("random error to illustrate need to return resource to pool")
//			}
//			bb.WriteByte(byte(k % 256))
//		}
//		return nil
//	}
func New(setters ...Configurator) (Pool, error) {
	pc := &config{
		size: DefaultSize,
	}
	for _, setter := range setters {
		if err := setter(pc); err != nil {
			return nil, err
		}
	}
	if pc.factory == nil {
		return nil, errors.New("ought to specify factory method")
	}
	pool := &ChanPool{
		ch: make(chan interface{}, pc.size),
		pc: *pc,
	}
	for i := 0; i < pool.pc.size; i++ {
		item, err := pool.pc.factory()
		if err != nil {
			return nil, err
		}
		pool.ch <- item
	}
	return pool, nil
}

// Get acquires and returns an item from the pool of resources.
func (pool *ChanPool) Get() interface{} {
	return <-pool.ch
}

// Put will release a resource back to the pool.  If the Pool was initialized with a Reset function,
// it will be invoked with the resource as its sole argument, prior to the resource being added back
// to the pool.
func (pool *ChanPool) Put(item interface{}) {
	if pool.pc.reset != nil {
		pool.pc.reset(item)
	}
	pool.ch <- item
}

// Close is called when the Pool is no longer needed, and the resources in the Pool ought to be
// released.  If a Pool has a close function, it will be invoked one time for each resource, with
// that resource as its sole argument.
func (pool *ChanPool) Close() error {
	var errs []error
	for {
		select {
		case item := <-pool.ch:
			if pool.pc.close != nil {
				if err := pool.pc.close(item); err != nil {
					errs = append(errs, err)
				}
			}
		default:
			if len(errs) == 0 {
				return nil
			}
			var messages []string
			for _, err := range errs {
				messages = append(messages, err.Error())
			}
			return errors.New(strings.Join(messages, ", "))
		}
	}
}
<commit_msg>godoc comments warn user when they would block<commit_after>package gopool

import (
	"errors"
	"strings"
)

// ChanPool implements the Pool interface, maintaining a pool of resources.
type ChanPool struct {
	ch chan interface{}
	pc config
}

// New creates a new Pool. The factory method used to create new items for the Pool must be
// specified using the gopool.Factory method. Optionally, the pool size and a reset function can be
// specified.
//
//	package main
//
//	import (
//		"bytes"
//		"errors"
//		"fmt"
//		"log"
//		"math/rand"
//		"sync"
//
//		"github.com/karrick/gopool"
//	)
//
//	const (
//		bufSize  = 64 * 1024
//		poolSize = 25
//	)
//
//	func main() {
//		const iterationCount = 1000
//		const parallelCount = 100
//
//		makeBuffer := func() (interface{}, error) {
//			return bytes.NewBuffer(make([]byte, 0, bufSize)), nil
//		}
//
//		resetBuffer := func(item interface{}) {
//			item.(*bytes.Buffer).Reset()
//		}
//
//		bp, err := gopool.New(gopool.Size(poolSize), gopool.Factory(makeBuffer), gopool.Reset(resetBuffer))
//		if err != nil {
//			log.Fatal(err)
//		}
//
//		var wg sync.WaitGroup
//		wg.Add(parallelCount)
//
//		for i := 0; i < parallelCount; i++ {
//			go func() {
//				defer wg.Done()
//
//				for j := 0; j < iterationCount; j++ {
//					if err := grabBufferAndUseIt(bp); err != nil {
//						fmt.Println(err)
//						return
//					}
//				}
//			}()
//		}
//		wg.Wait()
//	}
//
//	func grabBufferAndUseIt(pool gopool.Pool) error {
//		// WARNING: Must ensure resource returns to pool otherwise gopool will deadlock once all
//		// resources used.
//		bb := pool.Get().(*bytes.Buffer)
//		defer pool.Put(bb) // IMPORTANT: defer here to ensure invoked even when subsequent code bails
//
//		for k := 0; k < bufSize; k++ {
//			if rand.Intn(100000000) == 1 {
//				return errors.New("random error to illustrate need to return resource to pool")
//			}
//			bb.WriteByte(byte(k % 256))
//		}
//		return nil
//	}
func New(setters ...Configurator) (Pool, error) {
	pc := &config{
		size: DefaultSize,
	}
	for _, setter := range setters {
		if err := setter(pc); err != nil {
			return nil, err
		}
	}
	if pc.factory == nil {
		return nil, errors.New("ought to specify factory method")
	}
	pool := &ChanPool{
		ch: make(chan interface{}, pc.size),
		pc: *pc,
	}
	for i := 0; i < pool.pc.size; i++ {
		item, err := pool.pc.factory()
		if err != nil {
			return nil, err
		}
		pool.ch <- item
	}
	return pool, nil
}

// Get acquires and returns an item from the pool of resources. Get blocks while there are no items in the pool.
func (pool *ChanPool) Get() interface{} {
	return <-pool.ch
}

// Put will release a resource back to the pool. Put blocks if pool already full. If the Pool was
// initialized with a Reset function, it will be invoked with the resource as its sole argument,
// prior to the resource being added back to the pool. If Put is called when adding the resource to
// the pool _would_ result in having more elements in the pool than the pool size, the resource is
// effectively dropped on the floor after calling any optional Reset and Close methods on the
// resource.
func (pool *ChanPool) Put(item interface{}) {
	if pool.pc.reset != nil {
		pool.pc.reset(item)
	}
	pool.ch <- item
}

// Close is called when the Pool is no longer needed, and the resources in the Pool ought to be
// released.  If a Pool has a close function, it will be invoked one time for each resource, with
// that resource as its sole argument.
func (pool *ChanPool) Close() error {
	var errs []error
	for {
		select {
		case item := <-pool.ch:
			if pool.pc.close != nil {
				if err := pool.pc.close(item); err != nil {
					errs = append(errs, err)
				}
			}
		default:
			if len(errs) == 0 {
				return nil
			}
			var messages []string
			for _, err := range errs {
				messages = append(messages, err.Error())
			}
			return errors.New(strings.Join(messages, ", "))
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Pilosa Corp.
//
// 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 pilosa

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/pilosa/pilosa/internal"
	"github.com/pkg/errors"
)

var schemeRegexp = regexp.MustCompile("^[+a-z]+$")
var hostRegexp = regexp.MustCompile("^[0-9a-z.-]+$|^\\[[:0-9a-fA-F]+\\]$")
var addressRegexp = regexp.MustCompile("^(([+a-z]+):\\/\\/)?([0-9a-z.-]+|\\[[:0-9a-fA-F]+\\])?(:([0-9]+))?$")

// URI represents a Pilosa URI.
// A Pilosa URI consists of three parts:
// 1) Scheme: Protocol of the URI. Default: http.
// 2) Host: Hostname or IP URI. Default: localhost. IPv6 addresses should be written in brackets, e.g., `[fd42:4201:f86b:7e09:216:3eff:fefa:ed80]`.
// 3) Port: Port of the URI. Default: 10101.
//
// All parts of the URI are optional. The following are equivalent:
// 	http://localhost:10101
// 	http://localhost
// 	http://:10101
// 	localhost:10101
// 	localhost
// 	:10101
type URI struct {
	scheme string `json:"scheme"`
	host   string `json:"host"`
	port   uint16 `json:"port"`
}

// DefaultURI creates and returns the default URI.
func DefaultURI() *URI {
	return &URI{
		scheme: "http",
		host:   "localhost",
		port:   10101,
	}
}

type URIs []URI

func (u URIs) HostPortStrings() []string {
	s := make([]string, len(u))
	for i, a := range u {
		s[i] = a.HostPort()
	}
	return s
}

// NewURIFromHostPort returns a URI with specified host and port.
func NewURIFromHostPort(host string, port uint16) (*URI, error) {
	uri := DefaultURI()
	err := uri.SetHost(host)
	if err != nil {
		return nil, errors.Wrap(err, "setting uri host")
	}
	uri.SetPort(port)
	return uri, nil
}

// NewURIFromAddress parses the passed address and returns a URI.
func NewURIFromAddress(address string) (*URI, error) {
	return parseAddress(address)
}

// Scheme returns the scheme of this URI.
func (u *URI) Scheme() string {
	return u.scheme
}

// SetScheme sets the scheme of this URI.
func (u *URI) SetScheme(scheme string) error {
	m := schemeRegexp.FindStringSubmatch(scheme)
	if m == nil {
		return errors.New("invalid scheme")
	}
	u.scheme = scheme
	return nil
}

// Host returns the host of this URI.
func (u *URI) Host() string {
	return u.host
}

// SetHost sets the host of this URI.
func (u *URI) SetHost(host string) error {
	m := hostRegexp.FindStringSubmatch(host)
	if m == nil {
		return errors.New("invalid host")
	}
	u.host = host
	return nil
}

// Port returns the port of this URI.
func (u *URI) Port() uint16 {
	return u.port
}

// SetPort sets the port of this URI.
func (u *URI) SetPort(port uint16) {
	u.port = port
}

// HostPort returns `Host:Port`
func (u *URI) HostPort() string {
	// XXX: The following is just to make TestHandler_Status; remove it
	if u == nil {
		return ""
	}
	s := fmt.Sprintf("%s:%d", u.host, u.port)
	return s
}

// Normalize returns the address in a form usable by a HTTP client.
func (u *URI) Normalize() string {
	scheme := u.scheme
	index := strings.Index(scheme, "+")
	if index >= 0 {
		scheme = scheme[:index]
	}
	return fmt.Sprintf("%s://%s:%d", scheme, u.host, u.port)
}

// String returns the address as a string.
func (u URI) String() string {
	return fmt.Sprintf("%s://%s:%d", u.scheme, u.host, u.port)
}

// Equals returns true if the checked URI is equivalent to this URI.
func (u URI) Equals(other *URI) bool {
	if other == nil {
		return false
	}
	return u == *other
}

// Path returns URI with path
func (u *URI) Path(path string) string {
	return fmt.Sprintf("%s%s", u.Normalize(), path)
}

// The following methods are required to implement pflag Value interface.

// Set sets the time quantum value.
func (u *URI) Set(value string) error {
	uri, err := NewURIFromAddress(value)
	if err != nil {
		return err
	}
	*u = *uri
	return nil
}

// Type returns the type of a time quantum value.
func (u URI) Type() string {
	return "URI"
}

func parseAddress(address string) (uri *URI, err error) {
	m := addressRegexp.FindStringSubmatch(address)
	if m == nil {
		return nil, errors.New("invalid address")
	}
	scheme := "http"
	if m[2] != "" {
		scheme = m[2]
	}
	host := "localhost"
	if m[3] != "" {
		host = m[3]
	}
	var port = 10101
	if m[5] != "" {
		port, err = strconv.Atoi(m[5])
		if err != nil {
			return nil, errors.New("converting port string to int")
		}
	}
	uri = &URI{
		scheme: scheme,
		host:   host,
		port:   uint16(port),
	}
	return uri, nil
}

// Encode converts o into its internal representation.
func (u URI) Encode() *internal.URI {
	return encodeURI(u)
}

func encodeURI(u URI) *internal.URI {
	return &internal.URI{
		Scheme: u.scheme,
		Host:   u.host,
		Port:   uint32(u.port),
	}
}

func DecodeURI(i *internal.URI) URI {
	return decodeURI(i)
}

func decodeURI(i *internal.URI) URI {
	if i == nil {
		return URI{}
	}
	return URI{
		scheme: i.Scheme,
		host:   i.Host,
		port:   uint16(i.Port),
	}
}

func encodeURIs(a []URI) []*internal.URI {
	if len(a) == 0 {
		return nil
	}
	other := make([]*internal.URI, len(a))
	for i := range a {
		other[i] = encodeURI(a[i])
	}
	return other
}

func decodeURIs(a []*internal.URI) []URI {
	if len(a) == 0 {
		return nil
	}
	other := make([]URI, len(a))
	for i := range a {
		other[i] = decodeURI(a[i])
	}
	return other
}

// MarshalJSON marshals URI into a JSON-encoded byte slice.
func (u *URI) MarshalJSON() ([]byte, error) {
	var output struct {
		Scheme string `json:"scheme,omitempty"`
		Host   string `json:"host,omitempty"`
		Port   uint16 `json:"port,omitempty"`
	}
	output.Scheme = u.scheme
	output.Host = u.host
	output.Port = u.port

	return json.Marshal(output)
}

func (u *URI) UnmarshalJSON(b []byte) error {
	var input struct {
		Scheme string `json:"scheme,omitempty"`
		Host   string `json:"host,omitempty"`
		Port   uint16 `json:"port,omitempty"`
	}
	if err := json.Unmarshal(b, &input); err != nil {
		return err
	}
	u.scheme = input.Scheme
	u.host = input.Host
	u.port = input.Port
	return nil
}
<commit_msg>remove encode/decodeURIs<commit_after>// Copyright 2017 Pilosa Corp.
//
// 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 pilosa

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/pilosa/pilosa/internal"
	"github.com/pkg/errors"
)

var schemeRegexp = regexp.MustCompile("^[+a-z]+$")
var hostRegexp = regexp.MustCompile("^[0-9a-z.-]+$|^\\[[:0-9a-fA-F]+\\]$")
var addressRegexp = regexp.MustCompile("^(([+a-z]+):\\/\\/)?([0-9a-z.-]+|\\[[:0-9a-fA-F]+\\])?(:([0-9]+))?$")

// URI represents a Pilosa URI.
// A Pilosa URI consists of three parts:
// 1) Scheme: Protocol of the URI. Default: http.
// 2) Host: Hostname or IP URI. Default: localhost. IPv6 addresses should be written in brackets, e.g., `[fd42:4201:f86b:7e09:216:3eff:fefa:ed80]`.
// 3) Port: Port of the URI. Default: 10101.
//
// All parts of the URI are optional. The following are equivalent:
// 	http://localhost:10101
// 	http://localhost
// 	http://:10101
// 	localhost:10101
// 	localhost
// 	:10101
type URI struct {
	scheme string `json:"scheme"`
	host   string `json:"host"`
	port   uint16 `json:"port"`
}

// DefaultURI creates and returns the default URI.
func DefaultURI() *URI {
	return &URI{
		scheme: "http",
		host:   "localhost",
		port:   10101,
	}
}

type URIs []URI

func (u URIs) HostPortStrings() []string {
	s := make([]string, len(u))
	for i, a := range u {
		s[i] = a.HostPort()
	}
	return s
}

// NewURIFromHostPort returns a URI with specified host and port.
func NewURIFromHostPort(host string, port uint16) (*URI, error) {
	uri := DefaultURI()
	err := uri.SetHost(host)
	if err != nil {
		return nil, errors.Wrap(err, "setting uri host")
	}
	uri.SetPort(port)
	return uri, nil
}

// NewURIFromAddress parses the passed address and returns a URI.
func NewURIFromAddress(address string) (*URI, error) {
	return parseAddress(address)
}

// Scheme returns the scheme of this URI.
func (u *URI) Scheme() string {
	return u.scheme
}

// SetScheme sets the scheme of this URI.
func (u *URI) SetScheme(scheme string) error {
	m := schemeRegexp.FindStringSubmatch(scheme)
	if m == nil {
		return errors.New("invalid scheme")
	}
	u.scheme = scheme
	return nil
}

// Host returns the host of this URI.
func (u *URI) Host() string {
	return u.host
}

// SetHost sets the host of this URI.
func (u *URI) SetHost(host string) error {
	m := hostRegexp.FindStringSubmatch(host)
	if m == nil {
		return errors.New("invalid host")
	}
	u.host = host
	return nil
}

// Port returns the port of this URI.
func (u *URI) Port() uint16 {
	return u.port
}

// SetPort sets the port of this URI.
func (u *URI) SetPort(port uint16) {
	u.port = port
}

// HostPort returns `Host:Port`
func (u *URI) HostPort() string {
	// XXX: The following is just to make TestHandler_Status; remove it
	if u == nil {
		return ""
	}
	s := fmt.Sprintf("%s:%d", u.host, u.port)
	return s
}

// Normalize returns the address in a form usable by a HTTP client.
func (u *URI) Normalize() string {
	scheme := u.scheme
	index := strings.Index(scheme, "+")
	if index >= 0 {
		scheme = scheme[:index]
	}
	return fmt.Sprintf("%s://%s:%d", scheme, u.host, u.port)
}

// String returns the address as a string.
func (u URI) String() string {
	return fmt.Sprintf("%s://%s:%d", u.scheme, u.host, u.port)
}

// Equals returns true if the checked URI is equivalent to this URI.
func (u URI) Equals(other *URI) bool {
	if other == nil {
		return false
	}
	return u == *other
}

// Path returns URI with path
func (u *URI) Path(path string) string {
	return fmt.Sprintf("%s%s", u.Normalize(), path)
}

// The following methods are required to implement pflag Value interface.

// Set sets the time quantum value.
func (u *URI) Set(value string) error {
	uri, err := NewURIFromAddress(value)
	if err != nil {
		return err
	}
	*u = *uri
	return nil
}

// Type returns the type of a time quantum value.
func (u URI) Type() string {
	return "URI"
}

func parseAddress(address string) (uri *URI, err error) {
	m := addressRegexp.FindStringSubmatch(address)
	if m == nil {
		return nil, errors.New("invalid address")
	}
	scheme := "http"
	if m[2] != "" {
		scheme = m[2]
	}
	host := "localhost"
	if m[3] != "" {
		host = m[3]
	}
	var port = 10101
	if m[5] != "" {
		port, err = strconv.Atoi(m[5])
		if err != nil {
			return nil, errors.New("converting port string to int")
		}
	}
	uri = &URI{
		scheme: scheme,
		host:   host,
		port:   uint16(port),
	}
	return uri, nil
}

// Encode converts o into its internal representation.
func (u URI) Encode() *internal.URI {
	return encodeURI(u)
}

func encodeURI(u URI) *internal.URI {
	return &internal.URI{
		Scheme: u.scheme,
		Host:   u.host,
		Port:   uint32(u.port),
	}
}

func DecodeURI(i *internal.URI) URI {
	return decodeURI(i)
}

func decodeURI(i *internal.URI) URI {
	if i == nil {
		return URI{}
	}
	return URI{
		scheme: i.Scheme,
		host:   i.Host,
		port:   uint16(i.Port),
	}
}

// MarshalJSON marshals URI into a JSON-encoded byte slice.
func (u *URI) MarshalJSON() ([]byte, error) {
	var output struct {
		Scheme string `json:"scheme,omitempty"`
		Host   string `json:"host,omitempty"`
		Port   uint16 `json:"port,omitempty"`
	}
	output.Scheme = u.scheme
	output.Host = u.host
	output.Port = u.port

	return json.Marshal(output)
}

func (u *URI) UnmarshalJSON(b []byte) error {
	var input struct {
		Scheme string `json:"scheme,omitempty"`
		Host   string `json:"host,omitempty"`
		Port   uint16 `json:"port,omitempty"`
	}
	if err := json.Unmarshal(b, &input); err != nil {
		return err
	}
	u.scheme = input.Scheme
	u.host = input.Host
	u.port = input.Port
	return nil
}
<|endoftext|>


<commit_before>package main

// URL matching and regular expressions

import (
	"log"
	"net/url"
	"regexp"
	"strings"

	"golang.org/x/net/idna"
	"golang.org/x/net/publicsuffix"
)

type regexRule struct {
	rule
	*regexp.Regexp
}

// A regexMap is a set of regular-expression rules.
// As an optimization, it uses Aho-Corasick string matching find which regular
// expressions might match—instead of trying them all.
type regexMap struct {
	stringList phraseList
	rules      map[string][]regexRule
}

func newRegexMap() *regexMap {
	return &regexMap{
		stringList: newPhraseList(),
		rules:      make(map[string][]regexRule),
	}
}

func (rm *regexMap) findMatches(s string, tally map[rule]int) {
	if len(rm.rules) == 0 {
		return
	}

	tried := map[string]bool{}
	scanner := newPhraseScanner(rm.stringList, func(p string) {
		if tried[p] {
			return
		}
		for _, r := range rm.rules[p] {
			if r.MatchString(s) {
				tally[r.rule] = 1
			}
		}
		tried[p] = true
	})

	for i := 0; i < len(s); i++ {
		scanner.scanByte(s[i])
	}

	// Now try the regexes that have no distinctive literal string component.
	for _, r := range rm.rules[""] {
		if r.MatchString(s) {
			tally[r.rule] = 1
		}
	}
}

// addRule adds a rule to the map.
func (rm *regexMap) addRule(r rule) {
	s := r.content

	re, err := regexp.Compile(s)
	if err != nil {
		log.Printf("Error parsing URL regular expression %s: %v", r, err)
		return
	}

	ss, err := regexStrings(s)
	if err != nil || ss.minLen() == 0 {
		// Store this rule in the list of rules without a literal string component.
		rm.rules[""] = append(rm.rules[""], regexRule{r, re})
		return
	}

	for _, p := range ss {
		rm.stringList.addPhrase(p)
		rm.rules[p] = append(rm.rules[p], regexRule{r, re})
	}
}

type URLMatcher struct {
	fragments     map[string]rule // a set of domain or domain+path URL fragments to test against
	regexes       *regexMap       // to match whole URL
	hostRegexes   *regexMap       // to match hostname only
	domainRegexes *regexMap
	pathRegexes   *regexMap
	queryRegexes  *regexMap
}

// finalize should be called after all rules have been added, but before
// using the URLMatcher.
func (m *URLMatcher) finalize() {
	m.regexes.stringList.findFallbackNodes(0, nil)
	m.hostRegexes.stringList.findFallbackNodes(0, nil)
	m.domainRegexes.stringList.findFallbackNodes(0, nil)
	m.pathRegexes.stringList.findFallbackNodes(0, nil)
	m.queryRegexes.stringList.findFallbackNodes(0, nil)
}

func newURLMatcher() *URLMatcher {
	m := new(URLMatcher)
	m.fragments = make(map[string]rule)
	m.regexes = newRegexMap()
	m.hostRegexes = newRegexMap()
	m.domainRegexes = newRegexMap()
	m.pathRegexes = newRegexMap()
	m.queryRegexes = newRegexMap()
	return m
}

// AddRule adds a rule to the matcher (unless it's already there).
func (m *URLMatcher) AddRule(r rule) {
	switch r.t {
	case urlMatch:
		m.fragments[r.content] = r
	case urlRegex:
		m.regexes.addRule(r)
	case hostRegex:
		m.hostRegexes.addRule(r)
	case domainRegex:
		m.domainRegexes.addRule(r)
	case pathRegex:
		m.pathRegexes.addRule(r)
	case queryRegex:
		m.queryRegexes.addRule(r)
	}
}

// MatchingRules returns a list of the rules that u matches.
// For consistency with phrase matching, it is a map with rules for keys
// and with all values equal to 1.
func (m *URLMatcher) MatchingRules(u *url.URL) map[rule]int {
	result := make(map[rule]int)

	host := strings.ToLower(u.Host)

	// strip off the port number, if present
	colon := strings.LastIndex(host, ":")
	// IPv6 addresses contain colons inside brackets, so be careful.
	if colon != -1 && !strings.Contains(host[colon:], "]") {
		host = host[:colon]
	}

	// Find the main domain name (e.g. "google" in "www.google.com").
	suffix := publicsuffix.List.PublicSuffix(host)
	if suffix != "" && suffix != host {
		domain := host[:len(host)-len(suffix)-1]
		dot := strings.LastIndex(domain, ".")
		if dot != -1 {
			domain = domain[dot+1:]
		}
		if idn, err := idna.ToUnicode(domain); err == nil {
			domain = idn
		}
		m.domainRegexes.findMatches(domain, result)
	}

	if idn, err := idna.ToUnicode(host); err == nil {
		host = idn
	}

	urlString := ""
	if u.Scheme != "" {
		urlString += strings.ToLower(u.Scheme) + ":"
	}
	if host != "" {
		urlString += "//" + host
		m.hostRegexes.findMatches(host, result)
	}

	path := strings.ToLower(u.Path)
	m.pathRegexes.findMatches(path, result)
	urlString += path

	query := strings.ToLower(u.RawQuery)
	if query != "" {
		q, err := url.QueryUnescape(query)
		if err == nil {
			// Change ' ' back to '+'.
			query = strings.Replace(q, " ", "+", -1)
		}
		m.queryRegexes.findMatches(query, result)
		urlString += "?" + query
	}

	m.regexes.findMatches(urlString, result)

	// Test for matches of the host and of the domains it belongs to.
	s := host
	for {
		// Test for matches with the path.
		s2 := s + path
		for {
			if r, ok := m.fragments[s2]; ok {
				result[r] = 1
			}
			slash := strings.LastIndex(s2, "/")
			if slash < 1 {
				// It's either not found, or at the first character.
				break
			}
			s2 = s2[:slash]
		}

		if r, ok := m.fragments[s]; ok {
			result[r] = 1
		}
		dot := strings.Index(s, ".")
		if dot == -1 {
			break
		}
		s = s[dot+1:]
	}

	return result
}
<commit_msg>Ignore trailing period in hostnames.<commit_after>package main

// URL matching and regular expressions

import (
	"log"
	"net/url"
	"regexp"
	"strings"

	"golang.org/x/net/idna"
	"golang.org/x/net/publicsuffix"
)

type regexRule struct {
	rule
	*regexp.Regexp
}

// A regexMap is a set of regular-expression rules.
// As an optimization, it uses Aho-Corasick string matching find which regular
// expressions might match—instead of trying them all.
type regexMap struct {
	stringList phraseList
	rules      map[string][]regexRule
}

func newRegexMap() *regexMap {
	return &regexMap{
		stringList: newPhraseList(),
		rules:      make(map[string][]regexRule),
	}
}

func (rm *regexMap) findMatches(s string, tally map[rule]int) {
	if len(rm.rules) == 0 {
		return
	}

	tried := map[string]bool{}
	scanner := newPhraseScanner(rm.stringList, func(p string) {
		if tried[p] {
			return
		}
		for _, r := range rm.rules[p] {
			if r.MatchString(s) {
				tally[r.rule] = 1
			}
		}
		tried[p] = true
	})

	for i := 0; i < len(s); i++ {
		scanner.scanByte(s[i])
	}

	// Now try the regexes that have no distinctive literal string component.
	for _, r := range rm.rules[""] {
		if r.MatchString(s) {
			tally[r.rule] = 1
		}
	}
}

// addRule adds a rule to the map.
func (rm *regexMap) addRule(r rule) {
	s := r.content

	re, err := regexp.Compile(s)
	if err != nil {
		log.Printf("Error parsing URL regular expression %s: %v", r, err)
		return
	}

	ss, err := regexStrings(s)
	if err != nil || ss.minLen() == 0 {
		// Store this rule in the list of rules without a literal string component.
		rm.rules[""] = append(rm.rules[""], regexRule{r, re})
		return
	}

	for _, p := range ss {
		rm.stringList.addPhrase(p)
		rm.rules[p] = append(rm.rules[p], regexRule{r, re})
	}
}

type URLMatcher struct {
	fragments     map[string]rule // a set of domain or domain+path URL fragments to test against
	regexes       *regexMap       // to match whole URL
	hostRegexes   *regexMap       // to match hostname only
	domainRegexes *regexMap
	pathRegexes   *regexMap
	queryRegexes  *regexMap
}

// finalize should be called after all rules have been added, but before
// using the URLMatcher.
func (m *URLMatcher) finalize() {
	m.regexes.stringList.findFallbackNodes(0, nil)
	m.hostRegexes.stringList.findFallbackNodes(0, nil)
	m.domainRegexes.stringList.findFallbackNodes(0, nil)
	m.pathRegexes.stringList.findFallbackNodes(0, nil)
	m.queryRegexes.stringList.findFallbackNodes(0, nil)
}

func newURLMatcher() *URLMatcher {
	m := new(URLMatcher)
	m.fragments = make(map[string]rule)
	m.regexes = newRegexMap()
	m.hostRegexes = newRegexMap()
	m.domainRegexes = newRegexMap()
	m.pathRegexes = newRegexMap()
	m.queryRegexes = newRegexMap()
	return m
}

// AddRule adds a rule to the matcher (unless it's already there).
func (m *URLMatcher) AddRule(r rule) {
	switch r.t {
	case urlMatch:
		m.fragments[r.content] = r
	case urlRegex:
		m.regexes.addRule(r)
	case hostRegex:
		m.hostRegexes.addRule(r)
	case domainRegex:
		m.domainRegexes.addRule(r)
	case pathRegex:
		m.pathRegexes.addRule(r)
	case queryRegex:
		m.queryRegexes.addRule(r)
	}
}

// MatchingRules returns a list of the rules that u matches.
// For consistency with phrase matching, it is a map with rules for keys
// and with all values equal to 1.
func (m *URLMatcher) MatchingRules(u *url.URL) map[rule]int {
	result := make(map[rule]int)

	host := strings.ToLower(u.Host)

	// strip off the port number, if present
	colon := strings.LastIndex(host, ":")
	// IPv6 addresses contain colons inside brackets, so be careful.
	if colon != -1 && !strings.Contains(host[colon:], "]") {
		host = host[:colon]
	}

	host = strings.TrimSuffix(host, ".")

	// Find the main domain name (e.g. "google" in "www.google.com").
	suffix := publicsuffix.List.PublicSuffix(host)
	if suffix != "" && suffix != host {
		domain := host[:len(host)-len(suffix)-1]
		dot := strings.LastIndex(domain, ".")
		if dot != -1 {
			domain = domain[dot+1:]
		}
		if idn, err := idna.ToUnicode(domain); err == nil {
			domain = idn
		}
		m.domainRegexes.findMatches(domain, result)
	}

	if idn, err := idna.ToUnicode(host); err == nil {
		host = idn
	}

	urlString := ""
	if u.Scheme != "" {
		urlString += strings.ToLower(u.Scheme) + ":"
	}
	if host != "" {
		urlString += "//" + host
		m.hostRegexes.findMatches(host, result)
	}

	path := strings.ToLower(u.Path)
	m.pathRegexes.findMatches(path, result)
	urlString += path

	query := strings.ToLower(u.RawQuery)
	if query != "" {
		q, err := url.QueryUnescape(query)
		if err == nil {
			// Change ' ' back to '+'.
			query = strings.Replace(q, " ", "+", -1)
		}
		m.queryRegexes.findMatches(query, result)
		urlString += "?" + query
	}

	m.regexes.findMatches(urlString, result)

	// Test for matches of the host and of the domains it belongs to.
	s := host
	for {
		// Test for matches with the path.
		s2 := s + path
		for {
			if r, ok := m.fragments[s2]; ok {
				result[r] = 1
			}
			slash := strings.LastIndex(s2, "/")
			if slash < 1 {
				// It's either not found, or at the first character.
				break
			}
			s2 = s2[:slash]
		}

		if r, ok := m.fragments[s]; ok {
			result[r] = 1
		}
		dot := strings.Index(s, ".")
		if dot == -1 {
			break
		}
		s = s[dot+1:]
	}

	return result
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"fmt"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
)

func performChecksum(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
	}

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

	files, _ := ioutil.ReadDir(filepath)
	for _, file := range files {
		if file.IsDir() {
			processPath(path.Join(filepath, file.Name()), file.Name(), c.GlobalBool("delete"))
		}
	}
}

func processPath(directory string, name string, deleteMode bool) {
	filename := path.Join(directory, name+".")

	if deleteMode {
		removeFile(filename + "ffp")
		removeFile(filename + "md5")
		return
	}

	ffp := createFile(filename + "ffp")
	processDirectory(directory, 1, ffp, "ffp")
	ffp.Close()

	md5 := createFile(filename + "md5")
	processDirectory(directory, 1, md5, "md5")
	md5.Close()
}

func processDirectory(filepath string, depth int, out *os.File, mode string) {
	files, _ := ioutil.ReadDir(filepath)
	if len(files) == 0 {
		if mode == "ffp" {
			fmt.Println("Empty folder found:", filepath)
		}
		return
	}

	var parser func(string, string, int) string
	if mode == "ffp" {
		parser = ffpParse
	} else if mode == "md5" {
		parser = md5Parse
	}

	for _, file := range files {
		name := file.Name()

		if file.IsDir() {
			processDirectory(path.Join(filepath, name), depth+1, out, mode)
		} else if (path.Ext(name) != ".md5") && !file.IsDir() {
			if result := parser(filepath, name, depth); result != "" {
				out.WriteString(result)
			}
		}
	}
}

func md5Parse(filepath string, name string, depth int) string {
	data, err := ioutil.ReadFile(path.Join(filepath, name))

	if err != nil {
		panic(err)
	}

	return fmt.Sprintf("%x *%s%s\r\n", md5.Sum(data), getLastPathComponents(filepath, depth), name)
}

func ffpParse(filepath string, name string, depth int) string {
	if path.Ext(name) != ".flac" {
		return ""
	}

	data, err := exec.Command(
		"metaflac",
		"--show-md5sum",
		path.Join(filepath, name),
	).Output()

	if err != nil {
		panic(err)
	}

	return fmt.Sprintf("%s%s:%s", getLastPathComponents(filepath, depth), name, data)
}
<commit_msg>Let 'checksum' work for files right inside folder<commit_after>package main

import (
	"crypto/md5"
	"fmt"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
)

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

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

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

	if !shouldContinue(c) {
		return
	}

	processPath(filepath, fileInfo.Name(), c.GlobalBool("delete"), singleMode)

	if !singleMode {
		files, _ := ioutil.ReadDir(filepath)
		for _, file := range files {
			if file.IsDir() {
				processPath(path.Join(filepath, file.Name()), file.Name(), c.GlobalBool("delete"), true)
			}
		}
	}
}

func processPath(directory string, name string, deleteMode bool, singleMode bool) {
	filename := path.Join(directory, name+".")

	depth := 0
	if singleMode {
		depth = 1
	}

	if deleteMode {
		removeFile(filename + "ffp")
		removeFile(filename + "md5")
		return
	}

	ffp := createFile(filename + "ffp")
	processDirectory(directory, depth, ffp, "ffp")
	ffp.Close()

	md5 := createFile(filename + "md5")
	processDirectory(directory, depth, md5, "md5")
	md5.Close()
}

func processDirectory(filepath string, depth int, out *os.File, mode string) {
	files, _ := ioutil.ReadDir(filepath)
	if len(files) == 0 {
		if mode == "ffp" {
			fmt.Println("Empty folder found:", filepath)
		}
		return
	}

	var parser func(string, string, int) string
	if mode == "ffp" {
		parser = ffpParse
	} else if mode == "md5" {
		parser = md5Parse
	}

	for _, file := range files {
		name := file.Name()

		if file.IsDir() && depth != 0 {
			processDirectory(path.Join(filepath, name), depth+1, out, mode)
		} else if (path.Ext(name) != ".md5") && !file.IsDir() {
			if result := parser(filepath, name, depth); result != "" {
				out.WriteString(result)
			}
		}
	}
}

func md5Parse(filepath string, name string, depth int) string {
	data, err := ioutil.ReadFile(path.Join(filepath, name))

	if err != nil {
		panic(err)
	}

	return fmt.Sprintf("%x *%s%s\r\n", md5.Sum(data), getLastPathComponents(filepath, depth), name)
}

func ffpParse(filepath string, name string, depth int) string {
	if path.Ext(name) != ".flac" {
		return ""
	}

	data, err := exec.Command(
		"metaflac",
		"--show-md5sum",
		path.Join(filepath, name),
	).Output()

	if err != nil {
		panic(err)
	}

	return fmt.Sprintf("%s%s:%s", getLastPathComponents(filepath, depth), name, data)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	bleed "github.com/FiloSottile/Heartbleed/bleed"
	"log"
	"net/http"
	"strings"
)

var PAYLOAD = []byte("heartbleed.filippo.io")

func defaultHandler(w http.ResponseWriter, r *http.Request) {
	http.Redirect(w, r, "http://filippo.io/Heartbleed", http.StatusFound)
}

func testHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Hi there.")
}

type result struct {
	Code int    `json:"code"`
	Data string `json:"data"`
}

func bleedHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	host := r.URL.Path[len("/bleed/"):]
	if strings.Index(host, ":") == -1 {
		host = host + ":443"
	}
	data, err := bleed.Heartbleed(string(host), PAYLOAD)
	var rc int
	if err == bleed.ErrPayloadNotFound {
		rc = 1
		data = []byte("")
		log.Printf("%v - SAFE", host)
	} else if err != nil {
		rc = 2
		data = []byte("")
		log.Printf("%v - ERROR", host)
	} else {
		rc = 0
		log.Printf("%v - VULNERABLE", host)
	}
	res := result{rc, string(data)}
	j, err := json.Marshal(res)
	if err != nil {
		log.Println("ERROR", err)
	}
	w.Write(j)
}

func main() {
	http.HandleFunc("/", defaultHandler)
	http.HandleFunc("/test", testHandler)
	http.HandleFunc("/bleed/", bleedHandler)
	http.ListenAndServe(":8080", nil)
}
<commit_msg>correct eror handling<commit_after>package main

import (
	"encoding/json"
	"fmt"
	bleed "github.com/FiloSottile/Heartbleed/bleed"
	"log"
	"net/http"
	"strings"
)

var PAYLOAD = []byte("heartbleed.filippo.io")

func defaultHandler(w http.ResponseWriter, r *http.Request) {
	http.Redirect(w, r, "http://filippo.io/Heartbleed", http.StatusFound)
}

func testHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Hi there.")
}

type result struct {
	Code int    `json:"code"`
	Data string `json:"data"`
}

func bleedHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	host := r.URL.Path[len("/bleed/"):]
	if strings.Index(host, ":") == -1 {
		host = host + ":443"
	}
	data, err := bleed.Heartbleed(string(host), PAYLOAD)
	var rc int
	if err == bleed.ErrPayloadNotFound {
		rc = 1
		data = []byte("")
		log.Printf("%v - SAFE", host)
	} else if err != nil {
		rc = 2
		data = []byte("")
		log.Printf("%v - ERROR", host)
	} else {
		rc = 0
		log.Printf("%v - VULNERABLE", host)
	}
	res := result{rc, string(data)}
	j, err := json.Marshal(res)
	if err != nil {
		log.Println("ERROR", err)
	} else {
		w.Write(j)
	}
}

func main() {
	http.HandleFunc("/", defaultHandler)
	http.HandleFunc("/test", testHandler)
	http.HandleFunc("/bleed/", bleedHandler)
	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>package ui

import (
	"fmt"
	"strings"

	"github.com/elves/elvish/util"
)

// Cell is an indivisible unit on the screen. It is not necessarily 1 column
// wide.
type Cell struct {
	Text  string
	Style string
}

// Pos is the position within a buffer.
type Pos struct {
	Line, Col int
}

// CellsWidth returns the total width of a Cell slice.
func CellsWidth(cs []Cell) int {
	w := 0
	for _, c := range cs {
		w += util.Wcswidth(c.Text)
	}
	return w
}

// CompareCells returns whether two Cell slices are equal, and when they are
// not, the first index at which they differ.
func CompareCells(r1, r2 []Cell) (bool, int) {
	for i, c := range r1 {
		if i >= len(r2) || c != r2[i] {
			return false, i
		}
	}
	if len(r1) < len(r2) {
		return false, len(r1)
	}
	return true, 0
}

// Buffer reflects a continuous range of lines on the terminal.
//
// The Unix terminal API provides only awkward ways of querying the terminal
// Buffer, so we keep an internal reflection and do one-way synchronizations
// (Buffer -> terminal, and not the other way around). This requires us to
// exactly match the terminal's idea of the width of characters (wcwidth) and
// where to insert soft carriage returns, so there could be bugs.
type Buffer struct {
	Width int
	// Lines the content of the buffer.
	Lines Lines
	// Dot is what the user perceives as the cursor.
	Dot Pos
}

// Lines stores multiple lines.
type Lines [][]Cell

// Line stores a single line.
type Line []Cell

// NewBuffer builds a new buffer, with one empty line.
func NewBuffer(width int) *Buffer {
	return &Buffer{Width: width, Lines: [][]Cell{make([]Cell, 0, width)}}
}

// Col returns the column the cursor is in.
func (b *Buffer) Col() int {
	return CellsWidth(b.Lines[len(b.Lines)-1])
}

// Cursor returns the current position of the cursor.
func (b *Buffer) Cursor() Pos {
	return Pos{len(b.Lines) - 1, b.Col()}
}

// BuffersHeight computes the combined height of a number of buffers.
func BuffersHeight(bufs ...*Buffer) (l int) {
	for _, buf := range bufs {
		if buf != nil {
			l += len(buf.Lines)
		}
	}
	return
}

// TrimToLines trims a buffer to the lines [low, high).
func (b *Buffer) TrimToLines(low, high int) {
	if low < 0 {
		low = 0
	}
	if high > len(b.Lines) {
		high = len(b.Lines)
	}
	for i := 0; i < low; i++ {
		b.Lines[i] = nil
	}
	for i := high; i < len(b.Lines); i++ {
		b.Lines[i] = nil
	}
	b.Lines = b.Lines[low:high]
	b.Dot.Line -= low
	if b.Dot.Line < 0 {
		b.Dot.Line = 0
	}
}

// Extend adds all lines from b2 to the bottom of this buffer. If moveDot is
// true, the dot is updated to match the dot of b2.
func (b *Buffer) Extend(b2 *Buffer, moveDot bool) {
	if b2 != nil && b2.Lines != nil {
		if moveDot {
			b.Dot.Line = b2.Dot.Line + len(b.Lines)
			b.Dot.Col = b2.Dot.Col
		}
		b.Lines = append(b.Lines, b2.Lines...)
	}
}

// ExtendRight extends bb to the right. It pads each line in b to be b.Width and
// appends the corresponding line in b2 to it, making new lines when b2 has more
// lines than bb.
func (b *Buffer) ExtendRight(b2 *Buffer) {
	i := 0
	w := b.Width
	b.Width += b2.Width
	for ; i < len(b.Lines) && i < len(b2.Lines); i++ {
		if w0 := CellsWidth(b.Lines[i]); w0 < w {
			b.Lines[i] = append(b.Lines[i], makeSpacing(w-w0)...)
		}
		b.Lines[i] = append(b.Lines[i], b2.Lines[i]...)
	}
	for ; i < len(b2.Lines); i++ {
		row := append(makeSpacing(w), b2.Lines[i]...)
		b.Lines = append(b.Lines, row)
	}
}

// Buffer returns itself.
func (b *Buffer) Buffer() *Buffer { return b }

// TTYString returns a string for representing the buffer on the terminal.
func (b *Buffer) TTYString() string {
	if b == nil {
		return "nil"
	}
	sb := new(strings.Builder)
	fmt.Fprintf(sb, "Width = %d, Dot = (%d, %d)\n", b.Width, b.Dot.Line, b.Dot.Col)
	// Top border
	sb.WriteString("┌" + strings.Repeat("─", b.Width) + "┐\n")
	for _, line := range b.Lines {
		// Left border
		sb.WriteRune('│')
		// Content
		lastStyle := ""
		usedWidth := 0
		for _, cell := range line {
			if cell.Style != lastStyle {
				if lastStyle == "" {
					sb.WriteString("\033[" + cell.Style + "m")
				} else {
					sb.WriteString("\033[;" + cell.Style + "m")
				}
				lastStyle = cell.Style
			}
			sb.WriteString(cell.Text)
			usedWidth += util.Wcswidth(cell.Text)
		}
		if lastStyle != "" {
			sb.WriteString("\033[m")
		}
		if usedWidth < b.Width {
			sb.WriteString("$" + strings.Repeat(" ", b.Width-usedWidth-1))
		}
		// Right border and newline
		sb.WriteString("│\n")
	}
	// Bottom border
	sb.WriteString("└" + strings.Repeat("─", b.Width) + "┘\n")
	return sb.String()
}
<commit_msg>edit/ui: Avoid writing "\033[;m" in (*Buffer).TTYString.<commit_after>package ui

import (
	"fmt"
	"strings"

	"github.com/elves/elvish/util"
)

// Cell is an indivisible unit on the screen. It is not necessarily 1 column
// wide.
type Cell struct {
	Text  string
	Style string
}

// Pos is the position within a buffer.
type Pos struct {
	Line, Col int
}

// CellsWidth returns the total width of a Cell slice.
func CellsWidth(cs []Cell) int {
	w := 0
	for _, c := range cs {
		w += util.Wcswidth(c.Text)
	}
	return w
}

// CompareCells returns whether two Cell slices are equal, and when they are
// not, the first index at which they differ.
func CompareCells(r1, r2 []Cell) (bool, int) {
	for i, c := range r1 {
		if i >= len(r2) || c != r2[i] {
			return false, i
		}
	}
	if len(r1) < len(r2) {
		return false, len(r1)
	}
	return true, 0
}

// Buffer reflects a continuous range of lines on the terminal.
//
// The Unix terminal API provides only awkward ways of querying the terminal
// Buffer, so we keep an internal reflection and do one-way synchronizations
// (Buffer -> terminal, and not the other way around). This requires us to
// exactly match the terminal's idea of the width of characters (wcwidth) and
// where to insert soft carriage returns, so there could be bugs.
type Buffer struct {
	Width int
	// Lines the content of the buffer.
	Lines Lines
	// Dot is what the user perceives as the cursor.
	Dot Pos
}

// Lines stores multiple lines.
type Lines [][]Cell

// Line stores a single line.
type Line []Cell

// NewBuffer builds a new buffer, with one empty line.
func NewBuffer(width int) *Buffer {
	return &Buffer{Width: width, Lines: [][]Cell{make([]Cell, 0, width)}}
}

// Col returns the column the cursor is in.
func (b *Buffer) Col() int {
	return CellsWidth(b.Lines[len(b.Lines)-1])
}

// Cursor returns the current position of the cursor.
func (b *Buffer) Cursor() Pos {
	return Pos{len(b.Lines) - 1, b.Col()}
}

// BuffersHeight computes the combined height of a number of buffers.
func BuffersHeight(bufs ...*Buffer) (l int) {
	for _, buf := range bufs {
		if buf != nil {
			l += len(buf.Lines)
		}
	}
	return
}

// TrimToLines trims a buffer to the lines [low, high).
func (b *Buffer) TrimToLines(low, high int) {
	if low < 0 {
		low = 0
	}
	if high > len(b.Lines) {
		high = len(b.Lines)
	}
	for i := 0; i < low; i++ {
		b.Lines[i] = nil
	}
	for i := high; i < len(b.Lines); i++ {
		b.Lines[i] = nil
	}
	b.Lines = b.Lines[low:high]
	b.Dot.Line -= low
	if b.Dot.Line < 0 {
		b.Dot.Line = 0
	}
}

// Extend adds all lines from b2 to the bottom of this buffer. If moveDot is
// true, the dot is updated to match the dot of b2.
func (b *Buffer) Extend(b2 *Buffer, moveDot bool) {
	if b2 != nil && b2.Lines != nil {
		if moveDot {
			b.Dot.Line = b2.Dot.Line + len(b.Lines)
			b.Dot.Col = b2.Dot.Col
		}
		b.Lines = append(b.Lines, b2.Lines...)
	}
}

// ExtendRight extends bb to the right. It pads each line in b to be b.Width and
// appends the corresponding line in b2 to it, making new lines when b2 has more
// lines than bb.
func (b *Buffer) ExtendRight(b2 *Buffer) {
	i := 0
	w := b.Width
	b.Width += b2.Width
	for ; i < len(b.Lines) && i < len(b2.Lines); i++ {
		if w0 := CellsWidth(b.Lines[i]); w0 < w {
			b.Lines[i] = append(b.Lines[i], makeSpacing(w-w0)...)
		}
		b.Lines[i] = append(b.Lines[i], b2.Lines[i]...)
	}
	for ; i < len(b2.Lines); i++ {
		row := append(makeSpacing(w), b2.Lines[i]...)
		b.Lines = append(b.Lines, row)
	}
}

// Buffer returns itself.
func (b *Buffer) Buffer() *Buffer { return b }

// TTYString returns a string for representing the buffer on the terminal.
func (b *Buffer) TTYString() string {
	if b == nil {
		return "nil"
	}
	sb := new(strings.Builder)
	fmt.Fprintf(sb, "Width = %d, Dot = (%d, %d)\n", b.Width, b.Dot.Line, b.Dot.Col)
	// Top border
	sb.WriteString("┌" + strings.Repeat("─", b.Width) + "┐\n")
	for _, line := range b.Lines {
		// Left border
		sb.WriteRune('│')
		// Content
		lastStyle := ""
		usedWidth := 0
		for _, cell := range line {
			if cell.Style != lastStyle {
				switch {
				case lastStyle == "":
					sb.WriteString("\033[" + cell.Style + "m")
				case cell.Style == "":
					sb.WriteString("\033[m")
				default:
					sb.WriteString("\033[;" + cell.Style + "m")
				}
				lastStyle = cell.Style
			}
			sb.WriteString(cell.Text)
			usedWidth += util.Wcswidth(cell.Text)
		}
		if lastStyle != "" {
			sb.WriteString("\033[m")
		}
		if usedWidth < b.Width {
			sb.WriteString("$" + strings.Repeat(" ", b.Width-usedWidth-1))
		}
		// Right border and newline
		sb.WriteString("│\n")
	}
	// Bottom border
	sb.WriteString("└" + strings.Repeat("─", b.Width) + "┘\n")
	return sb.String()
}
<|endoftext|>


<commit_before>package edn

import . "testing"

func TestEmptyGivesOnlyEOF(t *T) {
	lexer := Lex("")
	token, _ := lexer.Next()

	if token.kind != tEOF {
		t.Error("expecting EOF")
	}
}

// I suspect there's a potential race condition here first since
// the lexer is in a different thread. If `Next()` is called while the lexer
// is still in its main `for{}` loop, `done` could still be `false`
func TestEmptyIsDoneAfterFirstToken(t *T) {
	lexer := Lex("")
	_, done := lexer.Next()

	if !done {
		t.Error("expecting no more tokens")
	}
}
<commit_msg>Add a failing test for next step in lexer implementation<commit_after>package edn

import . "testing"

func TestEmptyGivesOnlyEOF(t *T) {
	lexer := Lex("")
	token, _ := lexer.Next()

	if token.kind != tEOF {
		t.Error("expecting EOF")
	}
}

// I suspect there's a potential race condition here first since
// the lexer is in a different thread. If `Next()` is called while the lexer
// is still in its main `for{}` loop, `done` could still be `false`
func TestEmptyIsDoneAfterFirstToken(t *T) {
	lexer := Lex("")
	_, done := lexer.Next()

	if !done {
		t.Error("expecting no more tokens")
	}
}

func TestOpenCloseParens(t *T) {
	lexer := Lex("()")

	token, _ := lexer.Next()
	if token.kind != tOpenParen {
		t.Error("expecting open parenthesis")
	}

	token, _ = lexer.Next()
	if token.kind != tCloseParen {
		t.Error("expecting close parenthesis")
	}

	token, _ = lexer.Next()
	if token.kind != tEOF {
		t.Error("expecting EOF")
	}
}
<|endoftext|>


<commit_before>package main

import (
	//	"database/sql"
	"html/template"
	//	"log"
	"net/http"
	"strings"
)

type Zone struct {
	ZoneAbbr string
	ZoneDisp string
}

type Attrib struct {
	AttAbbr string
	AttDisp string
}

type Slot struct {
	SlotAbbr string
	SlotDisp string
}

type IType struct {
	TypeAbbr string
	TypeDisp string
}

type Effect struct {
	EffAbbr string
	EffDisp string
}

type Resist struct {
	ResAbbr string
	ResDisp string
}

type Flag struct {
	FlagAbbr string
	FlagDisp string
}

type Supp struct {
	SuppAbbr string
	SuppDisp string
}

type Page struct {
	Date    string
	Results []string
	Zones   []Zone
	Attribs []Attrib
	Slots   []Slot
	Types   []IType
	Effects []Effect
	Resists []Resist
	Flags   []Flag
	Supps   []Supp
}

func fillStructs() Page {
	p := Page{
		Date: "2021-01-01",
	}

	db := OpenDB()
	defer db.Close()

	query := "SELECT MAX(last_id) FROM items"
	stmt, err := db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	err = stmt.QueryRow().Scan(&p.Date)
	ChkErr(err)

	query = "SELECT zone_abbr, zone_name FROM zones " +
		"ORDER BY zone_name ASC"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err := stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var z Zone
		err = rows.Scan(&z.ZoneAbbr, &z.ZoneDisp)
		ChkErr(err)
		p.Zones = append(p.Zones, z)
	}

	query = "SELECT attrib_abbr, attrib_display FROM attribs"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err = stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var a Attrib
		err = rows.Scan(&a.AttAbbr, &a.AttDisp)
		ChkErr(err)
		p.Attribs = append(p.Attribs, a)
	}

	query = "SELECT slot_abbr, slot_display FROM slots"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err = stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var s Slot
		err = rows.Scan(&s.SlotAbbr, &s.SlotDisp)
		ChkErr(err)
		p.Slots = append(p.Slots, s)
	}

	query = "SELECT type_abbr, type_display FROM item_types"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err = stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var t IType
		err = rows.Scan(&t.TypeAbbr, &t.TypeDisp)
		ChkErr(err)
		p.Types = append(p.Types, t)
	}

	query = "SELECT effect_abbr, effect_display FROM effects"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err = stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var e Effect
		err = rows.Scan(&e.EffAbbr, &e.EffDisp)
		ChkErr(err)
		p.Effects = append(p.Effects, e)
	}

	query = "SELECT resist_abbr, resist_display FROM resists"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err = stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var r Resist
		err = rows.Scan(&r.ResAbbr, &r.ResDisp)
		ChkErr(err)
		p.Resists = append(p.Resists, r)
	}

	query = "SELECT flag_abbr, flag_display FROM flags"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err = stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var f Flag
		err = rows.Scan(&f.FlagAbbr, &f.FlagDisp)
		ChkErr(err)
		p.Flags = append(p.Flags, f)
	}

	query = "SELECT supp_abbr, supp_display FROM supps"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err = stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var s Supp
		err = rows.Scan(&s.SuppAbbr, &s.SuppDisp)
		ChkErr(err)
		p.Supps = append(p.Supps, s)
	}

	return p
}

func parseForm(p Page, r *http.Request) []string {
	var results []string                           // slice holding final results of query
	query := "SELECT long_stats FROM items WHERE " // query builder
	var vals []string                              // slice holding query values
	//baseLen := len(query)                          // base length of the initial query

	if r.PostFormValue("itemName") != "" {
		query += "item_name LIKE ? " // TODO needs a lot of work
		vals = append(vals, "%"+r.PostFormValue("itemName")+"%")
	}

	if r.PostFormValue("zoneName") != "" {
		if len(query) > 35 {
			query += " AND "
		}
		query += "from_zone = ? "
		vals = append(vals, r.PostFormValue("zoneName"))
	}

	if r.PostFormValue("attrib1") != "" {
		if len(query) > 35 {
			query += " AND "
		}
		query += "item_id IN " +
			"(SELECT item_id FROM item_attribs " +
			"WHERE attrib_abbr = ? "
		vals = append(vals, r.PostFormValue("attrib1"))

		if r.PostFormValue("compareAttrib1") != "" {
			if r.PostFormValue("valueAttrib1") != "" {
				switch r.PostFormValue("compareAttrib1") {
				case "gt":
					query += "AND attrib_value > ?"
				case "lt":
					query += "AND attrib_value < ?"
				case "et":
					query += "AND attrib_value = ?"
				}
				vals = append(vals, r.PostFormValue("valueAttrib1"))
			}
		}
		query += ") "
	}

	if r.PostFormValue("attrib2") != "" {
		if len(query) > 35 {
			query += " AND "
		}
		query += "item_id IN " +
			"(SELECT item_id FROM item_attribs " +
			"WHERE attrib_abbr = ?"
		vals = append(vals, r.PostFormValue("attrib2"))

		if r.PostFormValue("compareAttrib2") != "" {
			if r.PostFormValue("valueAttrib2") != "" {
				switch r.PostFormValue("compareAttrib2") {
				case "gt":
					query += "AND attrib_value > ?"
				case "lt":
					query += "AND attrib_value < ?"
				case "et":
					query += "AND attrib_value = ?"
				}
				vals = append(vals, r.PostFormValue("valueAttrib2"))
			}
		}

		query += ") "
	}

	// TODO add item_specials like AC and damage dice

	if r.PostFormValue("worn") != "" {
		if len(query) > 35 {
			query += " AND "
		}
		query += "item_id IN " +
			"(SELECT item_id FROM item_slots " +
			"WHERE slot_abbr = ?) "
		vals = append(vals, r.PostFormValue("worn"))
	}

	if r.PostFormValue("type") != "" {
		if len(query) > 35 {
			query += " AND "
		}
		query += "item_id IN " +
			"(SELECT item_id FROM items " +
			"WHERE item_type = ?) "
		vals = append(vals, r.PostFormValue("type"))
	}

	// iterate through restricts
	restricts := []string{
		"!fighter", "!priest", "!thief", "!mage", "!male", "!female",
		"!good", "!neut", "!evil", "!goodrace", "!evilrace",
	}
	for _, v := range restricts {
		if r.PostFormValue(v) != "" {
			if len(query) > 35 {
				query += " AND "
			}
			query += "item_id NOT IN " +
				"(SELECT item_id FROM item_restricts " +
				"WHERE restrict_abbr = ?) "
			vals = append(vals, v)
		}
	}

	// iterate through effects
	for _, v := range p.Effects {
		if r.PostFormValue(v.EffAbbr) != "" {
			if len(query) > 35 {
				query += " AND "
			}
			query += "item_id IN " +
				"(SELECT item_id FROM item_effects " +
				"WHERE effect_abbr = ?) "
			vals = append(vals, v.EffAbbr)
		}
	}

	// iterate through resists
	for _, v := range p.Resists {
		if r.PostFormValue(v.ResAbbr) != "" {
			if len(query) > 35 {
				query += " AND "
			}
			query += "item_id IN " +
				"(SELECT item_id FROM item_resists " +
				"WHERE resist_abbr = ?) "
			vals = append(vals, v.ResAbbr)
		}
	}

	// iterate through flags
	for _, v := range p.Flags {
		if r.PostFormValue(v.FlagAbbr) != "" {
			if len(query) > 35 {
				query += " AND "
			}
			query += "item_id IN " +
				"(SELECT item_id FROM item_flags " +
				"WHERE flag_abbr = ?) "
			vals = append(vals, v.FlagAbbr)
		}
	}

	// iterate through supps
	for _, v := range p.Supps {
		if r.PostFormValue(v.SuppAbbr) != "" {
			if len(query) > 35 {
				query += " AND "
			}
			query += "item_id IN " +
				"(SELECT item_id FROM item_supps " +
				"WHERE supp_abbr = ?) "
			vals = append(vals, v.SuppAbbr)
		}
	}

	if len(query) > 35 {
		switch r.PostFormValue("orderBy") {
		case "name":
			query += "ORDER BY item_name "
		case "zone":
			query += "ORDER BY from_zone "
		case "attrib1": // TODO fix attribs and slot sorting, if possible
			query += "ORDER BY attrib_value "
		case "attrib2":
			query += "ORDER BY attrib_value "
		case "worn":
			query += "ORDER BY slot_abbr "
		case "type":
			query += "ORDER BY item_type "
		}
		switch r.PostFormValue("ascDesc") {
		case "up":
			query += "ASC"
		case "down":
			query += "DESC"
		}

		db := OpenDB()
		defer db.Close()

		stmt, err := db.Prepare(query)
		ChkErr(err)
		defer stmt.Close()

		args := make([]interface{}, len(vals))
		for n, val := range vals {
			args[n] = val
		}

		rows, err := stmt.Query(args...)
		ChkErr(err)
		defer rows.Close()

		for rows.Next() {
			var s string
			err = rows.Scan(&s)
			ChkErr(err)
			results = append(results, s)
		}
	}
	return results
}

func parseList(r *http.Request) []string {
	var results []string
	txt := r.PostFormValue("list")
	list := strings.Split(strings.Replace(txt, "\r\n", "\n", -1), "\n")

	for _, v := range list {
		if v != "" && !strings.Contains(v, ":") && !strings.Contains(v, "h/") &&
			!strings.Contains(v, ";") && !strings.Contains(v, "[RETURN") &&
			!strings.Contains(v, "======") && !strings.Contains(v, "#") {
			// to parse auction results
			if n := strings.Index(v, ")     "); n > 0 {
				x := strings.Count(v, "'")
				v = v[n+12 : 48+x]
			}
			// remove everything after the first " ("
			// to remove (illuminating) or whatever from the end of items
			if n := strings.Index(v, " ("); n > 0 {
				v = v[0:n]
			}
			// remove everything before the first "] "
			// to parse combined [5]-style items from inv or look in
			if n := strings.Index(v, "] "); n > 0 {
				v = v[n+1 : len(v)]
			}
			// remove everything before the first "   "
			// to parse items from a 'glist'
			if n := strings.Index(v, "   "); n > 0 {
				v = v[n+1 : len(v)]
			}
			// remove everything before the first "> "
			// to parse items from an 'eq' list
			if n := strings.Index(v, "> "); n > 0 {
				v = v[n+1 : len(v)]
			}
			// remove leading and trailing whitespace
			v = strings.TrimSpace(v)

			// TODO new func FindExactItem
			for _, result := range FindItem(v, "long_stats") {
				results = append(results, result)
			}
		}
	}

	return results
}

var tmpl = template.Must(template.ParseFiles(
	"html/index.html",
))

func eqHandler(w http.ResponseWriter, r *http.Request) {
	p := fillStructs()
	if r.Method == "POST" {
		if r.PostFormValue("list") != "" {
			p.Results = parseList(r)
		} else {
			p.Results = parseForm(p, r)
			// TODO make item names javascript link
			// TODO make first / single result full_stats
		}
	}
	err := tmpl.Execute(w, p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}
<commit_msg>built FindExactItem to search for known input like list copy/paste<commit_after>package main

import (
	//	"database/sql"
	"html/template"
	//	"log"
	"net/http"
	"strings"
)

type Zone struct {
	ZoneAbbr string
	ZoneDisp string
}

type Attrib struct {
	AttAbbr string
	AttDisp string
}

type Slot struct {
	SlotAbbr string
	SlotDisp string
}

type IType struct {
	TypeAbbr string
	TypeDisp string
}

type Effect struct {
	EffAbbr string
	EffDisp string
}

type Resist struct {
	ResAbbr string
	ResDisp string
}

type Flag struct {
	FlagAbbr string
	FlagDisp string
}

type Supp struct {
	SuppAbbr string
	SuppDisp string
}

type Page struct {
	Date    string
	Results []string
	Zones   []Zone
	Attribs []Attrib
	Slots   []Slot
	Types   []IType
	Effects []Effect
	Resists []Resist
	Flags   []Flag
	Supps   []Supp
}

func fillStructs() Page {
	p := Page{
		Date: "2021-01-01",
	}

	db := OpenDB()
	defer db.Close()

	query := "SELECT MAX(last_id) FROM items"
	stmt, err := db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	err = stmt.QueryRow().Scan(&p.Date)
	ChkErr(err)

	query = "SELECT zone_abbr, zone_name FROM zones " +
		"ORDER BY zone_name ASC"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err := stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var z Zone
		err = rows.Scan(&z.ZoneAbbr, &z.ZoneDisp)
		ChkErr(err)
		p.Zones = append(p.Zones, z)
	}

	query = "SELECT attrib_abbr, attrib_display FROM attribs"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err = stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var a Attrib
		err = rows.Scan(&a.AttAbbr, &a.AttDisp)
		ChkErr(err)
		p.Attribs = append(p.Attribs, a)
	}

	query = "SELECT slot_abbr, slot_display FROM slots"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err = stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var s Slot
		err = rows.Scan(&s.SlotAbbr, &s.SlotDisp)
		ChkErr(err)
		p.Slots = append(p.Slots, s)
	}

	query = "SELECT type_abbr, type_display FROM item_types"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err = stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var t IType
		err = rows.Scan(&t.TypeAbbr, &t.TypeDisp)
		ChkErr(err)
		p.Types = append(p.Types, t)
	}

	query = "SELECT effect_abbr, effect_display FROM effects"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err = stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var e Effect
		err = rows.Scan(&e.EffAbbr, &e.EffDisp)
		ChkErr(err)
		p.Effects = append(p.Effects, e)
	}

	query = "SELECT resist_abbr, resist_display FROM resists"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err = stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var r Resist
		err = rows.Scan(&r.ResAbbr, &r.ResDisp)
		ChkErr(err)
		p.Resists = append(p.Resists, r)
	}

	query = "SELECT flag_abbr, flag_display FROM flags"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err = stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var f Flag
		err = rows.Scan(&f.FlagAbbr, &f.FlagDisp)
		ChkErr(err)
		p.Flags = append(p.Flags, f)
	}

	query = "SELECT supp_abbr, supp_display FROM supps"
	stmt, err = db.Prepare(query)
	ChkErr(err)
	defer stmt.Close()

	rows, err = stmt.Query()
	ChkErr(err)
	defer rows.Close()

	for rows.Next() {
		var s Supp
		err = rows.Scan(&s.SuppAbbr, &s.SuppDisp)
		ChkErr(err)
		p.Supps = append(p.Supps, s)
	}

	return p
}

func parseForm(p Page, r *http.Request) []string {
	var results []string                           // slice holding final results of query
	query := "SELECT long_stats FROM items WHERE " // query builder
	var vals []string                              // slice holding query values
	//baseLen := len(query)                          // base length of the initial query

	if r.PostFormValue("itemName") != "" {
		query += "item_name LIKE ? " // TODO needs a lot of work
		vals = append(vals, "%"+r.PostFormValue("itemName")+"%")
	}

	if r.PostFormValue("zoneName") != "" {
		if len(query) > 35 {
			query += " AND "
		}
		query += "from_zone = ? "
		vals = append(vals, r.PostFormValue("zoneName"))
	}

	if r.PostFormValue("attrib1") != "" {
		if len(query) > 35 {
			query += " AND "
		}
		query += "item_id IN " +
			"(SELECT item_id FROM item_attribs " +
			"WHERE attrib_abbr = ? "
		vals = append(vals, r.PostFormValue("attrib1"))

		if r.PostFormValue("compareAttrib1") != "" {
			if r.PostFormValue("valueAttrib1") != "" {
				switch r.PostFormValue("compareAttrib1") {
				case "gt":
					query += "AND attrib_value > ?"
				case "lt":
					query += "AND attrib_value < ?"
				case "et":
					query += "AND attrib_value = ?"
				}
				vals = append(vals, r.PostFormValue("valueAttrib1"))
			}
		}
		query += ") "
	}

	if r.PostFormValue("attrib2") != "" {
		if len(query) > 35 {
			query += " AND "
		}
		query += "item_id IN " +
			"(SELECT item_id FROM item_attribs " +
			"WHERE attrib_abbr = ?"
		vals = append(vals, r.PostFormValue("attrib2"))

		if r.PostFormValue("compareAttrib2") != "" {
			if r.PostFormValue("valueAttrib2") != "" {
				switch r.PostFormValue("compareAttrib2") {
				case "gt":
					query += "AND attrib_value > ?"
				case "lt":
					query += "AND attrib_value < ?"
				case "et":
					query += "AND attrib_value = ?"
				}
				vals = append(vals, r.PostFormValue("valueAttrib2"))
			}
		}

		query += ") "
	}

	// TODO add item_specials like AC and damage dice

	if r.PostFormValue("worn") != "" {
		if len(query) > 35 {
			query += " AND "
		}
		query += "item_id IN " +
			"(SELECT item_id FROM item_slots " +
			"WHERE slot_abbr = ?) "
		vals = append(vals, r.PostFormValue("worn"))
	}

	if r.PostFormValue("type") != "" {
		if len(query) > 35 {
			query += " AND "
		}
		query += "item_id IN " +
			"(SELECT item_id FROM items " +
			"WHERE item_type = ?) "
		vals = append(vals, r.PostFormValue("type"))
	}

	// iterate through restricts
	restricts := []string{
		"!fighter", "!priest", "!thief", "!mage", "!male", "!female",
		"!good", "!neut", "!evil", "!goodrace", "!evilrace",
	}
	for _, v := range restricts {
		if r.PostFormValue(v) != "" {
			if len(query) > 35 {
				query += " AND "
			}
			query += "item_id NOT IN " +
				"(SELECT item_id FROM item_restricts " +
				"WHERE restrict_abbr = ?) "
			vals = append(vals, v)
		}
	}

	// iterate through effects
	for _, v := range p.Effects {
		if r.PostFormValue(v.EffAbbr) != "" {
			if len(query) > 35 {
				query += " AND "
			}
			query += "item_id IN " +
				"(SELECT item_id FROM item_effects " +
				"WHERE effect_abbr = ?) "
			vals = append(vals, v.EffAbbr)
		}
	}

	// iterate through resists
	for _, v := range p.Resists {
		if r.PostFormValue(v.ResAbbr) != "" {
			if len(query) > 35 {
				query += " AND "
			}
			query += "item_id IN " +
				"(SELECT item_id FROM item_resists " +
				"WHERE resist_abbr = ?) "
			vals = append(vals, v.ResAbbr)
		}
	}

	// iterate through flags
	for _, v := range p.Flags {
		if r.PostFormValue(v.FlagAbbr) != "" {
			if len(query) > 35 {
				query += " AND "
			}
			query += "item_id IN " +
				"(SELECT item_id FROM item_flags " +
				"WHERE flag_abbr = ?) "
			vals = append(vals, v.FlagAbbr)
		}
	}

	// iterate through supps
	for _, v := range p.Supps {
		if r.PostFormValue(v.SuppAbbr) != "" {
			if len(query) > 35 {
				query += " AND "
			}
			query += "item_id IN " +
				"(SELECT item_id FROM item_supps " +
				"WHERE supp_abbr = ?) "
			vals = append(vals, v.SuppAbbr)
		}
	}

	if len(query) > 35 {
		switch r.PostFormValue("orderBy") {
		case "name":
			query += "ORDER BY item_name "
		case "zone":
			query += "ORDER BY from_zone "
		case "attrib1": // TODO fix attribs and slot sorting, if possible
			query += "ORDER BY attrib_value "
		case "attrib2":
			query += "ORDER BY attrib_value "
		case "worn":
			query += "ORDER BY slot_abbr "
		case "type":
			query += "ORDER BY item_type "
		}
		switch r.PostFormValue("ascDesc") {
		case "up":
			query += "ASC"
		case "down":
			query += "DESC"
		}

		db := OpenDB()
		defer db.Close()

		stmt, err := db.Prepare(query)
		ChkErr(err)
		defer stmt.Close()

		args := make([]interface{}, len(vals))
		for n, val := range vals {
			args[n] = val
		}

		rows, err := stmt.Query(args...)
		ChkErr(err)
		defer rows.Close()

		for rows.Next() {
			var s string
			err = rows.Scan(&s)
			ChkErr(err)
			results = append(results, s)
		}
	}
	return results
}

func FindExactItem(itemName string, statType string) []string {
	var results []string
	query := "SELECT ? FROM items WHERE item_name = ?"

	db := OpenDB()
    defer db.Close()

    stmt, err := db.Prepare(query)
    ChkErr(err)
    defer stmt.Close()

	rows, err := stmt.Query(statType, itemName)
    ChkErr(err)
    defer rows.Close()

	for rows.Next() {
		var s string
		err = rows.Scan(&s)
		ChkErr(err)
		results = append(results, s)
	}
	// TODO search for items with ' 1' on the end if results empty
	// TODO make sure results is only one item? LIMIT 1? string instead of []string?
	return results
}

func parseList(r *http.Request) []string {
	var results []string
	txt := r.PostFormValue("list")
	list := strings.Split(strings.Replace(txt, "\r\n", "\n", -1), "\n")

	for _, v := range list {
		if v != "" && !strings.Contains(v, ":") && !strings.Contains(v, "h/") &&
			!strings.Contains(v, ";") && !strings.Contains(v, "[RETURN") &&
			!strings.Contains(v, "======") && !strings.Contains(v, "#") {
			// to parse auction results
			if n := strings.Index(v, ")     "); n > 0 {
				x := strings.Count(v, "'")
				v = v[n+12 : 48+x]
			}
			// remove everything after the first " ("
			// to remove (illuminating) or whatever from the end of items
			if n := strings.Index(v, " ("); n > 0 {
				v = v[0:n]
			}
			// remove everything before the first "] "
			// to parse combined [5]-style items from inv or look in
			if n := strings.Index(v, "] "); n > 0 {
				v = v[n+1 : len(v)]
			}
			// remove everything before the first "   "
			// to parse items from a 'glist'
			if n := strings.Index(v, "   "); n > 0 {
				v = v[n+1 : len(v)]
			}
			// remove everything before the first "> "
			// to parse items from an 'eq' list
			if n := strings.Index(v, "> "); n > 0 {
				v = v[n+1 : len(v)]
			}
			// remove leading and trailing whitespace
			v = strings.TrimSpace(v)

			// TODO new func FindExactItem
			for _, result := range FindItem(v, "long_stats") {
				results = append(results, result)
			}
		}
	}

	return results
}

var tmpl = template.Must(template.ParseFiles(
	"html/index.html",
))

func eqHandler(w http.ResponseWriter, r *http.Request) {
	p := fillStructs()
	if r.Method == "POST" {
		if r.PostFormValue("list") != "" {
			p.Results = parseList(r)
		} else {
			p.Results = parseForm(p, r)
			// TODO make item names javascript link
			// TODO make first / single result full_stats
		}
	}
	err := tmpl.Execute(w, p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}
<|endoftext|>


<commit_before>/*
 * (c) 2014, Caoimhe Chaos <caoimhechaos@protonmail.com>,
 *	     Ancient Solutions. All rights reserved.
 *
 * Redistribution and use in source  and binary forms, with or without
 * modification, are permitted  provided that the following conditions
 * are met:
 *
 * * Redistributions of  source code  must retain the  above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * 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.
 * * Neither  the  name  of  Ancient Solutions  nor  the  name  of its
 *   contributors may  be used to endorse or  promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS"  AND ANY EXPRESS  OR IMPLIED WARRANTIES  OF MERCHANTABILITY
 * AND FITNESS  FOR A PARTICULAR  PURPOSE ARE DISCLAIMED. IN  NO EVENT
 * SHALL THE COPYRIGHT OWNER 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 main

import (
	"html/template"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"strings"

	"ancient-solutions.com/ancientauth"
)

type WebPikShrService struct {
	auth     *ancientauth.Authenticator
	db       *PikShrDB
	skel     *template.Template
	upload   *template.Template
	num_pics int32
	num_own  int32
}

type webMetadata struct {
	User       string
	UploadedId string
	OwnPics    []*Picture
	AllPics    []*Picture
}

// Either allow people to upload new pictures, or serve existing ones.
func (w *WebPikShrService) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	var mfh *multipart.FileHeader
	var mf multipart.File
	var wmd webMetadata
	var res *Picture
	var err error

	if req.RequestURI == "/favicon.ico" {
		http.NotFound(rw, req)
		return
	} else if strings.HasSuffix(req.RequestURI, ".png") {
		var id string

		if strings.HasSuffix(req.RequestURI, ".thumb.png") {
			id = req.RequestURI[1 : len(req.RequestURI)-10]
			res, err = w.db.GetThumbnail(id)
		} else {
			id = req.RequestURI[1 : len(req.RequestURI)-4]
			res, err = w.db.GetPicture(id)
		}
		if err == Err_ImageNotFound {
			// TODO(caoimhe): this wants to be a template.
			http.NotFound(rw, req)
			return
		}
		if err != nil {
			log.Print("Unable to retrieve image ", id, ": ", err)
			// TODO(caoimhe): this wants to be a template.
			rw.WriteHeader(http.StatusInternalServerError)
			rw.Write([]byte(err.Error()))
			return
		}
		if len(res.Contents) == 0 {
			// TODO(caoimhe): this wants to be a template.
			http.NotFound(rw, req)
			return
		}

		rw.Header().Set("Content-Disposition", "inline")
		rw.Header().Set("Content-Type", "image/png")
		_, err = rw.Write(res.Contents)
		if err != nil {
			log.Print("Error writing out image ", id, ": ", err)
		}
		return
	} else if req.RequestURI != "/" {
		var md *Picture
		md, err = w.db.GetMetadata(req.RequestURI[1:])
		if err == Err_ImageNotFound {
			// TODO(caoimhe): this wants to be a template.
			http.NotFound(rw, req)
			return
		}
		if err != nil {
			log.Print("Unable to fetch metadata for ", req.RequestURI[1:],
				":", err)
			// TODO(caoimhe): this wants to be a template.
			rw.WriteHeader(http.StatusInternalServerError)
			rw.Write([]byte(err.Error()))
			return
		}
		err = w.skel.Execute(rw, md)
		if err != nil {
			rw.Write([]byte(err.Error()))
			log.Print("Error executing skeleton template: ", err)
		}
		return
	}

	wmd.User = w.auth.GetAuthenticatedUser(req)
	mf, mfh, err = req.FormFile("imageupload")
	if err != nil {
		log.Print("Unable to retrieve uploaded file: ", err)
	} else if len(wmd.User) > 0 {
		res = new(Picture)
		res.ContentType = mfh.Header["Content-Type"][0]
		res.AltText = req.FormValue("alt")
		res.Description = req.FormValue("description")
		res.Title = req.FormValue("title")
		res.Contents, err = ioutil.ReadAll(mf)
		if err != nil {
			log.Print("Unable to read multipart upload file: ", err)
		} else {
			wmd.UploadedId, err = w.db.InsertPicture(res, wmd.User)
			if err != nil {
				log.Print("Error storing ", mfh.Filename, ": ",
					err)
			}
		}
		mf.Close()
	} else if req.FormValue("ensure") == "authenticated" {
		w.auth.RequestAuthorization(rw, req)
		return
	}

	if req.FormValue("outform") == "json" {
		rw.Write([]byte(wmd.UploadedId))
		return
	}
	if len(wmd.User) > 0 {
		wmd.OwnPics, err = w.db.GetRecentPics(wmd.User, w.num_own)
		if err != nil {
			log.Print("Error determining the most recent pics of ",
				wmd.User, ": ", err)
		}
	}
	wmd.AllPics, err = w.db.GetRecentPics("", w.num_pics)
	if err != nil {
		log.Print("Error determining the most recent pics of ",
			wmd.User, ": ", err)
	}

	err = w.upload.Execute(rw, &wmd)
	if err != nil {
		rw.Write([]byte(err.Error()))
		log.Print("Unable to execute upload template: ", err)
	}
}
<commit_msg>Fix authentication by moving the check upwards.<commit_after>/*
 * (c) 2014, Caoimhe Chaos <caoimhechaos@protonmail.com>,
 *	     Ancient Solutions. All rights reserved.
 *
 * Redistribution and use in source  and binary forms, with or without
 * modification, are permitted  provided that the following conditions
 * are met:
 *
 * * Redistributions of  source code  must retain the  above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * 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.
 * * Neither  the  name  of  Ancient Solutions  nor  the  name  of its
 *   contributors may  be used to endorse or  promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS"  AND ANY EXPRESS  OR IMPLIED WARRANTIES  OF MERCHANTABILITY
 * AND FITNESS  FOR A PARTICULAR  PURPOSE ARE DISCLAIMED. IN  NO EVENT
 * SHALL THE COPYRIGHT OWNER 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 main

import (
	"html/template"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"strings"

	"ancient-solutions.com/ancientauth"
)

type WebPikShrService struct {
	auth     *ancientauth.Authenticator
	db       *PikShrDB
	skel     *template.Template
	upload   *template.Template
	num_pics int32
	num_own  int32
}

type webMetadata struct {
	User       string
	UploadedId string
	OwnPics    []*Picture
	AllPics    []*Picture
}

// Either allow people to upload new pictures, or serve existing ones.
func (w *WebPikShrService) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	var mfh *multipart.FileHeader
	var mf multipart.File
	var wmd webMetadata
	var res *Picture
	var err error

	if req.URL.Path == "/favicon.ico" {
		http.NotFound(rw, req)
		return
	} else if strings.HasSuffix(req.URL.Path, ".png") {
		var id string

		if strings.HasSuffix(req.URL.Path, ".thumb.png") {
			id = req.URL.Path[1 : len(req.URL.Path)-10]
			res, err = w.db.GetThumbnail(id)
		} else {
			id = req.URL.Path[1 : len(req.URL.Path)-4]
			res, err = w.db.GetPicture(id)
		}
		if err == Err_ImageNotFound {
			// TODO(caoimhe): this wants to be a template.
			http.NotFound(rw, req)
			return
		}
		if err != nil {
			log.Print("Unable to retrieve image ", id, ": ", err)
			// TODO(caoimhe): this wants to be a template.
			rw.WriteHeader(http.StatusInternalServerError)
			rw.Write([]byte(err.Error()))
			return
		}
		if len(res.Contents) == 0 {
			// TODO(caoimhe): this wants to be a template.
			http.NotFound(rw, req)
			return
		}

		rw.Header().Set("Content-Disposition", "inline")
		rw.Header().Set("Content-Type", "image/png")
		_, err = rw.Write(res.Contents)
		if err != nil {
			log.Print("Error writing out image ", id, ": ", err)
		}
		return
	} else if req.URL.Path != "/" {
		var md *Picture
		md, err = w.db.GetMetadata(req.URL.Path[1:])
		if err == Err_ImageNotFound {
			// TODO(caoimhe): this wants to be a template.
			http.NotFound(rw, req)
			return
		}
		if err != nil {
			log.Print("Unable to fetch metadata for ", req.URL.Path[1:],
				":", err)
			// TODO(caoimhe): this wants to be a template.
			rw.WriteHeader(http.StatusInternalServerError)
			rw.Write([]byte(err.Error()))
			return
		}
		err = w.skel.Execute(rw, md)
		if err != nil {
			rw.Write([]byte(err.Error()))
			log.Print("Error executing skeleton template: ", err)
		}
		return
	}

	wmd.User = w.auth.GetAuthenticatedUser(req)
	if wmd.User == "" && req.FormValue("ensure") == "authenticated" {
		w.auth.RequestAuthorization(rw, req)
		return
	}

	mf, mfh, err = req.FormFile("imageupload")
	if err != nil {
		log.Print("Unable to retrieve uploaded file: ", err)
	} else if len(wmd.User) > 0 {
		res = new(Picture)
		res.ContentType = mfh.Header["Content-Type"][0]
		res.AltText = req.FormValue("alt")
		res.Description = req.FormValue("description")
		res.Title = req.FormValue("title")
		res.Contents, err = ioutil.ReadAll(mf)
		if err != nil {
			log.Print("Unable to read multipart upload file: ", err)
		} else {
			wmd.UploadedId, err = w.db.InsertPicture(res, wmd.User)
			if err != nil {
				log.Print("Error storing ", mfh.Filename, ": ",
					err)
			}
		}
		mf.Close()
	}

	if req.FormValue("outform") == "json" {
		rw.Write([]byte(wmd.UploadedId))
		return
	}
	if len(wmd.User) > 0 {
		wmd.OwnPics, err = w.db.GetRecentPics(wmd.User, w.num_own)
		if err != nil {
			log.Print("Error determining the most recent pics of ",
				wmd.User, ": ", err)
		}
	}
	wmd.AllPics, err = w.db.GetRecentPics("", w.num_pics)
	if err != nil {
		log.Print("Error determining the most recent pics of ",
			wmd.User, ": ", err)
	}

	err = w.upload.Execute(rw, &wmd)
	if err != nil {
		rw.Write([]byte(err.Error()))
		log.Print("Unable to execute upload template: ", err)
	}
}
<|endoftext|>