<commit_before>// Copyright 2012 Google, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package ahocorasick provides a simple implementation of Aho-Corasick
// string matching for go.
//
// This implementation allows you to very quickly match a number of
// strings against a stream of input, returning matched strings as they appear.
//
// Example:
//   aho := ahocorasick.NewAhoCorasick([]string{"abc", "abd", "abab", "blah"})
//   var input io.ByteReader = ...
//   for match := range aho.Match(input) {
//     fmt.Printf("Found match %q at index %d\n", match.Value, match.Index)
//   }
// There's also some helper functions so you don't have to convert to an
// io.ByteReader manually:
//   * MatchString - matches against a string
//   * MatchBytes  - matches against a []byte
//   * MatchReader - matches against an io.Reader
// Example:
//   aho := ahocorasick.NewAhoCorasick([]string{"abc", "abd", "abab", "blah"})
//   for match := range ahocorasick.MatchReader(os.Stdin, aho) {
//     fmt.Printf("Found match %q at index %d\n", match.Value, match.Index)
//   }
package ahocorasick

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"sort"
)

// acNode is a node within the Aho-Corasick parse tree.
type acNode struct {
	key          string
	suffix, dict *acNode
	transitions  transitionMap
	dictIndex    *int
}

// acTree stores the root node of the Aho-Corasick parse tree.
type acTree struct {
	root *acNode
}

// Match provides information on a single keyword within a byte-stream.
type Match struct {
	// The dictionary value found in the byte stream
	Value string
	// The start index of the value in the byte stream
	Index int
	// The index of this value in the dictionary used to create this AC matcher
	DictIndex int
}

// transition is used by transitionMap to store transitions within the AC tree.
type transition struct {
	b byte
	n *acNode
}

// transitionMap provides a sorted vector of transitions for nodes in the AC tree,
// with binary search to find the next node for a single byte.
// This seems to take about 50% more time than a [256]*acNode, but with a
// substantial size savings.
// Note:  This idea taken from http://cs.haifa.ac.il/~landau/gadi/shiri.pdf
type transitionMap []*transition

// Implements sort.Interface for our transitionMap
func (v transitionMap) Len() int {
	return len(v)
}
func (v transitionMap) Less(i, j int) bool {
	return v[i].b < v[j].b
}
func (v transitionMap) Swap(i, j int) {
	v[i], v[j] = v[j], v[i]
}

// add adds a transition to the transitionMap
func (v *transitionMap) add(b byte, n *acNode) {
	for _, e := range *v {
		if e.b == b {
			e.n = n
			return
		}
	}
	*v = append(*v, &transition{b, n})
}

// compile sorts our transition map so binary search will work
func (v transitionMap) compile() {
	sort.Sort(v)
}

// get returns the node we should transition to if we see the given byte,
// or nil if we have no viable transition.
func (v transitionMap) get(b byte) *acNode {
	top, bottom := len(v), 0
	for top > bottom {
		i := (top-bottom)/2 + bottom
		b2 := v[i].b
		if b2 > b {
			top = i
		} else if b2 < b {
			bottom = i + 1
		} else {
			return v[i].n
		}
	}
	return nil
}

// AhoCorasick provides Aho-Corsick matching for byte streams.
type AhoCorasick interface {
	// Match reads in bytes from its input and streams matches to the
	// channel it returns.  It closes the channel when it reaches the
	// EOF of the byte reader.
	//
	// Note: matches are returned in the order they're found, NOT the
	// order of their index.  So given the strings 'abcd' and 'b' in the
	// stream 'zabcdef', Match{'b', 2} will be returned BEFORE
	// Match{'abcd', 1}, since 'b' ends first.
	Match(io.ByteReader) chan Match
}

// MatchString is a helpful wrapper to look for matches in a string.
func MatchString(input string, aho AhoCorasick) chan Match {
	return MatchBytes([]byte(input), aho)
}

// MatchBytes is a helpful wrapper to look for matches in a []byte.
func MatchBytes(input []byte, aho AhoCorasick) chan Match {
	return aho.Match(bytes.NewBuffer(input))
}

// MatchReader is a helpful wrapper to look for matches in an io.Reader.
func MatchReader(input io.Reader, aho AhoCorasick) chan Match {
	return aho.Match(bufio.NewReader(input))
}

// NewAhoCorasick creates a new AhoCorasick string matcher that matches
// the given set of strings against byte streams.  Constructs the tree in O(n),
// where n is the total number of characters in all strings in the dictionary.
func NewAhoCorasick(dict []string) AhoCorasick {
	root := &acNode{
		key:         "",
		transitions: transitionMap{},
	}
	tree := &acTree{}
	tree.root = root
	substrs := map[string]*acNode{}
	substrs[""] = root
	// This first for loop constructs the Trie for all of our string elements
	// O(n) time, since the outer loop iterates over all strings, and the inner
	// loop iterates over each character, thus the combination loops once over
	// each character in all strings.
	for index, str := range dict {
		if str == "" {
			panic(errors.New("Can't look for empty string with Aho"))
		}
		from := root
		for j := 1; j <= len(str); j++ {
			substr := str[:j]
			to := substrs[substr]
			if to == nil {
				to = &acNode{
					key:         substr,
					transitions: transitionMap{},
				}
				substrs[substr] = to
			}
			if j == len(str) {
				di := index
				to.dictIndex = &di
			}
			b := str[j-1]
			from.transitions.add(b, to)
			from = to
		}
	}
	// This second for loop post-processes the Trie, making it Aho-Corasick
	// compatible.  It adds suffix and dictionary links to each internal node.
	// Again, this loop takes O(n) time, since the inner loops are done once per
	// character in a string, while the outer loop is done once per string.
	// There's a bunch of map lookups, but since they're hash maps, they're O(1)
	// apiece.
	for str, node := range substrs {
		// Compile all transitions, now that we've added everything.  This takes
		// O(c) time for each node, since the number of transitions is capped at
		// 256 (we're using bytes).
		node.transitions.compile()
		// All nodes WILL have a suffix link except root.  Nodes without other
		// suffix nodes will link to root.
		for i := 1; i <= len(str); i++ {
			suffix := str[i:]
			if sNode, ok := substrs[suffix]; ok {
				node.suffix = sNode
				break
			}
		}
		for i := 1; i < len(str); i++ {
			suffix := str[i:]
			if sNode, ok := substrs[suffix]; ok && sNode.dictIndex != nil {
				node.dict = sNode
				break
			}
		}
	}
	return tree
}

// Print prints out a node in an easy-to-read manner, for debugging.
func (n *acNode) Print() {
	fmt.Printf("Node %q dictIndex:%v\n", n.key, n.dictIndex)
	for _, t := range n.transitions {
		fmt.Printf("  transition: %q -> %q\n", t.b, t.n.key)
	}
	if n.suffix != nil {
		fmt.Printf("  suffix: %q\n", n.suffix.key)
	}
	if n.dict != nil {
		fmt.Printf("  dict: %q\n", n.dict.key)
	}
}

// Match implements the AhoCorasick.Match interface, matching input from
// an io.ByteReader against its internal tree and streaming matces out through
// its returned channel.  The internal tree is not modified during this,
// so code should be fully concurrency-safe.
func (t *acTree) Match(r io.ByteReader) chan Match {
	// We add a little extra space so we don't need to block immediately if we
	// find a string.  For inputs with lots of matches, this seems to have a good
	// affect on speed (~30-35% speedup).  In BenchmarkMatchingManyMatches
	// in aho_test.go, this brings processing time down from 485ns -> 320ns on
	// a 2.53GHz Intel Core i5.  Without matches, we regularly get 20-30ns per
	// character, so many matches still slows us down substantially.
	c := make(chan Match, 20)
	go t.match(r, c)
	return c
}

// Matcher allows you to do your own matching without a ByteReader, by passing
// in bytes one at a time via the Next call and seeing if there are as yet any
// matches.  Note that each Matcher assumes it's working from a single input
// stream, so Match.Index will be the number of bytes starting from the first
// byte the Matcher saw.
type Matcher struct {
	tree    *acTree
	current *acNode
	seek    int
}

func (t *acTree) Matcher() *Matcher {
	return &Matcher{tree: t}
}

func (m *Matcher) Next(b byte) (out []Match) {
	m.seek++
	if m.current == nil {
		m.current = m.tree.root
	}
	for m.current != nil {
		if to := m.current.transitions.get(b); to != nil {
			m.current = to
			if m.current.dictIndex != nil {
				// We've hit a node whose substring is in our dictionary, so output its key
				out = append(out, Match{m.current.key, m.seek - len(m.current.key), *m.current.dictIndex})
			}
			// If this node links to others in the dictionary, output their keys
			for dict := m.current.dict; dict != nil; dict = dict.dict {
				out = append(out, Match{dict.key, m.seek - len(dict.key), *dict.dictIndex})
			}
			// We've found a transition in the graph, so we're done
			return
		}
		// No transition found yet, so check our suffix
		m.current = m.current.suffix
	}
	return nil
}

// match implements the internal logic for running a single match, reading
// in each byte of the ByteReader and traversing the internal AC tree to look
// for matches.
func (t *acTree) match(reader io.ByteReader, output chan Match) {
	m := t.Matcher()
	for b, err := reader.ReadByte(); err != io.EOF; b, err = reader.ReadByte() {
		for _, match := range m.Next(b) {
			output <- match
		}
	}
	close(output)
}
<commit_msg>Added more documentation and switched to returning an interface.<commit_after>// Copyright 2012 Google, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package ahocorasick provides a simple implementation of Aho-Corasick
// string matching for go.
//
// This implementation allows you to very quickly match a number of
// strings against a stream of input, returning matched strings as they appear.
//
// Example:
//   aho := ahocorasick.NewAhoCorasick([]string{"abc", "abd", "abab", "blah"})
//   var input io.ByteReader = ...
//   for match := range aho.Match(input) {
//     fmt.Printf("Found match %q at index %d\n", match.Value, match.Index)
//   }
// There's also some helper functions so you don't have to convert to an
// io.ByteReader manually:
//   * MatchString - matches against a string
//   * MatchBytes  - matches against a []byte
//   * MatchReader - matches against an io.Reader
// Example:
//   aho := ahocorasick.NewAhoCorasick([]string{"abc", "abd", "abab", "blah"})
//   for match := range ahocorasick.MatchReader(os.Stdin, aho) {
//     fmt.Printf("Found match %q at index %d\n", match.Value, match.Index)
//   }
package ahocorasick

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"sort"
)

// acNode is a node within the Aho-Corasick parse tree.
type acNode struct {
	key          string
	suffix, dict *acNode
	transitions  transitionMap
	dictIndex    *int
}

// acTree stores the root node of the Aho-Corasick parse tree.
type acTree struct {
	root *acNode
}

// Match provides information on a single keyword within a byte-stream.
type Match struct {
	// The dictionary value found in the byte stream
	Value string
	// The start index of the value in the byte stream
	Index int
	// The index of this value in the dictionary used to create this AC matcher
	DictIndex int
}

// transition is used by transitionMap to store transitions within the AC tree.
type transition struct {
	b byte
	n *acNode
}

// transitionMap provides a sorted vector of transitions for nodes in the AC tree,
// with binary search to find the next node for a single byte.
// This seems to take about 50% more time than a [256]*acNode, but with a
// substantial size savings.
// Note:  This idea taken from http://cs.haifa.ac.il/~landau/gadi/shiri.pdf
type transitionMap []*transition

// Implements sort.Interface for our transitionMap
func (v transitionMap) Len() int {
	return len(v)
}
func (v transitionMap) Less(i, j int) bool {
	return v[i].b < v[j].b
}
func (v transitionMap) Swap(i, j int) {
	v[i], v[j] = v[j], v[i]
}

// add adds a transition to the transitionMap
func (v *transitionMap) add(b byte, n *acNode) {
	for _, e := range *v {
		if e.b == b {
			e.n = n
			return
		}
	}
	*v = append(*v, &transition{b, n})
}

