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

package language_test

import (
	"fmt"
	"golang.org/x/text/language"
)

func ExampleCanonType() {
	p := func(id string) {
		fmt.Printf("BCP47(%s) -> %s\n", id, language.BCP47.Make(id))
		fmt.Printf("Macro(%s) -> %s\n", id, language.Macro.Make(id))
		fmt.Printf("All(%s) -> %s\n", id, language.All.Make(id))
	}
	p("en-Latn")
	p("sh")
	p("zh-cmn")
	p("bjd")
	p("iw-Latn-fonipa-u-cu-usd")
	// Output:
	// BCP47(en-Latn) -> en
	// Macro(en-Latn) -> en-Latn
	// All(en-Latn) -> en
	// BCP47(sh) -> sh
	// Macro(sh) -> sh
	// All(sh) -> sr-Latn
	// BCP47(zh-cmn) -> cmn
	// Macro(zh-cmn) -> zh
	// All(zh-cmn) -> zh
	// BCP47(bjd) -> drl
	// Macro(bjd) -> bjd
	// All(bjd) -> drl
	// BCP47(iw-Latn-fonipa-u-cu-usd) -> he-Latn-fonipa-u-cu-usd
	// Macro(iw-Latn-fonipa-u-cu-usd) -> iw-Latn-fonipa-u-cu-usd
	// All(iw-Latn-fonipa-u-cu-usd) -> he-Latn-fonipa-u-cu-usd
}

func ExampleTag_Base() {
	fmt.Println(language.Make("und").Base())
	fmt.Println(language.Make("und-US").Base())
	fmt.Println(language.Make("und-NL").Base())
	fmt.Println(language.Make("und-419").Base()) // Latin America
	fmt.Println(language.Make("und-ZZ").Base())
	// Output:
	// en Low
	// en High
	// nl High
	// es Low
	// en Low
}

func ExampleTag_Script() {
	en := language.Make("en")
	sr := language.Make("sr")
	sr_Latn := language.Make("sr_Latn")
	fmt.Println(en.Script())
	fmt.Println(sr.Script())
	// Was a script explicitly specified?
	_, c := sr.Script()
	fmt.Println(c == language.Exact)
	_, c = sr_Latn.Script()
	fmt.Println(c == language.Exact)
	// Output:
	// Latn High
	// Cyrl Low
	// false
	// true
}

func ExampleTag_Region() {
	ru := language.Make("ru")
	en := language.Make("en")
	fmt.Println(ru.Region())
	fmt.Println(en.Region())
	// Output:
	// RU Low
	// US Low
}

func ExampleRegion_TLD() {
	us := language.MustParseRegion("US")
	gb := language.MustParseRegion("GB")
	bu := language.MustParseRegion("BU")

	fmt.Println(us.TLD())
	fmt.Println(gb.TLD())
	fmt.Println(bu.TLD())

	fmt.Println(us.Canonicalize().TLD())
	fmt.Println(gb.Canonicalize().TLD())
	fmt.Println(bu.Canonicalize().TLD())
	// Output:
	// US <nil>
	// UK <nil>
	// ZZ language: region is not a valid ccTLD
	// US <nil>
	// UK <nil>
	// MM <nil>
}

func ExampleCompose() {
	nl, _ := language.ParseBase("nl")
	us, _ := language.ParseRegion("US")
	de := language.Make("de-1901-u-co-phonebk")
	jp := language.Make("ja-JP")
	fi := language.Make("fi-x-ing")

	u, _ := language.ParseExtension("u-nu-arabic")
	x, _ := language.ParseExtension("x-piglatin")

	// Combine a base language and region.
	fmt.Println(language.Compose(nl, us))
	// Combine a base language and extension.
	fmt.Println(language.Compose(nl, x))
	// Replace the region.
	fmt.Println(language.Compose(jp, us))
	// Combine several tags.
	fmt.Println(language.Compose(us, nl, u))

	// Replace the base language of a tag.
	fmt.Println(language.Compose(de, nl))
	fmt.Println(language.Compose(de, nl, u))
	// Remove the base language.
	fmt.Println(language.Compose(de, language.Base{}))
	// Remove all variants.
	fmt.Println(language.Compose(de, []language.Variant{}))
	// Remove all extensions.
	fmt.Println(language.Compose(de, []language.Extension{}))
	fmt.Println(language.Compose(fi, []language.Extension{}))
	// Remove all variants and extensions.
	fmt.Println(language.Compose(de.Raw()))

	// An error is gobbled or returned if non-nil.
	fmt.Println(language.Compose(language.ParseRegion("ZA")))
	fmt.Println(language.Compose(language.ParseRegion("HH")))

	// Compose uses the same Default canonicalization as Make.
	fmt.Println(language.Compose(language.Raw.Parse("en-Latn-UK")))

	// Call compose on a different CanonType for different results.
	fmt.Println(language.All.Compose(language.Raw.Parse("en-Latn-UK")))

	// Output:
	// nl-US <nil>
	// nl-x-piglatin <nil>
	// ja-US <nil>
	// nl-US-u-nu-arabic <nil>
	// nl-1901-u-co-phonebk <nil>
	// nl-1901-u-nu-arabic <nil>
	// und-1901-u-co-phonebk <nil>
	// de-u-co-phonebk <nil>
	// de-1901 <nil>
	// fi <nil>
	// de <nil>
	// und-ZA <nil>
	// und language: subtag "HH" is well-formed but unknown
	// en-Latn-GB <nil>
	// en-GB <nil>
}

func ExampleParse_errors() {
	for _, s := range []string{"Foo", "Bar", "Foobar"} {
		_, err := language.Parse(s)
		if err != nil {
			if inv, ok := err.(language.ValueError); ok {
				fmt.Println(inv.Subtag())
			} else {
				fmt.Println(s)
			}
		}
	}
	for _, s := range []string{"en", "aa-Uuuu", "AC", "ac-u"} {
		_, err := language.Parse(s)
		switch e := err.(type) {
		case language.ValueError:
			fmt.Printf("%s: culprit %q\n", s, e.Subtag())
		case nil:
			// No error.
		default:
			// A syntax error.
			fmt.Printf("%s: ill-formed\n", s)
		}
	}
	// Output:
	// foo
	// Foobar
	// aa-Uuuu: culprit "Uuuu"
	// AC: culprit "ac"
	// ac-u: ill-formed
}

func ExampleParent() {
	p := func(tag string) {
		fmt.Printf("parent(%v): %v\n", tag, language.Make(tag).Parent())
	}
	p("zh-CN")

	// Australian English inherits from World English.
	p("en-AU")

	// If the tag has a different maximized script from its parent, a tag with
	// this maximized script is inserted. This allows different language tags
	// which have the same base language and script in common to inherit from
	// a common set of settings.
	p("zh-HK")

	// If the maximized script of the parent is not identical, CLDR will skip
	// inheriting from it, as it means there will not be many entries in common
	// and inheriting from it is nonsensical.
	p("zh-Hant")

	// The parent of a tag with variants and extensions is the tag with all
	// variants and extensions removed.
	p("de-1994-u-co-phonebk")

	// Remove default script.
	p("de-Latn-LU")

	// Output:
	// parent(zh-CN): zh
	// parent(en-AU): en-001
	// parent(zh-HK): zh-Hant
	// parent(zh-Hant): und
	// parent(de-1994-u-co-phonebk): de
	// parent(de-Latn-LU): de
}

// ExampleMatcher_bestMatch gives some examples of getting the best match of
// a set of tags to any of the tags of given set.
func ExampleMatcher() {
	// This is the set of tags from which we want to pick the best match. These
	// can be, for example, the supported languages for some package.
	tags := []language.Tag{
		language.English,
		language.BritishEnglish,
		language.French,
		language.Afrikaans,
		language.BrazilianPortuguese,
		language.EuropeanPortuguese,
		language.Croatian,
		language.SimplifiedChinese,
		language.Raw.Make("iw-IL"),
		language.Raw.Make("iw"),
		language.Raw.Make("he"),
	}
	m := language.NewMatcher(tags)

	// A simple match.
	fmt.Println(m.Match(language.Make("fr")))

	// Australian English is closer to British than American English.
	fmt.Println(m.Match(language.Make("en-AU")))

	// Default to the first tag passed to the Matcher if there is no match.
	fmt.Println(m.Match(language.Make("ar")))

	// Get the default tag.
	fmt.Println(m.Match())

	fmt.Println("----")

	// Croatian speakers will likely understand Serbian written in Latin script.
	fmt.Println(m.Match(language.Make("sr-Latn")))

	// We match SimplifiedChinese, but with Low confidence.
	fmt.Println(m.Match(language.TraditionalChinese))

	// Serbian in Latin script is a closer match to Croatian than Traditional
	// Chinese to Simplified Chinese.
	fmt.Println(m.Match(language.TraditionalChinese, language.Make("sr-Latn")))

	fmt.Println("----")

	// In case a multiple variants of a language are available, the most spoken
	// variant is typically returned.
	fmt.Println(m.Match(language.Portuguese))

	// Pick the first value passed to Match in case of a tie.
	fmt.Println(m.Match(language.Dutch, language.Make("fr-BE"), language.Make("af-NA")))
	fmt.Println(m.Match(language.Dutch, language.Make("af-NA"), language.Make("fr-BE")))

	fmt.Println("----")

	// If a Matcher is initialized with a language and it's deprecated version,
	// it will distinguish between them.
	fmt.Println(m.Match(language.Raw.Make("iw")))

	// However, for non-exact matches, it will treat deprecated versions as
	// equivalent and consider other factors first.
	fmt.Println(m.Match(language.Raw.Make("he-IL")))

	// Output:
	// fr 2 Exact
	// en-GB 1 High
	// en 0 No
	// en 0 No
	// ----
	// hr 6 High
	// zh-Hans 7 Low
	// hr 6 High
	// ----
	// pt-BR 4 High
	// fr 2 High
	// af 3 High
	// ----
	// iw 9 Exact
	// iw-IL 8 Exact
}

func ExampleTag_ComprehensibleTo() {
	// Various levels of comprehensibility.
	fmt.Println(language.English.ComprehensibleTo(language.English))
	fmt.Println(language.BritishEnglish.ComprehensibleTo(language.AmericanEnglish))

	// An explicit Und results in no match.
	fmt.Println(language.Und.ComprehensibleTo(language.English))

	fmt.Println("----")

	// There is usually no mutual comprehensibility between different scripts.
	fmt.Println(language.English.ComprehensibleTo(language.Make("en-Dsrt")))

	// One exception is for Traditional versus Simplified Chinese, albeit with
	// a low confidence.
	fmt.Println(language.SimplifiedChinese.ComprehensibleTo(language.TraditionalChinese))

	fmt.Println("----")

	// A Swiss German speaker will often understand High German.
	fmt.Println(language.Make("de").ComprehensibleTo(language.Make("gsw")))

	// The converse is not generally the case.
	fmt.Println(language.Make("gsw").ComprehensibleTo(language.Make("de")))

	// Output:
	// Exact
	// High
	// No
	// ----
	// No
	// Low
	// ----
	// High
	// No
}

func ExampleParseAcceptLanguage() {
	// Tags are reordered based on their q rating. A missing q value means 1.0.
	fmt.Println(language.ParseAcceptLanguage(" nn;q=0.3, en-gb;q=0.8, en,"))

	m := language.NewMatcher([]language.Tag{language.Norwegian, language.Make("en-AU")})

	t, _, _ := language.ParseAcceptLanguage("da, en-gb;q=0.8, en;q=0.7")
	fmt.Println(m.Match(t...))

	// Danish is pretty close to Norwegian.
	t, _, _ = language.ParseAcceptLanguage(" da, nl")
	fmt.Println(m.Match(t...))

	// Output:
	// [en en-GB nn] [1 0.8 0.3] <nil>
	// en-AU 1 High
	// no 0 High
}

func ExampleTag_values() {
	us := language.MustParseRegion("US")
	en := language.MustParseBase("en")

	lang, _, region := language.AmericanEnglish.Raw()
	fmt.Println(lang == en, region == us)

	lang, _, region = language.BritishEnglish.Raw()
	fmt.Println(lang == en, region == us)

	// Tags can be compared for exact equivalence using '=='.
	en_us, _ := language.Compose(en, us)
	fmt.Println(en_us == language.AmericanEnglish)

	// Output:
	// true true
	// true false
	// true
}
<commit_msg>language: improved example for parsing regions<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package language_test

import (
	"fmt"

	"golang.org/x/text/language"
)

func ExampleCanonType() {
	p := func(id string) {
		fmt.Printf("BCP47(%s) -> %s\n", id, language.BCP47.Make(id))
		fmt.Printf("Macro(%s) -> %s\n", id, language.Macro.Make(id))
		fmt.Printf("All(%s) -> %s\n", id, language.All.Make(id))
	}
	p("en-Latn")
	p("sh")
	p("zh-cmn")
	p("bjd")
	p("iw-Latn-fonipa-u-cu-usd")
	// Output:
	// BCP47(en-Latn) -> en
	// Macro(en-Latn) -> en-Latn
	// All(en-Latn) -> en
	// BCP47(sh) -> sh
	// Macro(sh) -> sh
	// All(sh) -> sr-Latn
	// BCP47(zh-cmn) -> cmn
	// Macro(zh-cmn) -> zh
	// All(zh-cmn) -> zh
	// BCP47(bjd) -> drl
	// Macro(bjd) -> bjd
	// All(bjd) -> drl
	// BCP47(iw-Latn-fonipa-u-cu-usd) -> he-Latn-fonipa-u-cu-usd
	// Macro(iw-Latn-fonipa-u-cu-usd) -> iw-Latn-fonipa-u-cu-usd
	// All(iw-Latn-fonipa-u-cu-usd) -> he-Latn-fonipa-u-cu-usd
}

