<commit_before>/*
 * Copyright (c) 2015 Alex Yatskov <alex@foosoft.net>
 * Author: Alex Yatskov <alex@foosoft.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"github.com/russross/blackfriday"
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [options] input output\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "http://foosoft.net/projects/md2vim/\n\n")
	fmt.Fprintf(os.Stderr, "Parameters:\n")
	flag.PrintDefaults()
}

func main() {
	cols := flag.Int("cols", DEFAULT_NUM_COLUMNS, "number of columns to use for layout")
	tabs := flag.Int("tabs", DEFAULT_TAB_SIZE, "tab width specified in number of spaces")
	notoc := flag.Bool("notoc", false, "do not generate table of contents for headings")
	norules := flag.Bool("norules", false, "do not generate horizontal rules above headings")
	pascal := flag.Bool("pascal", false, "use PascalCase for abbreviating tags")
	desc := flag.String("desc", "", "short description of the help file")
	flag.Usage = usage
	flag.Parse()

	args := flag.Args()
	if len(args) < 2 {
		flag.Usage()
		os.Exit(-1)
	}

	input, err := ioutil.ReadFile(args[0])
	if err != nil {
		log.Fatalf("error: unable to read from file %s", args[0])
	}

	flags := 0
	if *notoc {
		flags |= FLAG_NO_TOC
	}
	if *norules {
		flags |= FLAG_NO_RULES
	}
	if *pascal {
		flags |= FLAG_PASCAL
	}

	renderer := VimDocRenderer(args[1], *desc, *cols, *tabs, flags)
	extensions := blackfriday.EXTENSION_FENCED_CODE | blackfriday.EXTENSION_NO_INTRA_EMPHASIS | blackfriday.EXTENSION_SPACE_HEADERS
	output := blackfriday.Markdown(input, renderer, extensions)

	file, err := os.Create(args[1])
	if err != nil {
		log.Fatalf("error: unable to write to file %s", args[1])
	}
	defer file.Close()

	if _, err := file.Write(output); err != nil {
		log.Fatal("error: unable to write output")
	}
}
<commit_msg>Clean up help output<commit_after>/*
 * Copyright (c) 2015 Alex Yatskov <alex@foosoft.net>
 * Author: Alex Yatskov <alex@foosoft.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"

	"github.com/russross/blackfriday"
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [options] input output\n", path.Base(os.Args[0]))
	fmt.Fprintf(os.Stderr, "http://foosoft.net/projects/md2vim/\n\n")
	fmt.Fprintf(os.Stderr, "Parameters:\n")
	flag.PrintDefaults()
}

func main() {
	cols := flag.Int("cols", DEFAULT_NUM_COLUMNS, "number of columns to use for layout")
	tabs := flag.Int("tabs", DEFAULT_TAB_SIZE, "tab width specified in number of spaces")
	notoc := flag.Bool("notoc", false, "do not generate table of contents for headings")
	norules := flag.Bool("norules", false, "do not generate horizontal rules above headings")
	pascal := flag.Bool("pascal", false, "use PascalCase for abbreviating tags")
	desc := flag.String("desc", "", "short description of the help file")
	flag.Usage = usage
	flag.Parse()

	args := flag.Args()
	if len(args) < 2 {
		flag.Usage()
		os.Exit(-1)
	}

	input, err := ioutil.ReadFile(args[0])
	if err != nil {
		log.Fatalf("error: unable to read from file %s", args[0])
	}

	flags := 0
	if *notoc {
		flags |= FLAG_NO_TOC
	}
	if *norules {
		flags |= FLAG_NO_RULES
	}
	if *pascal {
		flags |= FLAG_PASCAL
	}

	renderer := VimDocRenderer(args[1], *desc, *cols, *tabs, flags)
	extensions := blackfriday.EXTENSION_FENCED_CODE | blackfriday.EXTENSION_NO_INTRA_EMPHASIS | blackfriday.EXTENSION_SPACE_HEADERS
	output := blackfriday.Markdown(input, renderer, extensions)

	file, err := os.Create(args[1])
	if err != nil {
		log.Fatalf("error: unable to write to file %s", args[1])
	}
	defer file.Close()

	if _, err := file.Write(output); err != nil {
		log.Fatal("error: unable to write output")
	}
}
<|endoftext|>


<commit_before>package bluemonday

import (
	"regexp"
	"strings"
)

// policy encapsulates the whitelist of HTML elements and attributes that will
// be applied to the sanitised HTML.
type policy struct {
	// Allows the <!DOCTYPE > tag to exist in the sanitized document
	allowDocType bool

	// When true, add rel="nofollow" to HTML anchors
	requireNoFollow bool

	// When true, URLs must be parseable by "net/url" url.Parse()
	requireParseableURLs bool

	// When true, u, _ := url.Parse("url"); !u.IsAbs() is permitted
	allowRelativeURLs bool

	// map[urlScheme]bool
	urlSchemes map[string]bool

	// map[htmlElementName]map[htmlAttributeName]attrPolicy
	elsAndAttrs map[string]map[string]attrPolicy

	// map[htmlAttributeName]attrPolicy
	globalAttrs map[string]attrPolicy

	// map[htmlElementName]bool
	elsWithoutAttrs map[string]bool
}

type attrPolicy struct {
	// optional pattern to match, when not nil the regexp needs to match
	// otherwise the attribute is removed
	regexp *regexp.Regexp
}

type attrPolicyBuilder struct {
	p *policy

	attrNames []string
	regexp    *regexp.Regexp
}

// NewPolicy returns a blank policy with nothing whitelisted or permitted. This
// is the building block for a policy and you should now use AllowAttrs() and/or
// AllowElements() to construct the whitelist of HTML elements and attributes.
func NewPolicy() *policy {
	p := policy{}

	p.urlSchemes = make(map[string]bool)
	p.elsAndAttrs = make(map[string]map[string]attrPolicy)
	p.globalAttrs = make(map[string]attrPolicy)
	p.elsWithoutAttrs = make(map[string]bool)

	p.addDefaultElsWithoutAttrs()

	return &p
}

// AllowAttrs takes a range of HTML attribute names and returns an
// attribute policy builder that allows you to specify the pattern and scope of
// the whitelisted attribute.
//
// Examples:
//   AllowAttrs("title").Globally()
//   AllowAttrs("abbr").OnElements("td", "th")
//   AllowAttrs("colspan", "rowspan").Matching(
//           regexp.MustCompile("[0-9]+"),
//       ).OnElements("td", "th")
//
// The attribute policy is only added to the core policy when either Globally()
// or OnElements(...) are called.
func (p *policy) AllowAttrs(attrNames ...string) *attrPolicyBuilder {

	abp := attrPolicyBuilder{p: p}

	for _, attrName := range attrNames {
		abp.attrNames = append(abp.attrNames, strings.ToLower(attrName))
	}

	return &abp
}

// Matching allows a regular expression to be applied to a nascent attribute
// policy, and returns the attribute policy. Calling this more than once will
// replace the existing regexp.
func (abp *attrPolicyBuilder) Matching(regex *regexp.Regexp) *attrPolicyBuilder {

	abp.regexp = regex

	return abp
}

// OnElements will bind an attribute policy to a given range of HTML elements
// and return the updated policy
func (abp *attrPolicyBuilder) OnElements(elements ...string) *policy {

	for _, element := range elements {
		element = strings.ToLower(element)

		for _, attr := range abp.attrNames {

			if _, ok := abp.p.elsAndAttrs[element]; !ok {
				abp.p.elsAndAttrs[element] = make(map[string]attrPolicy)
			}

			ap := attrPolicy{}
			if abp.regexp != nil {
				ap.regexp = abp.regexp
			}

			abp.p.elsAndAttrs[element][attr] = ap
		}
	}

	return abp.p
}

// Globally will bind an attribute policy to all HTML elements and return the
// updated policy
func (abp *attrPolicyBuilder) Globally() *policy {

	for _, attr := range abp.attrNames {
		if _, ok := abp.p.globalAttrs[attr]; !ok {
			abp.p.globalAttrs[attr] = attrPolicy{}
		}

		ap := attrPolicy{}
		if abp.regexp != nil {
			ap.regexp = abp.regexp
		}

		abp.p.globalAttrs[attr] = ap
	}

	return abp.p
}

// AllowElements will append HTML elements to the whitelist without applying an
// attribute policy to those elements (the elements are permitted
// sans-attributes)
func (p *policy) AllowElements(names ...string) *policy {

	for _, element := range names {
		element = strings.ToLower(element)

		if _, ok := p.elsAndAttrs[element]; !ok {
			p.elsAndAttrs[element] = make(map[string]attrPolicy)
		}
	}

	return p
}

// RequireNoFollowOnLinks will result in all <a> tags having a rel="nofollow"
// added to them if one does not already exist
func (p *policy) RequireNoFollowOnLinks(require bool) *policy {
	p.requireNoFollow = require

	return p
}

// RequireParseableURLs will result in all URLs requiring that they be parseable
// by "net/url" url.Parse()
// This applies to:
// - a.href
// - area.href
// - blockquote.cite
// - img.src
// - link.href
// - script.src
func (p *policy) RequireParseableURLs(require bool) *policy {
	p.requireParseableURLs = require

	return p
}

// AllowRelativeURLs enables RequireParseableURLs and then permits URLs that
// are parseable, have no schema information and url.IsAbs() returns false
// This permits local URLs
func (p *policy) AllowRelativeURLs(require bool) *policy {
	p.RequireParseableURLs(true)
	p.allowRelativeURLs = require

	return p
}

// AllowURLSchemes will append URL schems to the whitelist
// Example: p.AllowURLSchemes("mailto", "http", "https")
func (p *policy) AllowURLSchemes(schemes ...string) *policy {

	for _, scheme := range schemes {
		scheme = strings.ToLower(scheme)

		if _, ok := p.urlSchemes[scheme]; !ok {
			p.urlSchemes[scheme] = true
		}
	}

	return p
}

// AllowDocType states whether the HTML sanitised by the sanitizer is allowed to
// contain the HTML DocType tag: <!DOCTYPE HTML> or one of it's variants.
//
// The HTML spec only permits one doctype per document, and as you know how you
// are using the output of this, you know best as to whether we should ignore it
// (default) or not.
//
// If you are sanitizing a HTML fragment the default (false) is fine.
func (p *policy) AllowDocType(allow bool) *policy {
	p.allowDocType = allow

	return p
}

// addDefaultElsWithoutAttrs adds the HTML elements that we know are valid
// without any attributes to an internal map.
// i.e. we know that <table> is valid, but <bdo> isn't valid as the "dir" attr
// is mandatory
func (p *policy) addDefaultElsWithoutAttrs() {
	p.elsWithoutAttrs["abbr"] = true
	p.elsWithoutAttrs["acronym"] = true
	p.elsWithoutAttrs["article"] = true
	p.elsWithoutAttrs["aside"] = true
	p.elsWithoutAttrs["audio"] = true
	p.elsWithoutAttrs["b"] = true
	p.elsWithoutAttrs["bdi"] = true
	p.elsWithoutAttrs["blockquote"] = true
	p.elsWithoutAttrs["body"] = true
	p.elsWithoutAttrs["br"] = true
	p.elsWithoutAttrs["button"] = true
	p.elsWithoutAttrs["canvas"] = true
	p.elsWithoutAttrs["caption"] = true
	p.elsWithoutAttrs["cite"] = true
	p.elsWithoutAttrs["code"] = true
	p.elsWithoutAttrs["col"] = true
	p.elsWithoutAttrs["colgroup"] = true
	p.elsWithoutAttrs["datalist"] = true
	p.elsWithoutAttrs["dd"] = true
	p.elsWithoutAttrs["del"] = true
	p.elsWithoutAttrs["details"] = true
	p.elsWithoutAttrs["dfn"] = true
	p.elsWithoutAttrs["div"] = true
	p.elsWithoutAttrs["dl"] = true
	p.elsWithoutAttrs["dt"] = true
	p.elsWithoutAttrs["em"] = true
	p.elsWithoutAttrs["fieldset"] = true
	p.elsWithoutAttrs["figcaption"] = true
	p.elsWithoutAttrs["figure"] = true
	p.elsWithoutAttrs["footer"] = true
	p.elsWithoutAttrs["h1"] = true
	p.elsWithoutAttrs["h2"] = true
	p.elsWithoutAttrs["h3"] = true
	p.elsWithoutAttrs["h4"] = true
	p.elsWithoutAttrs["h5"] = true
	p.elsWithoutAttrs["h6"] = true
	p.elsWithoutAttrs["head"] = true
	p.elsWithoutAttrs["header"] = true
	p.elsWithoutAttrs["hgroup"] = true
	p.elsWithoutAttrs["hr"] = true
	p.elsWithoutAttrs["html"] = true
	p.elsWithoutAttrs["i"] = true
	p.elsWithoutAttrs["ins"] = true
	p.elsWithoutAttrs["kbd"] = true
	p.elsWithoutAttrs["li"] = true
	p.elsWithoutAttrs["mark"] = true
	p.elsWithoutAttrs["nav"] = true
	p.elsWithoutAttrs["ol"] = true
	p.elsWithoutAttrs["optgroup"] = true
	p.elsWithoutAttrs["option"] = true
	p.elsWithoutAttrs["p"] = true
	p.elsWithoutAttrs["pre"] = true
	p.elsWithoutAttrs["q"] = true
	p.elsWithoutAttrs["rp"] = true
	p.elsWithoutAttrs["rt"] = true
	p.elsWithoutAttrs["ruby"] = true
	p.elsWithoutAttrs["s"] = true
	p.elsWithoutAttrs["samp"] = true
	p.elsWithoutAttrs["section"] = true
	p.elsWithoutAttrs["select"] = true
	p.elsWithoutAttrs["small"] = true
	p.elsWithoutAttrs["span"] = true
	p.elsWithoutAttrs["strike"] = true
	p.elsWithoutAttrs["strong"] = true
	p.elsWithoutAttrs["style"] = true
	p.elsWithoutAttrs["sub"] = true
	p.elsWithoutAttrs["summary"] = true
	p.elsWithoutAttrs["sup"] = true
	p.elsWithoutAttrs["svg"] = true
	p.elsWithoutAttrs["table"] = true
	p.elsWithoutAttrs["tbody"] = true
	p.elsWithoutAttrs["td"] = true
	p.elsWithoutAttrs["textarea"] = true
	p.elsWithoutAttrs["tfoot"] = true
	p.elsWithoutAttrs["th"] = true
	p.elsWithoutAttrs["thead"] = true
	p.elsWithoutAttrs["time"] = true
	p.elsWithoutAttrs["tr"] = true
	p.elsWithoutAttrs["tt"] = true
	p.elsWithoutAttrs["u"] = true
	p.elsWithoutAttrs["ul"] = true
	p.elsWithoutAttrs["var"] = true
	p.elsWithoutAttrs["video"] = true
	p.elsWithoutAttrs["wbr"] = true
}
<commit_msg>If we're adding URL schemas we must need parseability to be enabled<commit_after>package bluemonday

import (
	"regexp"
	"strings"
)

// policy encapsulates the whitelist of HTML elements and attributes that will
// be applied to the sanitised HTML.
type policy struct {

	// Allows the <!DOCTYPE > tag to exist in the sanitized document
	allowDocType bool

	// When true, add rel="nofollow" to HTML anchors
	requireNoFollow bool

	// When true, URLs must be parseable by "net/url" url.Parse()
	requireParseableURLs bool

	// When true, u, _ := url.Parse("url"); !u.IsAbs() is permitted
	allowRelativeURLs bool

	// map[urlScheme]bool
	urlSchemes map[string]bool

	// map[htmlElementName]map[htmlAttributeName]attrPolicy
	elsAndAttrs map[string]map[string]attrPolicy

	// map[htmlAttributeName]attrPolicy
	globalAttrs map[string]attrPolicy

	// map[htmlElementName]bool
	elsWithoutAttrs map[string]bool
}

type attrPolicy struct {

	// optional pattern to match, when not nil the regexp needs to match
	// otherwise the attribute is removed
	regexp *regexp.Regexp
}

type attrPolicyBuilder struct {
	p *policy

	attrNames []string
	regexp    *regexp.Regexp
}

// NewPolicy returns a blank policy with nothing whitelisted or permitted. This
// is the building block for a policy and you should now use AllowAttrs() and/or
// AllowElements() to construct the whitelist of HTML elements and attributes.
func NewPolicy() *policy {

	p := policy{}

	p.urlSchemes = make(map[string]bool)
	p.elsAndAttrs = make(map[string]map[string]attrPolicy)
	p.globalAttrs = make(map[string]attrPolicy)
	p.elsWithoutAttrs = make(map[string]bool)

	p.addDefaultElsWithoutAttrs()

	return &p
}

// AllowAttrs takes a range of HTML attribute names and returns an
// attribute policy builder that allows you to specify the pattern and scope of
// the whitelisted attribute.
//
// Examples:
//   AllowAttrs("title").Globally()
//   AllowAttrs("abbr").OnElements("td", "th")
//   AllowAttrs("colspan", "rowspan").Matching(
//           regexp.MustCompile("[0-9]+"),
//       ).OnElements("td", "th")
//
// The attribute policy is only added to the core policy when either Globally()
// or OnElements(...) are called.
func (p *policy) AllowAttrs(attrNames ...string) *attrPolicyBuilder {

	abp := attrPolicyBuilder{p: p}

	for _, attrName := range attrNames {
		abp.attrNames = append(abp.attrNames, strings.ToLower(attrName))
	}

	return &abp
}

// Matching allows a regular expression to be applied to a nascent attribute
// policy, and returns the attribute policy. Calling this more than once will
// replace the existing regexp.
func (abp *attrPolicyBuilder) Matching(regex *regexp.Regexp) *attrPolicyBuilder {

	abp.regexp = regex

	return abp
}

// OnElements will bind an attribute policy to a given range of HTML elements
// and return the updated policy
func (abp *attrPolicyBuilder) OnElements(elements ...string) *policy {

	for _, element := range elements {
		element = strings.ToLower(element)

		for _, attr := range abp.attrNames {

			if _, ok := abp.p.elsAndAttrs[element]; !ok {
				abp.p.elsAndAttrs[element] = make(map[string]attrPolicy)
			}

			ap := attrPolicy{}
			if abp.regexp != nil {
				ap.regexp = abp.regexp
			}

			abp.p.elsAndAttrs[element][attr] = ap
		}
	}

	return abp.p
}

// Globally will bind an attribute policy to all HTML elements and return the
// updated policy
func (abp *attrPolicyBuilder) Globally() *policy {

	for _, attr := range abp.attrNames {
		if _, ok := abp.p.globalAttrs[attr]; !ok {
			abp.p.globalAttrs[attr] = attrPolicy{}
		}

		ap := attrPolicy{}
		if abp.regexp != nil {
			ap.regexp = abp.regexp
		}

		abp.p.globalAttrs[attr] = ap
	}

	return abp.p
}

// AllowElements will append HTML elements to the whitelist without applying an
// attribute policy to those elements (the elements are permitted
// sans-attributes)
func (p *policy) AllowElements(names ...string) *policy {

	for _, element := range names {
		element = strings.ToLower(element)

		if _, ok := p.elsAndAttrs[element]; !ok {
			p.elsAndAttrs[element] = make(map[string]attrPolicy)
		}
	}

	return p
}

// RequireNoFollowOnLinks will result in all <a> tags having a rel="nofollow"
// added to them if one does not already exist
func (p *policy) RequireNoFollowOnLinks(require bool) *policy {
	p.requireNoFollow = require

	return p
}

// RequireParseableURLs will result in all URLs requiring that they be parseable
// by "net/url" url.Parse()
// This applies to:
// - a.href
// - area.href
// - blockquote.cite
// - img.src
// - link.href
// - script.src
func (p *policy) RequireParseableURLs(require bool) *policy {

	p.requireParseableURLs = require

	return p
}

// AllowRelativeURLs enables RequireParseableURLs and then permits URLs that
// are parseable, have no schema information and url.IsAbs() returns false
// This permits local URLs
func (p *policy) AllowRelativeURLs(require bool) *policy {

	p.RequireParseableURLs(true)
	p.allowRelativeURLs = require

	return p
}

// AllowURLSchemes will append URL schems to the whitelist
// Example: p.AllowURLSchemes("mailto", "http", "https")
func (p *policy) AllowURLSchemes(schemes ...string) *policy {

	p.RequireParseableURLs(true)

	for _, scheme := range schemes {
		scheme = strings.ToLower(scheme)

		if _, ok := p.urlSchemes[scheme]; !ok {
			p.urlSchemes[scheme] = true
		}
	}

	return p
}

// AllowDocType states whether the HTML sanitised by the sanitizer is allowed to
// contain the HTML DocType tag: <!DOCTYPE HTML> or one of it's variants.
//
// The HTML spec only permits one doctype per document, and as you know how you
// are using the output of this, you know best as to whether we should ignore it
// (default) or not.
//
// If you are sanitizing a HTML fragment the default (false) is fine.
func (p *policy) AllowDocType(allow bool) *policy {

	p.allowDocType = allow

	return p
}

// addDefaultElsWithoutAttrs adds the HTML elements that we know are valid
// without any attributes to an internal map.
// i.e. we know that <table> is valid, but <bdo> isn't valid as the "dir" attr
// is mandatory
func (p *policy) addDefaultElsWithoutAttrs() {

	p.elsWithoutAttrs["abbr"] = true
	p.elsWithoutAttrs["acronym"] = true
	p.elsWithoutAttrs["article"] = true
	p.elsWithoutAttrs["aside"] = true
	p.elsWithoutAttrs["audio"] = true
	p.elsWithoutAttrs["b"] = true
	p.elsWithoutAttrs["bdi"] = true
	p.elsWithoutAttrs["blockquote"] = true
	p.elsWithoutAttrs["body"] = true
	p.elsWithoutAttrs["br"] = true
	p.elsWithoutAttrs["button"] = true
	p.elsWithoutAttrs["canvas"] = true
	p.elsWithoutAttrs["caption"] = true
	p.elsWithoutAttrs["cite"] = true
	p.elsWithoutAttrs["code"] = true
	p.elsWithoutAttrs["col"] = true
	p.elsWithoutAttrs["colgroup"] = true
	p.elsWithoutAttrs["datalist"] = true
	p.elsWithoutAttrs["dd"] = true
	p.elsWithoutAttrs["del"] = true
	p.elsWithoutAttrs["details"] = true
	p.elsWithoutAttrs["dfn"] = true
	p.elsWithoutAttrs["div"] = true
	p.elsWithoutAttrs["dl"] = true
	p.elsWithoutAttrs["dt"] = true
	p.elsWithoutAttrs["em"] = true
	p.elsWithoutAttrs["fieldset"] = true
	p.elsWithoutAttrs["figcaption"] = true
	p.elsWithoutAttrs["figure"] = true
	p.elsWithoutAttrs["footer"] = true
	p.elsWithoutAttrs["h1"] = true
	p.elsWithoutAttrs["h2"] = true
	p.elsWithoutAttrs["h3"] = true
	p.elsWithoutAttrs["h4"] = true
	p.elsWithoutAttrs["h5"] = true
	p.elsWithoutAttrs["h6"] = true
	p.elsWithoutAttrs["head"] = true
	p.elsWithoutAttrs["header"] = true
	p.elsWithoutAttrs["hgroup"] = true
	p.elsWithoutAttrs["hr"] = true
	p.elsWithoutAttrs["html"] = true
	p.elsWithoutAttrs["i"] = true
	p.elsWithoutAttrs["ins"] = true
	p.elsWithoutAttrs["kbd"] = true
	p.elsWithoutAttrs["li"] = true
	p.elsWithoutAttrs["mark"] = true
	p.elsWithoutAttrs["nav"] = true
	p.elsWithoutAttrs["ol"] = true
	p.elsWithoutAttrs["optgroup"] = true
	p.elsWithoutAttrs["option"] = true
	p.elsWithoutAttrs["p"] = true
	p.elsWithoutAttrs["pre"] = true
	p.elsWithoutAttrs["q"] = true
	p.elsWithoutAttrs["rp"] = true
	p.elsWithoutAttrs["rt"] = true
	p.elsWithoutAttrs["ruby"] = true
	p.elsWithoutAttrs["s"] = true
	p.elsWithoutAttrs["samp"] = true
	p.elsWithoutAttrs["section"] = true
	p.elsWithoutAttrs["select"] = true
	p.elsWithoutAttrs["small"] = true
	p.elsWithoutAttrs["span"] = true
	p.elsWithoutAttrs["strike"] = true
	p.elsWithoutAttrs["strong"] = true
	p.elsWithoutAttrs["style"] = true
	p.elsWithoutAttrs["sub"] = true
	p.elsWithoutAttrs["summary"] = true
	p.elsWithoutAttrs["sup"] = true
	p.elsWithoutAttrs["svg"] = true
	p.elsWithoutAttrs["table"] = true
	p.elsWithoutAttrs["tbody"] = true
	p.elsWithoutAttrs["td"] = true
	p.elsWithoutAttrs["textarea"] = true
	p.elsWithoutAttrs["tfoot"] = true
	p.elsWithoutAttrs["th"] = true
	p.elsWithoutAttrs["thead"] = true
	p.elsWithoutAttrs["time"] = true
	p.elsWithoutAttrs["tr"] = true
	p.elsWithoutAttrs["tt"] = true
	p.elsWithoutAttrs["u"] = true
	p.elsWithoutAttrs["ul"] = true
	p.elsWithoutAttrs["var"] = true
	p.elsWithoutAttrs["video"] = true
	p.elsWithoutAttrs["wbr"] = true

}
<|endoftext|>


<commit_before>package telebot

import (
	"time"

	"github.com/pkg/errors"
)

// Poller is a provider of Updates.
//
// All pollers must implement Poll(), which accepts bot
// pointer and subscription channel and start polling
// synchronously straight away.
type Poller interface {
	// Poll is supposed to take the bot object
	// subscription channel and start polling
	// for Updates immediately.
	//
	// Poller must listen for stop constantly and close
	// it as soon as it's done polling.
	Poll(b *Bot, updates chan Update, stop chan struct{})
}

// MiddlewarePoller is a special kind of poller that acts
// like a filter for updates. It could be used for spam
// handling, banning or whatever.
//
// For heavy middleware, use increased capacity.
//
type MiddlewarePoller struct {
	Poller Poller
	filter func(*Update) bool

	Capacity int // Default: 1
}

// NewMiddlewarePoller wait for it... constructs a new middleware poller.
func NewMiddlewarePoller(original Poller, filter func(*Update) bool) *MiddlewarePoller {
	return &MiddlewarePoller{
		Poller: original,
		filter: filter,
	}
}

// Poll sieves updates through middleware filter.
func (p *MiddlewarePoller) Poll(b *Bot, dest chan Update, stop chan struct{}) {
	cap := 1
	if p.Capacity > 1 {
		cap = p.Capacity
	}

	middle := make(chan Update, cap)
	stopPoller := make(chan struct{})

	go p.Poller.Poll(b, middle, stopPoller)

	for {
		select {
		// call to stop
		case <-stop:
			stopPoller <- struct{}{}

		// poller is done
		case <-stopPoller:
			close(stop)
			return

		case upd := <-middle:
			if p.filter(&upd) {
				dest <- upd
			}
		}
	}
}

// LongPoller is a classic LongPoller with timeout.
type LongPoller struct {
	Timeout time.Duration

	LastUpdateID int
}

// Poll does long polling.
func (p *LongPoller) Poll(b *Bot, dest chan Update, stop chan struct{}) {
	go func(stop chan struct{}) {
		<-stop
		close(stop)
	}(stop)

	for {
		updates, err := b.getUpdates(p.LastUpdateID+1, p.Timeout)

		if err != nil {
			b.debug(errors.Wrap(err, "getUpdates() failed"))
			continue
		}

		for _, update := range updates {
			p.LastUpdateID = update.ID
			dest <- update
		}
	}
}
<commit_msg>MiddlewarePoller's Filter field is now exported.<commit_after>package telebot

import (
	"time"

	"github.com/pkg/errors"
)

// Poller is a provider of Updates.
//
// All pollers must implement Poll(), which accepts bot
// pointer and subscription channel and start polling
// synchronously straight away.
type Poller interface {
	// Poll is supposed to take the bot object
	// subscription channel and start polling
	// for Updates immediately.
	//
	// Poller must listen for stop constantly and close
	// it as soon as it's done polling.
	Poll(b *Bot, updates chan Update, stop chan struct{})
}

// MiddlewarePoller is a special kind of poller that acts
// like a filter for updates. It could be used for spam
// handling, banning or whatever.
//
// For heavy middleware, use increased capacity.
//
type MiddlewarePoller struct {
	Capacity int // Default: 1
	Poller   Poller
	Filter   func(*Update) bool
}

// NewMiddlewarePoller wait for it... constructs a new middleware poller.
func NewMiddlewarePoller(original Poller, filter func(*Update) bool) *MiddlewarePoller {
	return &MiddlewarePoller{
		Poller: original,
		Filter: filter,
	}
}

// Poll sieves updates through middleware filter.
func (p *MiddlewarePoller) Poll(b *Bot, dest chan Update, stop chan struct{}) {
	cap := 1
	if p.Capacity > 1 {
		cap = p.Capacity
	}

	middle := make(chan Update, cap)
	stopPoller := make(chan struct{})

	go p.Poller.Poll(b, middle, stopPoller)

	for {
		select {
		// call to stop
		case <-stop:
			stopPoller <- struct{}{}

		// poller is done
		case <-stopPoller:
			close(stop)
			return

		case upd := <-middle:
			if p.Filter(&upd) {
				dest <- upd
			}
		}
	}
}

// LongPoller is a classic LongPoller with timeout.
type LongPoller struct {
	Timeout time.Duration

	LastUpdateID int
}

// Poll does long polling.
func (p *LongPoller) Poll(b *Bot, dest chan Update, stop chan struct{}) {
	go func(stop chan struct{}) {
		<-stop
		close(stop)
	}(stop)

	for {
		updates, err := b.getUpdates(p.LastUpdateID+1, p.Timeout)

		if err != nil {
			b.debug(errors.Wrap(err, "getUpdates() failed"))
			continue
		}

		for _, update := range updates {
			p.LastUpdateID = update.ID
			dest <- update
		}
	}
}
<|endoftext|>


<commit_before>package pointer

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"github.com/github/git-lfs/lfs"
	"io"
	"regexp"
	"strconv"
	"strings"
)

var (
	v1Aliases = []string{
		"https://hawser.github.com/spec/v1",  // pre-release
		"https://git-lfs.github.com/spec/v1", // public launch
	}
	latest = "https://git-lfs.github.com/spec/v1"

	oidType  = "sha256"
	oidRE    = regexp.MustCompile(`\A[0-9a-fA-F]{64}`)
	template = `version %s
oid sha256:%s
size %d
`
	matcherRE   = regexp.MustCompile("git-media|hawser|git-lfs")
	pointerKeys = []string{"version", "oid", "size"}
)

type Pointer struct {
	Version string
	Oid     string
	Size    int64
	OidType string
}

func NewPointer(oid string, size int64) *Pointer {
	return &Pointer{latest, oid, size, oidType}
}

func (p *Pointer) Smudge(writer io.Writer, workingfile string, cb lfs.CopyCallback) error {
	return Smudge(writer, p, workingfile, cb)
}

func (p *Pointer) Encode(writer io.Writer) (int, error) {
	return Encode(writer, p)
}

func (p *Pointer) Encoded() string {
	return fmt.Sprintf(template, latest, p.Oid, p.Size)
}

func Encode(writer io.Writer, pointer *Pointer) (int, error) {
	return writer.Write([]byte(pointer.Encoded()))
}

func Decode(reader io.Reader) (*Pointer, error) {
	buf := make([]byte, 200)
	written, err := reader.Read(buf)
	if err != nil {
		return nil, err
	}

	return decodeKV(bytes.TrimSpace(buf[0:written]))
}

func verifyVersion(version string) error {
	if len(version) == 0 {
		return errors.New("Missing version")
	}

	for _, v := range v1Aliases {
		if v == version {
			return nil
		}
	}

	return errors.New("Invalid version: " + version)
}

func decodeKV(data []byte) (*Pointer, error) {
	parsed, err := decodeKVData(data)
	if err != nil {
		return nil, err
	}

	if err := verifyVersion(parsed["version"]); err != nil {
		return nil, err
	}

	oidValue, ok := parsed["oid"]
	if !ok {
		return nil, errors.New("Invalid Oid")
	}

	oidParts := strings.SplitN(oidValue, ":", 2)
	if len(oidParts) != 2 {
		return nil, errors.New("Invalid Oid type in" + oidValue)
	}
	if oidParts[0] != oidType {
		return nil, errors.New("Invalid Oid type: " + oidParts[0])
	}
	oid := oidParts[1]

	var size int64
	sizeStr, ok := parsed["size"]
	if !ok {
		return nil, errors.New("Invalid Oid")
	} else {
		size, err = strconv.ParseInt(sizeStr, 10, 0)
		if err != nil {
			return nil, errors.New("Invalid size: " + sizeStr)
		}
	}

	return NewPointer(oid, size), nil
}

func decodeKVData(data []byte) (map[string]string, error) {
	m := make(map[string]string)

	if !matcherRE.Match(data) {
		return m, fmt.Errorf("Not a valid Git LFS pointer file.")
	}

	scanner := bufio.NewScanner(bytes.NewBuffer(data))
	line := 0
	numKeys := len(pointerKeys)
	for scanner.Scan() {
		text := scanner.Text()
		if len(text) == 0 {
			continue
		}

		parts := strings.SplitN(text, " ", 2)
		key := parts[0]

		if numKeys <= line {
			return m, fmt.Errorf("Extra line: %s", text)
		}

		if expected := pointerKeys[line]; key != expected {
			return m, fmt.Errorf("Expected key %s, got %s", expected, key)
		}

		line += 1
		if len(parts) < 2 {
			return m, fmt.Errorf("Error reading line %d: %s", line, text)
		}

		m[key] = parts[1]
	}

	return m, scanner.Err()
}
<commit_msg>add an old alpha alias<commit_after>package pointer

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"github.com/github/git-lfs/lfs"
	"io"
	"regexp"
	"strconv"
	"strings"
)

var (
	v1Aliases = []string{
		"http://git-media.io/v/2",            // alpha
		"https://hawser.github.com/spec/v1",  // pre-release
		"https://git-lfs.github.com/spec/v1", // public launch
	}
	latest = "https://git-lfs.github.com/spec/v1"

	oidType  = "sha256"
	oidRE    = regexp.MustCompile(`\A[0-9a-fA-F]{64}`)
	template = `version %s
oid sha256:%s
size %d
`
	matcherRE   = regexp.MustCompile("git-media|hawser|git-lfs")
	pointerKeys = []string{"version", "oid", "size"}
)

type Pointer struct {
	Version string
	Oid     string
	Size    int64
	OidType string
}

func NewPointer(oid string, size int64) *Pointer {
	return &Pointer{latest, oid, size, oidType}
}

func (p *Pointer) Smudge(writer io.Writer, workingfile string, cb lfs.CopyCallback) error {
	return Smudge(writer, p, workingfile, cb)
}

func (p *Pointer) Encode(writer io.Writer) (int, error) {
	return Encode(writer, p)
}

func (p *Pointer) Encoded() string {
	return fmt.Sprintf(template, latest, p.Oid, p.Size)
}

func Encode(writer io.Writer, pointer *Pointer) (int, error) {
	return writer.Write([]byte(pointer.Encoded()))
}

func Decode(reader io.Reader) (*Pointer, error) {
	buf := make([]byte, 200)
	written, err := reader.Read(buf)
	if err != nil {
		return nil, err
	}

	return decodeKV(bytes.TrimSpace(buf[0:written]))
}

func verifyVersion(version string) error {
	if len(version) == 0 {
		return errors.New("Missing version")
	}

	for _, v := range v1Aliases {
		if v == version {
			return nil
		}
	}

	return errors.New("Invalid version: " + version)
}

func decodeKV(data []byte) (*Pointer, error) {
	parsed, err := decodeKVData(data)
	if err != nil {
		return nil, err
	}

	if err := verifyVersion(parsed["version"]); err != nil {
		return nil, err
	}

	oidValue, ok := parsed["oid"]
	if !ok {
		return nil, errors.New("Invalid Oid")
	}

	oidParts := strings.SplitN(oidValue, ":", 2)
	if len(oidParts) != 2 {
		return nil, errors.New("Invalid Oid type in" + oidValue)
	}
	if oidParts[0] != oidType {
		return nil, errors.New("Invalid Oid type: " + oidParts[0])
	}
	oid := oidParts[1]

	var size int64
	sizeStr, ok := parsed["size"]
	if !ok {
		return nil, errors.New("Invalid Oid")
	} else {
		size, err = strconv.ParseInt(sizeStr, 10, 0)
		if err != nil {
			return nil, errors.New("Invalid size: " + sizeStr)
		}
	}

	return NewPointer(oid, size), nil
}

func decodeKVData(data []byte) (map[string]string, error) {
	m := make(map[string]string)

	if !matcherRE.Match(data) {
		return m, fmt.Errorf("Not a valid Git LFS pointer file.")
	}

	scanner := bufio.NewScanner(bytes.NewBuffer(data))
	line := 0
	numKeys := len(pointerKeys)
	for scanner.Scan() {
		text := scanner.Text()
		if len(text) == 0 {
			continue
		}

		parts := strings.SplitN(text, " ", 2)
		key := parts[0]

		if numKeys <= line {
			return m, fmt.Errorf("Extra line: %s", text)
		}

		if expected := pointerKeys[line]; key != expected {
			return m, fmt.Errorf("Expected key %s, got %s", expected, key)
		}

		line += 1
		if len(parts) < 2 {
			return m, fmt.Errorf("Error reading line %d: %s", line, text)
		}

		m[key] = parts[1]
	}

	return m, scanner.Err()
}
<|endoftext|>


<commit_before>// Copyright GoFrame gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

// Package errors provides simple functions to manipulate errors.
//
// Very note that, this package is quite a base package, which should not import extra
// packages except standard packages, to avoid cycle imports.
package gerror

import (
	"fmt"
)

// apiCode is the interface for Code feature.
type apiCode interface {
	Error() string // It should be an error.
	Code() int
}

// apiStack is the interface for Stack feature.
type apiStack interface {
	Error() string // It should be an error.
	Stack() string
}

// apiCause is the interface for Cause feature.
type apiCause interface {
	Error() string // It should be an error.
	Cause() error
}

// apiCurrent is the interface for Current feature.
type apiCurrent interface {
	Error() string // It should be an error.
	Current() error
}

// apiNext is the interface for Next feature.
type apiNext interface {
	Error() string // It should be an error.
	Next() error
}

// New creates and returns an error which is formatted from given text.
func New(text string) error {
	return &Error{
		stack: callers(),
		text:  text,
		code:  -1,
	}
}

// Newf returns an error that formats as the given format and args.
func Newf(format string, args ...interface{}) error {
	return &Error{
		stack: callers(),
		text:  fmt.Sprintf(format, args...),
		code:  -1,
	}
}

// NewSkip creates and returns an error which is formatted from given text.
// The parameter <skip> specifies the stack callers skipped amount.
func NewSkip(skip int, text string) error {
	return &Error{
		stack: callers(skip),
		text:  text,
		code:  -1,
	}
}

// NewSkipf returns an error that formats as the given format and args.
// The parameter <skip> specifies the stack callers skipped amount.
func NewSkipf(skip int, format string, args ...interface{}) error {
	return &Error{
		stack: callers(skip),
		text:  fmt.Sprintf(format, args...),
		code:  -1,
	}
}

// Wrap wraps error with text.
// It returns nil if given err is nil.
func Wrap(err error, text string) error {
	if err == nil {
		return nil
	}
	return &Error{
		error: err,
		stack: callers(),
		text:  text,
		code:  -1,
	}
}

// Wrapf returns an error annotating err with a stack trace
// at the point Wrapf is called, and the format specifier.
// It returns nil if given <err> is nil.
func Wrapf(err error, format string, args ...interface{}) error {
	if err == nil {
		return nil
	}
	return &Error{
		error: err,
		stack: callers(),
		text:  fmt.Sprintf(format, args...),
		code:  -1,
	}
}

// NewCode creates and returns an error that has error code and given text.
func NewCode(code int, text string) error {
	return &Error{
		stack: callers(),
		text:  text,
		code:  code,
	}
}

// NewCodef returns an error that has error code and formats as the given format and args.
func NewCodef(code int, format string, args ...interface{}) error {
	return &Error{
		stack: callers(),
		text:  fmt.Sprintf(format, args...),
		code:  code,
	}
}

// NewCodeSkip creates and returns an error which has error code and is formatted from given text.
// The parameter <skip> specifies the stack callers skipped amount.
func NewCodeSkip(code, skip int, text string) error {
	return &Error{
		stack: callers(skip),
		text:  text,
		code:  code,
	}
}

// NewCodeSkipf returns an error that has error code and formats as the given format and args.
// The parameter <skip> specifies the stack callers skipped amount.
func NewCodeSkipf(code, skip int, format string, args ...interface{}) error {
	return &Error{
		stack: callers(skip),
		text:  fmt.Sprintf(format, args...),
		code:  code,
	}
}

// WrapCode wraps error with code and text.
// It returns nil if given err is nil.
func WrapCode(code int, err error, text string) error {
	if err == nil {
		return nil
	}
	return &Error{
		error: err,
		stack: callers(),
		text:  text,
		code:  code,
	}
}

// WrapCodef wraps error with code and format specifier.
// It returns nil if given <err> is nil.
func WrapCodef(code int, err error, format string, args ...interface{}) error {
	if err == nil {
		return nil
	}
	return &Error{
		error: err,
		stack: callers(),
		text:  fmt.Sprintf(format, args...),
		code:  code,
	}
}

// Cause returns the error code of current error.
// It returns -1 if it has no error code or it does not implements interface Code.
func Code(err error) int {
	if err != nil {
		if e, ok := err.(apiCode); ok {
			return e.Code()
		}
	}
	return -1
}

// Cause returns the root cause error of <err>.
func Cause(err error) error {
	if err != nil {
		if e, ok := err.(apiCause); ok {
			return e.Cause()
		}
	}
	return err
}

// Stack returns the stack callers as string.
// It returns an empty string if the <err> does not support stacks.
func Stack(err error) string {
	if err == nil {
		return ""
	}
	if e, ok := err.(apiStack); ok {
		return e.Stack()
	}
	return ""
}

// Current creates and returns the current level error.
// It returns nil if current level error is nil.
func Current(err error) error {
	if err == nil {
		return nil
	}
	if e, ok := err.(apiCurrent); ok {
		return e.Current()
	}
	return err
}

// Next returns the next level error.
// It returns nil if current level error or the next level error is nil.
func Next(err error) error {
	if err == nil {
		return nil
	}
	if e, ok := err.(apiNext); ok {
		return e.Next()
	}
	return nil
}
<commit_msg>improve gerror.Stack<commit_after>// Copyright GoFrame gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

// Package errors provides simple functions to manipulate errors.
//
// Very note that, this package is quite a base package, which should not import extra
// packages except standard packages, to avoid cycle imports.
package gerror

import (
	"fmt"
)

// apiCode is the interface for Code feature.
type apiCode interface {
	Error() string // It should be an error.
	Code() int
}

// apiStack is the interface for Stack feature.
type apiStack interface {
	Error() string // It should be an error.
	Stack() string
}

// apiCause is the interface for Cause feature.
type apiCause interface {
	Error() string // It should be an error.
	Cause() error
}

// apiCurrent is the interface for Current feature.
type apiCurrent interface {
	Error() string // It should be an error.
	Current() error
}

// apiNext is the interface for Next feature.
type apiNext interface {
	Error() string // It should be an error.
	Next() error
}

// New creates and returns an error which is formatted from given text.
func New(text string) error {
	return &Error{
		stack: callers(),
		text:  text,
		code:  -1,
	}
}

// Newf returns an error that formats as the given format and args.
func Newf(format string, args ...interface{}) error {
	return &Error{
		stack: callers(),
		text:  fmt.Sprintf(format, args...),
		code:  -1,
	}
}

// NewSkip creates and returns an error which is formatted from given text.
// The parameter <skip> specifies the stack callers skipped amount.
func NewSkip(skip int, text string) error {
	return &Error{
		stack: callers(skip),
		text:  text,
		code:  -1,
	}
}

// NewSkipf returns an error that formats as the given format and args.
// The parameter <skip> specifies the stack callers skipped amount.
func NewSkipf(skip int, format string, args ...interface{}) error {
	return &Error{
		stack: callers(skip),
		text:  fmt.Sprintf(format, args...),
		code:  -1,
	}
}

// Wrap wraps error with text.
// It returns nil if given err is nil.
func Wrap(err error, text string) error {
	if err == nil {
		return nil
	}
	return &Error{
		error: err,
		stack: callers(),
		text:  text,
		code:  -1,
	}
}

// Wrapf returns an error annotating err with a stack trace
// at the point Wrapf is called, and the format specifier.
// It returns nil if given <err> is nil.
func Wrapf(err error, format string, args ...interface{}) error {
	if err == nil {
		return nil
	}
	return &Error{
		error: err,
		stack: callers(),
		text:  fmt.Sprintf(format, args...),
		code:  -1,
	}
}

// NewCode creates and returns an error that has error code and given text.
func NewCode(code int, text string) error {
	return &Error{
		stack: callers(),
		text:  text,
		code:  code,
	}
}

// NewCodef returns an error that has error code and formats as the given format and args.
func NewCodef(code int, format string, args ...interface{}) error {
	return &Error{
		stack: callers(),
		text:  fmt.Sprintf(format, args...),
		code:  code,
	}
}

// NewCodeSkip creates and returns an error which has error code and is formatted from given text.
// The parameter <skip> specifies the stack callers skipped amount.
func NewCodeSkip(code, skip int, text string) error {
	return &Error{
		stack: callers(skip),
		text:  text,
		code:  code,
	}
}

// NewCodeSkipf returns an error that has error code and formats as the given format and args.
// The parameter <skip> specifies the stack callers skipped amount.
func NewCodeSkipf(code, skip int, format string, args ...interface{}) error {
	return &Error{
		stack: callers(skip),
		text:  fmt.Sprintf(format, args...),
		code:  code,
	}
}

// WrapCode wraps error with code and text.
// It returns nil if given err is nil.
func WrapCode(code int, err error, text string) error {
	if err == nil {
		return nil
	}
	return &Error{
		error: err,
		stack: callers(),
		text:  text,
		code:  code,
	}
}

// WrapCodef wraps error with code and format specifier.
// It returns nil if given <err> is nil.
func WrapCodef(code int, err error, format string, args ...interface{}) error {
	if err == nil {
		return nil
	}
	return &Error{
		error: err,
		stack: callers(),
		text:  fmt.Sprintf(format, args...),
		code:  code,
	}
}

// Cause returns the error code of current error.
// It returns -1 if it has no error code or it does not implements interface Code.
func Code(err error) int {
	if err != nil {
		if e, ok := err.(apiCode); ok {
			return e.Code()
		}
	}
	return -1
}

// Cause returns the root cause error of <err>.
func Cause(err error) error {
	if err != nil {
		if e, ok := err.(apiCause); ok {
			return e.Cause()
		}
	}
	return err
}

// Stack returns the stack callers as string.
// It returns the error string directly if the <err> does not support stacks.
func Stack(err error) string {
	if err == nil {
		return ""
	}
	if e, ok := err.(apiStack); ok {
		return e.Stack()
	}
	return err.Error()
}

// Current creates and returns the current level error.
// It returns nil if current level error is nil.
func Current(err error) error {
	if err == nil {
		return nil
	}
	if e, ok := err.(apiCurrent); ok {
		return e.Current()
	}
	return err
}

// Next returns the next level error.
// It returns nil if current level error or the next level error is nil.
func Next(err error) error {
	if err == nil {
		return nil
	}
	if e, ok := err.(apiNext); ok {
		return e.Next()
	}
	return nil
}
<|endoftext|>


<commit_before>package dulbecco

import (
	"bytes"
	"crypto/sha1"
	"fmt"
	"log"
	"math/rand"
	"os"
	"os/exec"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"text/template"
	"time"
)

type IRCCallback func(message *Message)

type CallbackMap map[string]map[string]IRCCallback

func (c *Connection) AddCallback(name string, callback IRCCallback) string {
	name = strings.ToUpper(name)

	if _, ok := c.events[name]; !ok {
		c.events[name] = make(map[string]IRCCallback)
	}

	signature := sha1.Sum([]byte(fmt.Sprintf("%v%d", reflect.ValueOf(callback).Pointer(), rand.Int63())))
	id := fmt.Sprintf("%x", signature)
	c.events[name][id] = callback
	return id
}

func (c *Connection) RemoveCallback(name string, id string) bool {
	name = strings.ToUpper(name)

	if event, ok := c.events[name]; ok {
		if _, ok := event[id]; ok {
			delete(c.events[name], id)
			return true
		}
		fmt.Printf("No callback found in %s with id %s\n", name, id)
		return false
	}

	fmt.Printf("Event not found: %s\n", name)
	return false
}

// Execute registered callbacks for message
func (c *Connection) RunCallbacks(message *Message) {
	if callbacks, ok := c.events[message.Cmd]; ok {
		for _, callback := range callbacks {
			callback(message)
		}
	}

	// catch-all handlers
	if callbacks, ok := c.events["*"]; ok {
		for _, callback := range callbacks {
			callback(message)
		}
	}
}

// Add internal callbacks.
func (c *Connection) SetupCallbacks() {
	c.AddCallback("INIT", c.h_INIT)
	c.AddCallback("001", c.h_001)
	c.AddCallback("433", c.h_433)
	c.AddCallback("PRIVMSG", c.h_PRIVMSG)
	c.AddCallback("NOTICE", c.h_NOTICE)
	c.AddCallback("PING", c.h_PING)
	c.AddCallback("PONG", c.h_PONG)
	c.AddCallback("CTCP", c.h_CTCP)
	c.AddCallback("JOIN", c.h_JOIN)
	c.AddCallback("332", c.h_332)
	c.AddCallback("352", c.h_352)
	c.AddCallback("353", c.h_353)
	c.AddCallback("KICK", c.h_KICK)
	c.AddCallback("PART", c.h_PART)
}

// Add callbacks for every configured plugin.
func (c *Connection) SetupPlugins(plugins []PluginConfiguration) {
	for _, plugin := range plugins {
		// log.Println("Adding callback for plugin:", plugin.Name)
		c.addPluginCallback(plugin)
	}
}

// Add a callback for a plugin.
// We use a separate method because we need a "copy" of the "plugin" variable,
// since it will be bound inside the closure.
func (c *Connection) addPluginCallback(plugin PluginConfiguration) {

	// this is the actual plugin callback
	c.AddCallback("PRIVMSG", func(message *Message) {
		// "trigger" contains a regular expression with optional capture groups
		// command is a text/template that can contain captures from the trigger
		// regexp.
		re := regexp.MustCompile(plugin.Trigger)
		match := re.FindStringSubmatch(message.Args[1])
		if match == nil {
			return
		}
		captures := make(map[string]string)
		for i, name := range re.SubexpNames() {
			if i == 0 || name == "" {
				continue
			}
			captures[name] = match[i]
		}
		cmdtpl, err := template.New("cmd").Parse(plugin.Command)
		if err != nil {
			log.Printf("Cannot parse template: %s\n", err)
			return
		}
		var cmdbuf bytes.Buffer
		if err := cmdtpl.Execute(&cmdbuf, captures); err != nil {
			log.Printf("Cannot execute template: %s\n", err)
			return
		}
		cmdline := cmdbuf.String()

		log.Printf("Running plugin %s: %s", plugin.Name, cmdline)

		cmds := strings.Fields(cmdline)
		cmd := exec.Command(cmds[0], cmds[1:]...)
		env := []string{
			"IRC_NICKNAME=" + message.Nick,
			"IRC_HOST=" + message.Host,
			"IRC_IDENT=" + message.Ident,
			"IRC_ARGS=" + strings.Join(message.Args, " "),
			"IRC_COMMAND=" + message.Cmd,
			"IRC_TIMESTAMP=" + message.Time.String(),
			"IRC_RAW=" + message.Raw,
		}
		cmd.Env = append(os.Environ(), env...)

		if out, err := cmd.Output(); err == nil {
			lines := strings.Trim(string(out), "\n")
			target := message.ReplyTarget()
			for _, line := range strings.Split(lines, "\n") {
				c.Privmsg(target, line)
			}
		} else {
			log.Printf("Failed exec for plugin '%s': %s\n", plugin.Name, err)
		}
	})
}

// callbacks

// helper function to call when a user leave a channel
func (c *Connection) removeUserChannel(nickname, channelname string) {
	_, exists := c.chanmap[channelname]
	if !exists {
		log.Println("removeUserChannel: channel not found:", channelname)
		return
	}
	user, exists := c.usermap[nickname]
	if !exists {
		log.Println("removeUserChannel: nickname not found:", nickname)
		return
	}
	delete(user.channels, channelname)
	if len(user.channels) == 0 {
		delete(c.usermap, nickname)
	}
}

// The INIT pseudo-event is fired when the TCP connection to the IRC
// server is established successfully.
func (c *Connection) h_INIT(message *Message) {
	if len(c.config.Password) > 0 {
		c.Pass(c.config.Password)
	}
	c.Nick(c.nickname)
	c.User(c.config.Username, c.config.Realname)
}

func (c *Connection) JoinChannels() {
	for _, channel := range c.config.Channels {
		c.Join(channel)
	}
}

// 001 numeric means we are "really connected" to the server. In this callback
// is safe to do things like joining channels or identifying with IRC services.
func (c *Connection) h_001(message *Message) {
	if c.config.Nickserv != "" {
		c.LoginNickserv()
	} else {
		c.JoinChannels()
	}
}

// someone joined a channel
func (c *Connection) h_JOIN(message *Message) {
	var channame = &message.Args[0]

	// are we the one joining the channel?
	if message.Nick == c.nickname {
		delete(c.chanmap, *channame)
		channel := &Channel{name: message.Args[0]}
		c.chanmap[channel.name] = channel
		log.Printf("we have joined %s\n", message.Args[0])

		log.Println("Sending a WHO")
		c.Who(channel.name)
	} else {
		log.Printf("%s joined %s\n", message.Nick, message.Args[0])
	}
}

func (c *Connection) h_PART(message *Message) {
	if message.Nick == c.nickname {
		delete(c.chanmap, message.Args[0])
	} else {
		c.removeUserChannel(message.Nick, message.Args[0])
	}
}

// RPL_TOPIC
func (c *Connection) h_332(message *Message) {
	var channame = &message.Args[1]
	var topic = &message.Args[2]

	if channel, ok := c.chanmap[*channame]; ok {
		channel.topic = *topic
	} else {
		log.Printf("[332] Channel %s not in map??\n", *channame)
	}
}

// WHO
func (c *Connection) h_352(message *Message) {
	if len(message.Args) != 8 {
		log.Println("Invalid 352:", message)
		return
	}
	nickname := &message.Args[5]
	user, exists := c.usermap[*nickname]
	if !exists {
		user = NewUser(*nickname)
	}

	user.username = message.Args[2]
	user.hostname = message.Args[3]
	user.channels[message.Args[1]] = true

	realnameStr := strings.SplitN(message.Args[7], " ", 2)
	if len(realnameStr) > 1 {
		user.realname = realnameStr[1]
	}
}

// NAMES
func (c *Connection) h_353(message *Message) {
	var channame = message.Args[2]
	var names = strings.Split(strings.Trim(message.Args[3], " "), " ")

	for _, name := range names {
		if user, ok := c.usermap[name]; ok {
			user.channels[channame] = true
		} else {
			user := NewUser(name)
			user.channels[channame] = true
			c.usermap[name] = user
		}
	}

	if channel, ok := c.chanmap[channame]; ok {
		channel.names = append(channel.names, names...)
		log.Printf("NAMES on %s: %v\n", channame, channel.names)
	}
}

// rejoin when kicked after 5 seconds
func (c *Connection) h_KICK(message *Message) {
	var channame = message.Args[0]
	var nick = strings.Trim(message.Args[1], " ")

	if nick == c.nickname {
		cTimer := time.After(5 * time.Second)
		go func() {
			<-cTimer
			c.Join(channame)
		}()
	} else {
		c.removeUserChannel(nick, channame)
	}
}

// ERR_NICKNAMEINUSE
func (c *Connection) h_433(message *Message) {
	c.Nick(c.nickname + "_")
}

// Server PING.
func (c *Connection) h_PING(message *Message) {
	c.Raw("PONG " + message.Args[0])
}

func (c *Connection) h_PONG(message *Message) {
	theirTime, err := strconv.ParseInt(message.Args[0], 10, 64)
	if err != nil {
		return
	}
	delta := time.Duration(time.Now().UnixNano() - theirTime)
	log.Printf("Lag: %v\n", delta)
}

// generic PRIVMSG callback handling QUIT command and dummy reply.
func (c *Connection) h_PRIVMSG(message *Message) {
	if strings.HasPrefix(message.Args[1], "!quit") &&
		message.Nick == "sand" {
		// XXX we should find a smarter way to disable auto-reconnect
		c.tryReconnect = false
		c.Quit()
		return
	} else if message.Nick == NickservName && strings.Index(message.Args[1], "accepted") != -1 {
		c.JoinChannels()
		return
	} else if strings.HasPrefix(message.Args[1], "!") {
		// this is a command, let it be handled by plugins callbacks
		return
	} else if !strings.HasPrefix(message.Args[1], c.nickname) {
		// it's not a message directed to us, but we can still train markov from it
		c.mdb.ReadSentence(message.Args[1])
		return
	}

	// strip our own nickname from the input text
	renick := regexp.MustCompile(fmt.Sprintf("%s *[:,] *", c.nickname))
	text := renick.ReplaceAllLiteralString(message.Args[1], "")

	// markov!
	c.mdb.ReadSentence(text)
	reply := c.mdb.Generate(text)

	// do not bother answering if the answer is the same as the input phrase
	if reply == text || len(reply) == 0 {
		c.Privmsg(message.Args[0], message.Nick+": "+GetRandomReply())
		return
	}

	if message.IsFromChannel() {
		c.Privmsg(message.Args[0], message.Nick+": "+reply)
	} else {
		c.Privmsg(message.Nick, reply)
	}
}

func (c *Connection) h_NOTICE(message *Message) {
	nick := strings.ToLower(message.Nick)
	if nick == NickservName && strings.Index(message.Args[1], "accepted") != -1 {
		c.JoinChannels()
		return
	}
}

// general CTCP handler
func (c *Connection) h_CTCP(message *Message) {
	if message.Args[0] == "PING" {
		c.CtcpReply(message.Nick, fmt.Sprintf("PING %s", message.Args[2]))
	}
}
<commit_msg>simplify callbacks code<commit_after>package dulbecco

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"text/template"
	"time"
)

type IRCCallback func(message *Message)

type CallbackMap map[string][]IRCCallback

func (c *Connection) AddCallback(name string, callback IRCCallback) {
	name = strings.ToUpper(name)

	if _, ok := c.events[name]; !ok {
		c.events[name] = make([]IRCCallback, 0)
	}
	c.events[name] = append(c.events[name], callback)
}

// Execute registered callbacks for message
func (c *Connection) RunCallbacks(message *Message) {
	if _, ok := c.events[message.Cmd]; ok {
		for _, callback := range c.events[message.Cmd] {
			callback(message)
		}
	}

	// catch-all handlers
	if _, ok := c.events["*"]; ok {
		for _, callback := range c.events["*"] {
			callback(message)
		}
	}
}

// Add internal callbacks.
func (c *Connection) SetupCallbacks() {
	c.AddCallback("INIT", c.h_INIT)
	c.AddCallback("001", c.h_001)
	c.AddCallback("433", c.h_433)
	c.AddCallback("PRIVMSG", c.h_PRIVMSG)
	c.AddCallback("NOTICE", c.h_NOTICE)
	c.AddCallback("PING", c.h_PING)
	c.AddCallback("PONG", c.h_PONG)
	c.AddCallback("CTCP", c.h_CTCP)
	c.AddCallback("JOIN", c.h_JOIN)
	c.AddCallback("332", c.h_332)
	c.AddCallback("352", c.h_352)
	c.AddCallback("353", c.h_353)
	c.AddCallback("KICK", c.h_KICK)
	c.AddCallback("PART", c.h_PART)
}

// Add callbacks for every configured plugin.
func (c *Connection) SetupPlugins(plugins []PluginConfiguration) {
	for _, plugin := range plugins {
		c.addPluginCallback(plugin)
	}
}

// Add a callback for a plugin.
// We use a separate method because we need a "copy" of the "plugin" variable,
// since it will be bound inside the closure.
func (c *Connection) addPluginCallback(plugin PluginConfiguration) {

	// this is the actual plugin callback
	c.AddCallback("PRIVMSG", func(message *Message) {
		// "trigger" contains a regular expression with optional capture groups
		// command is a text/template that can contain captures from the trigger
		// regexp.
		re := regexp.MustCompile(plugin.Trigger)
		match := re.FindStringSubmatch(message.Args[1])
		if match == nil {
			return
		}
		captures := make(map[string]string)
		for i, name := range re.SubexpNames() {
			if i == 0 || name == "" {
				continue
			}
			captures[name] = match[i]
		}
		cmdtpl, err := template.New("cmd").Parse(plugin.Command)
		if err != nil {
			log.Printf("Cannot parse template: %s\n", err)
			return
		}
		var cmdbuf bytes.Buffer
		if err := cmdtpl.Execute(&cmdbuf, captures); err != nil {
			log.Printf("Cannot execute template: %s\n", err)
			return
		}
		cmdline := cmdbuf.String()

		log.Printf("Running plugin %s: %s", plugin.Name, cmdline)

		cmds := strings.Fields(cmdline)
		cmd := exec.Command(cmds[0], cmds[1:]...)
		env := []string{
			"IRC_NICKNAME=" + message.Nick,
			"IRC_HOST=" + message.Host,
			"IRC_IDENT=" + message.Ident,
			"IRC_ARGS=" + strings.Join(message.Args, " "),
			"IRC_COMMAND=" + message.Cmd,
			"IRC_TIMESTAMP=" + message.Time.String(),
			"IRC_RAW=" + message.Raw,
		}
		cmd.Env = append(os.Environ(), env...)

		if out, err := cmd.Output(); err == nil {
			lines := strings.Trim(string(out), "\n")
			target := message.ReplyTarget()
			for _, line := range strings.Split(lines, "\n") {
				c.Privmsg(target, line)
			}
		} else {
			log.Printf("Failed exec for plugin '%s': %s\n", plugin.Name, err)
		}
	})
}

// callbacks

// helper function to call when a user leave a channel
func (c *Connection) removeUserChannel(nickname, channelname string) {
	_, exists := c.chanmap[channelname]
	if !exists {
		log.Println("removeUserChannel: channel not found:", channelname)
		return
	}
	user, exists := c.usermap[nickname]
	if !exists {
		log.Println("removeUserChannel: nickname not found:", nickname)
		return
	}
	delete(user.channels, channelname)
	if len(user.channels) == 0 {
		delete(c.usermap, nickname)
	}
}

// The INIT pseudo-event is fired when the TCP connection to the IRC
// server is established successfully.
func (c *Connection) h_INIT(message *Message) {
	if len(c.config.Password) > 0 {
		c.Pass(c.config.Password)
	}
	c.Nick(c.nickname)
	c.User(c.config.Username, c.config.Realname)
}

func (c *Connection) JoinChannels() {
	for _, channel := range c.config.Channels {
		c.Join(channel)
	}
}

// 001 numeric means we are "really connected" to the server. In this callback
// is safe to do things like joining channels or identifying with IRC services.
func (c *Connection) h_001(message *Message) {
	if c.config.Nickserv != "" {
		c.LoginNickserv()
	} else {
		c.JoinChannels()
	}
}

// someone joined a channel
func (c *Connection) h_JOIN(message *Message) {
	var channame = &message.Args[0]

	// are we the one joining the channel?
	if message.Nick == c.nickname {
		delete(c.chanmap, *channame)
		channel := &Channel{name: message.Args[0]}
		c.chanmap[channel.name] = channel
		log.Printf("we have joined %s\n", message.Args[0])

		log.Println("Sending a WHO")
		c.Who(channel.name)
	} else {
		log.Printf("%s joined %s\n", message.Nick, message.Args[0])
	}
}

func (c *Connection) h_PART(message *Message) {
	if message.Nick == c.nickname {
		delete(c.chanmap, message.Args[0])
	} else {
		c.removeUserChannel(message.Nick, message.Args[0])
	}
}

// RPL_TOPIC
func (c *Connection) h_332(message *Message) {
	var channame = &message.Args[1]
	var topic = &message.Args[2]

	if channel, ok := c.chanmap[*channame]; ok {
		channel.topic = *topic
	} else {
		log.Printf("[332] Channel %s not in map??\n", *channame)
	}
}

// WHO
func (c *Connection) h_352(message *Message) {
	if len(message.Args) != 8 {
		log.Println("Invalid 352:", message)
		return
	}
	nickname := &message.Args[5]
	user, exists := c.usermap[*nickname]
	if !exists {
		user = NewUser(*nickname)
	}

	user.username = message.Args[2]
	user.hostname = message.Args[3]
	user.channels[message.Args[1]] = true

	realnameStr := strings.SplitN(message.Args[7], " ", 2)
	if len(realnameStr) > 1 {
		user.realname = realnameStr[1]
	}
}

// NAMES
func (c *Connection) h_353(message *Message) {
	var channame = message.Args[2]
	var names = strings.Split(strings.Trim(message.Args[3], " "), " ")

	for _, name := range names {
		if user, ok := c.usermap[name]; ok {
			user.channels[channame] = true
		} else {
			user := NewUser(name)
			user.channels[channame] = true
			c.usermap[name] = user
		}
	}

	if channel, ok := c.chanmap[channame]; ok {
		channel.names = append(channel.names, names...)
		log.Printf("NAMES on %s: %v\n", channame, channel.names)
	}
}

// rejoin when kicked after 5 seconds
func (c *Connection) h_KICK(message *Message) {
	var channame = message.Args[0]
	var nick = strings.Trim(message.Args[1], " ")

	if nick == c.nickname {
		cTimer := time.After(5 * time.Second)
		go func() {
			<-cTimer
			c.Join(channame)
		}()
	} else {
		c.removeUserChannel(nick, channame)
	}
}

// ERR_NICKNAMEINUSE
func (c *Connection) h_433(message *Message) {
	c.Nick(c.nickname + "_")
}

// Server PING.
func (c *Connection) h_PING(message *Message) {
	c.Raw("PONG " + message.Args[0])
}

func (c *Connection) h_PONG(message *Message) {
	theirTime, err := strconv.ParseInt(message.Args[0], 10, 64)
	if err != nil {
		return
	}
	delta := time.Duration(time.Now().UnixNano() - theirTime)
	log.Printf("Lag: %v\n", delta)
}

// generic PRIVMSG callback handling QUIT command and dummy reply.
func (c *Connection) h_PRIVMSG(message *Message) {
	if strings.HasPrefix(message.Args[1], "!quit") &&
		message.Nick == "sand" {
		// XXX we should find a smarter way to disable auto-reconnect
		c.tryReconnect = false
		c.Quit()
		return
	} else if message.Nick == NickservName && strings.Index(message.Args[1], "accepted") != -1 {
		c.JoinChannels()
		return
	} else if strings.HasPrefix(message.Args[1], "!") {
		// this is a command, let it be handled by plugins callbacks
		return
	} else if !strings.HasPrefix(message.Args[1], c.nickname) {
		// it's not a message directed to us, but we can still train markov from it
		c.mdb.ReadSentence(message.Args[1])
		return
	}

	// strip our own nickname from the input text
	renick := regexp.MustCompile(fmt.Sprintf("%s *[:,] *", c.nickname))
	text := renick.ReplaceAllLiteralString(message.Args[1], "")

	// markov!
	c.mdb.ReadSentence(text)
	reply := c.mdb.Generate(text)

	// do not bother answering if the answer is the same as the input phrase
	if reply == text || len(reply) == 0 {
		c.Privmsg(message.Args[0], message.Nick+": "+GetRandomReply())
		return
	}

	if message.IsFromChannel() {
		c.Privmsg(message.Args[0], message.Nick+": "+reply)
	} else {
		c.Privmsg(message.Nick, reply)
	}
}

func (c *Connection) h_NOTICE(message *Message) {
	nick := strings.ToLower(message.Nick)
	if nick == NickservName && strings.Index(message.Args[1], "accepted") != -1 {
		c.JoinChannels()
		return
	}
}

// general CTCP handler
func (c *Connection) h_CTCP(message *Message) {
	if message.Args[0] == "PING" {
		c.CtcpReply(message.Nick, fmt.Sprintf("PING %s", message.Args[2]))
	}
}
<|endoftext|>


<commit_before>// pounce.go
package main

import (
	"flag"
	"fmt"
	"github.com/guelfey/go.dbus"
	"io"
	"net/http"
	"os"
	"strings"
	"time"
)

// download takes a url of type string as an argument
// and returns a response pointer.
func download(url string) *http.Response {

	fmt.Printf("Downloading...\n")
	resp, err := http.Get(url)

	if err != nil {
		panic(err)
	}

	fmt.Printf("Download complete!\n")

	return resp
}

// create takes a filename of type string as an argument
// and returns a file pointer.  It basically calls
// os.Create(filename) and checks err.
func create(filename string) *os.File {

	fmt.Printf("Creating %v ...\n", filename)
	file, err := os.Create(filename)

	if err != nil {
		panic(err)
	}

	fmt.Printf("%v created!\n", filename)

	return file
}

func readFile(infilename string) {

	file, err := os.Open(infilename)

	if err != nil {
		panic(err)
	}

	scanner := bufio.NewScanner(file)

	for scanner.Scan() {
		// TODO: From here we want to download the URL, but we want to spawn a downloader for each line
		// without blocking.  Goroutines, but how? More thought needed.
		line := scanner.Text()
	}

	if err := scanner.Err(); err != nil {
		panic(err)
	}
}

// save takes a file of type io.Writer and a response pointer
// as an argument so that it can return a size as type int64.
// It calls io.Copy, handles any errors, and returns.
func save(file io.Writer, resp *http.Response) int64 {

	fmt.Printf("Writing file...\n")
	complete, err := io.Copy(file, resp.Body)

	if err != nil {
		panic(err)
	}

	return complete
}

// notify takes a message string and channel as an
// argument and displays a dbus notification.
func notify(msg string, done chan<- bool) {

	conn, err := dbus.SessionBus()

	if err != nil {
		panic(err)
	}

	obj := conn.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
	call := obj.Call("org.freedesktop.Notifications.Notify", 0, "", uint32(0),
		"", "GoPounce", msg, []string{}, map[string]dbus.Variant{}, int32(5000))

	if call.Err != nil {
		panic(call.Err)
	}
	done <- true
}

func main() {

	var url string
	var infile string
	var filename string
	done := make(chan bool)

	flag.StringVar(&url, "url", "", "URL to get")
	flag.StringVar(&infile, "infile", "", "An input file with URLs on each line. Newline delimitted.")
	flag.StringVar(&filename, "filename", "", "Destination file to write.")
	flag.Parse()
	flag.Args()

	// TODO: Redesign this as we don't want to have to check again if we have a URL to decide
	// whether to immediately download(url) or if we want to run readFile(infile)
	if url == "" || infile == "" {
		if strings.Contains(flag.Arg(0), "http") || strings.Contains(flag.Arg(0), "www") {
			url = flag.Arg(0)
		} else {
			infile := flag.Arg(0)
		}
	}

	if filename == "" {
		filename = flag.Arg(1)
	}
	start := time.Now()
	resp := download(url)
	defer resp.Body.Close()

	file := create(filename)
	defer file.Close()

	complete := save(file, resp)
	end := time.Now()
	size := complete / 1024
	msg := fmt.Sprintf("Downloaded ~%v kb in %v \n", size, end.Sub(start))
	go notify(msg, done)
	fmt.Printf(msg)

	<-done
}
<commit_msg>We are no longer mangling files when downloading via file input.  See TODO line 178.<commit_after>// pounce.go
package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/guelfey/go.dbus"
	"io"
	"net/http"
	"os"
	"strings"
	"time"
)

// download takes a url of type string as an argument
// and returns a response pointer.
func download(url string) *http.Response {

	fmt.Printf("Downloading...\n")
	resp, err := http.Get(url)

	if err != nil {
		panic(err)
	}
	fmt.Printf("Download complete!\n")

	return resp
}

// create takes a filename of type string as an argument
// and returns a file pointer.  It basically calls
// os.Create(filename) and checks err.
func create(filename string) *os.File {

	file, err := os.Create(filename)

	if err != nil {
		panic(err)
	}

	fmt.Printf("Created %v ...\n", filename)

	return file
}

func multiDownload(url, destination string, channel chan<- bool) {

	fmt.Printf("Downloading...\n")
	resp, err := http.Get(url)

	if err != nil {
		panic(err)
	}
	fmt.Printf("Download complete!\n")

	go multiCreate(destination, resp, channel)
	channel <- true
}

func multiCreate(destination string, response *http.Response, channel chan<- bool) {

	fmt.Printf("Creating %v ...\n", destination)
	file, err := os.Create(destination)

	if err != nil {
		panic(err)
	}

	fmt.Printf("Writing file %v...\n", destination)
	complete, err := io.Copy(file, response.Body)
	defer response.Body.Close()
	defer file.Close()
	fmt.Println(complete)

	if err != nil {
		panic(err)
	}

	channel <- true
}

func readFile(infilename, destination string) {

	file, err := os.Open(infilename)

	if err != nil {
		panic(err)
	}

	scanner := bufio.NewScanner(file)

	lineCount := 0
	channel := make(chan bool)
	for scanner.Scan() {
		lineCount++

		line := scanner.Text()
		splitCount := strings.Count(line, "/")
		endOfSplit := strings.SplitAfterN(line, "/", splitCount)
		filename := string(endOfSplit[len(endOfSplit)-1])

		if strings.Count(filename, "/") > 0 {
			filename = string(strings.SplitAfterN(filename, "/", 2)[1])
		}

		toSave := fmt.Sprintf("%v%v", destination, filename)
		go multiDownload(line, toSave, channel)
	}

	if err := scanner.Err(); err != nil {
		panic(err)
	}

	for i := 0; i < lineCount; i++ {
		<-channel
	}
}

// save takes a file of type io.Writer and a response pointer
// as an argument so that it can return a size as type int64.
// It calls io.Copy, handles any errors, and returns.
func save(file io.Writer, resp *http.Response) int64 {

	fmt.Printf("Writing file...\n")
	complete, err := io.Copy(file, resp.Body)

	if err != nil {
		panic(err)
	}

	return complete
}

// notify takes a message string and channel as an
// argument and displays a dbus notification.
func notify(msg string, done chan<- bool) {

	conn, err := dbus.SessionBus()

	if err != nil {
		panic(err)
	}

	obj := conn.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
	call := obj.Call("org.freedesktop.Notifications.Notify", 0, "", uint32(0),
		"", "GoPounce", msg, []string{}, map[string]dbus.Variant{}, int32(5000))

	if call.Err != nil {
		panic(call.Err)
	}
	done <- true
}

func main() {

	var input string
	var output string
	done := make(chan bool)

	flag.StringVar(&input, "url", "", "URL to get")
	flag.StringVar(&input, "infile", "", "An input file with URLs on each line that are newline delimitted.")
	flag.StringVar(&output, "filename", "", "Destination file to write.")
	flag.StringVar(&output, "directory", "", "Destination directory to write into with trailing '/' (only works with -infile flag)")
	flag.Parse()
	flag.Args()

	if input == "" {
		input = flag.Arg(0)
	}

	if output == "" {
		output = flag.Arg(1)
	}

	start := time.Now()

	// TODO: Refactor, this doesn't conform to DRY.
	if strings.Contains(input, "http") || strings.Contains(input, "www") {
		resp := download(input)
		defer resp.Body.Close()
		file := create(output)
		defer file.Close()
		complete := save(file, resp)

		end := time.Now()
		size := complete / 1024
		msg := fmt.Sprintf("Downloaded ~%v kb in %v \n", size, end.Sub(start))

		go notify(msg, done)
		fmt.Printf(msg)
		<-done
	} else {
		readFile(input, output)
		end := time.Now()
		msg := fmt.Sprintf("Downloaded contents of %v in %v\n", input, end.Sub(start))
		go notify(msg, done)
		fmt.Printf(msg)
		<-done
	}
}
<|endoftext|>


<commit_before>package options

import (
	"fmt"
	"github.com/mitchellh/go-homedir"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime"

	"github.com/gruntwork-io/terragrunt/errors"
	"github.com/gruntwork-io/terragrunt/util"
	"github.com/hashicorp/go-version"
)

var TERRAFORM_COMMANDS_WITH_SUBCOMMAND = []string{
	"debug",
	"force-unlock",
	"state",
}

// TerragruntOptions represents options that configure the behavior of the Terragrunt program
type TerragruntOptions struct {
	// Location of the Terragrunt config file
	TerragruntConfigPath string

	// Location of the terraform binary
	TerraformPath string

	// Version of terraform (obtained by running 'terraform version')
	TerraformVersion *version.Version

	// Whether we should prompt the user for confirmation or always assume "yes"
	NonInteractive bool

	// Whether we should automatically run terraform init if necessary when executing other commands
	AutoInit bool

	// CLI args that are intended for Terraform (i.e. all the CLI args except the --terragrunt ones)
	TerraformCliArgs []string

	// The working directory in which to run Terraform
	WorkingDir string

	// The logger to use for all logging
	Logger *log.Logger

	// Environment variables at runtime
	Env map[string]string

	// Download Terraform configurations from the specified source location into a temporary folder and run
	// Terraform in that temporary folder
	Source string

	// If set to true, delete the contents of the temporary folder before downloading Terraform source code into it
	SourceUpdate bool

	// Download Terraform configurations specified in the Source parameter into this folder
	DownloadDir string

	// The ARN of an IAM Role to assume before running Terraform
	IamRole string

	// If set to true, continue running *-all commands even if a dependency has errors. This is mostly useful for 'output-all <some_variable>'. See https://github.com/gruntwork-io/terragrunt/issues/193
	IgnoreDependencyErrors bool

	// If you want stdout to go somewhere other than os.stdout
	Writer io.Writer

	// If you want stderr to go somewhere other than os.stderr
	ErrWriter io.Writer

	// A command that can be used to run Terragrunt with the given options. This is useful for running Terragrunt
	// multiple times (e.g. when spinning up a stack of Terraform modules). The actual command is normally defined
	// in the cli package, which depends on almost all other packages, so we declare it here so that other
	// packages can use the command without a direct reference back to the cli package (which would create a
	// circular dependency).
	RunTerragrunt func(*TerragruntOptions) error
}

// Create a new TerragruntOptions object with reasonable defaults for real usage
func NewTerragruntOptions(terragruntConfigPath string) (*TerragruntOptions, error) {
	workingDir := filepath.Dir(terragruntConfigPath)

	logger := util.CreateLogger("")

	homedir, err := homedir.Dir()
	if err != nil {
		logger.Panicf("error: %v\n", err)
		return nil, err
	}

	downloadDir := filepath.Join(homedir, ".terragrunt")
	// On some versions of Windows, the default temp dir is a fairly long path (e.g. C:/Users/JONDOE~1/AppData/Local/Temp/2/).
	// This is a problem because Windows also limits path lengths to 260 characters, and with nested folders and hashed folder names
	// (e.g. from running terraform get), you can hit that limit pretty quickly. Therefore, we try to set the temporary download
	// folder to something slightly shorter, but still reasonable.
	if runtime.GOOS == "windows" {
		downloadDir = `C:\\Windows\\Temp\\terragrunt`
	}

	return &TerragruntOptions{
		TerragruntConfigPath:   terragruntConfigPath,
		TerraformPath:          "terraform",
		AutoInit:               true,
		NonInteractive:         false,
		TerraformCliArgs:       []string{},
		WorkingDir:             workingDir,
		Logger:                 logger,
		Env:                    map[string]string{},
		Source:                 "",
		SourceUpdate:           false,
		DownloadDir:            downloadDir,
		IgnoreDependencyErrors: false,
		Writer:                 os.Stdout,
		ErrWriter:              os.Stderr,
		RunTerragrunt: func(terragruntOptions *TerragruntOptions) error {
			return errors.WithStackTrace(RunTerragruntCommandNotSet)
		},
	}, nil
}

// Create a new TerragruntOptions object with reasonable defaults for test usage
func NewTerragruntOptionsForTest(terragruntConfigPath string) (*TerragruntOptions, error) {
	logger := util.CreateLogger("")

	opts, err := NewTerragruntOptions(terragruntConfigPath)
	if err != nil {
		logger.Fatal("error: %v\n", errors.WithStackTrace(err))
		return nil, err
	}

	opts.NonInteractive = true

	return opts, nil
}

// Create a copy of this TerragruntOptions, but with different values for the given variables. This is useful for
// creating a TerragruntOptions that behaves the same way, but is used for a Terraform module in a different folder.
func (terragruntOptions *TerragruntOptions) Clone(terragruntConfigPath string) *TerragruntOptions {
	workingDir := filepath.Dir(terragruntConfigPath)

	return &TerragruntOptions{
		TerragruntConfigPath:   terragruntConfigPath,
		TerraformPath:          terragruntOptions.TerraformPath,
		TerraformVersion:       terragruntOptions.TerraformVersion,
		AutoInit:               terragruntOptions.AutoInit,
		NonInteractive:         terragruntOptions.NonInteractive,
		TerraformCliArgs:       terragruntOptions.TerraformCliArgs,
		WorkingDir:             workingDir,
		Logger:                 util.CreateLoggerWithWriter(terragruntOptions.ErrWriter, workingDir),
		Env:                    terragruntOptions.Env,
		Source:                 terragruntOptions.Source,
		SourceUpdate:           terragruntOptions.SourceUpdate,
		DownloadDir:            terragruntOptions.DownloadDir,
		IamRole:                terragruntOptions.IamRole,
		IgnoreDependencyErrors: terragruntOptions.IgnoreDependencyErrors,
		Writer:                 terragruntOptions.Writer,
		ErrWriter:              terragruntOptions.ErrWriter,
		RunTerragrunt:          terragruntOptions.RunTerragrunt,
	}
}

// Inserts the given argsToInsert after the terraform command argument, but before the remaining args
func (terragruntOptions *TerragruntOptions) InsertTerraformCliArgs(argsToInsert ...string) {

	commandLength := 1
	if util.ListContainsElement(TERRAFORM_COMMANDS_WITH_SUBCOMMAND, terragruntOptions.TerraformCliArgs[0]) {
		commandLength = 2
	}

	// Options must be inserted after command but before the other args
	// command is either 1 word or 2 words
	var args []string
	args = append(args, terragruntOptions.TerraformCliArgs[:commandLength]...)
	args = append(args, argsToInsert...)
	args = append(args, terragruntOptions.TerraformCliArgs[commandLength:]...)
	terragruntOptions.TerraformCliArgs = args
}

// Appends the given argsToAppend after the current TerraformCliArgs
func (terragruntOptions *TerragruntOptions) AppendTerraformCliArgs(argsToAppend ...string) {
	terragruntOptions.TerraformCliArgs = append(terragruntOptions.TerraformCliArgs, argsToAppend...)
}

// Custom error types

var RunTerragruntCommandNotSet = fmt.Errorf("The RunTerragrunt option has not been set on this TerragruntOptions object")
<commit_msg>change Panicf<commit_after>package options

import (
	"fmt"
	"github.com/mitchellh/go-homedir"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime"

	"github.com/gruntwork-io/terragrunt/errors"
	"github.com/gruntwork-io/terragrunt/util"
	"github.com/hashicorp/go-version"
)

var TERRAFORM_COMMANDS_WITH_SUBCOMMAND = []string{
	"debug",
	"force-unlock",
	"state",
}

// TerragruntOptions represents options that configure the behavior of the Terragrunt program
type TerragruntOptions struct {
	// Location of the Terragrunt config file
	TerragruntConfigPath string

	// Location of the terraform binary
	TerraformPath string

	// Version of terraform (obtained by running 'terraform version')
	TerraformVersion *version.Version

	// Whether we should prompt the user for confirmation or always assume "yes"
	NonInteractive bool

	// Whether we should automatically run terraform init if necessary when executing other commands
	AutoInit bool

	// CLI args that are intended for Terraform (i.e. all the CLI args except the --terragrunt ones)
	TerraformCliArgs []string

	// The working directory in which to run Terraform
	WorkingDir string

	// The logger to use for all logging
	Logger *log.Logger

	// Environment variables at runtime
	Env map[string]string

	// Download Terraform configurations from the specified source location into a temporary folder and run
	// Terraform in that temporary folder
	Source string

	// If set to true, delete the contents of the temporary folder before downloading Terraform source code into it
	SourceUpdate bool

	// Download Terraform configurations specified in the Source parameter into this folder
	DownloadDir string

	// The ARN of an IAM Role to assume before running Terraform
	IamRole string

	// If set to true, continue running *-all commands even if a dependency has errors. This is mostly useful for 'output-all <some_variable>'. See https://github.com/gruntwork-io/terragrunt/issues/193
	IgnoreDependencyErrors bool

	// If you want stdout to go somewhere other than os.stdout
	Writer io.Writer

	// If you want stderr to go somewhere other than os.stderr
	ErrWriter io.Writer

	// A command that can be used to run Terragrunt with the given options. This is useful for running Terragrunt
	// multiple times (e.g. when spinning up a stack of Terraform modules). The actual command is normally defined
	// in the cli package, which depends on almost all other packages, so we declare it here so that other
	// packages can use the command without a direct reference back to the cli package (which would create a
	// circular dependency).
	RunTerragrunt func(*TerragruntOptions) error
}

// Create a new TerragruntOptions object with reasonable defaults for real usage
func NewTerragruntOptions(terragruntConfigPath string) (*TerragruntOptions, error) {
	workingDir := filepath.Dir(terragruntConfigPath)

	logger := util.CreateLogger("")

	homedir, err := homedir.Dir()
	if err != nil {
		logger.Fatalf("error: %v\n", err)
		return nil, err
	}

	downloadDir := filepath.Join(homedir, ".terragrunt")
	// On some versions of Windows, the default temp dir is a fairly long path (e.g. C:/Users/JONDOE~1/AppData/Local/Temp/2/).
	// This is a problem because Windows also limits path lengths to 260 characters, and with nested folders and hashed folder names
	// (e.g. from running terraform get), you can hit that limit pretty quickly. Therefore, we try to set the temporary download
	// folder to something slightly shorter, but still reasonable.
	if runtime.GOOS == "windows" {
		downloadDir = `C:\\Windows\\Temp\\terragrunt`
	}

	return &TerragruntOptions{
		TerragruntConfigPath:   terragruntConfigPath,
		TerraformPath:          "terraform",
		AutoInit:               true,
		NonInteractive:         false,
		TerraformCliArgs:       []string{},
		WorkingDir:             workingDir,
		Logger:                 logger,
		Env:                    map[string]string{},
		Source:                 "",
		SourceUpdate:           false,
		DownloadDir:            downloadDir,
		IgnoreDependencyErrors: false,
		Writer:                 os.Stdout,
		ErrWriter:              os.Stderr,
		RunTerragrunt: func(terragruntOptions *TerragruntOptions) error {
			return errors.WithStackTrace(RunTerragruntCommandNotSet)
		},
	}, nil
}

// Create a new TerragruntOptions object with reasonable defaults for test usage
func NewTerragruntOptionsForTest(terragruntConfigPath string) (*TerragruntOptions, error) {
	logger := util.CreateLogger("")

	opts, err := NewTerragruntOptions(terragruntConfigPath)
	if err != nil {
		logger.Fatal("error: %v\n", errors.WithStackTrace(err))
		return nil, err
	}

	opts.NonInteractive = true

	return opts, nil
}

// Create a copy of this TerragruntOptions, but with different values for the given variables. This is useful for
// creating a TerragruntOptions that behaves the same way, but is used for a Terraform module in a different folder.
func (terragruntOptions *TerragruntOptions) Clone(terragruntConfigPath string) *TerragruntOptions {
	workingDir := filepath.Dir(terragruntConfigPath)

	return &TerragruntOptions{
		TerragruntConfigPath:   terragruntConfigPath,
		TerraformPath:          terragruntOptions.TerraformPath,
		TerraformVersion:       terragruntOptions.TerraformVersion,
		AutoInit:               terragruntOptions.AutoInit,
		NonInteractive:         terragruntOptions.NonInteractive,
		TerraformCliArgs:       terragruntOptions.TerraformCliArgs,
		WorkingDir:             workingDir,
		Logger:                 util.CreateLoggerWithWriter(terragruntOptions.ErrWriter, workingDir),
		Env:                    terragruntOptions.Env,
		Source:                 terragruntOptions.Source,
		SourceUpdate:           terragruntOptions.SourceUpdate,
		DownloadDir:            terragruntOptions.DownloadDir,
		IamRole:                terragruntOptions.IamRole,
		IgnoreDependencyErrors: terragruntOptions.IgnoreDependencyErrors,
		Writer:                 terragruntOptions.Writer,
		ErrWriter:              terragruntOptions.ErrWriter,
		RunTerragrunt:          terragruntOptions.RunTerragrunt,
	}
}

// Inserts the given argsToInsert after the terraform command argument, but before the remaining args
func (terragruntOptions *TerragruntOptions) InsertTerraformCliArgs(argsToInsert ...string) {

	commandLength := 1
	if util.ListContainsElement(TERRAFORM_COMMANDS_WITH_SUBCOMMAND, terragruntOptions.TerraformCliArgs[0]) {
		commandLength = 2
	}

	// Options must be inserted after command but before the other args
	// command is either 1 word or 2 words
	var args []string
	args = append(args, terragruntOptions.TerraformCliArgs[:commandLength]...)
	args = append(args, argsToInsert...)
	args = append(args, terragruntOptions.TerraformCliArgs[commandLength:]...)
	terragruntOptions.TerraformCliArgs = args
}

// Appends the given argsToAppend after the current TerraformCliArgs
func (terragruntOptions *TerragruntOptions) AppendTerraformCliArgs(argsToAppend ...string) {
	terragruntOptions.TerraformCliArgs = append(terragruntOptions.TerraformCliArgs, argsToAppend...)
}

// Custom error types

var RunTerragruntCommandNotSet = fmt.Errorf("The RunTerragrunt option has not been set on this TerragruntOptions object")
<|endoftext|>