// compile sorts our transition map so binary search will work
func (v transitionMap) compile() {
	sort.Sort(v)
}

// get returns the node we should transition to if we see the given byte,
// or nil if we have no viable transition.
func (v transitionMap) get(b byte) *acNode {
	top, bottom := len(v), 0
	for top > bottom {
		i := (top-bottom)/2 + bottom
		b2 := v[i].b
		if b2 > b {
			top = i
		} else if b2 < b {
			bottom = i + 1
		} else {
			return v[i].n
		}
	}
	return nil
}

// AhoCorasick provides Aho-Corsick matching for byte streams.
type AhoCorasick interface {
	// Match reads in bytes from its input and streams matches to the
	// channel it returns.  It closes the channel when it reaches the
	// EOF of the byte reader.
	//
	// Note: matches are returned in the order they're found, NOT the
	// order of their index.  So given the strings 'abcd' and 'b' in the
	// stream 'zabcdef', Match{'b', 2} will be returned BEFORE
	// Match{'abcd', 1}, since 'b' ends first.
	Match(io.ByteReader) chan Match
	// Matcher returns a Matcher object for doing your own byte-by-byte
	// matching, if you don't wanto to use a ByteReader/Channel.
	Matcher() Matcher
}

// MatchString is a helpful wrapper to look for matches in a string.
func MatchString(input string, aho AhoCorasick) chan Match {
	return MatchBytes([]byte(input), aho)
}

// MatchBytes is a helpful wrapper to look for matches in a []byte.
func MatchBytes(input []byte, aho AhoCorasick) chan Match {
	return aho.Match(bytes.NewBuffer(input))
}

// MatchReader is a helpful wrapper to look for matches in an io.Reader.
func MatchReader(input io.Reader, aho AhoCorasick) chan Match {
	return aho.Match(bufio.NewReader(input))
}

// NewAhoCorasick creates a new AhoCorasick string matcher that matches
// the given set of strings against byte streams.  Constructs the tree in O(n),
// where n is the total number of characters in all strings in the dictionary.
func NewAhoCorasick(dict []string) AhoCorasick {
	root := &acNode{
		key:         "",
		transitions: transitionMap{},
	}
	tree := &acTree{}
	tree.root = root
	substrs := map[string]*acNode{}
	substrs[""] = root
	// This first for loop constructs the Trie for all of our string elements
	// O(n) time, since the outer loop iterates over all strings, and the inner
	// loop iterates over each character, thus the combination loops once over
	// each character in all strings.
	for index, str := range dict {
		if str == "" {
			panic(errors.New("Can't look for empty string with Aho"))
		}
		from := root
		for j := 1; j <= len(str); j++ {
			substr := str[:j]
			to := substrs[substr]
			if to == nil {
				to = &acNode{
					key:         substr,
					transitions: transitionMap{},
				}
				substrs[substr] = to
			}
			if j == len(str) {
				di := index
				to.dictIndex = &di
			}
			b := str[j-1]
			from.transitions.add(b, to)
			from = to
		}
	}
	// This second for loop post-processes the Trie, making it Aho-Corasick
	// compatible.  It adds suffix and dictionary links to each internal node.
	// Again, this loop takes O(n) time, since the inner loops are done once per
	// character in a string, while the outer loop is done once per string.
	// There's a bunch of map lookups, but since they're hash maps, they're O(1)
	// apiece.
	for str, node := range substrs {
		// Compile all transitions, now that we've added everything.  This takes
		// O(c) time for each node, since the number of transitions is capped at
		// 256 (we're using bytes).
		node.transitions.compile()
		// All nodes WILL have a suffix link except root.  Nodes without other
		// suffix nodes will link to root.
		for i := 1; i <= len(str); i++ {
			suffix := str[i:]
			if sNode, ok := substrs[suffix]; ok {
				node.suffix = sNode
				break
			}
		}
		for i := 1; i < len(str); i++ {
			suffix := str[i:]
			if sNode, ok := substrs[suffix]; ok && sNode.dictIndex != nil {
				node.dict = sNode
				break
			}
		}
	}
	return tree
}

// Print prints out a node in an easy-to-read manner, for debugging.
func (n *acNode) Print() {
	fmt.Printf("Node %q dictIndex:%v\n", n.key, n.dictIndex)
	for _, t := range n.transitions {
		fmt.Printf("  transition: %q -> %q\n", t.b, t.n.key)
	}
	if n.suffix != nil {
		fmt.Printf("  suffix: %q\n", n.suffix.key)
	}
	if n.dict != nil {
		fmt.Printf("  dict: %q\n", n.dict.key)
	}
}

// Match implements the AhoCorasick.Match interface, matching input from
// an io.ByteReader against its internal tree and streaming matces out through
// its returned channel.  The internal tree is not modified during this,
// so code should be fully concurrency-safe.
func (t *acTree) Match(r io.ByteReader) chan Match {
	// We add a little extra space so we don't need to block immediately if we
	// find a string.  For inputs with lots of matches, this seems to have a good
	// affect on speed (~30-35% speedup).  In BenchmarkMatchingManyMatches
	// in aho_test.go, this brings processing time down from 485ns -> 320ns on
	// a 2.53GHz Intel Core i5.  Without matches, we regularly get 20-30ns per
	// character, so many matches still slows us down substantially.
	c := make(chan Match, 20)
	go t.match(r, c)
	return c
}

// Matcher allows you to do your own matching without a ByteReader, by passing
// in bytes one at a time via the Next call and seeing if there are as yet any
// matches.  Note that each Matcher assumes it's working from a single input
// stream, so Match.Index will be the number of bytes starting from the first
// byte the Matcher saw.
type Matcher interface {
	Next(b byte) []Match
}

type matcher struct {
	tree    *acTree
	current *acNode
	seek    int
}

func (t *acTree) Matcher() Matcher {
	return &matcher{tree: t}
}

// Next feeds the next byte in a stream to the matcher, and returns any Matches
// that have resulted because of the new byte... note that there may be 0, 1, or
// many.  For example, if you're searching for "abc" and "bc", the byte 'c'
// could return both of those if you've already input 'a' and 'b'.
func (m *matcher) Next(b byte) (out []Match) {
	m.seek++
	if m.current == nil {
		m.current = m.tree.root
	}
	for m.current != nil {
		if to := m.current.transitions.get(b); to != nil {
			m.current = to
			if m.current.dictIndex != nil {
				// We've hit a node whose substring is in our dictionary, so output its key
				out = append(out, Match{m.current.key, m.seek - len(m.current.key), *m.current.dictIndex})
			}
			// If this node links to others in the dictionary, output their keys
			for dict := m.current.dict; dict != nil; dict = dict.dict {
				out = append(out, Match{dict.key, m.seek - len(dict.key), *dict.dictIndex})
			}
			// We've found a transition in the graph, so we're done
			return
		}
		// No transition found yet, so check our suffix
		m.current = m.current.suffix
	}
	return nil
}

// match implements the internal logic for running a single match, reading
// in each byte of the ByteReader and traversing the internal AC tree to look
// for matches.
func (t *acTree) match(reader io.ByteReader, output chan Match) {
	m := t.Matcher()
	for b, err := reader.ReadByte(); err != io.EOF; b, err = reader.ReadByte() {
		for _, match := range m.Next(b) {
			output <- match
		}
	}
	close(output)
}
<|endoftext|>


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

// builder package defines the library for invoking the full compilation of Serulian code.
package builder

import (
	"io/ioutil"
	"log"
	"path"
	"sort"

	"github.com/serulian/compiler/compilercommon"
	"github.com/serulian/compiler/compilerutil"
	"github.com/serulian/compiler/generator/es5"
	"github.com/serulian/compiler/graphs/scopegraph"
	"github.com/serulian/compiler/packageloader"
)

// CORE_LIBRARY contains the location of the Serulian core library.
var CORE_LIBRARY = packageloader.Library{
	// TODO: this should be set to a defined tag once the compiler is stable.
	PathOrURL: "github.com/Serulian/corelib:master",
	IsSCM:     true,
}

type WarningsSlice []compilercommon.SourceWarning
type ErrorsSlice []compilercommon.SourceError

func (s WarningsSlice) Len() int {
	return len(s)
}
func (s WarningsSlice) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s WarningsSlice) Less(i, j int) bool {
	if s[i].SourceAndLocation().Location().LineNumber() == s[j].SourceAndLocation().Location().LineNumber() {
		return s[i].SourceAndLocation().Location().ColumnPosition() < s[j].SourceAndLocation().Location().ColumnPosition()
	}

	return s[i].SourceAndLocation().Location().LineNumber() < s[j].SourceAndLocation().Location().LineNumber()
}

func (s ErrorsSlice) Len() int {
	return len(s)
}
func (s ErrorsSlice) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s ErrorsSlice) Less(i, j int) bool {
	if s[i].SourceAndLocation().Location().LineNumber() == s[j].SourceAndLocation().Location().LineNumber() {
		return s[i].SourceAndLocation().Location().ColumnPosition() < s[j].SourceAndLocation().Location().ColumnPosition()
	}

	return s[i].SourceAndLocation().Location().LineNumber() < s[j].SourceAndLocation().Location().LineNumber()
}

func outputWarnings(warnings []compilercommon.SourceWarning) {
	sort.Sort(WarningsSlice(warnings))
	for _, warning := range warnings {
		compilerutil.LogToConsole(compilerutil.WarningLogLevel, warning.SourceAndLocation(), "%s", warning.String())
	}
}

func outputErrors(errors []compilercommon.SourceError) {
	sort.Sort(ErrorsSlice(errors))
	for _, err := range errors {
		compilerutil.LogToConsole(compilerutil.ErrorLogLevel, err.SourceAndLocation(), "%s", err.Error())
	}
}

// BuildSource invokes the compiler starting at the given root source file path.
func BuildSource(rootSourceFilePath string, debug bool, vcsDevelopmentDirectories ...string) bool {
	// Disable logging unless the debug flag is on.
	if !debug {
		log.SetOutput(ioutil.Discard)
	}

	// Build a scope graph for the project. This will conduct parsing and type graph
	// construction on our behalf.
	log.Println("Starting build")
	scopeResult := scopegraph.ParseAndBuildScopeGraph(rootSourceFilePath, vcsDevelopmentDirectories, CORE_LIBRARY)

	outputWarnings(scopeResult.Warnings)
	if !scopeResult.Status {
		log.Println("Scoping failure")
		outputErrors(scopeResult.Errors)
		return false
	}

	// Generate the program's source.
	filename := path.Base(rootSourceFilePath) + ".js"
	mapname := filename + ".map"

	log.Println("Generating ES5")
	generated, sourceMap, err := es5.GenerateES5(scopeResult.Graph, mapname, "")
	if err != nil {
		panic(err)
	}

	marshalledMap, err := sourceMap.Build().Marshal()
	if err != nil {
		panic(err)
	}

	generated += "\n//# sourceMappingURL=" + mapname

	// Write the source and its map.
	filepath := path.Join(path.Dir(rootSourceFilePath), filename)
	mappath := path.Join(path.Dir(rootSourceFilePath), mapname)

	log.Printf("Writing generated source to %s\n", filepath)
	ioutil.WriteFile(filepath, []byte(generated), 0644)
	ioutil.WriteFile(mappath, marshalledMap, 0644)

	log.Println("Work completed")
	return true
}
<commit_msg>Add logging of VCS development directory<commit_after>// Copyright 2015 The Serulian Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// builder package defines the library for invoking the full compilation of Serulian code.
package builder

import (
	"io/ioutil"
	"log"
	"path"
	"sort"

	"github.com/serulian/compiler/compilercommon"
	"github.com/serulian/compiler/compilerutil"
	"github.com/serulian/compiler/generator/es5"
	"github.com/serulian/compiler/graphs/scopegraph"
	"github.com/serulian/compiler/packageloader"
)