func ExampleTag_Base() {
	fmt.Println(language.Make("und").Base())
	fmt.Println(language.Make("und-US").Base())
	fmt.Println(language.Make("und-NL").Base())
	fmt.Println(language.Make("und-419").Base()) // Latin America
	fmt.Println(language.Make("und-ZZ").Base())
	// Output:
	// en Low
	// en High
	// nl High
	// es Low
	// en Low
}

func ExampleTag_Script() {
	en := language.Make("en")
	sr := language.Make("sr")
	sr_Latn := language.Make("sr_Latn")
	fmt.Println(en.Script())
	fmt.Println(sr.Script())
	// Was a script explicitly specified?
	_, c := sr.Script()
	fmt.Println(c == language.Exact)
	_, c = sr_Latn.Script()
	fmt.Println(c == language.Exact)
	// Output:
	// Latn High
	// Cyrl Low
	// false
	// true
}

func ExampleTag_Region() {
	ru := language.Make("ru")
	en := language.Make("en")
	fmt.Println(ru.Region())
	fmt.Println(en.Region())
	// Output:
	// RU Low
	// US Low
}

func ExampleRegion_TLD() {
	us := language.MustParseRegion("US")
	gb := language.MustParseRegion("GB")
	uk := language.MustParseRegion("UK")
	bu := language.MustParseRegion("BU")

	fmt.Println(us.TLD())
	fmt.Println(gb.TLD())
	fmt.Println(uk.TLD())
	fmt.Println(bu.TLD())

	fmt.Println(us.Canonicalize().TLD())
	fmt.Println(gb.Canonicalize().TLD())
	fmt.Println(uk.Canonicalize().TLD())
	fmt.Println(bu.Canonicalize().TLD())
	// Output:
	// US <nil>
	// UK <nil>
	// UK <nil>
	// ZZ language: region is not a valid ccTLD
	// US <nil>
	// UK <nil>
	// UK <nil>
	// MM <nil>
}

func ExampleCompose() {
	nl, _ := language.ParseBase("nl")
	us, _ := language.ParseRegion("US")
	de := language.Make("de-1901-u-co-phonebk")
	jp := language.Make("ja-JP")
	fi := language.Make("fi-x-ing")

	u, _ := language.ParseExtension("u-nu-arabic")
	x, _ := language.ParseExtension("x-piglatin")

	// Combine a base language and region.
	fmt.Println(language.Compose(nl, us))
	// Combine a base language and extension.
	fmt.Println(language.Compose(nl, x))
	// Replace the region.
	fmt.Println(language.Compose(jp, us))
	// Combine several tags.
	fmt.Println(language.Compose(us, nl, u))

	// Replace the base language of a tag.
	fmt.Println(language.Compose(de, nl))
	fmt.Println(language.Compose(de, nl, u))
	// Remove the base language.
	fmt.Println(language.Compose(de, language.Base{}))
	// Remove all variants.
	fmt.Println(language.Compose(de, []language.Variant{}))
	// Remove all extensions.
	fmt.Println(language.Compose(de, []language.Extension{}))
	fmt.Println(language.Compose(fi, []language.Extension{}))
	// Remove all variants and extensions.
	fmt.Println(language.Compose(de.Raw()))

	// An error is gobbled or returned if non-nil.
	fmt.Println(language.Compose(language.ParseRegion("ZA")))
	fmt.Println(language.Compose(language.ParseRegion("HH")))

	// Compose uses the same Default canonicalization as Make.
	fmt.Println(language.Compose(language.Raw.Parse("en-Latn-UK")))

	// Call compose on a different CanonType for different results.
	fmt.Println(language.All.Compose(language.Raw.Parse("en-Latn-UK")))

	// Output:
	// nl-US <nil>
	// nl-x-piglatin <nil>
	// ja-US <nil>
	// nl-US-u-nu-arabic <nil>
	// nl-1901-u-co-phonebk <nil>
	// nl-1901-u-nu-arabic <nil>
	// und-1901-u-co-phonebk <nil>
	// de-u-co-phonebk <nil>
	// de-1901 <nil>
	// fi <nil>
	// de <nil>
	// und-ZA <nil>
	// und language: subtag "HH" is well-formed but unknown
	// en-Latn-GB <nil>
	// en-GB <nil>
}

func ExampleParse_errors() {
	for _, s := range []string{"Foo", "Bar", "Foobar"} {
		_, err := language.Parse(s)
		if err != nil {
			if inv, ok := err.(language.ValueError); ok {
				fmt.Println(inv.Subtag())
			} else {
				fmt.Println(s)
			}
		}
	}
	for _, s := range []string{"en", "aa-Uuuu", "AC", "ac-u"} {
		_, err := language.Parse(s)
		switch e := err.(type) {
		case language.ValueError:
			fmt.Printf("%s: culprit %q\n", s, e.Subtag())
		case nil:
			// No error.
		default:
			// A syntax error.
			fmt.Printf("%s: ill-formed\n", s)
		}
	}
	// Output:
	// foo
	// Foobar
	// aa-Uuuu: culprit "Uuuu"
	// AC: culprit "ac"
	// ac-u: ill-formed
}

func ExampleParent() {
	p := func(tag string) {
		fmt.Printf("parent(%v): %v\n", tag, language.Make(tag).Parent())
	}
	p("zh-CN")

	// Australian English inherits from World English.
	p("en-AU")

	// If the tag has a different maximized script from its parent, a tag with
	// this maximized script is inserted. This allows different language tags
	// which have the same base language and script in common to inherit from
	// a common set of settings.
	p("zh-HK")

	// If the maximized script of the parent is not identical, CLDR will skip
	// inheriting from it, as it means there will not be many entries in common
	// and inheriting from it is nonsensical.
	p("zh-Hant")

	// The parent of a tag with variants and extensions is the tag with all
	// variants and extensions removed.
	p("de-1994-u-co-phonebk")

	// Remove default script.
	p("de-Latn-LU")

	// Output:
	// parent(zh-CN): zh
	// parent(en-AU): en-001
	// parent(zh-HK): zh-Hant
	// parent(zh-Hant): und
	// parent(de-1994-u-co-phonebk): de
	// parent(de-Latn-LU): de
}

// ExampleMatcher_bestMatch gives some examples of getting the best match of
// a set of tags to any of the tags of given set.
func ExampleMatcher() {
	// This is the set of tags from which we want to pick the best match. These
	// can be, for example, the supported languages for some package.
	tags := []language.Tag{
		language.English,
		language.BritishEnglish,
		language.French,
		language.Afrikaans,
		language.BrazilianPortuguese,
		language.EuropeanPortuguese,
		language.Croatian,
		language.SimplifiedChinese,
		language.Raw.Make("iw-IL"),
		language.Raw.Make("iw"),
		language.Raw.Make("he"),
	}
	m := language.NewMatcher(tags)

	// A simple match.
	fmt.Println(m.Match(language.Make("fr")))

	// Australian English is closer to British than American English.
	fmt.Println(m.Match(language.Make("en-AU")))

	// Default to the first tag passed to the Matcher if there is no match.
	fmt.Println(m.Match(language.Make("ar")))

	// Get the default tag.
	fmt.Println(m.Match())

	fmt.Println("----")

	// Croatian speakers will likely understand Serbian written in Latin script.
	fmt.Println(m.Match(language.Make("sr-Latn")))

	// We match SimplifiedChinese, but with Low confidence.
	fmt.Println(m.Match(language.TraditionalChinese))

	// Serbian in Latin script is a closer match to Croatian than Traditional
	// Chinese to Simplified Chinese.
	fmt.Println(m.Match(language.TraditionalChinese, language.Make("sr-Latn")))

	fmt.Println("----")

	// In case a multiple variants of a language are available, the most spoken
	// variant is typically returned.
	fmt.Println(m.Match(language.Portuguese))

	// Pick the first value passed to Match in case of a tie.
	fmt.Println(m.Match(language.Dutch, language.Make("fr-BE"), language.Make("af-NA")))
	fmt.Println(m.Match(language.Dutch, language.Make("af-NA"), language.Make("fr-BE")))

	fmt.Println("----")

	// If a Matcher is initialized with a language and it's deprecated version,
	// it will distinguish between them.
	fmt.Println(m.Match(language.Raw.Make("iw")))

	// However, for non-exact matches, it will treat deprecated versions as
	// equivalent and consider other factors first.
	fmt.Println(m.Match(language.Raw.Make("he-IL")))

	// Output:
	// fr 2 Exact
	// en-GB 1 High
	// en 0 No
	// en 0 No
	// ----
	// hr 6 High
	// zh-Hans 7 Low
	// hr 6 High
	// ----
	// pt-BR 4 High
	// fr 2 High
	// af 3 High
	// ----
	// iw 9 Exact
	// iw-IL 8 Exact
}

func ExampleTag_ComprehensibleTo() {
	// Various levels of comprehensibility.
	fmt.Println(language.English.ComprehensibleTo(language.English))
	fmt.Println(language.BritishEnglish.ComprehensibleTo(language.AmericanEnglish))

	// An explicit Und results in no match.
	fmt.Println(language.Und.ComprehensibleTo(language.English))

	fmt.Println("----")

	// There is usually no mutual comprehensibility between different scripts.
	fmt.Println(language.English.ComprehensibleTo(language.Make("en-Dsrt")))

	// One exception is for Traditional versus Simplified Chinese, albeit with
	// a low confidence.
	fmt.Println(language.SimplifiedChinese.ComprehensibleTo(language.TraditionalChinese))

	fmt.Println("----")

	// A Swiss German speaker will often understand High German.
	fmt.Println(language.Make("de").ComprehensibleTo(language.Make("gsw")))

	// The converse is not generally the case.
	fmt.Println(language.Make("gsw").ComprehensibleTo(language.Make("de")))

	// Output:
	// Exact
	// High
	// No
	// ----
	// No
	// Low
	// ----
	// High
	// No
}

func ExampleParseAcceptLanguage() {
	// Tags are reordered based on their q rating. A missing q value means 1.0.
	fmt.Println(language.ParseAcceptLanguage(" nn;q=0.3, en-gb;q=0.8, en,"))

	m := language.NewMatcher([]language.Tag{language.Norwegian, language.Make("en-AU")})

	t, _, _ := language.ParseAcceptLanguage("da, en-gb;q=0.8, en;q=0.7")
	fmt.Println(m.Match(t...))

	// Danish is pretty close to Norwegian.
	t, _, _ = language.ParseAcceptLanguage(" da, nl")
	fmt.Println(m.Match(t...))

	// Output:
	// [en en-GB nn] [1 0.8 0.3] <nil>
	// en-AU 1 High
	// no 0 High
}

func ExampleTag_values() {
	us := language.MustParseRegion("US")
	en := language.MustParseBase("en")

	lang, _, region := language.AmericanEnglish.Raw()
	fmt.Println(lang == en, region == us)

	lang, _, region = language.BritishEnglish.Raw()
	fmt.Println(lang == en, region == us)

	// Tags can be compared for exact equivalence using '=='.
	en_us, _ := language.Compose(en, us)
	fmt.Println(en_us == language.AmericanEnglish)

	// Output:
	// true true
	// true false
	// true
}
<|endoftext|>


<commit_before>package xiaoice

import wx "github.com/KevinGong2013/ggbot/wechat"
import "sync"

import log "github.com/Sirupsen/logrus"

var logger = log.WithFields(log.Fields{
	"module": "xiaoice",
})

// Brain ...
type Brain struct {
	sync.Mutex
	wx             *wx.WeChat
	xiaoice        *wx.Contact
	waittingReplay []string
}

// NewBrain ...
func NewBrain() *Brain {
	return &Brain{waittingReplay: []string{}}
}

// WechatDidLogin ...
func (b *Brain) WechatDidLogin(wechat *wx.WeChat) {
	b.wx = wechat
	b.xiaoice, _ = wechat.ContactByNickName(`小冰`)
}

// WechatDidLogout ...
func (b *Brain) WechatDidLogout(wechat *wx.WeChat) {
}

// MapMsgs ...
func (b *Brain) MapMsgs(msg *wx.CountedContent) {
	for _, m := range msg.Content {

		msgType, _ := m[`MsgType`].(float64)
		if msgType != 1 && msgType != 3 && msgType != 47 { // 目前只回复文字/图片/表情/消息
			m[`needXiaoiceResponse`] = false
			continue
		}

		isSendByMySelf, _ := m[`IsSendByMySelf`].(bool)
		if isSendByMySelf {
			continue
		}
		from, _ := m[`FromUserName`].(string)
		contact, err := b.wx.ContactByUserName(from)
		if err != nil {
			m[`needXiaoiceResponse`] = false
			continue
		}

		switch contact.Type {
		case wx.ContactTypeFriend:
			m[`needXiaoiceResponse`] = true
			m[`xiaoice_info`] = m[`Content`]
			m[`xiaoice_to`] = m[`FromUserName`]
		case wx.ContactTypeOfficial:
			if b.xiaoice.NickName == contact.NickName {
				len := len(b.waittingReplay)
				if len > 0 {
					b.Lock()
					defer b.Unlock()
					m[`isXiaoiceReplay`] = true
					m[`ReplayUserName`] = b.waittingReplay[len-1]
					m[`localFileId`] = m[`MsgId`]
					b.waittingReplay = b.waittingReplay[:len-1]
				}
			}
			m[`needXiaoiceResponse`] = false
		case wx.ContactTypeGroup:
			m[`needXiaoiceResponse`] = true
			m[`xiaoice_info`] = m[`Content`]
			m[`xiaoice_to`] = m[`FromUserName`]
		}
	}
	return
}