// CORE_LIBRARY contains the location of the Serulian core library.
var CORE_LIBRARY = packageloader.Library{
	// TODO: this should be set to a defined tag once the compiler is stable.
	PathOrURL: "github.com/Serulian/corelib:master",
	IsSCM:     true,
}

type WarningsSlice []compilercommon.SourceWarning
type ErrorsSlice []compilercommon.SourceError

func (s WarningsSlice) Len() int {
	return len(s)
}
func (s WarningsSlice) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s WarningsSlice) Less(i, j int) bool {
	if s[i].SourceAndLocation().Location().LineNumber() == s[j].SourceAndLocation().Location().LineNumber() {
		return s[i].SourceAndLocation().Location().ColumnPosition() < s[j].SourceAndLocation().Location().ColumnPosition()
	}

	return s[i].SourceAndLocation().Location().LineNumber() < s[j].SourceAndLocation().Location().LineNumber()
}

func (s ErrorsSlice) Len() int {
	return len(s)
}
func (s ErrorsSlice) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s ErrorsSlice) Less(i, j int) bool {
	if s[i].SourceAndLocation().Location().LineNumber() == s[j].SourceAndLocation().Location().LineNumber() {
		return s[i].SourceAndLocation().Location().ColumnPosition() < s[j].SourceAndLocation().Location().ColumnPosition()
	}

	return s[i].SourceAndLocation().Location().LineNumber() < s[j].SourceAndLocation().Location().LineNumber()
}

func outputWarnings(warnings []compilercommon.SourceWarning) {
	sort.Sort(WarningsSlice(warnings))
	for _, warning := range warnings {
		compilerutil.LogToConsole(compilerutil.WarningLogLevel, warning.SourceAndLocation(), "%s", warning.String())
	}
}

func outputErrors(errors []compilercommon.SourceError) {
	sort.Sort(ErrorsSlice(errors))
	for _, err := range errors {
		compilerutil.LogToConsole(compilerutil.ErrorLogLevel, err.SourceAndLocation(), "%s", err.Error())
	}
}

// BuildSource invokes the compiler starting at the given root source file path.
func BuildSource(rootSourceFilePath string, debug bool, vcsDevelopmentDirectories ...string) bool {
	// Disable logging unless the debug flag is on.
	if !debug {
		log.SetOutput(ioutil.Discard)
	}

	for _, vcsDevelopmentDir := range vcsDevelopmentDirectories {
		log.Printf("Using VCS development directory %s", vcsDevelopmentDir)
	}

	// Build a scope graph for the project. This will conduct parsing and type graph
	// construction on our behalf.
	log.Println("Starting build")
	scopeResult := scopegraph.ParseAndBuildScopeGraph(rootSourceFilePath, vcsDevelopmentDirectories, CORE_LIBRARY)

	outputWarnings(scopeResult.Warnings)
	if !scopeResult.Status {
		log.Println("Scoping failure")
		outputErrors(scopeResult.Errors)
		return false
	}

	// Generate the program's source.
	filename := path.Base(rootSourceFilePath) + ".js"
	mapname := filename + ".map"

	log.Println("Generating ES5")
	generated, sourceMap, err := es5.GenerateES5(scopeResult.Graph, mapname, "")
	if err != nil {
		panic(err)
	}

	marshalledMap, err := sourceMap.Build().Marshal()
	if err != nil {
		panic(err)
	}

	generated += "\n//# sourceMappingURL=" + mapname

	// Write the source and its map.
	filepath := path.Join(path.Dir(rootSourceFilePath), filename)
	mappath := path.Join(path.Dir(rootSourceFilePath), mapname)

	log.Printf("Writing generated source to %s\n", filepath)
	ioutil.WriteFile(filepath, []byte(generated), 0644)
	ioutil.WriteFile(mappath, marshalledMap, 0644)

	log.Println("Work completed")
	return true
}
<|endoftext|>


<commit_before>package lastfm

import "testing"

func TestBuildQueryURL(T *testing.T) {
	T.Parallel()
	expect := "http://ws.audioscrobbler.com/2.0/?a=b&c=d"
	sample := buildQueryURL(map[string]string{
		"a": "b",
		"c": "d"})
	if sample != expect {
		T.Error("Expected sample query URL \"" + expect + "\" -- Got \"" + sample + "\"")
	}
	return
}
<commit_msg>Remove comm_private_test.go<commit_after><|endoftext|>


<commit_before>package sirius

import (
	"errors"
	"fmt"
	"github.com/nlopes/slack"
	"strings"
)

type SlackID struct {
	UserID string
	TeamID string
}

type Connection interface {
	ID() (error, SlackID)
	Listen()
	Messages() chan Message
	Update(*Message) error
}

type RTMConnection struct {
	rtm           *slack.RTM
	id            SlackID
	messages      chan Message
	authenticated bool
	client        *slack.Client
	token         string
}

func NewSlackID(userID, teamID string) SlackID {
	return SlackID{
		UserID: userID,
		TeamID: teamID,
	}
}

/*
Notice that user IDs are not guaranteed to be globally unique across all Slack users.
The combination of user ID and team ID, on the other hand, is guaranteed to be globally unique.

- Slack API documentation
*/
func (s *SlackID) Equals(o *SlackID) bool {
	return s.UserID == o.UserID && s.TeamID == o.TeamID
}

func NewRTMConnection(token string) *RTMConnection {
	client := slack.New(token)

	rtm := client.NewRTM()
	msg := make(chan Message)

	return &RTMConnection{
		rtm:      rtm,
		messages: msg,
		client:   client,
		token:    token,
	}
}

func (conn *RTMConnection) ID() (error, SlackID) {
	if !conn.authenticated {
		return errors.New("Cannot get UserID before authentication completes"), SlackID{}
	}

	return nil, conn.id
}

func (conn *RTMConnection) Listen() {
	go conn.rtm.ManageConnection()

	for !conn.authenticated {
		select {
		case ev := <-conn.rtm.IncomingEvents:
			switch msg := ev.Data.(type) {
			case *slack.InvalidAuthEvent:
				panic(msg)
			case *slack.ConnectedEvent:
				conn.authenticate(msg)
				conn.handleIncomingEvent(ev)
			}
		}
	}

	for {
		select {
		case ev := <-conn.rtm.IncomingEvents:
			conn.handleIncomingEvent(ev)
		}
	}
}

func (conn *RTMConnection) Messages() chan Message {
	return conn.messages
}

func (conn *RTMConnection) SendMessage(msg *Message) {
	omsg := conn.rtm.NewOutgoingMessage(msg.Text, msg.Channel)
	conn.rtm.SendMessage(omsg)
}

func (conn *RTMConnection) Update(msg *Message) error {
	_, _, _, err := conn.rtm.UpdateMessage(msg.Channel, msg.Timestamp, msg.Text)
	return err
}

func (conn *RTMConnection) authenticate(e *slack.ConnectedEvent) bool {
	info := e.Info
	conn.id = NewSlackID(info.User.ID, info.Team.ID)
	conn.authenticated = true

	return true
}

func (conn *RTMConnection) handleIncomingEvent(ev slack.RTMEvent) {
	switch msg := ev.Data.(type) {
	case *slack.MessageEvent:
		conn.handleIncomingMessage(msg)

	case *slack.RTMError:
		fmt.Printf("Error: %s\n", msg.Error())
		panic(msg)
	}
}

func (conn *RTMConnection) handleIncomingMessage(ev *slack.MessageEvent) {
	text := removeEscapeCharacters(ev.Text)
	msg := NewMessage(text, ev.User, ev.Team, ev.Channel, ev.Timestamp)
	conn.messages <- msg
}

var escapeCharacters map[string]string = map[string]string{
	"<":  "<",
	">":  ">",
	"&": "&",
}

func removeEscapeCharacters(msg string) string {
	for html, unicode := range escapeCharacters {
		msg = strings.Replace(msg, html, unicode, -1)
	}

	return msg
}
<commit_msg>Add connection Auth() function for returning auth result channel<commit_after>package sirius

import (
	"errors"
	"fmt"
	"github.com/nlopes/slack"
	"strings"
)

type SlackID struct {
	UserID string
	TeamID string
}

type Connection interface {
	ID() (error, SlackID)
	Listen()
	Auth() chan SlackID
	Messages() chan Message
	Update(*Message) error
}

type RTMConnection struct {
	rtm           *slack.RTM
	id            SlackID
	messages      chan Message
	authenticated bool
	client        *slack.Client
	token         string
	auth     chan SlackID
}

func NewSlackID(userID, teamID string) SlackID {
	return SlackID{
		UserID: userID,
		TeamID: teamID,
	}
}

/*
Notice that user IDs are not guaranteed to be globally unique across all Slack users.
The combination of user ID and team ID, on the other hand, is guaranteed to be globally unique.

- Slack API documentation
*/
func (s *SlackID) Equals(o *SlackID) bool {
	return s.UserID == o.UserID && s.TeamID == o.TeamID
}

func NewRTMConnection(token string) *RTMConnection {
	client := slack.New(token)

	rtm := client.NewRTM()
	auth := make(chan SlackID)
	msg := make(chan Message)

	return &RTMConnection{
		rtm:      rtm,
		auth:     auth,
		messages: msg,
		client:   client,
		token:    token,
	}
}

func (conn *RTMConnection) ID() (error, SlackID) {
	if !conn.authenticated {
		return errors.New("Cannot get UserID before authentication completes"), SlackID{}
	}

	return nil, conn.id
}

func (conn *RTMConnection) Listen() {
	go conn.rtm.ManageConnection()

	for !conn.authenticated {
		select {
		case ev := <-conn.rtm.IncomingEvents:
			switch msg := ev.Data.(type) {
			case *slack.InvalidAuthEvent:
				panic(msg)
			case *slack.ConnectedEvent:
				conn.authenticate(msg)
				conn.handleIncomingEvent(ev)
			}
		}
	}

	for {
		select {
		case ev := <-conn.rtm.IncomingEvents:
			conn.handleIncomingEvent(ev)
		}
	}
}

func (conn *RTMConnection) Auth() chan SlackID {
	return conn.auth
}

func (conn *RTMConnection) Messages() chan Message {
	return conn.messages
}

func (conn *RTMConnection) SendMessage(msg *Message) {
	omsg := conn.rtm.NewOutgoingMessage(msg.Text, msg.Channel)
	conn.rtm.SendMessage(omsg)
}

func (conn *RTMConnection) Update(msg *Message) error {
	_, _, _, err := conn.rtm.UpdateMessage(msg.Channel, msg.Timestamp, msg.Text)
	return err
}

func (conn *RTMConnection) authenticate(e *slack.ConnectedEvent) bool {
	info := e.Info
	conn.id = NewSlackID(info.User.ID, info.Team.ID)
	conn.authenticated = true

	return true
}

func (conn *RTMConnection) handleIncomingEvent(ev slack.RTMEvent) {
	switch msg := ev.Data.(type) {
	case *slack.MessageEvent:
		conn.handleIncomingMessage(msg)

	case *slack.RTMError:
		fmt.Printf("Error: %s\n", msg.Error())
		panic(msg)
	}
}

func (conn *RTMConnection) handleIncomingMessage(ev *slack.MessageEvent) {
	text := removeEscapeCharacters(ev.Text)
	msg := NewMessage(text, ev.User, ev.Team, ev.Channel, ev.Timestamp)
	conn.messages <- msg
}

var escapeCharacters map[string]string = map[string]string{
	"<":  "<",
	">":  ">",
	"&": "&",
}

func removeEscapeCharacters(msg string) string {
	for html, unicode := range escapeCharacters {
		msg = strings.Replace(msg, html, unicode, -1)
	}

	return msg
}
<|endoftext|>


<commit_before>package url

import (
	"bytes"
	"fmt"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/cred"
	"github.com/viant/toolbox/storage"
	"io/ioutil"
	"net/url"
	"os"
	"path"
	"strings"
	"time"
)

//Resource represents a URL based resource, with enriched meta info
type Resource struct {
	URL           string   //URL of resource
	Credential    string   //name of file or alias to the file defined via credential service
	ParsedURL     *url.URL //parsed URL resource
	Cache         string   //Cache path for the resource, if specified resource will be cached in the specified path
	CacheExpiryMs int      //CacheExpiryMs expiry time in ms

	Name    string //name of a resource
	Version string //version of resource
	Type    string //resource type
}

//Clone creates a clone of the resource
func (r *Resource) Clone() *Resource {
	return &Resource{
		Name:          r.Name,
		Version:       r.Version,
		URL:           r.URL,
		Type:          r.Type,
		Credential:    r.Credential,
		ParsedURL:     r.ParsedURL,
		Cache:         r.Cache,
		CacheExpiryMs: r.CacheExpiryMs,
	}
}

var defaultSchemePorts = map[string]int{
	"ssh":   22,
	"http":  80,
	"https": 443,
}

//Host returns url's host name with user name if user name is part of url
func (r *Resource) Host() string {
	result := r.ParsedURL.Hostname() + ":" + r.Port()
	if r.ParsedURL.User != nil {
		result = r.ParsedURL.User.Username() + "@" + result
	}
	return result
}

//Port returns url's port
func (r *Resource) Port() string {
	port := r.ParsedURL.Port()
	if port == "" && r.ParsedURL != nil {
		if value, ok := defaultSchemePorts[r.ParsedURL.Scheme]; ok {
			port = toolbox.AsString(value)
		}
	}
	return port
}

//LoadCredentialload credential, returns username, password. It takes errorIfEmpty flag to return an error if there is issue with credential
func (r *Resource) LoadCredential(errorIfEmpty bool) (string, string, error) {
	if r.Credential == "" {
		if errorIfEmpty {
			return "", "", fmt.Errorf("Credential was empty: %v", r.Credential)
		}
		return "", "", nil
	}
	credential, err := cred.NewConfig(r.Credential)
	if err != nil {
		return "", "", fmt.Errorf("Failed to load credential: %v %v", r.Credential, err)
	}
	return credential.Username, credential.Password, nil
}

//Download downloads data from URL, it returns data as []byte, or error, if resource is cacheable it first look into cache
func (r *Resource) Download() ([]byte, error) {
	if r == nil {
		return nil, fmt.Errorf("Fail to download content on empty resource")
	}

	if r.Cachable() {
		content := r.readFromCache()
		if content != nil {
			return content, nil
		}
	}

	service, err := storage.NewServiceForURL(r.URL, r.Credential)
	if err != nil {
		return nil, err
	}
	object, err := service.StorageObject(r.URL)
	if err != nil {
		return nil, err
	}
	reader, err := service.Download(object)
	if err != nil {
		return nil, err
	}
	content, err := ioutil.ReadAll(reader)
	if err != nil {
		return nil, err
	}
	if r.Cachable() {
		_ = ioutil.WriteFile(r.Cache, content, 0666)
	}
	return content, err
}

//DownloadText returns a text downloaded from url
func (r *Resource) DownloadText() (string, error) {
	var result, err = r.Download()
	if err != nil {
		return "", err
	}
	return string(result), err
}

//Decode decodes url's data into target, it takes decoderFactory which decodes data into target
func (r *Resource) Decode(target interface{}, decoderFactory toolbox.DecoderFactory) error {
	if r == nil {
		return fmt.Errorf("Fail to %T decode on empty resource", decoderFactory)
	}
	if r == nil {
		return fmt.Errorf("Fail to decode %v,  decoderFactory was empty", r.URL, decoderFactory)
	}
	var content, err = r.Download()
	if err != nil {
		return err
	}
	return decoderFactory.Create(bytes.NewReader(content)).Decode(target)
}

//JSONDecode decodes json resource into target
func (r *Resource) JSONDecode(target interface{}) error {
	return r.Decode(target, toolbox.NewJSONDecoderFactory())
}

//JSONDecode decodes yaml resource into target
func (r *Resource) YAMLDecode(target interface{}) error {
	return r.Decode(target, toolbox.NewYamlDecoderFactory())
}

func (r *Resource) readFromCache() []byte {
	if toolbox.FileExists(r.Cache) {
		info, err := os.Stat(r.Cache)
		var isExpired = false
		if err == nil && r.CacheExpiryMs > 0 {
			elapsed := time.Now().Sub(info.ModTime())
			isExpired = elapsed > time.Second*time.Duration(r.CacheExpiryMs)
		}
		content, err := ioutil.ReadFile(r.Cache)
		if err == nil && !isExpired {
			return content
		}
	}
	return nil
}

//Cachable returns true if resource is cachable
func (r *Resource) Cachable() bool {
	return r.Cache != ""
}

func normalizeURL(URL string) string {
	if strings.Contains(URL, "://") {
		return URL
	}
	if !strings.HasPrefix(URL, "/") {
		currentDirectory, err := os.Getwd()
		if err == nil {
			candidate := path.Join(currentDirectory, URL)
			if toolbox.FileExists(candidate) {
				URL = candidate
			}
		}
	}
	return toolbox.FileSchema + URL
}

//NewResource returns a new resource for provided URL, followed by optional credential, cache and cache expiryMs.
func NewResource(Params ...interface{}) *Resource {
	if len(Params) == 0 {
		return nil
	}
	var URL = toolbox.AsString(Params[0])
	URL = normalizeURL(URL)

	var credential string
	if len(Params) > 1 {
		credential = toolbox.AsString(Params[1])
	}
	var cache string
	if len(Params) > 2 {
		cache = toolbox.AsString(Params[2])
	}
	var cacheExpiryMs int
	if len(Params) > 3 {
		cacheExpiryMs = toolbox.AsInt(Params[3])
	}
	parsedURL, _ := url.Parse(URL)
	return &Resource{
		ParsedURL:     parsedURL,
		URL:           URL,
		Credential:    credential,
		Cache:         cache,
		CacheExpiryMs: cacheExpiryMs,
	}
}
<commit_msg>added rename<commit_after>package url

import (
	"bytes"
	"fmt"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/cred"
	"github.com/viant/toolbox/storage"
	"io/ioutil"
	"net/url"
	"os"
	"path"
	"strings"
	"time"
)

//Resource represents a URL based resource, with enriched meta info
type Resource struct {
	URL           string   //URL of resource
	Credential    string   //name of file or alias to the file defined via credential service
	ParsedURL     *url.URL //parsed URL resource
	Cache         string   //Cache path for the resource, if specified resource will be cached in the specified path
	CacheExpiryMs int      //CacheExpiryMs expiry time in ms

	Name    string //name of a resource
	Version string //version of resource
	Type    string //resource type
}

//Clone creates a clone of the resource
func (r *Resource) Clone() *Resource {
	return &Resource{
		Name:          r.Name,
		Version:       r.Version,
		URL:           r.URL,
		Type:          r.Type,
		Credential:    r.Credential,
		ParsedURL:     r.ParsedURL,
		Cache:         r.Cache,
		CacheExpiryMs: r.CacheExpiryMs,
	}
}

var defaultSchemePorts = map[string]int{
	"ssh":   22,
	"http":  80,
	"https": 443,
}

//Host returns url's host name with user name if user name is part of url
func (r *Resource) Host() string {
	result := r.ParsedURL.Hostname() + ":" + r.Port()
	if r.ParsedURL.User != nil {
		result = r.ParsedURL.User.Username() + "@" + result
	}
	return result
}

//Port returns url's port
func (r *Resource) Port() string {
	port := r.ParsedURL.Port()
	if port == "" && r.ParsedURL != nil {
		if value, ok := defaultSchemePorts[r.ParsedURL.Scheme]; ok {
			port = toolbox.AsString(value)
		}
	}
	return port
}

//LoadCredentialload credential, returns username, password. It takes errorIfEmpty flag to return an error if there is issue with credential
func (r *Resource) LoadCredential(errorIfEmpty bool) (string, string, error) {
	if r.Credential == "" {
		if errorIfEmpty {
			return "", "", fmt.Errorf("Credential was empty: %v", r.Credential)
		}
		return "", "", nil
	}
	credential, err := cred.NewConfig(r.Credential)
	if err != nil {
		return "", "", fmt.Errorf("Failed to load credential: %v %v", r.Credential, err)
	}
	return credential.Username, credential.Password, nil
}

//Download downloads data from URL, it returns data as []byte, or error, if resource is cacheable it first look into cache
func (r *Resource) Download() ([]byte, error) {
	if r == nil {
		return nil, fmt.Errorf("Fail to download content on empty resource")
	}

	if r.Cachable() {
		content := r.readFromCache()
		if content != nil {
			return content, nil
		}
	}

	service, err := storage.NewServiceForURL(r.URL, r.Credential)
	if err != nil {
		return nil, err
	}
	object, err := service.StorageObject(r.URL)
	if err != nil {
		return nil, err
	}
	reader, err := service.Download(object)
	if err != nil {
		return nil, err
	}
	content, err := ioutil.ReadAll(reader)
	if err != nil {
		return nil, err
	}
	if r.Cachable() {
		_ = ioutil.WriteFile(r.Cache, content, 0666)
	}
	return content, err
}

//DownloadText returns a text downloaded from url
func (r *Resource) DownloadText() (string, error) {
	var result, err = r.Download()
	if err != nil {
		return "", err
	}
	return string(result), err
}

//Decode decodes url's data into target, it takes decoderFactory which decodes data into target
func (r *Resource) Decode(target interface{}, decoderFactory toolbox.DecoderFactory) error {
	if r == nil {
		return fmt.Errorf("Fail to %T decode on empty resource", decoderFactory)
	}
	if r == nil {
		return fmt.Errorf("Fail to decode %v,  decoderFactory was empty", r.URL, decoderFactory)
	}
	var content, err = r.Download()
	if err != nil {
		return err
	}
	return decoderFactory.Create(bytes.NewReader(content)).Decode(target)
}

//Rename renames URI name of this resource
func (r *Resource) Rename(name string) (err error) {
	var _, currentName = toolbox.URLSplit(r.URL)
	r.URL = strings.Replace(r.URL, currentName, name, 1)
	r.ParsedURL, err = url.Parse(r.URL)
	return err
}

//JSONDecode decodes json resource into target
func (r *Resource) JSONDecode(target interface{}) error {
	return r.Decode(target, toolbox.NewJSONDecoderFactory())
}

//JSONDecode decodes yaml resource into target
func (r *Resource) YAMLDecode(target interface{}) error {
	return r.Decode(target, toolbox.NewYamlDecoderFactory())
}

func (r *Resource) readFromCache() []byte {
	if toolbox.FileExists(r.Cache) {
		info, err := os.Stat(r.Cache)
		var isExpired = false
		if err == nil && r.CacheExpiryMs > 0 {
			elapsed := time.Now().Sub(info.ModTime())
			isExpired = elapsed > time.Second*time.Duration(r.CacheExpiryMs)
		}
		content, err := ioutil.ReadFile(r.Cache)
		if err == nil && !isExpired {
			return content
		}
	}
	return nil
}

//Cachable returns true if resource is cachable
func (r *Resource) Cachable() bool {
	return r.Cache != ""
}

func normalizeURL(URL string) string {
	if strings.Contains(URL, "://") {
		return URL
	}
	if !strings.HasPrefix(URL, "/") {
		currentDirectory, err := os.Getwd()
		if err == nil {
			candidate := path.Join(currentDirectory, URL)
			if toolbox.FileExists(candidate) {
				URL = candidate
			}
		}
	}
	return toolbox.FileSchema + URL
}