// HandleMsgs ...
func (b *Brain) HandleMsgs(msg *wx.CountedContent) {
	for _, m := range msg.Content {
		needResponse, _ := m[`needXiaoiceResponse`].(bool)
		isReplay, _ := m[`isXiaoiceReplay`].(bool)
		if needResponse {
			c, _ := m[`xiaoice_info`].(string)
			to, _ := m[`xiaoice_to`].(string)

			if b.xiaoice != nil {
				err := b.wx.SendTextMsg(c, b.xiaoice.To())
				if err == nil {
					b.Lock()
					defer b.Unlock()
					b.waittingReplay = append(b.waittingReplay, to)
				} else {
					logger.Error(err)
				}
			}
		}
		if isReplay {
			to, _ := m[`ReplayUserName`].(string)
			c, _ := m[`Content`].(string)
			msgType, _ := m[`MsgType`].(float64)

			if msgType == 1 {
				b.wx.SendTextMsg(c, to)
			} else {
				// TODO: 这里的文件名需要处理一下
				path, _ := m[`localFileId`].(string)
				b.wx.SendFile(`.ggbot/media/`+path+`.jpeg`, to)
			}
		}
	}
}
<commit_msg>Added debug log for `xiaoice`<commit_after>package xiaoice

import wx "github.com/KevinGong2013/ggbot/wechat"
import "sync"

import log "github.com/Sirupsen/logrus"

var logger = log.WithFields(log.Fields{
	"module": "xiaoice",
})

// Brain ...
type Brain struct {
	sync.Mutex
	wx             *wx.WeChat
	xiaoice        *wx.Contact
	waittingReplay []string
}

// NewBrain ...
func NewBrain() *Brain {
	return &Brain{waittingReplay: []string{}}
}

// WechatDidLogin ...
func (b *Brain) WechatDidLogin(wechat *wx.WeChat) {
	b.wx = wechat
	b.xiaoice, _ = wechat.ContactByNickName(`小冰`)
}

// WechatDidLogout ...
func (b *Brain) WechatDidLogout(wechat *wx.WeChat) {
}

// MapMsgs ...
func (b *Brain) MapMsgs(msg *wx.CountedContent) {
	for _, m := range msg.Content {

		msgType, _ := m[`MsgType`].(float64)
		if msgType != 1 && msgType != 3 && msgType != 47 { // 目前只回复文字/图片/表情/消息
			m[`needXiaoiceResponse`] = false
			continue
		}

		isSendByMySelf, _ := m[`IsSendByMySelf`].(bool)
		if isSendByMySelf {
			continue
		}
		from, _ := m[`FromUserName`].(string)
		contact, err := b.wx.ContactByUserName(from)
		if err != nil {
			m[`needXiaoiceResponse`] = false
			logger.Error(err)
			continue
		}

		switch contact.Type {
		case wx.ContactTypeFriend:
			m[`needXiaoiceResponse`] = true
			m[`xiaoice_info`] = m[`Content`]
			m[`xiaoice_to`] = m[`FromUserName`]
		case wx.ContactTypeOfficial:
			if b.xiaoice.NickName == contact.NickName {
				len := len(b.waittingReplay)
				if len > 0 {
					b.Lock()
					defer b.Unlock()
					m[`isXiaoiceReplay`] = true
					m[`ReplayUserName`] = b.waittingReplay[len-1]
					m[`localFileId`] = m[`MsgId`]
					b.waittingReplay = b.waittingReplay[:len-1]
				} else {
					logger.Warnf(`xiaoice replay %s`, m)
				}
			} else {
				logger.Warn(`offical msg %s`, contact.NickName)
			}
			m[`needXiaoiceResponse`] = false
		case wx.ContactTypeGroup:
			m[`needXiaoiceResponse`] = true
			m[`xiaoice_info`] = m[`Content`]
			m[`xiaoice_to`] = m[`FromUserName`]
		}
	}
}

// HandleMsgs ...
func (b *Brain) HandleMsgs(msg *wx.CountedContent) {
	for _, m := range msg.Content {
		needResponse, _ := m[`needXiaoiceResponse`].(bool)
		isReplay, _ := m[`isXiaoiceReplay`].(bool)
		if needResponse {
			c, _ := m[`xiaoice_info`].(string)
			to, _ := m[`xiaoice_to`].(string)

			if b.xiaoice != nil {
				err := b.wx.SendTextMsg(c, b.xiaoice.To())
				if err == nil {
					b.Lock()
					defer b.Unlock()
					b.waittingReplay = append(b.waittingReplay, to)
				} else {
					logger.Error(err)
				}
			}
		}
		if isReplay {
			to, _ := m[`ReplayUserName`].(string)
			c, _ := m[`Content`].(string)
			msgType, _ := m[`MsgType`].(float64)

			if msgType == 1 {
				b.wx.SendTextMsg(c, to)
			} else {
				// TODO: 这里的文件名需要处理一下
				path, _ := m[`localFileId`].(string)
				b.wx.SendFile(`.ggbot/media/`+path+`.jpeg`, to)
			}
		}
	}
}
<|endoftext|>


<commit_before>// Package gcfg reads "gitconfig-like" text-based configuration files with
// "name=value" pairs grouped into sections (gcfg files). Support for modifying
// and/or exporting such files may be added later.
//
// This package is a work in progress, and both the supported file format and
// the API is subject to change.
//
// The syntax is based on that used by git config:
// http://git-scm.com/docs/git-config#_syntax .
// Note that the gcfg syntax may diverge from that of git config in the future
// to a limited degree. Current differences (apart from TODOs listed below) are:
//  - gcfg files must use UTF-8 encoding (for now)
//  - include is not supported (and not planned)
//
// The package may be usable for handling some of the various "INI file" formats
// used by some programs and libraries, but achieving or maintaining
// compatibility with any of those is not a primary concern.
//
// TODO: besides more docs and tests, add support for:
//  - pointer fields
//  - subsections
//  - multi-value variables (+ internal representation)
//  - returning error context (+ numeric error codes ?)
//  - multiple readers (strings, files)
//  - escaping in strings and long(er) lines (?) (+ regexp-free parser)
//  - modifying files
//  - exporting files (+ metadata handling) (?)
//  - declare encoding (?)
//
package gcfg

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os"
	"reflect"
	"regexp"
	"strings"
)

var (
	reCmnt    = regexp.MustCompile(`^([^;#"]*)[;#].*$`)
	reCmntQ   = regexp.MustCompile(`^([^;#"]*"[^"]*"[^;#"]*)[;#].*$`)
	reBlank   = regexp.MustCompile(`^\s*$`)
	reSect    = regexp.MustCompile(`^\s*\[\s*([^\s]*)\s*\]\s*$`)
	reVar     = regexp.MustCompile(`^\s*([^"=\s]+)\s*=\s*([^"\s]*)\s*$`)
	reVarQ    = regexp.MustCompile(`^\s*([^"=\s]+)\s*=\s*"([^"\n\\]*)"\s*$`)
	reVarDflt = regexp.MustCompile(`^\s*\b(.*)\b\s*$`)
)

const (
	// Default value in case a value for a variable isn't provided.
	DefaultValue = "true"
)

type Bool bool

var boolValues = map[string]interface{}{
	"true": true, "yes": true, "on": true, "1": true,
	"false": false, "no": false, "off": false, "0": false}

func scan(state fmt.ScanState, values map[string]interface{}) (interface{}, error) {
	var rd []rune
	var r rune
	var err error
	for r, _, err = state.ReadRune(); err == nil; r, _, err = state.ReadRune() {
		rd = append(rd, r)
		part := false
		for s, v := range values {
			if strings.EqualFold(string(rd), s) {
				return v, err
			}
			if len(rd) < len(s) && strings.EqualFold(string(rd), s[:len(rd)]) {
				part = true
			}
		}
		if part == false {
			state.UnreadRune()
			return nil, errors.New("unsupported value " + string(rd))
		}
	}
	return nil, err
}

func (b *Bool) Scan(state fmt.ScanState, verb rune) error {
	v, err := scan(state, boolValues)
	switch bb := v.(type) {
	case bool:
		*b = Bool(bb)
		return err
	}
	return err
}

func unref(v reflect.Value) reflect.Value {
	for v.Type().Kind() == reflect.Ptr {
		v = v.Elem()
	}
	return v
}

func fieldFold(v reflect.Value, name string) reflect.Value {
	n := strings.Replace(name, "-", "_", -1)
	return v.FieldByNameFunc(func(fieldName string) bool {
		return strings.EqualFold(n, fieldName)
	})
}

func set(cfg interface{}, sect, name, value string) error {
	vDest := unref(reflect.ValueOf(cfg))
	vSect := fieldFold(vDest, sect)
	vName := fieldFold(vSect, name)
	vAddr := vName.Addr().Interface()
	switch v := vAddr.(type) {
	case *string:
		*v = value
		return nil
	case *bool:
		vAddr = (*Bool)(v)
	}
	// attempt to read an extra rune to make sure the value is consumed
	var r rune
	n, err := fmt.Sscanf(value, "%v%c", vAddr, &r)
	switch {
	case n < 1 || n == 1 && err != io.EOF:
		return fmt.Errorf("failed to parse %q as %#v: parse error %v", value,
			vName.Type(), err)
	case n > 1:
		return fmt.Errorf("failed to parse %q as %#v: extra characters", value,
			vName.Type())
	case n == 1 && err == io.EOF:
		return nil
	}
	panic("never reached")
}

// Parse reads gcfg formatted data from reader and sets the values into the
// corresponding fields in config. Config must be a pointer to a struct.
func Parse(config interface{}, reader io.Reader) error {
	r := bufio.NewReader(reader)
	sect := (*string)(nil)
	for line := 1; true; line++ {
		l, pre, err := r.ReadLine()
		if err != nil && err != io.EOF {
			return err
		} else if pre {
			return errors.New("line too long")
		}
		// exclude comments
		if c := reCmnt.FindSubmatch(l); c != nil {
			l = c[1]
		} else if c := reCmntQ.FindSubmatch(l); c != nil {
			l = c[1]
		}
		if !reBlank.Match(l) {
			// "switch" based on line contents
			if sec := reSect.FindSubmatch(l); sec != nil {
				strsec := string(sec[1])
				sect = &strsec
			} else if v, vq, vd := reVar.FindSubmatch(l),
				reVarQ.FindSubmatch(l), reVarDflt.FindSubmatch(l); //
			v != nil || vq != nil || vd != nil {
				if sect == nil {
					return errors.New("no section")
				}
				var name, value string
				if v != nil {
					name, value = string(v[1]), string(v[2])
				} else if vq != nil {
					name, value = string(vq[1]), string(vq[2])
				} else { // vd != nil
					name, value = string(vd[1]), DefaultValue
				}
				err := set(config, *sect, name, value)
				if err != nil {
					return err
				}
			} else {
				return fmt.Errorf("invalid line %q", string(l))
			}
		}
		if err == io.EOF {
			break
		}
	}
	return nil
}

// ParseString reads gcfg formatted data from str and sets the values into the
// corresponding fields in cfg. It is a wrapper for Parse(config, reader).
func ParseString(config interface{}, str string) error {
	r := strings.NewReader(str)
	return Parse(config, r)
}

// ParseFile reads gcfg formatted data from the file filename and sets the
// values into the corresponding fields in cfg. It is a wrapper for
// Parse(config, reader).
func ParseFile(config interface{}, filename string) error {
	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer f.Close()
	return Parse(config, f)
}
<commit_msg>remove unnecessary reflect calls<commit_after>// Package gcfg reads "gitconfig-like" text-based configuration files with
// "name=value" pairs grouped into sections (gcfg files). Support for modifying
// and/or exporting such files may be added later.
//
// This package is a work in progress, and both the supported file format and
// the API is subject to change.
//
// The syntax is based on that used by git config:
// http://git-scm.com/docs/git-config#_syntax .
// Note that the gcfg syntax may diverge from that of git config in the future
// to a limited degree. Current differences (apart from TODOs listed below) are:
//  - gcfg files must use UTF-8 encoding (for now)
//  - include is not supported (and not planned)
//
// The package may be usable for handling some of the various "INI file" formats
// used by some programs and libraries, but achieving or maintaining
// compatibility with any of those is not a primary concern.
//
// TODO: besides more docs and tests, add support for:
//  - pointer fields
//  - subsections
//  - multi-value variables (+ internal representation)
//  - returning error context (+ numeric error codes ?)
//  - multiple readers (strings, files)
//  - escaping in strings and long(er) lines (?) (+ regexp-free parser)
//  - modifying files
//  - exporting files (+ metadata handling) (?)
//  - declare encoding (?)
//
package gcfg

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os"
	"reflect"
	"regexp"
	"strings"
)