//NewResource returns a new resource for provided URL, followed by optional credential, cache and cache expiryMs.
func NewResource(Params ...interface{}) *Resource {
	if len(Params) == 0 {
		return nil
	}
	var URL = toolbox.AsString(Params[0])
	URL = normalizeURL(URL)

	var credential string
	if len(Params) > 1 {
		credential = toolbox.AsString(Params[1])
	}
	var cache string
	if len(Params) > 2 {
		cache = toolbox.AsString(Params[2])
	}
	var cacheExpiryMs int
	if len(Params) > 3 {
		cacheExpiryMs = toolbox.AsInt(Params[3])
	}
	parsedURL, _ := url.Parse(URL)
	return &Resource{
		ParsedURL:     parsedURL,
		URL:           URL,
		Credential:    credential,
		Cache:         cache,
		CacheExpiryMs: cacheExpiryMs,
	}
}
<|endoftext|>


<commit_before>// vim: ts=8 sw=8 noet ai

package perigee

import (
	"net/http"
	"io"
	"io/ioutil"
	"encoding/json"
	"strings"
)

// The UnexpectedResponseCodeError structure represents a mismatch in understanding between server and client in terms of response codes.
// Most often, this is due to an actual error condition (e.g., getting a 404 for a resource when you expect a 200).
// However, it needn't always be the case (e.g., getting a 204 (No Content) response back when a 200 is expected).
type UnexpectedResponseCodeError struct {
	Expected, Actual int
}

func (err *UnexpectedResponseError) Error() string {
	return fmt.Sprintf("Expected HTTP response code %d; got %d instead", err.Expected, err.Actual)
}

// Post makes a POST request against a server using the provided HTTP client.
// The url must be a fully-formed URL string.
// If input is to be embedded in a POST request body, it will be encoded in JSON format.
// Otherwise, provide nil for input.
// If output is to be expected from the response,
// provide either a pointer to the container,
// or a pointer to a nil-initialized pointer variable.
// The latter method will cause Post to allocate the container type for you.
// If no response is expected, provide a nil output reference.
func Post(client *http.Client, url string, input interface{}, output interface{}) error {
	var body io.Reader

	body = nil
	if input != nil {
		bodyText, err := json.Marshal(input)
		if err != nil {
			return err
		}
		body = strings.NewReader(string(bodyText))
	}

	req, err := http.NewRequest("POST", url, body)
	if err != nil {
		return err
	}
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Accept", "application/json")

	response, err := client.Do(req)
	if err != nil {
		return err
	}
	if response.StatusCode != 200 {
		return &UnexpectedResponseCodeError{
			Expected: 200,
			Actual: response.StatusCode
		}
	}
	defer response.Body.Close()

	if output != nil {
		jsonResult, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return err
		}

		err = json.Unmarshal(jsonResult, output)
	}
	return err
}

<commit_msg>GET method added, as well as a means to customize headers.<commit_after>// vim: ts=8 sw=8 noet ai

package perigee

import (
	"fmt"
	"net/http"
	"io"
	"io/ioutil"
	"encoding/json"
	"strings"
)

// The UnexpectedResponseCodeError structure represents a mismatch in understanding between server and client in terms of response codes.
// Most often, this is due to an actual error condition (e.g., getting a 404 for a resource when you expect a 200).
// However, it needn't always be the case (e.g., getting a 204 (No Content) response back when a 200 is expected).
type UnexpectedResponseCodeError struct {
	Expected, Actual int
}

func (err *UnexpectedResponseCodeError) Error() string {
	return fmt.Sprintf("Expected HTTP response code %d; got %d instead", err.Expected, err.Actual)
}

// request is the procedure that does the ditch-work of making the request, marshaling parameters, and unmarshaling results.
func request(method string, client *http.Client, url string, input, output interface{}, reqConfig func(*http.Request)) error {
	var body io.Reader

	body = nil
	if input != nil {
		bodyText, err := json.Marshal(input)
		if err != nil {
			return err
		}
		body = strings.NewReader(string(bodyText))
	}

	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return err
	}
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Accept", "application/json")
	if reqConfig != nil {
		reqConfig(req)
	}

	response, err := client.Do(req)
	if err != nil {
		return err
	}
	if response.StatusCode != 200 {
		return &UnexpectedResponseCodeError{
			Expected: 200,
			Actual: response.StatusCode,
		}
	}
	defer response.Body.Close()

	if output != nil {
		jsonResult, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return err
		}

		err = json.Unmarshal(jsonResult, output)
	}
	return err
}

// Post makes a POST request against a server using the provided HTTP client.
// The url must be a fully-formed URL string.
// If input is to be embedded in a POST request body, it will be encoded in JSON format.
// Otherwise, provide nil for input.
// If output is to be expected from the response,
// provide either a pointer to the container,
// or a pointer to a nil-initialized pointer variable.
// The latter method will cause Post to allocate the container type for you.
// If no response is expected, provide a nil output reference.
func Post(client *http.Client, url string, input interface{}, output interface{}, reqConfig func(*http.Request)) error {
	return request("POST", client, url, input, output, reqConfig)
}

// Get makes a GET request against a server using the provided HTTP client.
// The url must be a fully-formed URL string.
// If input is to be embedded in a POST request body, it will be encoded in JSON format.
// Otherwise, provide nil for input.
// If output is to be expected from the response,
// provide either a pointer to the container,
// or a pointer to a nil-initialized pointer variable.
// The latter method will cause Post to allocate the container type for you.
// If no response is expected, provide a nil output reference.
func Get(client *http.Client, url string, input, output interface{}, reqConfig func(*http.Request)) error {
	return request("GET", client, url, input, output, reqConfig)
}

<|endoftext|>


<commit_before>package git

// everything git and github related

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"golang.org/x/net/context"
)

const GIT_BASE_DIR = "repo"

// Invoke a `command` in `workdir` with `args`, connecting up its Stdout and Stderr
func Command(workdir, command string, args ...string) *exec.Cmd {
	// log.Printf("wd = %s cmd = %s, args = %q", workdir, command, append([]string{}, args...))

	cmd := exec.Command(command, args...)
	cmd.Dir = workdir
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd
}

var (
	ErrEmptyRepoName         = errors.New("Empty repository name")
	ErrEmptyRepoOrganization = errors.New("Empty repository organization")
	ErrUserNotAllowed        = errors.New("User not in the allowed set")
)

type Repository struct {
	Name         string `json:"name"`
	Url          string `json:"url"`
	Organization string `json:"organization"`
}

type Pusher struct {
	Name string `json:"name"`
}

type NonGithub struct {
	NoBuild bool `json:"nobuild"`
	Wait    bool `json:"wait"`
}

type JustNongithub struct {
	NonGithub NonGithub `json:"nongithub"`
}

func ParseJustNongithub(in []byte) (j JustNongithub, err error) {
	err = json.Unmarshal(in, &j)
	return
}

type PushEvent struct {
	Ref        string     `json:"ref"`
	Deleted    bool       `json:"deleted"`
	Repository Repository `json:"repository"`
	After      string     `json:"after"`
	Pusher     Pusher     `json:"pusher"`
	NonGithub  NonGithub  `json:"nongithub"`
	HtmlUrl    string     `json:"html_url"`
}

type GithubStatus struct {
	State       string `json:"state"`
	TargetUrl   string `json:"target_url"`
	Description string `json:"description"`
}

var ErrSkipGithubEndpoint = errors.New("Github endpoint skipped")

// Creates or updates a mirror of `url` at `gitDir` using `git clone --mirror`
func LocalMirror(
	url, gitDir, ref string,
	messages io.Writer,
) (err error) {

	// When mirroring, allow up to two minutes before giving up.
	const MirrorTimeout = 2 * time.Minute
	ctx, done := context.WithTimeout(context.Background(), MirrorTimeout)
	defer done()

	if _, err := os.Stat(gitDir); err == nil {
		// Repo already exists, don't need to clone it.

		if AlreadyHaveRef(gitDir, ref) {
			// Sha already exists, don't need to fetch.
			// log.Printf("Already have ref: %v %v", gitDir, ref)
			return nil
		}

		return Fetch(ctx, gitDir, url, messages)
	}

	err = os.MkdirAll(filepath.Dir(gitDir), 0777)
	if err != nil {
		return err
	}

	return Clone(ctx, url, gitDir, messages)
}

func Clone(
	ctx context.Context,
	url, gitDir string,
	messages io.Writer,
) error {
	cmd := Command(".", "git", "clone", "-q", "--mirror", url, gitDir)
	cmd.Stdout = messages
	cmd.Stderr = messages
	return ContextRun(ctx, cmd)
}

// Run cmd within a net Context.
// If the context is cancelled or times out, the process is killed.
func ContextRun(ctx context.Context, cmd *exec.Cmd) error {
	errc := make(chan error)

	err := cmd.Start()
	if err != nil {
		return err
	}

	go func() { errc <- cmd.Wait() }()

	select {
	case <-ctx.Done():
		cmd.Process.Kill()
		return ctx.Err()
	case err := <-errc:
		return err // err may be nil
	}
	return nil
}

func Fetch(
	ctx context.Context,
	gitDir, url string,
	messages io.Writer,
) (err error) {

	cmd := Command(gitDir, "git", "fetch", "-f", url, "*:*")
	cmd.Stdout = messages
	cmd.Stderr = messages

	err = ContextRun(ctx, cmd)
	if err != nil {
		// git fetch where there is no update is exit status 1.
		if err.Error() != "exit status 1" {
			return err
		}
	}

	return nil
}

var ShaLike = regexp.MustCompile("[0-9a-zA-Z]{40}")

// Returns true if ref is sha-like and is in the object database.
// The "sha-like" condition ensures that refs like `master` are always
// freshened.
func AlreadyHaveRef(gitDir, sha string) bool {
	if !ShaLike.MatchString(sha) {
		return false
	}
	cmd := Command(gitDir, "git", "cat-file", "-t", sha)
	cmd.Stdout = nil
	cmd.Stderr = nil

	err := cmd.Run()
	return err == nil
}

func HaveFile(gitDir, ref, path string) (ok bool, err error) {
	cmd := Command(gitDir, "git", "show", fmt.Sprintf("%s:%s", ref, path))
	cmd.Stdout = nil // don't want to see the contents
	err = cmd.Run()
	ok = true
	if err != nil {
		ok = false
		if err.Error() == "exit status 128" {
			// This happens if the file doesn't exist.
			err = nil
		}
	}
	return ok, err
}

func RevParse(gitDir, ref string) (sha string, err error) {
	cmd := Command(gitDir, "git", "rev-parse", ref)
	cmd.Stdout = nil // for cmd.Output

	var stdout []byte
	stdout, err = cmd.Output()
	if err != nil {
		return
	}

	sha = strings.TrimSpace(string(stdout))
	return
}

func Describe(gitDir, ref string) (desc string, err error) {
	cmd := Command(gitDir, "git", "describe", "--all", "--tags", "--long", ref)
	cmd.Stdout = nil // for cmd.Output

	var stdout []byte
	stdout, err = cmd.Output()
	if err != nil {
		return
	}

	desc = strings.TrimSpace(string(stdout))
	desc = strings.TrimPrefix(desc, "heads/")
	return
}

func Checkout(gitDir, checkoutDir, ref string) error {

	err := os.MkdirAll(checkoutDir, 0777)
	if err != nil {
		return err
	}

	args := []string{"--work-tree", checkoutDir, "checkout", ref, "--", "."}
	err = Command(gitDir, "git", args...).Run()
	if err != nil {
		return err
	}

	// Set mtimes to time file is most recently affected by a commit.
	// This is annoying but unfortunately git sets the timestamps to now,
	// and docker depends on the mtime for cache invalidation.
	err = SetMTimes(gitDir, checkoutDir, ref)
	if err != nil {
		return err
	}

	return nil
}

type BuildDirectory struct {
	Name, Dir string
	Cleanup   func()
}

func PrepBuildDirectory(
	gitDir, remote, ref string,
) (*BuildDirectory, error) {

	start := time.Now()
	defer func() {
		log.Printf("Took %v to prep %v", time.Since(start), remote)
	}()

	if strings.HasPrefix(remote, "github.com/") {
		remote = "https://" + remote
	}

	gitDir, err := filepath.Abs(gitDir)
	if err != nil {
		return nil, fmt.Errorf("unable to determine abspath: %v", err)
	}

	err = LocalMirror(remote, gitDir, ref, os.Stderr)
	if err != nil {
		return nil, fmt.Errorf("unable to LocalMirror: %v", err)
	}

	rev, err := RevParse(gitDir, ref)
	if err != nil {
		return nil, fmt.Errorf("unable to parse rev: %v", err)
	}

	tagName, err := Describe(gitDir, rev)
	if err != nil {
		return nil, fmt.Errorf("unable to describe %v: %v", rev, err)
	}

	shortRev := rev[:10]
	checkoutPath := path.Join(gitDir, filepath.Join("c/", shortRev))

	err = recursiveCheckout(gitDir, checkoutPath, rev)
	if err != nil {
		return nil, err
	}

	cleanup := func() {
		err := SafeCleanup(checkoutPath)
		if err != nil {
			log.Printf("Error cleaning up path: ", checkoutPath)
		}
	}

	return &BuildDirectory{tagName, checkoutPath, cleanup}, nil
}

func recursiveCheckout(gitDir, checkoutPath, rev string) error {
	err := Checkout(gitDir, checkoutPath, rev)
	if err != nil {
		return fmt.Errorf("failed to checkout: %v", err)
	}

	err = PrepSubmodules(gitDir, checkoutPath, rev)
	if err != nil {
		return fmt.Errorf("failed to prep submodules: %v", err)
	}
	return nil
}

func SafeCleanup(path string) error {
	if path == "/" || path == "" || path == "." || strings.Contains(path, "..") {
		return fmt.Errorf("invalid path specified for deletion %q", path)
	}
	return os.RemoveAll(path)
}
<commit_msg>Don't use stdout for any executed commands<commit_after>package git

// everything git and github related

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"golang.org/x/net/context"
)

const GIT_BASE_DIR = "repo"

// Invoke a `command` in `workdir` with `args`, connecting Stdout and Stderr to Stderr.
func Command(workdir, command string, args ...string) *exec.Cmd {
	// log.Printf("wd = %s cmd = %s, args = %q", workdir, command, append([]string{}, args...))

	cmd := exec.Command(command, args...)
	cmd.Dir = workdir
	cmd.Stdout = os.Stderr
	cmd.Stderr = os.Stderr
	return cmd
}

var (
	ErrEmptyRepoName         = errors.New("Empty repository name")
	ErrEmptyRepoOrganization = errors.New("Empty repository organization")
	ErrUserNotAllowed        = errors.New("User not in the allowed set")
)

type Repository struct {
	Name         string `json:"name"`
	Url          string `json:"url"`
	Organization string `json:"organization"`
}

type Pusher struct {
	Name string `json:"name"`
}

type NonGithub struct {
	NoBuild bool `json:"nobuild"`
	Wait    bool `json:"wait"`
}

type JustNongithub struct {
	NonGithub NonGithub `json:"nongithub"`
}

func ParseJustNongithub(in []byte) (j JustNongithub, err error) {
	err = json.Unmarshal(in, &j)
	return
}

type PushEvent struct {
	Ref        string     `json:"ref"`
	Deleted    bool       `json:"deleted"`
	Repository Repository `json:"repository"`
	After      string     `json:"after"`
	Pusher     Pusher     `json:"pusher"`
	NonGithub  NonGithub  `json:"nongithub"`
	HtmlUrl    string     `json:"html_url"`
}

type GithubStatus struct {
	State       string `json:"state"`
	TargetUrl   string `json:"target_url"`
	Description string `json:"description"`
}

var ErrSkipGithubEndpoint = errors.New("Github endpoint skipped")

// Creates or updates a mirror of `url` at `gitDir` using `git clone --mirror`
func LocalMirror(
	url, gitDir, ref string,
	messages io.Writer,
) (err error) {

	// When mirroring, allow up to two minutes before giving up.
	const MirrorTimeout = 2 * time.Minute
	ctx, done := context.WithTimeout(context.Background(), MirrorTimeout)
	defer done()

	if _, err := os.Stat(gitDir); err == nil {
		// Repo already exists, don't need to clone it.

		if AlreadyHaveRef(gitDir, ref) {
			// Sha already exists, don't need to fetch.
			// log.Printf("Already have ref: %v %v", gitDir, ref)
			return nil
		}

		return Fetch(ctx, gitDir, url, messages)
	}

	err = os.MkdirAll(filepath.Dir(gitDir), 0777)
	if err != nil {
		return err
	}

	return Clone(ctx, url, gitDir, messages)
}

func Clone(
	ctx context.Context,
	url, gitDir string,
	messages io.Writer,
) error {
	cmd := Command(".", "git", "clone", "-q", "--mirror", url, gitDir)
	cmd.Stdout = messages
	cmd.Stderr = messages
	return ContextRun(ctx, cmd)
}

// Run cmd within a net Context.
// If the context is cancelled or times out, the process is killed.
func ContextRun(ctx context.Context, cmd *exec.Cmd) error {
	errc := make(chan error)

	err := cmd.Start()
	if err != nil {
		return err
	}

	go func() { errc <- cmd.Wait() }()

	select {
	case <-ctx.Done():
		cmd.Process.Kill()
		return ctx.Err()
	case err := <-errc:
		return err // err may be nil
	}
	return nil
}

func Fetch(
	ctx context.Context,
	gitDir, url string,
	messages io.Writer,
) (err error) {

	cmd := Command(gitDir, "git", "fetch", "-f", url, "*:*")
	cmd.Stdout = messages
	cmd.Stderr = messages

	err = ContextRun(ctx, cmd)
	if err != nil {
		// git fetch where there is no update is exit status 1.
		if err.Error() != "exit status 1" {
			return err
		}
	}

	return nil
}

var ShaLike = regexp.MustCompile("[0-9a-zA-Z]{40}")

// Returns true if ref is sha-like and is in the object database.
// The "sha-like" condition ensures that refs like `master` are always
// freshened.
func AlreadyHaveRef(gitDir, sha string) bool {
	if !ShaLike.MatchString(sha) {
		return false
	}
	cmd := Command(gitDir, "git", "cat-file", "-t", sha)
	cmd.Stdout = nil
	cmd.Stderr = nil

	err := cmd.Run()
	return err == nil
}

func HaveFile(gitDir, ref, path string) (ok bool, err error) {
	cmd := Command(gitDir, "git", "show", fmt.Sprintf("%s:%s", ref, path))
	cmd.Stdout = nil // don't want to see the contents
	err = cmd.Run()
	ok = true
	if err != nil {
		ok = false
		if err.Error() == "exit status 128" {
			// This happens if the file doesn't exist.
			err = nil
		}
	}
	return ok, err
}

func RevParse(gitDir, ref string) (sha string, err error) {
	cmd := Command(gitDir, "git", "rev-parse", ref)
	cmd.Stdout = nil // for cmd.Output

	var stdout []byte
	stdout, err = cmd.Output()
	if err != nil {
		return
	}

	sha = strings.TrimSpace(string(stdout))
	return
}

func Describe(gitDir, ref string) (desc string, err error) {
	cmd := Command(gitDir, "git", "describe", "--all", "--tags", "--long", ref)
	cmd.Stdout = nil // for cmd.Output

	var stdout []byte
	stdout, err = cmd.Output()
	if err != nil {
		return
	}

	desc = strings.TrimSpace(string(stdout))
	desc = strings.TrimPrefix(desc, "heads/")
	return
}

func Checkout(gitDir, checkoutDir, ref string) error {

	err := os.MkdirAll(checkoutDir, 0777)
	if err != nil {
		return err
	}

	args := []string{"--work-tree", checkoutDir, "checkout", ref, "--", "."}
	err = Command(gitDir, "git", args...).Run()
	if err != nil {
		return err
	}

	// Set mtimes to time file is most recently affected by a commit.
	// This is annoying but unfortunately git sets the timestamps to now,
	// and docker depends on the mtime for cache invalidation.
	err = SetMTimes(gitDir, checkoutDir, ref)
	if err != nil {
		return err
	}

	return nil
}

type BuildDirectory struct {
	Name, Dir string
	Cleanup   func()
}

func PrepBuildDirectory(
	gitDir, remote, ref string,
) (*BuildDirectory, error) {

	start := time.Now()
	defer func() {
		log.Printf("Took %v to prep %v", time.Since(start), remote)
	}()

	if strings.HasPrefix(remote, "github.com/") {
		remote = "https://" + remote
	}

	gitDir, err := filepath.Abs(gitDir)
	if err != nil {
		return nil, fmt.Errorf("unable to determine abspath: %v", err)
	}

	err = LocalMirror(remote, gitDir, ref, os.Stderr)
	if err != nil {
		return nil, fmt.Errorf("unable to LocalMirror: %v", err)
	}

	rev, err := RevParse(gitDir, ref)
	if err != nil {
		return nil, fmt.Errorf("unable to parse rev: %v", err)
	}

	tagName, err := Describe(gitDir, rev)
	if err != nil {
		return nil, fmt.Errorf("unable to describe %v: %v", rev, err)
	}

	shortRev := rev[:10]
	checkoutPath := path.Join(gitDir, filepath.Join("c/", shortRev))

	err = recursiveCheckout(gitDir, checkoutPath, rev)
	if err != nil {
		return nil, err
	}

	cleanup := func() {
		err := SafeCleanup(checkoutPath)
		if err != nil {
			log.Printf("Error cleaning up path: ", checkoutPath)
		}
	}

	return &BuildDirectory{tagName, checkoutPath, cleanup}, nil
}

func recursiveCheckout(gitDir, checkoutPath, rev string) error {
	err := Checkout(gitDir, checkoutPath, rev)
	if err != nil {
		return fmt.Errorf("failed to checkout: %v", err)
	}

	err = PrepSubmodules(gitDir, checkoutPath, rev)
	if err != nil {
		return fmt.Errorf("failed to prep submodules: %v", err)
	}
	return nil
}

func SafeCleanup(path string) error {
	if path == "/" || path == "" || path == "." || strings.Contains(path, "..") {
		return fmt.Errorf("invalid path specified for deletion %q", path)
	}
	return os.RemoveAll(path)
}
<|endoftext|>


<commit_before>package commands

import "os"
import "os/exec"
import "strings"
import "fmt"
import "bytes"
import "path/filepath"
import "bufio"

import alias "../alias/table"
import "../conio"
import "../history"
import "../interpreter"
import "../ls"
import "../currentwork"

import "github.com/shiena/ansicolor"

func cmd_exit(cmd *exec.Cmd) (interpreter.NextT, error) {
	return interpreter.SHUTDOWN, nil
}

func getHome() string {
	home := os.Getenv("HOME")
	if home != "" {
		return home
	}
	homeDrive := os.Getenv("HOMEDRIVE")
	if homeDrive != "" {
		homePath := os.Getenv("HOMEPATH")
		if homePath != "" {
			return homeDrive + homePath
		}
	}
	return ""
}

func cmd_pwd(cmd *exec.Cmd) (interpreter.NextT, error) {
	wd, _ := currentwork.Getwd()
	fmt.Fprintln(cmd.Stdout, wd)
	return interpreter.CONTINUE, nil
}

var prevDir string

func cmd_cd(cmd *exec.Cmd) (interpreter.NextT, error) {
	if len(cmd.Args) >= 2 {
		prevDir_, _ := currentwork.Getwd()
		var err error
		if cmd.Args[1] == "-" {
			err = currentwork.Chdir(prevDir)
		} else {
			err = currentwork.Chdir(cmd.Args[1])
		}
		prevDir = prevDir_
		return interpreter.CONTINUE, err
	}
	home := getHome()
	if home != "" {
		prevDir, _ = currentwork.Getwd()
		err := currentwork.Chdir(home)
		return interpreter.CONTINUE, err
	}
	return cmd_pwd(cmd)
}