var (
	reCmnt    = regexp.MustCompile(`^([^;#"]*)[;#].*$`)
	reCmntQ   = regexp.MustCompile(`^([^;#"]*"[^"]*"[^;#"]*)[;#].*$`)
	reBlank   = regexp.MustCompile(`^\s*$`)
	reSect    = regexp.MustCompile(`^\s*\[\s*([^\s]*)\s*\]\s*$`)
	reVar     = regexp.MustCompile(`^\s*([^"=\s]+)\s*=\s*([^"\s]*)\s*$`)
	reVarQ    = regexp.MustCompile(`^\s*([^"=\s]+)\s*=\s*"([^"\n\\]*)"\s*$`)
	reVarDflt = regexp.MustCompile(`^\s*\b(.*)\b\s*$`)
)

const (
	// Default value in case a value for a variable isn't provided.
	DefaultValue = "true"
)

type Bool bool

var boolValues = map[string]interface{}{
	"true": true, "yes": true, "on": true, "1": true,
	"false": false, "no": false, "off": false, "0": false}

func scan(state fmt.ScanState, values map[string]interface{}) (interface{}, error) {
	var rd []rune
	var r rune
	var err error
	for r, _, err = state.ReadRune(); err == nil; r, _, err = state.ReadRune() {
		rd = append(rd, r)
		part := false
		for s, v := range values {
			if strings.EqualFold(string(rd), s) {
				return v, err
			}
			if len(rd) < len(s) && strings.EqualFold(string(rd), s[:len(rd)]) {
				part = true
			}
		}
		if part == false {
			state.UnreadRune()
			return nil, errors.New("unsupported value " + string(rd))
		}
	}
	return nil, err
}

func (b *Bool) Scan(state fmt.ScanState, verb rune) error {
	v, err := scan(state, boolValues)
	switch bb := v.(type) {
	case bool:
		*b = Bool(bb)
		return err
	}
	return err
}

func fieldFold(v reflect.Value, name string) reflect.Value {
	n := strings.Replace(name, "-", "_", -1)
	return v.FieldByNameFunc(func(fieldName string) bool {
		return strings.EqualFold(n, fieldName)
	})
}

func set(cfg interface{}, sect, name, value string) error {
	vDest := reflect.ValueOf(cfg).Elem()
	vSect := fieldFold(vDest, sect)
	vName := fieldFold(vSect, name)
	vAddr := vName.Addr().Interface()
	switch v := vAddr.(type) {
	case *string:
		*v = value
		return nil
	case *bool:
		vAddr = (*Bool)(v)
	}
	// attempt to read an extra rune to make sure the value is consumed
	var r rune
	n, err := fmt.Sscanf(value, "%v%c", vAddr, &r)
	switch {
	case n < 1 || n == 1 && err != io.EOF:
		return fmt.Errorf("failed to parse %q as %#v: parse error %v", value,
			vName.Type(), err)
	case n > 1:
		return fmt.Errorf("failed to parse %q as %#v: extra characters", value,
			vName.Type())
	case n == 1 && err == io.EOF:
		return nil
	}
	panic("never reached")
}

// Parse reads gcfg formatted data from reader and sets the values into the
// corresponding fields in config. Config must be a pointer to a struct.
func Parse(config interface{}, reader io.Reader) error {
	r := bufio.NewReader(reader)
	sect := (*string)(nil)
	for line := 1; true; line++ {
		l, pre, err := r.ReadLine()
		if err != nil && err != io.EOF {
			return err
		} else if pre {
			return errors.New("line too long")
		}
		// exclude comments
		if c := reCmnt.FindSubmatch(l); c != nil {
			l = c[1]
		} else if c := reCmntQ.FindSubmatch(l); c != nil {
			l = c[1]
		}
		if !reBlank.Match(l) {
			// "switch" based on line contents
			if sec := reSect.FindSubmatch(l); sec != nil {
				strsec := string(sec[1])
				sect = &strsec
			} else if v, vq, vd := reVar.FindSubmatch(l),
				reVarQ.FindSubmatch(l), reVarDflt.FindSubmatch(l); //
			v != nil || vq != nil || vd != nil {
				if sect == nil {
					return errors.New("no section")
				}
				var name, value string
				if v != nil {
					name, value = string(v[1]), string(v[2])
				} else if vq != nil {
					name, value = string(vq[1]), string(vq[2])
				} else { // vd != nil
					name, value = string(vd[1]), DefaultValue
				}
				err := set(config, *sect, name, value)
				if err != nil {
					return err
				}
			} else {
				return fmt.Errorf("invalid line %q", string(l))
			}
		}
		if err == io.EOF {
			break
		}
	}
	return nil
}

// ParseString reads gcfg formatted data from str and sets the values into the
// corresponding fields in cfg. It is a wrapper for Parse(config, reader).
func ParseString(config interface{}, str string) error {
	r := strings.NewReader(str)
	return Parse(config, r)
}

// ParseFile reads gcfg formatted data from the file filename and sets the
// values into the corresponding fields in cfg. It is a wrapper for
// Parse(config, reader).
func ParseFile(config interface{}, filename string) error {
	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer f.Close()
	return Parse(config, f)
}
<|endoftext|>


<commit_before>// Copyright 2015 Hajime Hoshi
//
// 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 audio

// #cgo LDFLAGS: -lwinmm
//
// #include <windows.h>
// #include <mmsystem.h>
//
// #define sizeOfWavehdr (sizeof(WAVEHDR))
//
// MMRESULT waveOutOpen2(HWAVEOUT* waveOut, WAVEFORMATEX* format);
import "C"

import (
	"fmt"
	"io"
	"time"
	"unsafe"
)

type header struct {
	buffer     unsafe.Pointer
	bufferSize int
	waveHdr    C.WAVEHDR
}

// TODO: Reduce panics and use errors instead

func newHeader(waveOut C.HWAVEOUT, bufferSize int) header {
	// NOTE: This is never freed so far.
	buf := C.malloc(C.size_t(bufferSize))
	h := header{
		buffer:     buf,
		bufferSize: bufferSize,
		waveHdr: C.WAVEHDR{
			lpData:         C.LPSTR(buf),
			dwBufferLength: C.DWORD(bufferSize),
		},
	}
	// TODO: Need to unprepare to avoid memory leak?
	if err := C.waveOutPrepareHeader(waveOut, &h.waveHdr, C.sizeOfWavehdr); err != C.MMSYSERR_NOERROR {
		panic(fmt.Sprintf("audio: waveOutPrepareHeader error %d", err))
	}
	return h
}

func (h *header) Write(waveOut C.HWAVEOUT, data []byte) {
	if len(data) != h.bufferSize {
		panic("audio: len(data) must equal to h.bufferSize")
	}
	C.memcpy(h.buffer, unsafe.Pointer(&data[0]), C.size_t(h.bufferSize))
	if err := C.waveOutWrite(waveOut, &h.waveHdr, C.sizeOfWavehdr); err != C.MMSYSERR_NOERROR {
		panic(fmt.Sprintf("audio: waveOutWriter error %d", err))
	}
}

const numHeader = 8

var sem = make(chan struct{}, numHeader)

//export releaseSemaphore
func releaseSemaphore() {
	<-sem
}

type player struct {
	src     io.Reader
	out     C.HWAVEOUT
	i       int
	buffer  []byte
	headers []header
}

const bufferSize = 1024

func startPlaying(src io.Reader, sampleRate int) error {
	const numBlockAlign = channelNum * bitsPerSample / 8
	f := C.WAVEFORMATEX{
		wFormatTag:      C.WAVE_FORMAT_PCM,
		nChannels:       channelNum,
		nSamplesPerSec:  C.DWORD(sampleRate),
		nAvgBytesPerSec: C.DWORD(sampleRate) * numBlockAlign,
		wBitsPerSample:  bitsPerSample,
		nBlockAlign:     numBlockAlign,
	}
	var w C.HWAVEOUT
	if err := C.waveOutOpen2(&w, &f); err != C.MMSYSERR_NOERROR {
		return fmt.Errorf("audio: waveOutOpen error: %d", err)
	}
	p := &player{
		src:     src,
		out:     w,
		buffer:  []byte{},
		headers: make([]header, numHeader),
	}
	for i := 0; i < numHeader; i++ {
		p.headers[i] = newHeader(w, bufferSize)
	}
	go func() {
		defer p.close()
		for {
			err := p.proceed()
			if err == io.EOF {
				break
			}
			if err != nil {
				// TODO: Propagate this error?
				panic(err)
			}
			time.Sleep(1 * time.Millisecond)
		}
	}()
	return nil
}

func (p *player) proceed() error {
	b := make([]byte, bufferSize)
	n, err := p.src.Read(b)
	if 0 < n {
		p.buffer = append(p.buffer, b[:n]...)
		for bufferSize <= len(p.buffer) {
			sem <- struct{}{}
			p.headers[p.i].Write(p.out, p.buffer[:bufferSize])
			p.buffer = p.buffer[bufferSize:]
			p.i++
			p.i %= len(p.headers)
		}
	}
	return err
}

func (p *player) close() {
	// TODO: Implement this
}
<commit_msg>audio: Reduce panic on Windows<commit_after>// Copyright 2015 Hajime Hoshi
//
// 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 audio

// #cgo LDFLAGS: -lwinmm
//
// #include <windows.h>
// #include <mmsystem.h>
//
// #define sizeOfWavehdr (sizeof(WAVEHDR))
//
// MMRESULT waveOutOpen2(HWAVEOUT* waveOut, WAVEFORMATEX* format);
import "C"

import (
	"errors"
	"fmt"
	"io"
	"time"
	"unsafe"
)

type header struct {
	buffer     unsafe.Pointer
	bufferSize int
	waveHdr    C.WAVEHDR
}

func newHeader(waveOut C.HWAVEOUT, bufferSize int) (header, error) {
	// NOTE: This is never freed so far.
	buf := C.malloc(C.size_t(bufferSize))
	h := header{
		buffer:     buf,
		bufferSize: bufferSize,
		waveHdr: C.WAVEHDR{
			lpData:         C.LPSTR(buf),
			dwBufferLength: C.DWORD(bufferSize),
		},
	}
	// TODO: Need to unprepare to avoid memory leak?
	if err := C.waveOutPrepareHeader(waveOut, &h.waveHdr, C.sizeOfWavehdr); err != C.MMSYSERR_NOERROR {
		return header{}, fmt.Errorf("audio: waveOutPrepareHeader error: %d", err)
	}
	return h, nil
}

func (h *header) Write(waveOut C.HWAVEOUT, data []byte) error {
	if len(data) != h.bufferSize {
		return errors.New("audio: len(data) must equal to h.bufferSize")
	}
	C.memcpy(h.buffer, unsafe.Pointer(&data[0]), C.size_t(h.bufferSize))
	if err := C.waveOutWrite(waveOut, &h.waveHdr, C.sizeOfWavehdr); err != C.MMSYSERR_NOERROR {
		return fmt.Errorf("audio: waveOutWriter error: %d", err)
	}
	return nil
}

const numHeader = 8

var sem = make(chan struct{}, numHeader)

//export releaseSemaphore
func releaseSemaphore() {
	<-sem
}

type player struct {
	src     io.Reader
	out     C.HWAVEOUT
	i       int
	buffer  []byte
	headers []header
}

const bufferSize = 1024

func startPlaying(src io.Reader, sampleRate int) error {
	const numBlockAlign = channelNum * bitsPerSample / 8
	f := C.WAVEFORMATEX{
		wFormatTag:      C.WAVE_FORMAT_PCM,
		nChannels:       channelNum,
		nSamplesPerSec:  C.DWORD(sampleRate),
		nAvgBytesPerSec: C.DWORD(sampleRate) * numBlockAlign,
		wBitsPerSample:  bitsPerSample,
		nBlockAlign:     numBlockAlign,
	}
	var w C.HWAVEOUT
	if err := C.waveOutOpen2(&w, &f); err != C.MMSYSERR_NOERROR {
		return fmt.Errorf("audio: waveOutOpen error: %d", err)
	}
	p := &player{
		src:     src,
		out:     w,
		buffer:  []byte{},
		headers: make([]header, numHeader),
	}
	for i := 0; i < numHeader; i++ {
		var err error
		p.headers[i], err = newHeader(w, bufferSize)
		if err != nil {
			return err
		}
	}
	go func() {
		defer p.close()
		for {
			err := p.proceed()
			if err == io.EOF {
				break
			}
			if err != nil {
				// TODO: Propagate this error?
				panic(err)
			}
			time.Sleep(1 * time.Millisecond)
		}
	}()
	return nil
}

func (p *player) proceed() error {
	b := make([]byte, bufferSize)
	n, err := p.src.Read(b)
	if 0 < n {
		p.buffer = append(p.buffer, b[:n]...)
		for bufferSize <= len(p.buffer) {
			sem <- struct{}{}
			if err := p.headers[p.i].Write(p.out, p.buffer[:bufferSize]); err != nil {
				return err
			}
			p.buffer = p.buffer[bufferSize:]
			p.i++
			p.i %= len(p.headers)
		}
	}
	return err
}

func (p *player) close() {
	// TODO: Implement this
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC

// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

// Package glob provides equivalent functionality to filepath.Glob while
// meeting different performance requirements.
package glob

import (
	"context"
	"io"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

// Glob is similar to filepath.Glob but with different performance concerns.
//
// Firstly, It can be canceled via the context. Secondly, it makes no guarantees
// about the order of returned matches. This change allows it to run in O(d+m)
// memory and O(n) time, where m is the number of match results, d is the depth
// of the directory tree the pattern is concerned with, and n is the number of
// files in that tree.
func Glob(ctx context.Context, pattern string) ([]string, error) {
	gr := Stream(pattern)
	ctx, cancel := context.WithCancel(ctx)
	go func() {
		<-ctx.Done()
		gr.Close()
	}()
	defer cancel()

	var ret []string
	for {
		match, err := gr.Next()
		if err != nil {
			return nil, err
		}
		if match == "" {
			break
		}
		ret = append(ret, match)
	}
	return ret, nil
}

type GlobResult struct {
	errors  chan error
	results chan string
	cancel  context.CancelFunc
}

// Stream Returns a GlobResult from which glob matches can be streamed.
//
// Stream supports the same pattern syntax and produces the same matches as Go's
// filepath.Glob, but makes no ordering guarantees.
func Stream(pattern string) GlobResult {
	ctx, cancel := context.WithCancel(context.Background())
	g := GlobResult{
		errors:  make(chan error),
		results: make(chan string),
		cancel:  cancel,
	}
	go func() {
		defer close(g.results)
		defer close(g.errors)
		if err := stream(pattern, g.results, ctx.Done()); err != nil {
			g.errors <- err
		}
	}()
	return g
}

// Next returns the next match from the pattern. It returns an empty string when
// the matches are exhausted.
func (g *GlobResult) Next() (string, error) {
	// Note: Next never returns filepath.ErrBadPattern if it has previously
	// returned a match. This isn't specified but it's highly desirable in
	// terms of least-surprise. I don't think there's a concise way for this
	// comment to justify this claim; you have to just read `stream` and
	// `filepath.Match` to convince yourself.
	select {
	case err := <-g.errors:
		g.Close()
		return "", err
	case r := <-g.results:
		return r, nil
	}
}

// Close cancels the in-progress globbing and cleans up. You can call this any
// time, including concurrently with Next. You don't need to call it if Next has
// returned an empty string.
func (g *GlobResult) Close() error {
	g.cancel()
	for _ = range g.errors {
	}
	for _ = range g.results {
	}
	return nil
}

// stream finds files matching pattern and sends their paths on the results
// channel. It stops (returning nil) if the cancel channel is closed.
// The caller must drain the results channel.
func stream(pattern string, results chan<- string, cancel <-chan struct{}) (err error) {
	if !hasMeta(pattern) {
		if _, err = os.Lstat(pattern); err != nil {
			return nil
		}
		results <- pattern
		return nil
	}

	dir, file := filepath.Split(pattern)
	volumeLen := 0
	if runtime.GOOS == "windows" {
		volumeLen, dir = cleanGlobPathWindows(dir)
	} else {
		dir = cleanGlobPath(dir)
	}

	if !hasMeta(dir[volumeLen:]) {
		return glob(dir, file, results, cancel)
	}

	// Prevent infinite recursion. See Go issue 15879.
	if dir == pattern {
		return filepath.ErrBadPattern
	}

	dirMatches := make(chan string)
	go func() {
		err = stream(dir, dirMatches, cancel)
		close(dirMatches)
	}()

	for d := range dirMatches {
		if err = glob(d, file, results, cancel); err != nil {
			return
		}
	}
	return
}

// cleanGlobPath prepares path for glob matching.
func cleanGlobPath(path string) string {
	switch path {
	case "":
		return "."
	case string(filepath.Separator):
		// do nothing to the path
		return path
	default:
		return path[0 : len(path)-1] // chop off trailing separator
	}
}

// cleanGlobPathWindows is windows version of cleanGlobPath.
func cleanGlobPathWindows(path string) (prefixLen int, cleaned string) {
	vollen := len(filepath.VolumeName(path))
	switch {
	case path == "":
		return 0, "."
	case vollen+1 == len(path) && os.IsPathSeparator(path[len(path)-1]): // /, \, C:\ and C:/
		// do nothing to the path
		return vollen + 1, path
	case vollen == len(path) && len(path) == 2: // C:
		return vollen, path + "." // convert C: into C:.
	default:
		if vollen >= len(path) {
			vollen = len(path) - 1
		}
		return vollen, path[0 : len(path)-1] // chop off trailing separator
	}
}

// glob searches for files matching pattern in the directory dir
// and sends them down the results channel. It stops if the chancel channel is
// closed.
func glob(dir, pattern string, results chan<- string, cancel <-chan struct{}) error {
	fi, err := os.Stat(dir)
	if err != nil {
		return err
	}
	if !fi.IsDir() {
		return nil
	}
	d, err := os.Open(dir)
	if err != nil {
		return err
	}
	defer d.Close()

	for {
		select {
		case <-cancel:
			return nil
		default:
		}

		names, err := d.Readdirnames(1)
		if err == io.EOF {
			return nil
		}
		if err != nil {
			return err
		}
		n := names[0]

		matched, err := filepath.Match(pattern, n)
		if err != nil {
			return err
		}
		if matched {
			select {
			case results <- filepath.Join(dir, n):
			case <-cancel:
				return nil
			}
		}
	}
}

// hasMeta reports whether path contains any of the magic characters
// recognized by filepath.Match.
func hasMeta(path string) bool {
	magicChars := `*?[`
	if runtime.GOOS != "windows" {
		magicChars = `*?[\`
	}
	return strings.ContainsAny(path, magicChars)
}
<commit_msg>Rename GlobResult -> Result<commit_after>// Copyright 2020 Google LLC

// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

// Package glob provides equivalent functionality to filepath.Glob while
// meeting different performance requirements.
package glob

import (
	"context"
	"io"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

// Glob is similar to filepath.Glob but with different performance concerns.
//
// Firstly, It can be canceled via the context. Secondly, it makes no guarantees
// about the order of returned matches. This change allows it to run in O(d+m)
// memory and O(n) time, where m is the number of match results, d is the depth
// of the directory tree the pattern is concerned with, and n is the number of
// files in that tree.
func Glob(ctx context.Context, pattern string) ([]string, error) {
	gr := Stream(pattern)
	ctx, cancel := context.WithCancel(ctx)
	go func() {
		<-ctx.Done()
		gr.Close()
	}()
	defer cancel()

	var ret []string
	for {
		match, err := gr.Next()
		if err != nil {
			return nil, err
		}
		if match == "" {
			break
		}
		ret = append(ret, match)
	}
	return ret, nil
}

type Result struct {
	errors  chan error
	results chan string
	cancel  context.CancelFunc
}

// Stream Returns a Result from which glob matches can be streamed.
//
// Stream supports the same pattern syntax and produces the same matches as Go's
// filepath.Glob, but makes no ordering guarantees.
func Stream(pattern string) Result {
	ctx, cancel := context.WithCancel(context.Background())
	g := Result{
		errors:  make(chan error),
		results: make(chan string),
		cancel:  cancel,
	}
	go func() {
		defer close(g.results)
		defer close(g.errors)
		if err := stream(pattern, g.results, ctx.Done()); err != nil {
			g.errors <- err
		}
	}()
	return g
}

// Next returns the next match from the pattern. It returns an empty string when
// the matches are exhausted.
func (g *Result) Next() (string, error) {
	// Note: Next never returns filepath.ErrBadPattern if it has previously
	// returned a match. This isn't specified but it's highly desirable in
	// terms of least-surprise. I don't think there's a concise way for this
	// comment to justify this claim; you have to just read `stream` and
	// `filepath.Match` to convince yourself.
	select {
	case err := <-g.errors:
		g.Close()
		return "", err
	case r := <-g.results:
		return r, nil
	}
}

// Close cancels the in-progress globbing and cleans up. You can call this any
// time, including concurrently with Next. You don't need to call it if Next has
// returned an empty string.
func (g *Result) Close() error {
	g.cancel()
	for _ = range g.errors {
	}
	for _ = range g.results {
	}
	return nil
}

// stream finds files matching pattern and sends their paths on the results
// channel. It stops (returning nil) if the cancel channel is closed.
// The caller must drain the results channel.
func stream(pattern string, results chan<- string, cancel <-chan struct{}) (err error) {
	if !hasMeta(pattern) {
		if _, err = os.Lstat(pattern); err != nil {
			return nil
		}
		results <- pattern
		return nil
	}

	dir, file := filepath.Split(pattern)
	volumeLen := 0
	if runtime.GOOS == "windows" {
		volumeLen, dir = cleanGlobPathWindows(dir)
	} else {
		dir = cleanGlobPath(dir)
	}

	if !hasMeta(dir[volumeLen:]) {
		return glob(dir, file, results, cancel)
	}

	// Prevent infinite recursion. See Go issue 15879.
	if dir == pattern {
		return filepath.ErrBadPattern
	}

	dirMatches := make(chan string)
	go func() {
		err = stream(dir, dirMatches, cancel)
		close(dirMatches)
	}()

	for d := range dirMatches {
		if err = glob(d, file, results, cancel); err != nil {
			return
		}
	}
	return
}

// cleanGlobPath prepares path for glob matching.
func cleanGlobPath(path string) string {
	switch path {
	case "":
		return "."
	case string(filepath.Separator):
		// do nothing to the path
		return path
	default:
		return path[0 : len(path)-1] // chop off trailing separator
	}
}

// cleanGlobPathWindows is windows version of cleanGlobPath.
func cleanGlobPathWindows(path string) (prefixLen int, cleaned string) {
	vollen := len(filepath.VolumeName(path))
	switch {
	case path == "":
		return 0, "."
	case vollen+1 == len(path) && os.IsPathSeparator(path[len(path)-1]): // /, \, C:\ and C:/
		// do nothing to the path
		return vollen + 1, path
	case vollen == len(path) && len(path) == 2: // C:
		return vollen, path + "." // convert C: into C:.
	default:
		if vollen >= len(path) {
			vollen = len(path) - 1
		}
		return vollen, path[0 : len(path)-1] // chop off trailing separator
	}
}

// glob searches for files matching pattern in the directory dir
// and sends them down the results channel. It stops if the chancel channel is
// closed.
func glob(dir, pattern string, results chan<- string, cancel <-chan struct{}) error {
	fi, err := os.Stat(dir)
	if err != nil {
		return err
	}
	if !fi.IsDir() {
		return nil
	}
	d, err := os.Open(dir)
	if err != nil {
		return err
	}
	defer d.Close()

	for {
		select {
		case <-cancel:
			return nil
		default:
		}

		names, err := d.Readdirnames(1)
		if err == io.EOF {
			return nil
		}
		if err != nil {
			return err
		}
		n := names[0]

		matched, err := filepath.Match(pattern, n)
		if err != nil {
			return err
		}
		if matched {
			select {
			case results <- filepath.Join(dir, n):
			case <-cancel:
				return nil
			}
		}
	}
}

// hasMeta reports whether path contains any of the magic characters
// recognized by filepath.Match.
func hasMeta(path string) bool {
	magicChars := `*?[`
	if runtime.GOOS != "windows" {
		magicChars = `*?[\`
	}
	return strings.ContainsAny(path, magicChars)
}
<|endoftext|>


<commit_before>//
// Copyright © 2016 weirdgiraffe <weirdgiraffe@cyberzoo.xyz>
//
// Distributed under terms of the MIT license.
//

package gobf

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

// DataChunkSize count of bytes to use when need ot
// increase count of program data cells
var DataChunkSize = 30000

// AllowOverflows do allow overflow (255+1) and underflow (0-1) of a cell value
var AllowOverflows = true

// Program represents brainfuck programm
type Program struct {
	code     []byte
	cmdIndx  int
	data     []byte
	cellIndx int
	reader   io.Reader
	writer   io.Writer
}

// NewProgram initialize empty program
func NewProgram() *Program {
	return &Program{
		code:    []byte{},
		cmdIndx: 0,
		reader:  os.Stdin,
		writer:  os.Stdout,
	}
}

// Load load program code
func (p *Program) Load(r io.Reader) error {
	code, err := ioutil.ReadAll(r)
	if err != nil {
		return fmt.Errorf("Failed to read program code: %v", err)
	}
	p.code = code
	p.Reset()
	return nil
}

// Reset resets program. Run() will run program again
func (p *Program) Reset() {
	if len(p.data) > 0 {
		p.data = make([]byte, len(p.data))
	} else {
		p.data = make([]byte, DataChunkSize)
	}

	p.cellIndx = 0
	p.cmdIndx = 0
}

// Run runs brainfuck program
func (p *Program) Run() error {
	var err error
	for p.cmdIndx < len(p.code) {
		p.cmdIndx, err = p.runCmd()
		if err != nil {
			return err
		}
	}
	return nil
}

func (p *Program) runCmd() (int, error) {
	switch p.code[p.cmdIndx] {
	default:
		return p.cmdIndx + 1, nil
	case '+':
		return p.cmdIncCellValue()
	case '-':
		return p.cmdDecCellValue()
	case '>':
		return p.cmdNextCell()
	case '<':
		return p.cmdPrevCell()
	case '[':
		return p.cmdForward()
	case ']':
		return p.cmdBackward()
	case '.':
		return p.cmdPrintCell()
	case ',':
		return p.cmdScanCell()
	}
}

func (p *Program) cmd(indx int) byte {
	return p.code[indx]
}

func (p *Program) currentCell() byte {
	return p.data[p.cellIndx]
}

func (p *Program) opcount(op byte) int {
	for i, c := range p.code[p.cmdIndx:] {
		if c != op {
			return i
		}
	}
	return len(p.code)
}

func (p *Program) cmdIncCellValue() (int, error) {
	count := p.opcount('+')
	p.data[p.cellIndx] += byte(count)
	return p.cmdIndx + count, nil
}

func (p *Program) cmdDecCellValue() (int, error) {
	count := p.opcount('-')
	p.data[p.cellIndx] -= byte(count)
	return p.cmdIndx + count, nil
}

func (p *Program) cmdNextCell() (int, error) {
	count := p.opcount('>')
	if p.cellIndx+count >= len(p.data) {
		incSize := (count / DataChunkSize) + DataChunkSize
		p.data = append(p.data, make([]byte, incSize)...)
	}
	p.cellIndx += count
	return p.cmdIndx + count, nil
}

func (p *Program) cmdPrevCell() (int, error) {
	count := p.opcount('<')
	if p.cellIndx-count < 0 {
		return 0, fmt.Errorf("Data pointer underfow")
	}
	p.cellIndx -= count
	return p.cmdIndx + count, nil
}

func (p *Program) _cmdForward() (int, error) {
	for seen, i := 0, p.cmdIndx+1; i < len(p.code); i++ {
		switch p.cmd(i) {
		case '[':
			seen++
		case ']':
			if seen == 0 {
				return i + 1, nil
			}
			seen--
		}
	}
	return len(p.code), fmt.Errorf("No closing ']' found")
}

func (p *Program) cmdForward() (int, error) {
	// if current cell value is 0,
	// increase cmdIndx until matching bracket
	if p.currentCell() != 0 {
		return p.cmdIndx + 1, nil
	}
	return p._cmdForward()
}

func (p *Program) _cmdBackward() (int, error) {
	for seen, i := 0, p.cmdIndx-1; i >= 0; i-- {
		switch p.cmd(i) {
		case ']':
			seen++
		case '[':
			if seen == 0 {
				return i + 1, nil
			}
			seen--
		}
	}
	return len(p.code), fmt.Errorf("No closing '[' found")
}

func (p *Program) cmdBackward() (int, error) {
	// if current cell value is not 0,
	// decrease cmdIndx until matching bracket
	if p.currentCell() == 0 {
		return p.cmdIndx + 1, nil
	}
	return p._cmdBackward()
}

func (p *Program) cmdPrintCell() (int, error) {
	_, err := p.writer.Write([]byte{p.currentCell()})
	return p.cmdIndx + 1, err
}

func (p *Program) cmdScanCell() (int, error) {
	var b = make([]byte, 1)
	_, err := p.reader.Read(b)
	if err == nil {
		p.data[p.cellIndx] = b[0]
	}
	return p.cmdIndx + 1, err
}
<commit_msg>remove unused parameter<commit_after>//
// Copyright © 2016 weirdgiraffe <weirdgiraffe@cyberzoo.xyz>
//
// Distributed under terms of the MIT license.
//

package gobf

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

// DataChunkSize count of bytes to use when need ot
// increase count of program data cells
var DataChunkSize = 30000

// Program represents brainfuck programm
type Program struct {
	code     []byte
	cmdIndx  int
	data     []byte
	cellIndx int
	reader   io.Reader
	writer   io.Writer
}

// NewProgram initialize empty program
func NewProgram() *Program {
	return &Program{
		code:    []byte{},
		cmdIndx: 0,
		reader:  os.Stdin,
		writer:  os.Stdout,
	}
}

// Load load program code
func (p *Program) Load(r io.Reader) error {
	code, err := ioutil.ReadAll(r)
	if err != nil {
		return fmt.Errorf("Failed to read program code: %v", err)
	}
	p.code = code
	p.Reset()
	return nil
}

// Reset resets program. Run() will run program again
func (p *Program) Reset() {
	if len(p.data) > 0 {
		p.data = make([]byte, len(p.data))
	} else {
		p.data = make([]byte, DataChunkSize)
	}

	p.cellIndx = 0
	p.cmdIndx = 0
}

// Run runs brainfuck program
func (p *Program) Run() error {
	var err error
	for p.cmdIndx < len(p.code) {
		p.cmdIndx, err = p.runCmd()
		if err != nil {
			return err
		}
	}
	return nil
}

func (p *Program) runCmd() (int, error) {
	switch p.code[p.cmdIndx] {
	default:
		return p.cmdIndx + 1, nil
	case '+':
		return p.cmdIncCellValue()
	case '-':
		return p.cmdDecCellValue()
	case '>':
		return p.cmdNextCell()
	case '<':
		return p.cmdPrevCell()
	case '[':
		return p.cmdForward()
	case ']':
		return p.cmdBackward()
	case '.':
		return p.cmdPrintCell()
	case ',':
		return p.cmdScanCell()
	}
}

func (p *Program) cmd(indx int) byte {
	return p.code[indx]
}

func (p *Program) currentCell() byte {
	return p.data[p.cellIndx]
}

func (p *Program) opcount(op byte) int {
	for i, c := range p.code[p.cmdIndx:] {
		if c != op {
			return i
		}
	}
	return len(p.code)
}

func (p *Program) cmdIncCellValue() (int, error) {
	count := p.opcount('+')
	p.data[p.cellIndx] += byte(count)
	return p.cmdIndx + count, nil
}

func (p *Program) cmdDecCellValue() (int, error) {
	count := p.opcount('-')
	p.data[p.cellIndx] -= byte(count)
	return p.cmdIndx + count, nil
}

func (p *Program) cmdNextCell() (int, error) {
	count := p.opcount('>')
	if p.cellIndx+count >= len(p.data) {
		incSize := (count / DataChunkSize) + DataChunkSize
		p.data = append(p.data, make([]byte, incSize)...)
	}
	p.cellIndx += count
	return p.cmdIndx + count, nil
}

func (p *Program) cmdPrevCell() (int, error) {
	count := p.opcount('<')
	if p.cellIndx-count < 0 {
		return 0, fmt.Errorf("Data pointer underfow")
	}
	p.cellIndx -= count
	return p.cmdIndx + count, nil
}

func (p *Program) _cmdForward() (int, error) {
	for seen, i := 0, p.cmdIndx+1; i < len(p.code); i++ {
		switch p.cmd(i) {
		case '[':
			seen++
		case ']':
			if seen == 0 {
				return i + 1, nil
			}
			seen--
		}
	}
	return len(p.code), fmt.Errorf("No closing ']' found")
}

func (p *Program) cmdForward() (int, error) {
	// if current cell value is 0,
	// increase cmdIndx until matching bracket
	if p.currentCell() != 0 {
		return p.cmdIndx + 1, nil
	}
	return p._cmdForward()
}

func (p *Program) _cmdBackward() (int, error) {
	for seen, i := 0, p.cmdIndx-1; i >= 0; i-- {
		switch p.cmd(i) {
		case ']':
			seen++
		case '[':
			if seen == 0 {
				return i + 1, nil
			}
			seen--
		}
	}
	return len(p.code), fmt.Errorf("No closing '[' found")
}

func (p *Program) cmdBackward() (int, error) {
	// if current cell value is not 0,
	// decrease cmdIndx until matching bracket
	if p.currentCell() == 0 {
		return p.cmdIndx + 1, nil
	}
	return p._cmdBackward()
}

func (p *Program) cmdPrintCell() (int, error) {
	_, err := p.writer.Write([]byte{p.currentCell()})
	return p.cmdIndx + 1, err
}

func (p *Program) cmdScanCell() (int, error) {
	var b = make([]byte, 1)
	_, err := p.reader.Read(b)
	if err == nil {
		p.data[p.cellIndx] = b[0]
	}
	return p.cmdIndx + 1, err
}
<|endoftext|>


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

package goyo

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

var ROOT_DIRECTORY = flag.String("rootdir", "", "root directory for mail")

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

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

func init() {
	flag.Parse()
}

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

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

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

	if err != nil {
		return err
	}

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

	to_address := addresses[0].Address
	log.Printf("Found address %s", to_address)

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

	//Schedule future message for that yo-yoed time

	log.Printf("Scheduling message for %v", t)
	if err := scheduleFutureMessage(filename, t); err != nil {
		return err
	}

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

	return err
}

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

	matches := TIME_REGEX.FindStringSubmatch(to_address)

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

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

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

	var time_unit time.Duration

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

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

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

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

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

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

}

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

	return nil
}

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

package goyo

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

var ROOT_DIRECTORY = flag.String("rootdir", "", "root directory for mail")

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

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

func init() {
	flag.Parse()
}

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

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

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

	if err != nil {
		return err
	}

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

	to_address := addresses[0].Address
	log.Printf("Found address %s", to_address)

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

	//Schedule future message for that yo-yoed time

	log.Printf("Scheduling message for %v", t)
	if err := scheduleFutureMessage(filename, t); err != nil {
		return err
	}

	//Move message from /new to /cur, setting Maildir info flag to S (seen)
	destination := filepath.Join(*ROOT_DIRECTORY, "cur", strings.TrimPrefix(uniqueFromFilename(filename)+":2,S", filepath.Join(*ROOT_DIRECTORY, "new")))
	log.Printf("Moving message from %s to %s", filename, destination)
	err = os.Rename(filename, destination)

	return err
}

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

	matches := TIME_REGEX.FindStringSubmatch(to_address)

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

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

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

	var time_unit time.Duration

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

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

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

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

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

	delay := time.Duration(number) * time_unit
	//TODO use the time the message was sent instead of time.Now
	future_time := time.Now().Add(delay)
	return future_time, nil

}

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

	return nil
}

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


<commit_before>package gogadgets

import (
	"bitbucket.org/cswank/gogadgets/utils"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"syscall"
)

//GPIO interacts with the linux sysfs interface for GPIO
//to turn pins on and off.  The pins that are listed in
//gogadgets.Pins have been found to be availabe by default
//but by using the device tree overlay you can make more
//pins available.
//GPIO also has a Wait method and can poll a pin and wait
//for a change of direction.
type GPIO struct {
	OutputDevice
	Poller
	units         string
	export        string
	exportPath    string
	directionPath string
	valuePath     string
	edgePath      string
	direction     string
	edge          string
	fd            int
	fdSet         *syscall.FdSet
	buf           []byte
}

func NewGPIO(pin *Pin) (OutputDevice, error) {
	var export string
	var ok bool
	if pin.Platform == "rpi" {
		export, ok = PiPins[pin.Pin]
		if !ok {
			return nil, errors.New(fmt.Sprintf("no such pin: %s", pin.Pin))
		}
	} else {
		portMap, ok = Pins["gpio"][pin.Port]
		if !ok {
			return nil, errors.New(fmt.Sprintf("no such port: %s", pin.Port))
		}
		export, ok = portMap[pin.Pin]
		if !ok {
			return nil, errors.New(fmt.Sprintf("no such pin: %s", pin.Pin))
		}
	}
	if pin.Direction == "" {
		pin.Direction = "out"
	}
	g := &GPIO{
		export:        export,
		exportPath:    "/sys/class/gpio/export",
		directionPath: fmt.Sprintf("/sys/class/gpio/gpio%s/direction", export),
		edgePath:      fmt.Sprintf("/sys/class/gpio/gpio%s/edge", export),
		valuePath:     fmt.Sprintf("/sys/class/gpio/gpio%s/value", export),
		direction:     pin.Direction,
		edge:          pin.Edge,
	}
	err := g.Init()
	return g, err
}

func (g *GPIO) Init() error {
	var err error
	if !utils.FileExists(g.directionPath) {
		err = g.writeValue(g.exportPath, g.export)
	}
	if err == nil {
		err = g.writeValue(g.directionPath, g.direction)
		if err == nil && g.direction == "out" {
			err = g.writeValue(g.valuePath, "0")
		} else if err == nil && g.edge != "" {
			err = g.writeValue(g.edgePath, g.edge)
		}
	}
	return err
}

func (g *GPIO) Update(msg *Message) {

}

func (g *GPIO) On(val *Value) error {
	return g.writeValue(g.valuePath, "1")
}

func (g *GPIO) Status() interface{} {
	data, err := ioutil.ReadFile(g.valuePath)
	return err == nil && string(data) == "1\n"
}

func (g *GPIO) Off() error {
	return g.writeValue(g.valuePath, "0")
}

func (g *GPIO) writeValue(path, value string) error {
	return ioutil.WriteFile(path, []byte(value), os.ModeDevice)
}

func (g *GPIO) Wait() (bool, error) {
	if g.fd == 0 {
		fd, err := syscall.Open(g.valuePath, syscall.O_RDONLY, 0666)
		if err != nil {
			return false, err
		}
		g.fd = fd
		g.fdSet = new(syscall.FdSet)
		FD_SET(g.fd, g.fdSet)
		g.buf = make([]byte, 64)
		syscall.Read(g.fd, g.buf)
	}
	syscall.Select(g.fd+1, nil, nil, g.fdSet, nil)
	syscall.Seek(g.fd, 0, 0)
	_, err := syscall.Read(g.fd, g.buf)
	if err != nil {
		return false, err
	}
	return string(g.buf[:2]) == "1\n", nil
}

func FD_SET(fd int, p *syscall.FdSet) {
	p.Bits[fd/32] |= 1 << (uint(fd) % 32)
}
<commit_msg>added error check for rpi pins (fix typo)<commit_after>package gogadgets

import (
	"bitbucket.org/cswank/gogadgets/utils"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"syscall"
)

//GPIO interacts with the linux sysfs interface for GPIO
//to turn pins on and off.  The pins that are listed in
//gogadgets.Pins have been found to be availabe by default
//but by using the device tree overlay you can make more
//pins available.
//GPIO also has a Wait method and can poll a pin and wait
//for a change of direction.
type GPIO struct {
	OutputDevice
	Poller
	units         string
	export        string
	exportPath    string
	directionPath string
	valuePath     string
	edgePath      string
	direction     string
	edge          string
	fd            int
	fdSet         *syscall.FdSet
	buf           []byte
}

func NewGPIO(pin *Pin) (OutputDevice, error) {
	var portMap, export string
	var ok bool
	if pin.Platform == "rpi" {
		export, ok = PiPins[pin.Pin]
		if !ok {
			return nil, errors.New(fmt.Sprintf("no such pin: %s", pin.Pin))
		}
	} else {
		portMap, ok = Pins["gpio"][pin.Port]
		if !ok {
			return nil, errors.New(fmt.Sprintf("no such port: %s", pin.Port))
		}
		export, ok = portMap[pin.Pin]
		if !ok {
			return nil, errors.New(fmt.Sprintf("no such pin: %s", pin.Pin))
		}
	}
	if pin.Direction == "" {
		pin.Direction = "out"
	}
	g := &GPIO{
		export:        export,
		exportPath:    "/sys/class/gpio/export",
		directionPath: fmt.Sprintf("/sys/class/gpio/gpio%s/direction", export),
		edgePath:      fmt.Sprintf("/sys/class/gpio/gpio%s/edge", export),
		valuePath:     fmt.Sprintf("/sys/class/gpio/gpio%s/value", export),
		direction:     pin.Direction,
		edge:          pin.Edge,
	}
	err := g.Init()
	return g, err
}

func (g *GPIO) Init() error {
	var err error
	if !utils.FileExists(g.directionPath) {
		err = g.writeValue(g.exportPath, g.export)
	}
	if err == nil {
		err = g.writeValue(g.directionPath, g.direction)
		if err == nil && g.direction == "out" {
			err = g.writeValue(g.valuePath, "0")
		} else if err == nil && g.edge != "" {
			err = g.writeValue(g.edgePath, g.edge)
		}
	}
	return err
}

func (g *GPIO) Update(msg *Message) {

}

func (g *GPIO) On(val *Value) error {
	return g.writeValue(g.valuePath, "1")
}

func (g *GPIO) Status() interface{} {
	data, err := ioutil.ReadFile(g.valuePath)
	return err == nil && string(data) == "1\n"
}

func (g *GPIO) Off() error {
	return g.writeValue(g.valuePath, "0")
}

func (g *GPIO) writeValue(path, value string) error {
	return ioutil.WriteFile(path, []byte(value), os.ModeDevice)
}

func (g *GPIO) Wait() (bool, error) {
	if g.fd == 0 {
		fd, err := syscall.Open(g.valuePath, syscall.O_RDONLY, 0666)
		if err != nil {
			return false, err
		}
		g.fd = fd
		g.fdSet = new(syscall.FdSet)
		FD_SET(g.fd, g.fdSet)
		g.buf = make([]byte, 64)
		syscall.Read(g.fd, g.buf)
	}
	syscall.Select(g.fd+1, nil, nil, g.fdSet, nil)
	syscall.Seek(g.fd, 0, 0)
	_, err := syscall.Read(g.fd, g.buf)
	if err != nil {
		return false, err
	}
	return string(g.buf[:2]) == "1\n", nil
}

func FD_SET(fd int, p *syscall.FdSet) {
	p.Bits[fd/32] |= 1 << (uint(fd) % 32)
}
<|endoftext|>


<commit_before>package grim

// Copyright 2015 MediaMath <http://www.mediamath.com>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import "fmt"

// Instance models the state of a configured Grim instance.
type Instance struct {
	configRoot *string
	queue      *sqsQueue
}

// SetConfigRoot sets the base path of the configuration directory and clears any previously read config values from memory.
func (i *Instance) SetConfigRoot(path string) {
	i.configRoot = &path
	i.queue = nil
}

// PrepareGrimQueue creates or reuses the Amazon SQS queue named in the config.
func (i *Instance) PrepareGrimQueue() error {
	configRoot := getEffectiveConfigRoot(i.configRoot)

	config, err := getEffectiveGlobalConfig(configRoot)
	if err != nil {
		return fatalGrimErrorf("error while reading config: %v", err)
	}

	queue, err := prepareSQSQueue(config.awsKey, config.awsSecret, config.awsRegion, config.grimQueueName)
	if err != nil {
		return fatalGrimErrorf("error preparing queue: %v", err)
	}

	i.queue = queue

	return nil
}

// PrepareRepos discovers all repos that are configured then sets up SNS and GitHub.
// It is an error to call this without calling PrepareGrimQueue first.
func (i *Instance) PrepareRepos() error {
	if err := i.checkGrimQueue(); err != nil {
		return err
	}

	configRoot := getEffectiveConfigRoot(i.configRoot)

	config, err := getEffectiveGlobalConfig(configRoot)
	if err != nil {
		return fatalGrimErrorf("error while reading config: %v", err)
	}

	repos := getAllConfiguredRepos(configRoot)
	fmt.Println(repos)

	var topicARNs []string
	for _, repo := range repos {
		localConfig, err := getEffectiveConfig(configRoot, repo.owner, repo.name)
		if err != nil {
			return fatalGrimErrorf("error while reading local config: %v", err)
		}

		snsTopicName := fmt.Sprintf("grim-%v-%v-repo-topic", repo.owner, repo.name)

		snsTopicARN, err := prepareSNSTopic(config.awsKey, config.awsSecret, config.awsRegion, snsTopicName)
		if err != nil {
			return fatalGrimErrorf("error creating SNS topic: %v", err)
		}

		err = prepareSubscription(config.awsKey, config.awsSecret, config.awsRegion, snsTopicARN, i.queue.ARN)
		if err != nil {
			return fatalGrimErrorf("error subscribing Grim queue %q to SNS topic %q: %v", i.queue.ARN, snsTopicARN, err)
		}
		err = prepareAmazonSNSService(localConfig.gitHubToken, repo.owner, repo.name, snsTopicARN, config.awsKey, config.awsSecret, config.awsRegion)
		if err != nil {
			return fatalGrimErrorf("error creating configuring GitHub AmazonSNS service: %v", err)
		}
		topicARNs = append(topicARNs, snsTopicARN)
	}

	err = setPolicy(config.awsKey, config.awsSecret, config.awsRegion, i.queue.ARN, i.queue.URL, topicARNs)
	if err != nil {
		return fatalGrimErrorf("error setting policy for Grim queue %q with topics %v: %v", i.queue.ARN, topicARNs, err)
	}

	return nil
}

// BuildNextInGrimQueue creates or reuses an SQS queue as a source of work.
func (i *Instance) BuildNextInGrimQueue() error {
	if err := i.checkGrimQueue(); err != nil {
		return err
	}

	configRoot := getEffectiveConfigRoot(i.configRoot)

	globalConfig, err := getEffectiveGlobalConfig(configRoot)
	if err != nil {
		return grimErrorf("error while reading config: %v", err)
	}

	message, err := getNextMessage(globalConfig.awsKey, globalConfig.awsSecret, globalConfig.awsRegion, i.queue.URL)
	if err != nil {
		return grimErrorf("error retrieving message from Grim queue %q: %v", i.queue.URL, err)
	}

	if message != "" {
		hook, err := extractHookEvent(message)
		if err != nil {
			return grimErrorf("error extracting hook from message: %v", err)
		}

		if !(hook.eventName == "push" || hook.eventName == "pull_request" && (hook.action == "opened" || hook.action == "reopened" || hook.action == "synchronize")) {
			return nil
		}

		if hook.eventName == "pull_request" {
			sha, err := pollForMergeCommitSha(globalConfig.gitHubToken, hook.owner, hook.repo, hook.prNumber)
			if err != nil {
				return grimErrorf("error getting merge commit sha: %v", err)
			} else if sha == "" {
				return grimErrorf("error getting merge commit sha: field empty")
			}
			hook.ref = sha
		}

		localConfig, err := getEffectiveConfig(configRoot, hook.owner, hook.repo)
		if err != nil {
			return grimErrorf("error while reading config: %v", err)
		}

		return buildForHook(configRoot, localConfig, *hook)
	}

	return nil
}

// BuildRef builds a git ref immediately.
func (i *Instance) BuildRef(owner, repo, ref string) error {
	configRoot := getEffectiveConfigRoot(i.configRoot)

	config, err := getEffectiveConfig(configRoot, owner, repo)
	if err != nil {
		return fatalGrimErrorf("error while reading config: %v", err)
	}

	return buildForHook(configRoot, config, hookEvent{
		owner: owner,
		repo:  repo,
		ref:   ref,
	})
}

func buildForHook(configRoot string, config *effectiveConfig, hook hookEvent) error {
	extraEnv := hook.env()

	// TODO: do something with the err
	notifyPending(config, hook)

	result, err := build(configRoot, config.workspaceRoot, config.pathToCloneIn, hook.owner, hook.repo, extraEnv)
	if err != nil {
		notifyError(config, hook)
		return fatalGrimErrorf("error during %v: %v", describeHook(hook), err)
	}

	var notifyError error
	if result.ExitCode == 0 {
		notifyError = notifySuccess(config, hook)
	} else {
		notifyError = notifyFailure(config, hook)
	}

	err = appendResult(config.resultRoot, hook.owner, hook.repo, *result)
	if err != nil {
		return fatalGrimErrorf("error while storing result: %v", err)
	}

	return notifyError
}

func describeHook(hook hookEvent) string {
	return fmt.Sprintf("build of %v/%v initiated by a %q to %q by %q", hook.owner, hook.repo, hook.eventName, hook.target, hook.userName)
}

func notifyPending(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSPending, fmt.Sprintf("Starting %v", describeHook(hook)), ColorYellow)
}

func notifyError(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSError, fmt.Sprintf("Error during %v", describeHook(hook)), ColorGray)
}

func notifyFailure(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSFailure, fmt.Sprintf("Failure during %v", describeHook(hook)), ColorRed)
}

func notifySuccess(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSSuccess, fmt.Sprintf("Success after %v", describeHook(hook)), ColorGreen)
}

func notify(config *effectiveConfig, hook hookEvent, state refStatus, message string, color messageColor) error {
	if hook.eventName != "push" && hook.eventName != "pull_request" {
		return nil
	}

	//add grimServerID/grimQueueName to hipchat message
	message += "ServerID:" + config.grimServerID
	fmt.Printf("The message is:", message)

	ghErr := setRefStatus(config.gitHubToken, hook.owner, hook.repo, hook.statusRef, state, "", message)

	if config.hipChatToken != "" && config.hipChatRoom != "" {
		err := sendMessageToRoom(config.hipChatToken, config.hipChatRoom, "Grim", message, color)
		if err != nil {
			return err
		}
	}

	return ghErr
}

func (i *Instance) checkGrimQueue() error {
	if i.queue == nil {
		return fatalGrimErrorf("the Grim queue must be prepared first")
	}

	return nil
}
<commit_msg>grim local test<commit_after>package grim

// Copyright 2015 MediaMath <http://www.mediamath.com>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import "fmt"

// Instance models the state of a configured Grim instance.
type Instance struct {
	configRoot *string
	queue      *sqsQueue
}

// SetConfigRoot sets the base path of the configuration directory and clears any previously read config values from memory.
func (i *Instance) SetConfigRoot(path string) {
	i.configRoot = &path
	i.queue = nil
}

// PrepareGrimQueue creates or reuses the Amazon SQS queue named in the config.
func (i *Instance) PrepareGrimQueue() error {
	configRoot := getEffectiveConfigRoot(i.configRoot)

	config, err := getEffectiveGlobalConfig(configRoot)
	if err != nil {
		return fatalGrimErrorf("error while reading config: %v", err)
	}

	queue, err := prepareSQSQueue(config.awsKey, config.awsSecret, config.awsRegion, config.grimQueueName)
	if err != nil {
		return fatalGrimErrorf("error preparing queue: %v", err)
	}

	i.queue = queue

	return nil
}

// PrepareRepos discovers all repos that are configured then sets up SNS and GitHub.
// It is an error to call this without calling PrepareGrimQueue first.
func (i *Instance) PrepareRepos() error {
	if err := i.checkGrimQueue(); err != nil {
		return err
	}

	configRoot := getEffectiveConfigRoot(i.configRoot)

	config, err := getEffectiveGlobalConfig(configRoot)
	if err != nil {
		return fatalGrimErrorf("error while reading config: %v", err)
	}

	repos := getAllConfiguredRepos(configRoot)

	var topicARNs []string
	for _, repo := range repos {
		localConfig, err := getEffectiveConfig(configRoot, repo.owner, repo.name)
		if err != nil {
			return fatalGrimErrorf("error while reading local config: %v", err)
		}

		snsTopicName := fmt.Sprintf("grim-%v-%v-repo-topic", repo.owner, repo.name)

		snsTopicARN, err := prepareSNSTopic(config.awsKey, config.awsSecret, config.awsRegion, snsTopicName)
		if err != nil {
			return fatalGrimErrorf("error creating SNS topic: %v", err)
		}

		err = prepareSubscription(config.awsKey, config.awsSecret, config.awsRegion, snsTopicARN, i.queue.ARN)
		if err != nil {
			return fatalGrimErrorf("error subscribing Grim queue %q to SNS topic %q: %v", i.queue.ARN, snsTopicARN, err)
		}
		err = prepareAmazonSNSService(localConfig.gitHubToken, repo.owner, repo.name, snsTopicARN, config.awsKey, config.awsSecret, config.awsRegion)
		if err != nil {
			return fatalGrimErrorf("error creating configuring GitHub AmazonSNS service: %v", err)
		}
		topicARNs = append(topicARNs, snsTopicARN)
	}

	err = setPolicy(config.awsKey, config.awsSecret, config.awsRegion, i.queue.ARN, i.queue.URL, topicARNs)
	if err != nil {
		return fatalGrimErrorf("error setting policy for Grim queue %q with topics %v: %v", i.queue.ARN, topicARNs, err)
	}

	return nil
}

// BuildNextInGrimQueue creates or reuses an SQS queue as a source of work.
func (i *Instance) BuildNextInGrimQueue() error {
	if err := i.checkGrimQueue(); err != nil {
		return err
	}

	configRoot := getEffectiveConfigRoot(i.configRoot)

	globalConfig, err := getEffectiveGlobalConfig(configRoot)
	if err != nil {
		return grimErrorf("error while reading config: %v", err)
	}

	message, err := getNextMessage(globalConfig.awsKey, globalConfig.awsSecret, globalConfig.awsRegion, i.queue.URL)
	if err != nil {
		return grimErrorf("error retrieving message from Grim queue %q: %v", i.queue.URL, err)
	}

	if message != "" {
		hook, err := extractHookEvent(message)
		if err != nil {
			return grimErrorf("error extracting hook from message: %v", err)
		}

		if !(hook.eventName == "push" || hook.eventName == "pull_request" && (hook.action == "opened" || hook.action == "reopened" || hook.action == "synchronize")) {
			return nil
		}

		if hook.eventName == "pull_request" {
			sha, err := pollForMergeCommitSha(globalConfig.gitHubToken, hook.owner, hook.repo, hook.prNumber)
			if err != nil {
				return grimErrorf("error getting merge commit sha: %v", err)
			} else if sha == "" {
				return grimErrorf("error getting merge commit sha: field empty")
			}
			hook.ref = sha
		}

		localConfig, err := getEffectiveConfig(configRoot, hook.owner, hook.repo)
		if err != nil {
			return grimErrorf("error while reading config: %v", err)
		}

		return buildForHook(configRoot, localConfig, *hook)
	}

	return nil
}

// BuildRef builds a git ref immediately.
func (i *Instance) BuildRef(owner, repo, ref string) error {
	configRoot := getEffectiveConfigRoot(i.configRoot)

	config, err := getEffectiveConfig(configRoot, owner, repo)
	if err != nil {
		return fatalGrimErrorf("error while reading config: %v", err)
	}

	return buildForHook(configRoot, config, hookEvent{
		owner: owner,
		repo:  repo,
		ref:   ref,
	})
}

func buildForHook(configRoot string, config *effectiveConfig, hook hookEvent) error {
	extraEnv := hook.env()

	// TODO: do something with the err
	notifyPending(config, hook)

	result, err := build(configRoot, config.workspaceRoot, config.pathToCloneIn, hook.owner, hook.repo, extraEnv)
	if err != nil {
		notifyError(config, hook)
		return fatalGrimErrorf("error during %v: %v", describeHook(hook), err)
	}

	var notifyError error
	if result.ExitCode == 0 {
		notifyError = notifySuccess(config, hook)
	} else {
		notifyError = notifyFailure(config, hook)
	}

	err = appendResult(config.resultRoot, hook.owner, hook.repo, *result)
	if err != nil {
		return fatalGrimErrorf("error while storing result: %v", err)
	}

	return notifyError
}

func describeHook(hook hookEvent) string {
	return fmt.Sprintf("build of %v/%v initiated by a %q to %q by %q", hook.owner, hook.repo, hook.eventName, hook.target, hook.userName)
}

func notifyPending(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSPending, fmt.Sprintf("Starting %v", describeHook(hook)), ColorYellow)
}

func notifyError(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSError, fmt.Sprintf("Error during %v", describeHook(hook)), ColorGray)
}

func notifyFailure(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSFailure, fmt.Sprintf("Failure during %v", describeHook(hook)), ColorRed)
}

func notifySuccess(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSSuccess, fmt.Sprintf("Success after %v", describeHook(hook)), ColorGreen)
}

func notify(config *effectiveConfig, hook hookEvent, state refStatus, message string, color messageColor) error {
	if hook.eventName != "push" && hook.eventName != "pull_request" {
		return nil
	}

	//add grimServerID/grimQueueName to hipchat message
	message += "ServerID:" + config.grimServerID
	fmt.Printf("The message is:", message)

	ghErr := setRefStatus(config.gitHubToken, hook.owner, hook.repo, hook.statusRef, state, "", message)

	if config.hipChatToken != "" && config.hipChatRoom != "" {
		err := sendMessageToRoom(config.hipChatToken, config.hipChatRoom, "Grim", message, color)
		if err != nil {
			return err
		}
	}

	return ghErr
}

func (i *Instance) checkGrimQueue() error {
	if i.queue == nil {
		return fatalGrimErrorf("the Grim queue must be prepared first")
	}

	return nil
}
<|endoftext|>


<commit_before>package fzf

import (
	"bytes"
	"regexp"
	"strconv"
	"strings"
	"unicode/utf8"
)

type ansiOffset struct {
	offset [2]int32
	color  ansiState
}

type ansiState struct {
	fg   int
	bg   int
	bold bool
}

func (s *ansiState) colored() bool {
	return s.fg != -1 || s.bg != -1 || s.bold
}

func (s *ansiState) equals(t *ansiState) bool {
	if t == nil {
		return !s.colored()
	}
	return s.fg == t.fg && s.bg == t.bg && s.bold == t.bold
}

var ansiRegex *regexp.Regexp

func init() {
	ansiRegex = regexp.MustCompile("\x1b\\[[0-9;]*[mK]")
}

func extractColor(str string, state *ansiState, proc func(string, *ansiState) bool) (string, *[]ansiOffset, *ansiState) {
	var offsets []ansiOffset
	var output bytes.Buffer

	if state != nil {
		offsets = append(offsets, ansiOffset{[2]int32{0, 0}, *state})
	}

	idx := 0
	for _, offset := range ansiRegex.FindAllStringIndex(str, -1) {
		prev := str[idx:offset[0]]
		output.WriteString(prev)
		if proc != nil && !proc(prev, state) {
			return "", nil, nil
		}
		newState := interpretCode(str[offset[0]:offset[1]], state)

		if !newState.equals(state) {
			if state != nil {
				// Update last offset
				(&offsets[len(offsets)-1]).offset[1] = int32(utf8.RuneCount(output.Bytes()))
			}

			if newState.colored() {
				// Append new offset
				state = newState
				newLen := int32(utf8.RuneCount(output.Bytes()))
				offsets = append(offsets, ansiOffset{[2]int32{newLen, newLen}, *state})
			} else {
				// Discard state
				state = nil
			}
		}

		idx = offset[1]
	}

	rest := str[idx:]
	if len(rest) > 0 {
		output.WriteString(rest)
		if state != nil {
			// Update last offset
			(&offsets[len(offsets)-1]).offset[1] = int32(utf8.RuneCount(output.Bytes()))
		}
	}
	if proc != nil {
		proc(rest, state)
	}
	if len(offsets) == 0 {
		return output.String(), nil, state
	}
	return output.String(), &offsets, state
}

func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
	// State
	var state *ansiState
	if prevState == nil {
		state = &ansiState{-1, -1, false}
	} else {
		state = &ansiState{prevState.fg, prevState.bg, prevState.bold}
	}
	if ansiCode[len(ansiCode)-1] == 'K' {
		return state
	}

	ptr := &state.fg
	state256 := 0

	init := func() {
		state.fg = -1
		state.bg = -1
		state.bold = false
		state256 = 0
	}

	ansiCode = ansiCode[2 : len(ansiCode)-1]
	if len(ansiCode) == 0 {
		init()
	}
	for _, code := range strings.Split(ansiCode, ";") {
		if num, err := strconv.Atoi(code); err == nil {
			switch state256 {
			case 0:
				switch num {
				case 38:
					ptr = &state.fg
					state256++
				case 48:
					ptr = &state.bg
					state256++
				case 39:
					state.fg = -1
				case 49:
					state.bg = -1
				case 1:
					state.bold = true
				case 0:
					init()
				default:
					if num >= 30 && num <= 37 {
						state.fg = num - 30
					} else if num >= 40 && num <= 47 {
						state.bg = num - 40
					} else if num >= 90 && num <= 97 {
						state.fg = num - 90 + 8
					} else if num >= 100 && num <= 107 {
						state.bg = num - 100 + 8
					}
				}
			case 1:
				switch num {
				case 5:
					state256++
				default:
					state256 = 0
				}
			case 2:
				*ptr = num
				state256 = 0
			}
		}
	}
	return state
}
<commit_msg>Ignore VT100-related escape codes<commit_after>package fzf

import (
	"bytes"
	"regexp"
	"strconv"
	"strings"
	"unicode/utf8"
)

type ansiOffset struct {
	offset [2]int32
	color  ansiState
}

type ansiState struct {
	fg   int
	bg   int
	bold bool
}

func (s *ansiState) colored() bool {
	return s.fg != -1 || s.bg != -1 || s.bold
}

func (s *ansiState) equals(t *ansiState) bool {
	if t == nil {
		return !s.colored()
	}
	return s.fg == t.fg && s.bg == t.bg && s.bold == t.bold
}

var ansiRegex *regexp.Regexp

func init() {
	ansiRegex = regexp.MustCompile("\x1b.[0-9;]*.")
}

func extractColor(str string, state *ansiState, proc func(string, *ansiState) bool) (string, *[]ansiOffset, *ansiState) {
	var offsets []ansiOffset
	var output bytes.Buffer

	if state != nil {
		offsets = append(offsets, ansiOffset{[2]int32{0, 0}, *state})
	}

	idx := 0
	for _, offset := range ansiRegex.FindAllStringIndex(str, -1) {
		prev := str[idx:offset[0]]
		output.WriteString(prev)
		if proc != nil && !proc(prev, state) {
			return "", nil, nil
		}
		newState := interpretCode(str[offset[0]:offset[1]], state)

		if !newState.equals(state) {
			if state != nil {
				// Update last offset
				(&offsets[len(offsets)-1]).offset[1] = int32(utf8.RuneCount(output.Bytes()))
			}

			if newState.colored() {
				// Append new offset
				state = newState
				newLen := int32(utf8.RuneCount(output.Bytes()))
				offsets = append(offsets, ansiOffset{[2]int32{newLen, newLen}, *state})
			} else {
				// Discard state
				state = nil
			}
		}

		idx = offset[1]
	}

	rest := str[idx:]
	if len(rest) > 0 {
		output.WriteString(rest)
		if state != nil {
			// Update last offset
			(&offsets[len(offsets)-1]).offset[1] = int32(utf8.RuneCount(output.Bytes()))
		}
	}
	if proc != nil {
		proc(rest, state)
	}
	if len(offsets) == 0 {
		return output.String(), nil, state
	}
	return output.String(), &offsets, state
}

func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
	// State
	var state *ansiState
	if prevState == nil {
		state = &ansiState{-1, -1, false}
	} else {
		state = &ansiState{prevState.fg, prevState.bg, prevState.bold}
	}
	if ansiCode[1] != '[' || ansiCode[len(ansiCode)-1] != 'm' {
		return state
	}

	ptr := &state.fg
	state256 := 0

	init := func() {
		state.fg = -1
		state.bg = -1
		state.bold = false
		state256 = 0
	}

	ansiCode = ansiCode[2 : len(ansiCode)-1]
	if len(ansiCode) == 0 {
		init()
	}
	for _, code := range strings.Split(ansiCode, ";") {
		if num, err := strconv.Atoi(code); err == nil {
			switch state256 {
			case 0:
				switch num {
				case 38:
					ptr = &state.fg
					state256++
				case 48:
					ptr = &state.bg
					state256++
				case 39:
					state.fg = -1
				case 49:
					state.bg = -1
				case 1:
					state.bold = true
				case 0:
					init()
				default:
					if num >= 30 && num <= 37 {
						state.fg = num - 30
					} else if num >= 40 && num <= 47 {
						state.bg = num - 40
					} else if num >= 90 && num <= 97 {
						state.fg = num - 90 + 8
					} else if num >= 100 && num <= 107 {
						state.bg = num - 100 + 8
					}
				}
			case 1:
				switch num {
				case 5:
					state256++
				default:
					state256 = 0
				}
			case 2:
				*ptr = num
				state256 = 0
			}
		}
	}
	return state
}
<|endoftext|>