func cmd_ls(cmd *exec.Cmd) (interpreter.NextT, error) {
	err := ls.Main(cmd.Args[1:], ansicolor.NewAnsiColorWriter(cmd.Stdout))
	return interpreter.CONTINUE, err
}

func cmd_set(cmd *exec.Cmd) (interpreter.NextT, error) {
	if len(cmd.Args) <= 1 {
		for _, val := range os.Environ() {
			fmt.Fprintln(cmd.Stdout, val)
		}
		return interpreter.CONTINUE, nil
	}
	for _, arg := range cmd.Args[1:] {
		eqlPos := strings.Index(arg, "=")
		if eqlPos < 0 {
			fmt.Fprintf(cmd.Stdout, "%s=%s\n", arg, os.Getenv(arg))
		} else {
			os.Setenv(arg[:eqlPos], arg[eqlPos+1:])
		}
	}
	return interpreter.CONTINUE, nil
}

func cmd_source(cmd *exec.Cmd) (interpreter.NextT, error) {
	envTxtPath := filepath.Join(
		os.TempDir(),
		fmt.Sprintf("nyagos-%d.tmp", os.Getpid()))

	var buffer bytes.Buffer

	buffer.WriteString(cmd.Args[1])
	buffer.WriteString(" & set > ")
	buffer.WriteString(envTxtPath)

	args := make([]string, 3)
	args[0] = os.Getenv("COMSPEC")
	args[1] = "/c"
	args[2] = buffer.String()
	var cmd2 exec.Cmd
	cmd2.Path = args[0]
	cmd2.Args = args
	cmd2.Env = nil
	cmd2.Dir = ""
	if err := cmd2.Run(); err != nil {
		return interpreter.CONTINUE, err
	}
	fp, err := os.Open(envTxtPath)
	if err != nil {
		return interpreter.CONTINUE, nil
	}
	defer os.Remove(envTxtPath)
	defer fp.Close()

	scr := bufio.NewScanner(fp)
	for scr.Scan() {
		line := scr.Text()
		eqlPos := strings.Index(line, "=")
		if eqlPos > 0 {
			os.Setenv(line[:eqlPos], line[eqlPos+1:])
		}
	}
	return interpreter.CONTINUE, nil
}

func cmd_echo(cmd *exec.Cmd) (interpreter.NextT, error) {
	fmt.Fprintln(cmd.Stdout, strings.Join(cmd.Args[1:], " "))
	return interpreter.CONTINUE, nil
}

func cmd_cls(cmd *exec.Cmd) (interpreter.NextT, error) {
	conio.Cls()
	return interpreter.CONTINUE, nil
}

func cmd_alias(cmd *exec.Cmd) (interpreter.NextT, error) {
	if len(cmd.Args) <= 1 {
		for key, val := range alias.Table {
			fmt.Fprintf(cmd.Stdout, "%s=%s\n", key, val)
		}
		return interpreter.CONTINUE, nil
	}
	for _, args := range cmd.Args[1:] {
		if eqlPos := strings.IndexRune(args, '='); eqlPos >= 0 {
			key := args[0:eqlPos]
			val := args[eqlPos+1:]
			if len(val) > 0 {
				alias.Table[strings.ToLower(key)] = val
			} else {
				delete(alias.Table, strings.ToLower(key))
			}
		} else {
			key := strings.ToLower(args)
			val := alias.Table[key]

			fmt.Printf("%s=%s\n", key, val)
		}
	}
	return interpreter.CONTINUE, nil
}

var buildInCmd = map[string]func(cmd *exec.Cmd) (interpreter.NextT, error){
	".":       cmd_source,
	"alias":   cmd_alias,
	"cd":      cmd_cd,
	"cls":     cmd_cls,
	"echo":    cmd_echo,
	"exit":    cmd_exit,
	"history": history.CmdHistory,
	"ls":      cmd_ls,
	"pwd":     cmd_pwd,
	"set":     cmd_set,
	"rem":     cmd_rem,
	"source":  cmd_source,
}

func Exec(cmd *exec.Cmd, IsBackground bool) (interpreter.NextT, error) {
	name := strings.ToLower(cmd.Args[0])
	if len(name) == 2 && strings.HasSuffix(name, ":") {
		err := currentwork.Chdrive(name)
		return interpreter.CONTINUE, err
	}
	function, ok := buildInCmd[name]
	if ok {
		newArgs := make([]string, 0)
		for _, arg1 := range cmd.Args {
			matches, _ := filepath.Glob(arg1)
			if matches == nil {
				newArgs = append(newArgs, arg1)
			} else {
				for _, s := range matches {
					newArgs = append(newArgs, s)
				}
			}
		}
		cmd.Args = newArgs
		if IsBackground {
			go function(cmd)
			return interpreter.CONTINUE, nil
		} else {
			next, err := function(cmd)
			return next, err
		}
	} else {
		return interpreter.THROUGH, nil
	}
}
<commit_msg>x: による移動も cd - の対象に<commit_after>package commands

import "os"
import "os/exec"
import "strings"
import "fmt"
import "bytes"
import "path/filepath"
import "bufio"

import alias "../alias/table"
import "../conio"
import "../history"
import "../interpreter"
import "../ls"
import "../currentwork"

import "github.com/shiena/ansicolor"

func cmd_exit(cmd *exec.Cmd) (interpreter.NextT, error) {
	return interpreter.SHUTDOWN, nil
}

func getHome() string {
	home := os.Getenv("HOME")
	if home != "" {
		return home
	}
	homeDrive := os.Getenv("HOMEDRIVE")
	if homeDrive != "" {
		homePath := os.Getenv("HOMEPATH")
		if homePath != "" {
			return homeDrive + homePath
		}
	}
	return ""
}

func cmd_pwd(cmd *exec.Cmd) (interpreter.NextT, error) {
	wd, _ := currentwork.Getwd()
	fmt.Fprintln(cmd.Stdout, wd)
	return interpreter.CONTINUE, nil
}

var prevDir string

func cmd_cd(cmd *exec.Cmd) (interpreter.NextT, error) {
	if len(cmd.Args) >= 2 {
		prevDir_, _ := currentwork.Getwd()
		var err error
		if cmd.Args[1] == "-" {
			err = currentwork.Chdir(prevDir)
		} else {
			err = currentwork.Chdir(cmd.Args[1])
		}
		prevDir = prevDir_
		return interpreter.CONTINUE, err
	}
	home := getHome()
	if home != "" {
		prevDir, _ = currentwork.Getwd()
		err := currentwork.Chdir(home)
		return interpreter.CONTINUE, err
	}
	return cmd_pwd(cmd)
}

func cmd_ls(cmd *exec.Cmd) (interpreter.NextT, error) {
	err := ls.Main(cmd.Args[1:], ansicolor.NewAnsiColorWriter(cmd.Stdout))
	return interpreter.CONTINUE, err
}

func cmd_set(cmd *exec.Cmd) (interpreter.NextT, error) {
	if len(cmd.Args) <= 1 {
		for _, val := range os.Environ() {
			fmt.Fprintln(cmd.Stdout, val)
		}
		return interpreter.CONTINUE, nil
	}
	for _, arg := range cmd.Args[1:] {
		eqlPos := strings.Index(arg, "=")
		if eqlPos < 0 {
			fmt.Fprintf(cmd.Stdout, "%s=%s\n", arg, os.Getenv(arg))
		} else {
			os.Setenv(arg[:eqlPos], arg[eqlPos+1:])
		}
	}
	return interpreter.CONTINUE, nil
}

func cmd_source(cmd *exec.Cmd) (interpreter.NextT, error) {
	envTxtPath := filepath.Join(
		os.TempDir(),
		fmt.Sprintf("nyagos-%d.tmp", os.Getpid()))

	var buffer bytes.Buffer

	buffer.WriteString(cmd.Args[1])
	buffer.WriteString(" & set > ")
	buffer.WriteString(envTxtPath)

	args := make([]string, 3)
	args[0] = os.Getenv("COMSPEC")
	args[1] = "/c"
	args[2] = buffer.String()
	var cmd2 exec.Cmd
	cmd2.Path = args[0]
	cmd2.Args = args
	cmd2.Env = nil
	cmd2.Dir = ""
	if err := cmd2.Run(); err != nil {
		return interpreter.CONTINUE, err
	}
	fp, err := os.Open(envTxtPath)
	if err != nil {
		return interpreter.CONTINUE, nil
	}
	defer os.Remove(envTxtPath)
	defer fp.Close()

	scr := bufio.NewScanner(fp)
	for scr.Scan() {
		line := scr.Text()
		eqlPos := strings.Index(line, "=")
		if eqlPos > 0 {
			os.Setenv(line[:eqlPos], line[eqlPos+1:])
		}
	}
	return interpreter.CONTINUE, nil
}

func cmd_echo(cmd *exec.Cmd) (interpreter.NextT, error) {
	fmt.Fprintln(cmd.Stdout, strings.Join(cmd.Args[1:], " "))
	return interpreter.CONTINUE, nil
}

func cmd_cls(cmd *exec.Cmd) (interpreter.NextT, error) {
	conio.Cls()
	return interpreter.CONTINUE, nil
}

func cmd_alias(cmd *exec.Cmd) (interpreter.NextT, error) {
	if len(cmd.Args) <= 1 {
		for key, val := range alias.Table {
			fmt.Fprintf(cmd.Stdout, "%s=%s\n", key, val)
		}
		return interpreter.CONTINUE, nil
	}
	for _, args := range cmd.Args[1:] {
		if eqlPos := strings.IndexRune(args, '='); eqlPos >= 0 {
			key := args[0:eqlPos]
			val := args[eqlPos+1:]
			if len(val) > 0 {
				alias.Table[strings.ToLower(key)] = val
			} else {
				delete(alias.Table, strings.ToLower(key))
			}
		} else {
			key := strings.ToLower(args)
			val := alias.Table[key]

			fmt.Printf("%s=%s\n", key, val)
		}
	}
	return interpreter.CONTINUE, nil
}

var buildInCmd = map[string]func(cmd *exec.Cmd) (interpreter.NextT, error){
	".":       cmd_source,
	"alias":   cmd_alias,
	"cd":      cmd_cd,
	"cls":     cmd_cls,
	"echo":    cmd_echo,
	"exit":    cmd_exit,
	"history": history.CmdHistory,
	"ls":      cmd_ls,
	"pwd":     cmd_pwd,
	"set":     cmd_set,
	"rem":     cmd_rem,
	"source":  cmd_source,
}

func Exec(cmd *exec.Cmd, IsBackground bool) (interpreter.NextT, error) {
	name := strings.ToLower(cmd.Args[0])
	if len(name) == 2 && strings.HasSuffix(name, ":") {
		prevDir, _ = currentwork.Getwd()
		err := currentwork.Chdrive(name)
		return interpreter.CONTINUE, err
	}
	function, ok := buildInCmd[name]
	if ok {
		newArgs := make([]string, 0)
		for _, arg1 := range cmd.Args {
			matches, _ := filepath.Glob(arg1)
			if matches == nil {
				newArgs = append(newArgs, arg1)
			} else {
				for _, s := range matches {
					newArgs = append(newArgs, s)
				}
			}
		}
		cmd.Args = newArgs
		if IsBackground {
			go function(cmd)
			return interpreter.CONTINUE, nil
		} else {
			next, err := function(cmd)
			return next, err
		}
	} else {
		return interpreter.THROUGH, nil
	}
}
<|endoftext|>


<commit_before>// package commands ...
package commands

import (
	"fmt"
	"os"
	"os/exec"

	"github.com/nanobox-io/nanobox-golang-stylish"
	"github.com/spf13/cobra"

	"github.com/nanobox-io/nanobox/commands/box"
	"github.com/nanobox-io/nanobox/commands/engine"
	"github.com/nanobox-io/nanobox/commands/production"
	"github.com/nanobox-io/nanobox/config"
	"github.com/nanobox-io/nanobox/util"
)

//
var (

	//
	NanoboxCmd = &cobra.Command{
		Use:   "nanobox",
		Short: "",
		Long:  ``,

		//
		PersistentPreRun: func(ccmd *cobra.Command, args []string) {

			// ensures all dependencies are satisfied before running nanobox commands
			runnable(ccmd, args)

			// if the verbose flag is used, up the log level (this will cascade into
			// all subcommands since this is the root command)
			if config.Verbose {
				config.LogLevel = "debug"
			}
		},

		//
		Run: func(ccmd *cobra.Command, args []string) {

			// hijack the verbose flag (-v), and use it to display the version of the
			// CLI
			if version || config.Verbose {
				fmt.Printf("nanobox v%s\n", config.VERSION)
				os.Exit(0)
			}

			// fall back on default help if no args/flags are passed
			ccmd.HelpFunc()(ccmd, args)
		},
	}

	//
	version bool // display the version of nanobox
)

// init creates the list of available nanobox commands and sub commands
func init() {

	// internal flags
	NanoboxCmd.PersistentFlags().BoolVarP(&config.Devmode, "dev", "", false, "")
	NanoboxCmd.PersistentFlags().MarkHidden("dev")

	// persistent flags
	NanoboxCmd.PersistentFlags().BoolVarP(&config.Background, "background", "", false, "Stops nanobox from auto-suspending.")
	NanoboxCmd.PersistentFlags().BoolVarP(&config.Force, "force", "f", false, "Forces a command to run (effects vary per command).")
	NanoboxCmd.PersistentFlags().BoolVarP(&config.Verbose, "verbose", "v", false, "Increase command output from 'info' to 'debug'.")

	// local flags
	NanoboxCmd.Flags().BoolVarP(&version, "version", "", false, "Display the current version of this CLI")

	// 'hidden' commands
	NanoboxCmd.AddCommand(buildCmd)
	NanoboxCmd.AddCommand(createCmd)
	NanoboxCmd.AddCommand(deployCmd)
	NanoboxCmd.AddCommand(execCmd)
	NanoboxCmd.AddCommand(initCmd)
	NanoboxCmd.AddCommand(logCmd)
	NanoboxCmd.AddCommand(reloadCmd)
	NanoboxCmd.AddCommand(resumeCmd)
	NanoboxCmd.AddCommand(sshCmd)
	NanoboxCmd.AddCommand(watchCmd)

	// 'nanobox' commands
	NanoboxCmd.AddCommand(runCmd)
	NanoboxCmd.AddCommand(devCmd)
	NanoboxCmd.AddCommand(bootstrapCmd)
	NanoboxCmd.AddCommand(infoCmd)
	NanoboxCmd.AddCommand(consoleCmd)
	NanoboxCmd.AddCommand(destroyCmd)
	NanoboxCmd.AddCommand(publishCmd)
	NanoboxCmd.AddCommand(stopCmd)
	NanoboxCmd.AddCommand(updateCmd)
	NanoboxCmd.AddCommand(updateImagesCmd)

	// subcommands
	NanoboxCmd.AddCommand(box.BoxCmd)
	NanoboxCmd.AddCommand(engine.EngineCmd)
	NanoboxCmd.AddCommand(production.ProductionCmd)
}

// runnable ensures all dependencies are satisfied before running box commands
func runnable(ccmd *cobra.Command, args []string) {

	// ensure vagrant exists
	if exists := Vagrant.Exists(); !exists {
		fmt.Println("Missing dependency 'Vagrant'. Please download and install it to continue (https://www.vagrantup.com/).")
		os.Exit(1)
	}

	// ensure virtualbox exists
	if exists := util.VboxExists(); !exists {
		fmt.Println("Missing dependency 'Virtualbox'. Please download and install it to continue (https://www.virtualbox.org/wiki/Downloads).")
		os.Exit(1)
	}
}

// boot
func boot(ccmd *cobra.Command, args []string) {

	// ensure vagrant exists before trying to run any nanobox command
	if exists := Vagrant.Exists(); !exists {
		fmt.Println("Missing dependency 'Vagrant'. Please download and install it to continue (https://www.vagrantup.com/).")
		os.Exit(1)
	}

	// ensure virtualbox exists before trying to run any nanobox command
	if exists := util.VboxExists(); !exists {
		fmt.Println("Missing dependency 'Virtualbox'. Please download and install it to continue (https://www.virtualbox.org/wiki/Downloads).")
		os.Exit(1)
	}

	// ensure a Vagrantfile is available before attempting to boot the VM
	initialize(nil, args)

	// get the status to know what needs to happen with the VM
	status := Vagrant.Status()

	switch status {

	// vm is running - do nothing
	case "running":
		fmt.Printf(stylish.Bullet("Nanobox is already running"))
		break

	// vm is suspended - resume it
	case "saved":
		resume(nil, args)

	// vm is not created - create it
	case "not created":
		create(nil, args)

	// vm is in some unknown state - reload it
	default:
		fmt.Printf(stylish.Bullet("Nanobox is in an unknown state (%s). Reloading...", status))
		reload(nil, args)
	}

	//
	Server.Lock()

	// if the background flag is passed, set the mode to "background"
	if config.Background {
		config.VMfile.BackgroundIs(true)
	}
}

// halt
func halt(ccmd *cobra.Command, args []string) {

	//
	Server.Unlock()

	//

	if err := Server.Suspend(); err != nil {
		Config.Fatal("[commands/halt] server.Suspend() failed", err.Error())
	}

	//
	if err := Vagrant.Suspend(); err != nil {
		Config.Fatal("[commands/halt] vagrant.Suspend() failed", err.Error())
	}

	//
	// os.Exit(0)
}

// sudo runs a command as sudo
func sudo(command, msg string) {
	fmt.Printf(stylish.Bullet(msg))

	//
	cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("sudo %v %v", os.Args[0], command))

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// run command
	if err := cmd.Run(); err != nil {
		Config.Fatal("[commands/halt]", err.Error())
	}
}
<commit_msg>using runnable in boot rather than duplicating logic<commit_after>// package commands ...
package commands

import (
	"fmt"
	"os"
	"os/exec"

	"github.com/nanobox-io/nanobox-golang-stylish"
	"github.com/spf13/cobra"

	"github.com/nanobox-io/nanobox/commands/box"
	"github.com/nanobox-io/nanobox/commands/engine"
	"github.com/nanobox-io/nanobox/commands/production"
	"github.com/nanobox-io/nanobox/config"
	"github.com/nanobox-io/nanobox/util"
)

//
var (

	//
	NanoboxCmd = &cobra.Command{
		Use:   "nanobox",
		Short: "",
		Long:  ``,

		//
		PersistentPreRun: func(ccmd *cobra.Command, args []string) {

			// ensures all dependencies are satisfied before running nanobox commands
			runnable(ccmd, args)

			// if the verbose flag is used, up the log level (this will cascade into
			// all subcommands since this is the root command)
			if config.Verbose {
				config.LogLevel = "debug"
			}
		},

		//
		Run: func(ccmd *cobra.Command, args []string) {

			// hijack the verbose flag (-v), and use it to display the version of the
			// CLI
			if version || config.Verbose {
				fmt.Printf("nanobox v%s\n", config.VERSION)
				os.Exit(0)
			}

			// fall back on default help if no args/flags are passed
			ccmd.HelpFunc()(ccmd, args)
		},
	}

	//
	version bool // display the version of nanobox
)

// init creates the list of available nanobox commands and sub commands
func init() {

	// internal flags
	NanoboxCmd.PersistentFlags().BoolVarP(&config.Devmode, "dev", "", false, "")
	NanoboxCmd.PersistentFlags().MarkHidden("dev")

	// persistent flags
	NanoboxCmd.PersistentFlags().BoolVarP(&config.Background, "background", "", false, "Stops nanobox from auto-suspending.")
	NanoboxCmd.PersistentFlags().BoolVarP(&config.Force, "force", "f", false, "Forces a command to run (effects vary per command).")
	NanoboxCmd.PersistentFlags().BoolVarP(&config.Verbose, "verbose", "v", false, "Increase command output from 'info' to 'debug'.")

	// local flags
	NanoboxCmd.Flags().BoolVarP(&version, "version", "", false, "Display the current version of this CLI")

	// 'hidden' commands
	NanoboxCmd.AddCommand(buildCmd)
	NanoboxCmd.AddCommand(createCmd)
	NanoboxCmd.AddCommand(deployCmd)
	NanoboxCmd.AddCommand(execCmd)
	NanoboxCmd.AddCommand(initCmd)
	NanoboxCmd.AddCommand(logCmd)
	NanoboxCmd.AddCommand(reloadCmd)
	NanoboxCmd.AddCommand(resumeCmd)
	NanoboxCmd.AddCommand(sshCmd)
	NanoboxCmd.AddCommand(watchCmd)

	// 'nanobox' commands
	NanoboxCmd.AddCommand(runCmd)
	NanoboxCmd.AddCommand(devCmd)
	NanoboxCmd.AddCommand(bootstrapCmd)
	NanoboxCmd.AddCommand(infoCmd)
	NanoboxCmd.AddCommand(consoleCmd)
	NanoboxCmd.AddCommand(destroyCmd)
	NanoboxCmd.AddCommand(publishCmd)
	NanoboxCmd.AddCommand(stopCmd)
	NanoboxCmd.AddCommand(updateCmd)
	NanoboxCmd.AddCommand(updateImagesCmd)

	// subcommands
	NanoboxCmd.AddCommand(box.BoxCmd)
	NanoboxCmd.AddCommand(engine.EngineCmd)
	NanoboxCmd.AddCommand(production.ProductionCmd)
}

// runnable ensures all dependencies are satisfied before running box commands
func runnable(ccmd *cobra.Command, args []string) {

	// ensure vagrant exists
	if exists := Vagrant.Exists(); !exists {
		fmt.Println("Missing dependency 'Vagrant'. Please download and install it to continue (https://www.vagrantup.com/).")
		os.Exit(1)
	}

	// ensure virtualbox exists
	if exists := util.VboxExists(); !exists {
		fmt.Println("Missing dependency 'Virtualbox'. Please download and install it to continue (https://www.virtualbox.org/wiki/Downloads).")
		os.Exit(1)
	}
}

// boot
func boot(ccmd *cobra.Command, args []string) {

	// ensure the cli can run before trying to boot vm
	runnable(ccmd, nil)

	// ensure a Vagrantfile is available before attempting to boot the VM
	initialize(nil, args)

	// get the status to know what needs to happen with the VM
	status := Vagrant.Status()

	switch status {

	// vm is running - do nothing
	case "running":
		fmt.Printf(stylish.Bullet("Nanobox is already running"))
		break

	// vm is suspended - resume it
	case "saved":
		resume(nil, args)

	// vm is not created - create it
	case "not created":
		create(nil, args)

	// vm is in some unknown state - reload it
	default:
		fmt.Printf(stylish.Bullet("Nanobox is in an unknown state (%s). Reloading...", status))
		reload(nil, args)
	}

	//
	Server.Lock()

	// if the background flag is passed, set the mode to "background"
	if config.Background {
		config.VMfile.BackgroundIs(true)
	}
}

// halt
func halt(ccmd *cobra.Command, args []string) {

	//
	Server.Unlock()

	//

	if err := Server.Suspend(); err != nil {
		Config.Fatal("[commands/halt] server.Suspend() failed", err.Error())
	}

	//
	if err := Vagrant.Suspend(); err != nil {
		Config.Fatal("[commands/halt] vagrant.Suspend() failed", err.Error())
	}

	//
	// os.Exit(0)
}

// sudo runs a command as sudo
func sudo(command, msg string) {
	fmt.Printf(stylish.Bullet(msg))

	//
	cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("sudo %v %v", os.Args[0], command))

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// run command
	if err := cmd.Run(); err != nil {
		Config.Fatal("[commands/halt]", err.Error())
	}
}
<|endoftext|>