<commit_before>// Copyright 2016 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 render

import (
	"bytes"
	"fmt"
	"html"
	"io"
	"path"
	"sort"
	"strconv"
	"strings"

	"github.com/googlecodelabs/tools/claat/types"
)

// MD renders nodes as markdown for the target env.
func MD(ctx Context, nodes ...types.Node) (string, error) {
	var buf bytes.Buffer
	if err := WriteMD(&buf, ctx.Env, nodes...); err != nil {
		return "", err
	}
	return buf.String(), nil
}

// WriteMD does the same as MD but outputs rendered markup to w.
func WriteMD(w io.Writer, env string, nodes ...types.Node) error {
	mw := mdWriter{w: w, env: env, Prefix: ""}
	return mw.write(nodes...)
}

type mdWriter struct {
	w                  io.Writer // output writer
	env                string    // target environment
	err                error     // error during any writeXxx methods
	lineStart          bool
	isWritingTableCell bool   // used to override lineStart for correct cell formatting
	isWritingList      bool   // used for override newblock when needed
	Prefix             string // prefix for e.g. blockquote content
}

func (mw *mdWriter) writeBytes(b []byte) {
	if mw.err != nil {
		return
	}
	mw.lineStart = len(b) > 0 && b[len(b)-1] == '\n'
	_, mw.err = mw.w.Write(b)
}

func (mw *mdWriter) writeString(s string) {
	if mw.lineStart {
		s = mw.Prefix + s
	}
	mw.writeBytes([]byte(s))
}

func (mw *mdWriter) writeEscape(s string) {
	s = html.EscapeString(s)
	mw.writeString(ReplaceDoubleCurlyBracketsWithEntity(s))
}

func (mw *mdWriter) space() {
	if !mw.lineStart {
		mw.writeString(" ")
	}
}

func (mw *mdWriter) newBlock() {
	if !mw.lineStart {
		mw.writeBytes(newLine)
	}
	mw.writeBytes(newLine)
}

func (mw *mdWriter) matchEnv(v []string) bool {
	if len(v) == 0 || mw.env == "" {
		return true
	}
	i := sort.SearchStrings(v, mw.env)
	return i < len(v) && v[i] == mw.env
}

func (mw *mdWriter) write(nodes ...types.Node) error {
	for _, n := range nodes {
		if !mw.matchEnv(n.Env()) {
			continue
		}
		switch n := n.(type) {
		case *types.TextNode:
			mw.text(n)
		case *types.ImageNode:
			mw.image(n)
		case *types.URLNode:
			mw.url(n)
		case *types.ButtonNode:
			mw.write(n.Content.Nodes...)
		case *types.CodeNode:
			mw.code(n)
		case *types.ListNode:
			mw.list(n)
		case *types.ImportNode:
			if len(n.Content.Nodes) == 0 {
				break
			}
			mw.write(n.Content.Nodes...)
		case *types.ItemsListNode:
			mw.itemsList(n)
		case *types.GridNode:
			mw.table(n)
		case *types.InfoboxNode:
			mw.infobox(n)
		case *types.SurveyNode:
			mw.survey(n)
		case *types.HeaderNode:
			mw.header(n)
		case *types.YouTubeNode:
			mw.youtube(n)
		}
		if mw.err != nil {
			return mw.err
		}
	}
	return nil
}

func (mw *mdWriter) text(n *types.TextNode) {
	tr := strings.TrimLeft(n.Value, " \t\n\r\f\v")
	left := n.Value[0:(len(n.Value) - len(tr))]
	t := strings.TrimRight(tr, " \t\n\r\f\v")
	right := tr[len(t):len(tr)]

	mw.writeString(left)

	if n.Bold {
		mw.writeString("**")
	}
	if n.Italic {
		mw.writeString("*")
	}
	if n.Code {
		mw.writeString("`")
	}

	t = strings.ReplaceAll(t, "<", "<")
	t = strings.ReplaceAll(t, ">", ">")

	mw.writeString(t)

	if n.Code {
		mw.writeString("`")
	}
	if n.Italic {
		mw.writeString("*")
	}
	if n.Bold {
		mw.writeString("**")
	}

	mw.writeString(right)
}

func (mw *mdWriter) image(n *types.ImageNode) {
	mw.space()
	mw.writeString("<img ")
	mw.writeString(fmt.Sprintf("src=%q ", n.Src))

	if n.Alt != "" {
		mw.writeString(fmt.Sprintf("alt=%q ", n.Alt))
	} else {
		mw.writeString(fmt.Sprintf("alt=%q ", path.Base(n.Src)))
	}

	if n.Title != "" {
		mw.writeString(fmt.Sprintf("title=%q ", n.Title))
	}

	// If available append width to the src string of the image.
	if n.Width > 0 {
		mw.writeString(fmt.Sprintf(" width=\"%.2f\" ", n.Width))
	}

	mw.writeString("/>")
}

func (mw *mdWriter) url(n *types.URLNode) {
	mw.space()
	if n.URL != "" {
		// Look-ahead for button syntax.
		if _, ok := n.Content.Nodes[0].(*types.ButtonNode); ok {
			mw.writeString("<button>")
		}
		mw.writeString("[")
	}
	mw.write(n.Content.Nodes...)
	if n.URL != "" {
		mw.writeString("](")
		mw.writeString(n.URL)
		mw.writeString(")")
		if _, ok := n.Content.Nodes[0].(*types.ButtonNode); ok {
			// Look-ahead for button syntax.
			mw.writeString("</button>")
		}
	}
}

func (mw *mdWriter) code(n *types.CodeNode) {
	if n.Empty() {
		return
	}
	mw.newBlock()
	defer mw.writeBytes(newLine)
	mw.writeString("```")
	if n.Term {
		mw.writeString("console")
	} else {
		mw.writeString(n.Lang)
	}
	mw.writeBytes(newLine)
	mw.writeString(n.Value)
	if !mw.lineStart {
		mw.writeBytes(newLine)
	}
	mw.writeString("```")
}

func (mw *mdWriter) list(n *types.ListNode) {
	if n.Block() == true {
		mw.newBlock()
	}
	mw.write(n.Nodes...)
	if !mw.lineStart && !mw.isWritingTableCell {
		mw.writeBytes(newLine)
	}
}

func (mw *mdWriter) itemsList(n *types.ItemsListNode) {
	mw.isWritingList = true
	if n.Block() == true {
		mw.newBlock()
	}
	for i, item := range n.Items {
		s := "* "
		if n.Type() == types.NodeItemsList && n.Start > 0 {
			s = strconv.Itoa(i+n.Start) + ". "
		}
		mw.writeString(s)
		mw.write(item.Nodes...)
		if !mw.lineStart {
			mw.writeBytes(newLine)
		}
	}
	mw.isWritingList = false
}

func (mw *mdWriter) infobox(n *types.InfoboxNode) {
	// InfoBoxes are comprised of a ListNode with the contents of the InfoBox.
	// Writing the ListNode directly results in extra newlines in the md output
	// which breaks the formatting. So instead, write the ListNode's children
	// directly and don't write the ListNode itself.
	mw.newBlock()
	k := "aside positive"
	if n.Kind == types.InfoboxNegative {
		k = "aside negative"
	}
	mw.Prefix = "> "
	mw.writeString(k)
	mw.writeString("\n")

	for _, cn := range n.Content.Nodes {
		cn.MutateBlock(false)
		mw.write(cn)
	}

	mw.Prefix = ""
}

func (mw *mdWriter) survey(n *types.SurveyNode) {
	mw.newBlock()
	mw.writeString("<form>")
	mw.writeBytes(newLine)
	for _, g := range n.Groups {
		mw.writeString("<name>")
		mw.writeEscape(g.Name)
		mw.writeString("</name>")
		mw.writeBytes(newLine)
		for _, o := range g.Options {
			mw.writeString("<input value=\"")
			mw.writeEscape(o)
			mw.writeString("\">")
			mw.writeBytes(newLine)
		}
	}
	mw.writeString("</form>")
}

func (mw *mdWriter) header(n *types.HeaderNode) {
	mw.newBlock()
	mw.writeString(strings.Repeat("#", n.Level+1))
	mw.writeString(" ")
	mw.write(n.Content.Nodes...)
	if !mw.lineStart {
		mw.writeBytes(newLine)
	}
}

func (mw *mdWriter) youtube(n *types.YouTubeNode) {
	if !mw.isWritingList {
		mw.newBlock()
	}
	mw.writeString(fmt.Sprintf(`<video id="%s"></video>`, n.VideoID))
}

func (mw *mdWriter) table(n *types.GridNode) {
	mw.writeBytes(newLine)
	for rowIndex, row := range n.Rows {
		mw.writeString("|")
		for _, cell := range row {
			mw.isWritingTableCell = true
			mw.writeString(" ")
			for _, cn := range cell.Content.Nodes {
				cn.MutateBlock(false) // don't treat content as a new block
				mw.write(cn)
			}
			mw.writeString(" |")
		}
		mw.writeBytes(newLine)

		// Write header bottom border
		if rowIndex == 0 {
			mw.writeString("|")
			for range row {
				mw.writeString(" --- |")
			}
			mw.writeBytes(newLine)
		}

		mw.isWritingTableCell = false
	}
}
<commit_msg>s/ReplaceAll/Replace to avoid requiring Go 1.12.<commit_after>// Copyright 2016 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 render

import (
	"bytes"
	"fmt"
	"html"
	"io"
	"path"
	"sort"
	"strconv"
	"strings"

	"github.com/googlecodelabs/tools/claat/types"
)

// MD renders nodes as markdown for the target env.
func MD(ctx Context, nodes ...types.Node) (string, error) {
	var buf bytes.Buffer
	if err := WriteMD(&buf, ctx.Env, nodes...); err != nil {
		return "", err
	}
	return buf.String(), nil
}

// WriteMD does the same as MD but outputs rendered markup to w.
func WriteMD(w io.Writer, env string, nodes ...types.Node) error {
	mw := mdWriter{w: w, env: env, Prefix: ""}
	return mw.write(nodes...)
}

type mdWriter struct {
	w                  io.Writer // output writer
	env                string    // target environment
	err                error     // error during any writeXxx methods
	lineStart          bool
	isWritingTableCell bool   // used to override lineStart for correct cell formatting
	isWritingList      bool   // used for override newblock when needed
	Prefix             string // prefix for e.g. blockquote content
}

func (mw *mdWriter) writeBytes(b []byte) {
	if mw.err != nil {
		return
	}
	mw.lineStart = len(b) > 0 && b[len(b)-1] == '\n'
	_, mw.err = mw.w.Write(b)
}

func (mw *mdWriter) writeString(s string) {
	if mw.lineStart {
		s = mw.Prefix + s
	}
	mw.writeBytes([]byte(s))
}

func (mw *mdWriter) writeEscape(s string) {
	s = html.EscapeString(s)
	mw.writeString(ReplaceDoubleCurlyBracketsWithEntity(s))
}

func (mw *mdWriter) space() {
	if !mw.lineStart {
		mw.writeString(" ")
	}
}

func (mw *mdWriter) newBlock() {
	if !mw.lineStart {
		mw.writeBytes(newLine)
	}
	mw.writeBytes(newLine)
}

func (mw *mdWriter) matchEnv(v []string) bool {
	if len(v) == 0 || mw.env == "" {
		return true
	}
	i := sort.SearchStrings(v, mw.env)
	return i < len(v) && v[i] == mw.env
}

func (mw *mdWriter) write(nodes ...types.Node) error {
	for _, n := range nodes {
		if !mw.matchEnv(n.Env()) {
			continue
		}
		switch n := n.(type) {
		case *types.TextNode:
			mw.text(n)
		case *types.ImageNode:
			mw.image(n)
		case *types.URLNode:
			mw.url(n)
		case *types.ButtonNode:
			mw.write(n.Content.Nodes...)
		case *types.CodeNode:
			mw.code(n)
		case *types.ListNode:
			mw.list(n)
		case *types.ImportNode:
			if len(n.Content.Nodes) == 0 {
				break
			}
			mw.write(n.Content.Nodes...)
		case *types.ItemsListNode:
			mw.itemsList(n)
		case *types.GridNode:
			mw.table(n)
		case *types.InfoboxNode:
			mw.infobox(n)
		case *types.SurveyNode:
			mw.survey(n)
		case *types.HeaderNode:
			mw.header(n)
		case *types.YouTubeNode:
			mw.youtube(n)
		}
		if mw.err != nil {
			return mw.err
		}
	}
	return nil
}

func (mw *mdWriter) text(n *types.TextNode) {
	tr := strings.TrimLeft(n.Value, " \t\n\r\f\v")
	left := n.Value[0:(len(n.Value) - len(tr))]
	t := strings.TrimRight(tr, " \t\n\r\f\v")
	right := tr[len(t):len(tr)]

	mw.writeString(left)

	if n.Bold {
		mw.writeString("**")
	}
	if n.Italic {
		mw.writeString("*")
	}
	if n.Code {
		mw.writeString("`")
	}

	t = strings.Replace(t, "<", "<", -1)
	t = strings.Replace(t, ">", ">", -1)

	mw.writeString(t)

	if n.Code {
		mw.writeString("`")
	}
	if n.Italic {
		mw.writeString("*")
	}
	if n.Bold {
		mw.writeString("**")
	}

	mw.writeString(right)
}

func (mw *mdWriter) image(n *types.ImageNode) {
	mw.space()
	mw.writeString("<img ")
	mw.writeString(fmt.Sprintf("src=%q ", n.Src))

	if n.Alt != "" {
		mw.writeString(fmt.Sprintf("alt=%q ", n.Alt))
	} else {
		mw.writeString(fmt.Sprintf("alt=%q ", path.Base(n.Src)))
	}

	if n.Title != "" {
		mw.writeString(fmt.Sprintf("title=%q ", n.Title))
	}

	// If available append width to the src string of the image.
	if n.Width > 0 {
		mw.writeString(fmt.Sprintf(" width=\"%.2f\" ", n.Width))
	}

	mw.writeString("/>")
}

func (mw *mdWriter) url(n *types.URLNode) {
	mw.space()
	if n.URL != "" {
		// Look-ahead for button syntax.
		if _, ok := n.Content.Nodes[0].(*types.ButtonNode); ok {
			mw.writeString("<button>")
		}
		mw.writeString("[")
	}
	mw.write(n.Content.Nodes...)
	if n.URL != "" {
		mw.writeString("](")
		mw.writeString(n.URL)
		mw.writeString(")")
		if _, ok := n.Content.Nodes[0].(*types.ButtonNode); ok {
			// Look-ahead for button syntax.
			mw.writeString("</button>")
		}
	}
}

func (mw *mdWriter) code(n *types.CodeNode) {
	if n.Empty() {
		return
	}
	mw.newBlock()
	defer mw.writeBytes(newLine)
	mw.writeString("```")
	if n.Term {
		mw.writeString("console")
	} else {
		mw.writeString(n.Lang)
	}
	mw.writeBytes(newLine)
	mw.writeString(n.Value)
	if !mw.lineStart {
		mw.writeBytes(newLine)
	}
	mw.writeString("```")
}

func (mw *mdWriter) list(n *types.ListNode) {
	if n.Block() == true {
		mw.newBlock()
	}
	mw.write(n.Nodes...)
	if !mw.lineStart && !mw.isWritingTableCell {
		mw.writeBytes(newLine)
	}
}

func (mw *mdWriter) itemsList(n *types.ItemsListNode) {
	mw.isWritingList = true
	if n.Block() == true {
		mw.newBlock()
	}
	for i, item := range n.Items {
		s := "* "
		if n.Type() == types.NodeItemsList && n.Start > 0 {
			s = strconv.Itoa(i+n.Start) + ". "
		}
		mw.writeString(s)
		mw.write(item.Nodes...)
		if !mw.lineStart {
			mw.writeBytes(newLine)
		}
	}
	mw.isWritingList = false
}

func (mw *mdWriter) infobox(n *types.InfoboxNode) {
	// InfoBoxes are comprised of a ListNode with the contents of the InfoBox.
	// Writing the ListNode directly results in extra newlines in the md output
	// which breaks the formatting. So instead, write the ListNode's children
	// directly and don't write the ListNode itself.
	mw.newBlock()
	k := "aside positive"
	if n.Kind == types.InfoboxNegative {
		k = "aside negative"
	}
	mw.Prefix = "> "
	mw.writeString(k)
	mw.writeString("\n")

	for _, cn := range n.Content.Nodes {
		cn.MutateBlock(false)
		mw.write(cn)
	}

	mw.Prefix = ""
}

func (mw *mdWriter) survey(n *types.SurveyNode) {
	mw.newBlock()
	mw.writeString("<form>")
	mw.writeBytes(newLine)
	for _, g := range n.Groups {
		mw.writeString("<name>")
		mw.writeEscape(g.Name)
		mw.writeString("</name>")
		mw.writeBytes(newLine)
		for _, o := range g.Options {
			mw.writeString("<input value=\"")
			mw.writeEscape(o)
			mw.writeString("\">")
			mw.writeBytes(newLine)
		}
	}
	mw.writeString("</form>")
}

func (mw *mdWriter) header(n *types.HeaderNode) {
	mw.newBlock()
	mw.writeString(strings.Repeat("#", n.Level+1))
	mw.writeString(" ")
	mw.write(n.Content.Nodes...)
	if !mw.lineStart {
		mw.writeBytes(newLine)
	}
}

func (mw *mdWriter) youtube(n *types.YouTubeNode) {
	if !mw.isWritingList {
		mw.newBlock()
	}
	mw.writeString(fmt.Sprintf(`<video id="%s"></video>`, n.VideoID))
}

func (mw *mdWriter) table(n *types.GridNode) {
	mw.writeBytes(newLine)
	for rowIndex, row := range n.Rows {
		mw.writeString("|")
		for _, cell := range row {
			mw.isWritingTableCell = true
			mw.writeString(" ")
			for _, cn := range cell.Content.Nodes {
				cn.MutateBlock(false) // don't treat content as a new block
				mw.write(cn)
			}
			mw.writeString(" |")
		}
		mw.writeBytes(newLine)

		// Write header bottom border
		if rowIndex == 0 {
			mw.writeString("|")
			for range row {
				mw.writeString(" --- |")
			}
			mw.writeBytes(newLine)
		}

		mw.isWritingTableCell = false
	}
}
<|endoftext|>


<commit_before>package parser

import (
  "goprotobuf.googlecode.com/hg/proto"
  ir "tritium/proto"
  "io/ioutil"
  . "tritium/tokenizer" // was meant to be in this package
  "path"
  //"fmt"
)

type Parser struct {
  *Tokenizer
  FileName string
  DirName string
  FullPath string
  Lookahead *Token
  counter int
  header bool
}

func (p *Parser) gensym() string {
  p.counter++
  return p.FullPath + string(p.counter)
}

func (p *Parser) peek() *Token {
  return p.Lookahead
}

func (p *Parser) pop() *Token {
  val := p.Lookahead
  p.Lookahead = p.Tokenizer.Pop()
  return val
}

func MakeParser(fullpath string) *Parser {
  src, _ := ioutil.ReadFile(fullpath)
  d, f := path.Split(fullpath)
  p := &Parser {
    Tokenizer: MakeTokenizer(src),
    FileName: f,
    DirName: d,
    FullPath: fullpath,
    Lookahead: nil,
    counter: 0,
  }
  p.pop()
  return p
}

func (p *Parser) Parse() *ir.ScriptObject {
  script := new(ir.ScriptObject)
  script.Name = proto.String(p.FullPath)

  stmts := make([]*ir.Instruction, 0)
  defs := make([]*ir.Function, 0)

  switch p.peek().Lexeme {
  case FUNC:
    for p.peek().Lexeme != EOF {
      defs = append(defs, p.definition())
    }
    if len(defs) == 0 {
      defs = nil
    }
    script.Functions = defs
  default:
    for p.peek().Lexeme != EOF {
      stmts = append(stmts, p.statement())
    }
    if len(stmts) == 0 {
      stmts = nil
    }
    script.Root = &ir.Instruction {
      Type: ir.NewInstruction_InstructionType(ir.Instruction_BLOCK),
      Children: stmts,
    }
  }
  return script
}

func (p *Parser) statement() *ir.Instruction {
  node := new(ir.Instruction)
  switch p.peek().Lexeme {
  case IMPORT:
    token := p.pop() // pop the "@import" keyword
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_IMPORT)
    node.Value = proto.String(path.Join(p.DirName, token.Value))
  default:
    node = p.expression()
  }
  return node
}

func (p *Parser) expression() *ir.Instruction {
  node := new(ir.Instruction)
  switch p.peek().Lexeme {
  case STRING, REGEXP, POS:
    node = p.literal()
  case READ:
    node = p.read()
  case ID:
    node = p.call()
  case GVAR, LVAR:
    node = p.variable()
  default:
    panic("malformed expression")
  }
  return node
}

func (p *Parser) literal() *ir.Instruction {
  node := new(ir.Instruction)
  token := p.pop()
  switch token.Lexeme {
  case STRING:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
    node.Value = proto.String(token.Value)
  case REGEXP:
    pattern := new(ir.Instruction)
    options := new(ir.Instruction)
    po := make([]*ir.Instruction, 2)
    pattern.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
    pattern.Value = proto.String(token.Value)
    options.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
    options.Value = proto.String(token.ExtraValue)
    po[0] = pattern
    po[1] = options
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    node.Value = proto.String("regexp")
    node.Arguments = po
  case POS:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_POSITION)
    node.Value = proto.String(token.Value)
  }
  return node
}

func (p *Parser) read() *ir.Instruction {
  node := new(ir.Instruction)
  p.pop() // pop the "read" keyword
  if p.peek().Lexeme != LPAREN {
    panic("argument list expected for read")
  }
  p.pop() // pop the lparen
  if p.peek().Lexeme != STRING {
    panic("read requires a literal string argument")
  }
  readPath := p.pop().Value
  if p.peek().Lexeme != RPAREN {
    panic("unterminated argument list in read")
  }
  p.pop() // pop the rparen
  contents, _ := ioutil.ReadFile(path.Join(p.DirName, readPath))
  node.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
  node.Value = proto.String(string(contents))
  return node
}

func (p *Parser) call() *ir.Instruction {
  node := new(ir.Instruction)
  funcName := p.pop().Value // grab the function name
  if p.peek().Lexeme != LPAREN {
    panic("argument list expected for call to " + funcName)
  }
  p.pop() // pop the lparen
  ords, kwds := p.arguments() // gather the arguments
  if p.peek().Lexeme != RPAREN {
    panic("unterminated argument list in call to " + funcName)
  }
  p.pop() // pop the rparen
  var block []*ir.Instruction
  if p.peek().Lexeme == LBRACE {
    block = p.block()
  }

  // Expand keyword args
  if kwds != nil {
    kwdToGensym := make(map[string]string, len(kwds))
    outer := make([]*ir.Instruction, 0)
    for k, v:= range kwds {
      temp := new(ir.Instruction)
      temp.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
      temp.Value = proto.String("var")
      temp.Arguments = make([]*ir.Instruction, 2)
      tempName := p.gensym()
      temp.Arguments[0] = &ir.Instruction {
        Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
        Value: proto.String(tempName),
      }
      temp.Arguments[1] = v
      outer = append(outer, temp)
      kwdToGensym[k] = tempName
    }
    inner := make([]*ir.Instruction, 0)
    for k, _ := range kwds {
      getter := new(ir.Instruction)
      getter.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
      getter.Value = proto.String("var")
      getter.Arguments = make([]*ir.Instruction, 1)
      getter.Arguments[0] = &ir.Instruction {
        Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
        Value: proto.String(kwdToGensym[k]),
      }
      setter := new(ir.Instruction)
      setter.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
      setter.Value = proto.String("set")
      setter.Arguments = make([]*ir.Instruction, 2)
      setter.Arguments[0] = &ir.Instruction {
        Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
        Value: proto.String(k),
      }
      setter.Arguments[1] = getter
      inner = append(inner, setter)
    }

    theCall := new(ir.Instruction)
    theCall.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    theCall.Value = proto.String(funcName)
    theCall.Arguments = ords

    if block == nil {
      block = inner
    } else {
      for _, v := range block {
        inner = append(inner, v)
      }
    }

    theCall.Children = inner
    outer = append(outer, theCall)

    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_BLOCK)
    node.Children = outer

  } else {
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    node.Value = proto.String(funcName)
    node.Arguments = ords
    node.Children = block
  }
  return node
}

func (p *Parser) arguments() (ords []*ir.Instruction, kwds map[string]*ir.Instruction) {
  ords, kwds = make([]*ir.Instruction, 0), make(map[string]*ir.Instruction, 0)
  if p.peek().Lexeme != RPAREN {
    if p.peek().Lexeme == KWD {
      k := p.pop().Value
      kwds[k] = p.expression()
    } else {
      ords = append(ords, p.expression())
    }
  }
  for p.peek().Lexeme != RPAREN {
    if p.peek().Lexeme == COMMA {
      p.pop()
    } else {
      panic("arguments must be separated by commas")
    }
    if p.peek().Lexeme == KWD {
      k := p.pop().Value
      kwds[k] = p.expression()
    } else {
      ords = append(ords, p.expression())
    }
  }
  if len(ords) == 0 {
    ords = nil
  }
  if len(kwds) == 0 {
    kwds = nil
  }
  return ords, kwds
}

func (p *Parser) variable() *ir.Instruction {
  node := new(ir.Instruction)
  switch p.peek().Lexeme {
  case LVAR:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_LOCAL_VAR)
    node.Value = proto.String(p.pop().Value)
    if p.peek().Lexeme == EQUAL {
      p.pop() // pop the equal sign
      node.Arguments = make([]*ir.Instruction, 1)
      node.Arguments[0] = p.expression()
    }
  case GVAR:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    node.Value = proto.String("var")
    name := p.pop().Value
    value := new(ir.Instruction)
    if p.peek().Lexeme == EQUAL {
      p.pop() // pop the equal sign
      value = p.expression()
    } else {
      value = nil
    }
    if value == nil {
      node.Arguments = make([]*ir.Instruction, 1)
    } else {
      node.Arguments = make([]*ir.Instruction, 2)
      node.Arguments[1] = value
    }
    node.Arguments[0] = &ir.Instruction {
      Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
      Value: proto.String(name),
    }
  }
  if p.peek().Lexeme == LBRACE {
    node.Children = p.block()
  }
  return node
}

func (p *Parser) block() []*ir.Instruction {
  stmts := make([]*ir.Instruction, 0)
  p.pop() // pop the lbrace
  for p.peek().Lexeme != RBRACE {
    stmts = append(stmts, p.statement())
  }
  p.pop() // pop the rbrace
  if len(stmts) == 0 {
    stmts = nil
  }
  return stmts
}

func (p *Parser) definition() *ir.Function {
  isSignature := false
  node := new(ir.Function)

  p.pop() // pop the "@func" keyword
  contextType := ""
  if p.peek().Lexeme == TYPE {
    contextType = p.pop().Value
    if p.peek().Lexeme != DOT {
      panic("function context and function name must be separated by '.'")
    }
    p.pop() // pop the dot
  }

  if p.peek().Lexeme != ID {
    panic("invalid function name in definition")
  }
  funcName := p.pop().Value

  if p.peek().Lexeme != LPAREN {
    panic("malformed parameter list in function signature")
  }
  p.pop() // pop the lparen
  params := p.parameters()
  if len(params) == 0 {
    params = nil
  }
  p.pop() // pop the rparen

  returnType := ""
  opensType := ""
  if p.peek().Lexeme == TYPE {
    isSignature = true
    returnType = p.pop().Value
    if p.peek().Lexeme == TYPE {
      opensType = p.pop().Value
    }
  }

  node.Name = proto.String(funcName)
  node.Args = params
  node.ScopeType = proto.String(contextType)
  node.ReturnType = proto.String(returnType)
  node.OpensType = proto.String(opensType)

  if isSignature {
    if p.peek().Lexeme == LBRACE {
      panic("body not permitted in function signature")
    }
    return node
  }

  if p.peek().Lexeme != LBRACE {
    panic("function definition is missing a body")
  }
  funcBody := &ir.Instruction {
    Type: ir.NewInstruction_InstructionType(ir.Instruction_BLOCK),
    Children: p.block(),
  }
  node.Instruction = funcBody
  return node
}

func (p *Parser) parameters() []*ir.Function_Argument {
  params := make([]*ir.Function_Argument, 0)
  for p.peek().Lexeme != RPAREN {
    if p.peek().Lexeme != TYPE {
      panic("function parameter is missing a type")
    }
    param := &ir.Function_Argument {
      TypeString: proto.String(p.pop().Value),
    }
    if p.peek().Lexeme != LVAR {
      panic("function parameter has invalid name")
    }
    param.Name = proto.String(p.pop().Value)
    params = append(params, param)
  }
  return params
}

<commit_msg>Keep forgetting to parse the commas.<commit_after>package parser

import (
  "goprotobuf.googlecode.com/hg/proto"
  ir "tritium/proto"
  "io/ioutil"
  . "tritium/tokenizer" // was meant to be in this package
  "path"
  //"fmt"
)

type Parser struct {
  *Tokenizer
  FileName string
  DirName string
  FullPath string
  Lookahead *Token
  counter int
  header bool
}

func (p *Parser) gensym() string {
  p.counter++
  return p.FullPath + string(p.counter)
}

func (p *Parser) peek() *Token {
  return p.Lookahead
}

func (p *Parser) pop() *Token {
  val := p.Lookahead
  p.Lookahead = p.Tokenizer.Pop()
  return val
}

func MakeParser(fullpath string) *Parser {
  src, _ := ioutil.ReadFile(fullpath)
  d, f := path.Split(fullpath)
  p := &Parser {
    Tokenizer: MakeTokenizer(src),
    FileName: f,
    DirName: d,
    FullPath: fullpath,
    Lookahead: nil,
    counter: 0,
  }
  p.pop()
  return p
}

func (p *Parser) Parse() *ir.ScriptObject {
  script := new(ir.ScriptObject)
  script.Name = proto.String(p.FullPath)

  stmts := make([]*ir.Instruction, 0)
  defs := make([]*ir.Function, 0)

  switch p.peek().Lexeme {
  case FUNC:
    for p.peek().Lexeme != EOF {
      defs = append(defs, p.definition())
    }
    if len(defs) == 0 {
      defs = nil
    }
    script.Functions = defs
  default:
    for p.peek().Lexeme != EOF {
      stmts = append(stmts, p.statement())
    }
    if len(stmts) == 0 {
      stmts = nil
    }
    script.Root = &ir.Instruction {
      Type: ir.NewInstruction_InstructionType(ir.Instruction_BLOCK),
      Children: stmts,
    }
  }
  return script
}

func (p *Parser) statement() *ir.Instruction {
  node := new(ir.Instruction)
  switch p.peek().Lexeme {
  case IMPORT:
    token := p.pop() // pop the "@import" keyword
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_IMPORT)
    node.Value = proto.String(path.Join(p.DirName, token.Value))
  default:
    node = p.expression()
  }
  return node
}

func (p *Parser) expression() *ir.Instruction {
  node := new(ir.Instruction)
  switch p.peek().Lexeme {
  case STRING, REGEXP, POS:
    node = p.literal()
  case READ:
    node = p.read()
  case ID:
    node = p.call()
  case GVAR, LVAR:
    node = p.variable()
  default:
    panic("malformed expression")
  }
  return node
}

func (p *Parser) literal() *ir.Instruction {
  node := new(ir.Instruction)
  token := p.pop()
  switch token.Lexeme {
  case STRING:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
    node.Value = proto.String(token.Value)
  case REGEXP:
    pattern := new(ir.Instruction)
    options := new(ir.Instruction)
    po := make([]*ir.Instruction, 2)
    pattern.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
    pattern.Value = proto.String(token.Value)
    options.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
    options.Value = proto.String(token.ExtraValue)
    po[0] = pattern
    po[1] = options
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    node.Value = proto.String("regexp")
    node.Arguments = po
  case POS:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_POSITION)
    node.Value = proto.String(token.Value)
  }
  return node
}

func (p *Parser) read() *ir.Instruction {
  node := new(ir.Instruction)
  p.pop() // pop the "read" keyword
  if p.peek().Lexeme != LPAREN {
    panic("argument list expected for read")
  }
  p.pop() // pop the lparen
  if p.peek().Lexeme != STRING {
    panic("read requires a literal string argument")
  }
  readPath := p.pop().Value
  if p.peek().Lexeme != RPAREN {
    panic("unterminated argument list in read")
  }
  p.pop() // pop the rparen
  contents, _ := ioutil.ReadFile(path.Join(p.DirName, readPath))
  node.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
  node.Value = proto.String(string(contents))
  return node
}

func (p *Parser) call() *ir.Instruction {
  node := new(ir.Instruction)
  funcName := p.pop().Value // grab the function name
  if p.peek().Lexeme != LPAREN {
    panic("argument list expected for call to " + funcName)
  }
  p.pop() // pop the lparen
  ords, kwds := p.arguments() // gather the arguments
  if p.peek().Lexeme != RPAREN {
    panic("unterminated argument list in call to " + funcName)
  }
  p.pop() // pop the rparen
  var block []*ir.Instruction
  if p.peek().Lexeme == LBRACE {
    block = p.block()
  }

  // Expand keyword args
  if kwds != nil {
    kwdToGensym := make(map[string]string, len(kwds))
    outer := make([]*ir.Instruction, 0)
    for k, v:= range kwds {
      temp := new(ir.Instruction)
      temp.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
      temp.Value = proto.String("var")
      temp.Arguments = make([]*ir.Instruction, 2)
      tempName := p.gensym()
      temp.Arguments[0] = &ir.Instruction {
        Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
        Value: proto.String(tempName),
      }
      temp.Arguments[1] = v
      outer = append(outer, temp)
      kwdToGensym[k] = tempName
    }
    inner := make([]*ir.Instruction, 0)
    for k, _ := range kwds {
      getter := new(ir.Instruction)
      getter.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
      getter.Value = proto.String("var")
      getter.Arguments = make([]*ir.Instruction, 1)
      getter.Arguments[0] = &ir.Instruction {
        Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
        Value: proto.String(kwdToGensym[k]),
      }
      setter := new(ir.Instruction)
      setter.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
      setter.Value = proto.String("set")
      setter.Arguments = make([]*ir.Instruction, 2)
      setter.Arguments[0] = &ir.Instruction {
        Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
        Value: proto.String(k),
      }
      setter.Arguments[1] = getter
      inner = append(inner, setter)
    }

    theCall := new(ir.Instruction)
    theCall.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    theCall.Value = proto.String(funcName)
    theCall.Arguments = ords

    if block == nil {
      block = inner
    } else {
      for _, v := range block {
        inner = append(inner, v)
      }
    }

    theCall.Children = inner
    outer = append(outer, theCall)

    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_BLOCK)
    node.Children = outer

  } else {
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    node.Value = proto.String(funcName)
    node.Arguments = ords
    node.Children = block
  }
  return node
}

func (p *Parser) arguments() (ords []*ir.Instruction, kwds map[string]*ir.Instruction) {
  ords, kwds = make([]*ir.Instruction, 0), make(map[string]*ir.Instruction, 0)
  if p.peek().Lexeme != RPAREN {
    if p.peek().Lexeme == KWD {
      k := p.pop().Value
      kwds[k] = p.expression()
    } else {
      ords = append(ords, p.expression())
    }
  }
  for p.peek().Lexeme != RPAREN {
    if p.peek().Lexeme == COMMA {
      p.pop()
    } else {
      panic("arguments must be separated by commas")
    }
    if p.peek().Lexeme == KWD {
      k := p.pop().Value
      kwds[k] = p.expression()
    } else {
      ords = append(ords, p.expression())
    }
  }
  if len(ords) == 0 {
    ords = nil
  }
  if len(kwds) == 0 {
    kwds = nil
  }
  return ords, kwds
}

func (p *Parser) variable() *ir.Instruction {
  node := new(ir.Instruction)
  switch p.peek().Lexeme {
  case LVAR:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_LOCAL_VAR)
    node.Value = proto.String(p.pop().Value)
    if p.peek().Lexeme == EQUAL {
      p.pop() // pop the equal sign
      node.Arguments = make([]*ir.Instruction, 1)
      node.Arguments[0] = p.expression()
    }
  case GVAR:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    node.Value = proto.String("var")
    name := p.pop().Value
    value := new(ir.Instruction)
    if p.peek().Lexeme == EQUAL {
      p.pop() // pop the equal sign
      value = p.expression()
    } else {
      value = nil
    }
    if value == nil {
      node.Arguments = make([]*ir.Instruction, 1)
    } else {
      node.Arguments = make([]*ir.Instruction, 2)
      node.Arguments[1] = value
    }
    node.Arguments[0] = &ir.Instruction {
      Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
      Value: proto.String(name),
    }
  }
  if p.peek().Lexeme == LBRACE {
    node.Children = p.block()
  }
  return node
}

func (p *Parser) block() []*ir.Instruction {
  stmts := make([]*ir.Instruction, 0)
  p.pop() // pop the lbrace
  for p.peek().Lexeme != RBRACE {
    stmts = append(stmts, p.statement())
  }
  p.pop() // pop the rbrace
  if len(stmts) == 0 {
    stmts = nil
  }
  return stmts
}

func (p *Parser) definition() *ir.Function {
  isSignature := false
  node := new(ir.Function)

  p.pop() // pop the "@func" keyword
  contextType := ""
  if p.peek().Lexeme == TYPE {
    contextType = p.pop().Value
    if p.peek().Lexeme != DOT {
      panic("function context and function name must be separated by '.'")
    }
    p.pop() // pop the dot
  }

  if p.peek().Lexeme != ID {
    panic("invalid function name in definition")
  }
  funcName := p.pop().Value

  if p.peek().Lexeme != LPAREN {
    panic("malformed parameter list in function signature")
  }
  p.pop() // pop the lparen
  params := p.parameters()
  if len(params) == 0 {
    params = nil
  }
  p.pop() // pop the rparen

  returnType := ""
  opensType := ""
  if p.peek().Lexeme == TYPE {
    isSignature = true
    returnType = p.pop().Value
    if p.peek().Lexeme == TYPE {
      opensType = p.pop().Value
    }
  }

  node.Name = proto.String(funcName)
  node.Args = params
  node.ScopeType = proto.String(contextType)
  node.ReturnType = proto.String(returnType)
  node.OpensType = proto.String(opensType)

  if isSignature {
    if p.peek().Lexeme == LBRACE {
      panic("body not permitted in function signature")
    }
    return node
  }

  if p.peek().Lexeme != LBRACE {
    panic("function definition is missing a body")
  }
  funcBody := &ir.Instruction {
    Type: ir.NewInstruction_InstructionType(ir.Instruction_BLOCK),
    Children: p.block(),
  }
  node.Instruction = funcBody
  return node
}

func (p *Parser) parameters() []*ir.Function_Argument {
  params := make([]*ir.Function_Argument, 0)
  // if p.peek().Lexeme != RPAREN {
  //   if p.peek().Lexeme != TYPE {
  //     panic("function parameter is missing a type")
  //   }
  //   param := &ir.Function_Argument {
  //     TypeString: proto.String(p.pop().Value),
  //   }
  //   if p.peek().Lexeme != LVAR {
  //     panic("function parameter has invalid name")
  //   }
  //   param.Name = proto.String(p.pop().Value)
  //   params = append(params, param)
  // }
  counter := 0
  for p.peek().Lexeme != RPAREN {
    if counter > 0 {
      if p.peek().Lexeme != COMMA {
        panic("parameter list must be separated by commas")
      }
      p.pop() // pop the comma
    }
    if p.peek().Lexeme != TYPE {
      panic("function parameter is missing a type")
    }
    param := &ir.Function_Argument {
      TypeString: proto.String(p.pop().Value),
    }
    if p.peek().Lexeme != LVAR {
      panic("function parameter has invalid name")
    }
    param.Name = proto.String(p.pop().Value)
    params = append(params, param)
    counter++
  }
  return params
}

<|endoftext|>


<commit_before>package hel
<commit_msg>remove empty file<commit_after><|endoftext|>


<commit_before>package sirius

import (
	"fmt"
	"github.com/kayex/sirius/slack"
	api "github.com/nlopes/slack"
	"golang.org/x/net/context"
	"strings"
)

type Connection interface {
	Auth() chan slack.UserID
	Messages() chan Message
	Listen(context.Context)
	Send(*Message) error
	Update(*Message) error
}

type RTMConnection struct {
	token    string
	rtm      *api.RTM
	client   *api.Client
	auth     chan slack.UserID
	messages chan Message
}

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

	rtm := client.NewRTM()
	auth := make(chan slack.UserID, 1)
	msg := make(chan Message)

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

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

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

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

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

func (conn *RTMConnection) Send(msg *Message) error {
	oMsg := conn.rtm.NewOutgoingMessage(msg.Text, msg.Channel)
	conn.rtm.SendMessage(oMsg)

	return nil
}

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

func (conn *RTMConnection) authenticate(e *api.ConnectedEvent) {
	info := e.Info
	id := slack.UserID{info.User.ID, info.Team.ID}
	conn.auth <- id
}

func (conn *RTMConnection) handleIncomingEvent(ev api.RTMEvent) {
	switch msg := ev.Data.(type) {
	case *api.ConnectedEvent:
		conn.authenticate(msg)
	case *api.InvalidAuthEvent:
		panic(msg)
	case *api.MessageEvent:
		conn.handleIncomingMessage(msg)

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

func (conn *RTMConnection) handleIncomingMessage(ev *api.MessageEvent) {
	// Drop messages with incomplete data
	if ev.User == "" || ev.Team == "" {
		return
	}

	text := removeEscapeCharacters(ev.Text)
	msg := NewMessage(slack.UserID{ev.User, ev.Team}, text, 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>Remove explicit error outputting<commit_after>package sirius

import (
	"github.com/kayex/sirius/slack"
	api "github.com/nlopes/slack"
	"golang.org/x/net/context"
	"strings"
)

type Connection interface {
	Auth() chan slack.UserID
	Messages() chan Message
	Listen(context.Context)
	Send(*Message) error
	Update(*Message) error
}

type RTMConnection struct {
	token    string
	rtm      *api.RTM
	client   *api.Client
	auth     chan slack.UserID
	messages chan Message
}

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

	rtm := client.NewRTM()
	auth := make(chan slack.UserID, 1)
	msg := make(chan Message)

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

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

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

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

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

func (conn *RTMConnection) Send(msg *Message) error {
	oMsg := conn.rtm.NewOutgoingMessage(msg.Text, msg.Channel)
	conn.rtm.SendMessage(oMsg)

	return nil
}

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

func (conn *RTMConnection) authenticate(e *api.ConnectedEvent) {
	info := e.Info
	id := slack.UserID{info.User.ID, info.Team.ID}
	conn.auth <- id
}

func (conn *RTMConnection) handleIncomingEvent(ev api.RTMEvent) {
	switch msg := ev.Data.(type) {
	case *api.ConnectedEvent:
		conn.authenticate(msg)
	case *api.InvalidAuthEvent:
		panic(msg)
	case *api.MessageEvent:
		conn.handleIncomingMessage(msg)

	case *api.RTMError:
		panic(msg)
	}
}

func (conn *RTMConnection) handleIncomingMessage(ev *api.MessageEvent) {
	// Drop messages with incomplete data
	if ev.User == "" || ev.Team == "" {
		return
	}

	text := removeEscapeCharacters(ev.Text)
	msg := NewMessage(slack.UserID{ev.User, ev.Team}, text, 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>// Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use
// of this source code is governed by the MIT license that can be found in
// the LICENSE file.

package main

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"strconv"
	"strings"

	"github.com/bluele/gcache"
	"github.com/go-chi/chi"
	bogon "github.com/lrstanley/go-bogon"
)

func registerAPI(r chi.Router) {
	r.Get("/api/{addr}", apiLookup)
	r.Get("/api/{addr}/{filters}", apiLookup)
}

func apiLookup(w http.ResponseWriter, r *http.Request) {
	addr := strings.TrimSpace(chi.URLParam(r, "addr"))
	filters := strings.Split(chi.URLParam(r, "filters"), ",")

	// If they're trying to send us way too many filters (which could cause
	// unwanted extra memory usage/be considered a resource usage attack),
	// we shouldn't handle their request.
	if len(filters) > 20 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "error: too many filters supplied")
		return
	}

	if len(filters) == 1 && filters[0] == "" {
		filters = []string{}
	}

	// Allow users to query themselves without having to have them specify
	// their own IP address. Note that this will not work if you are querying
	// the IP address locally.
	if self := strings.ToLower(addr); self == "self" || self == "me" {
		addr, _, _ = net.SplitHostPort(r.RemoteAddr)
	}

	// This would be the index key used for arc cache, if they request custom
	// filters, we should add that to the key, because those filters may
	// mean that the returned lookup has excluded information, which may
	// cause issues if the same query is returned with no requested filters.
	key := addr
	if len(filters) > 0 {
		key = addr + ":" + strings.Join(filters, ",")
	}

	var result *AddrResult

	query, err := arc.GetIFPresent(key)
	if err == nil {
		resultFromARC, _ := query.(AddrResult)
		result = &resultFromARC
		w.Header().Set("X-Cache", "HIT")
		logger.Printf("query %s fetched from arc cache", addr)

		apiResponse(w, r, result, filters)
		return
	}

	w.Header().Set("X-Cache", "MISS")
	if err != gcache.KeyNotFoundError {
		logger.Printf("unable to get %s off arc stack: %s", addr, err)
	}

	ip := net.ParseIP(addr)
	if ip == nil {
		var ips []string
		ips, err = net.LookupHost(addr)
		if err != nil || len(ips) == 0 {
			logger.Printf("error looking up %q as host address: %s", addr, err)

			result = &AddrResult{Error: fmt.Sprintf("invalid ip/host specified: %s", addr)}
			apiResponse(w, r, result, filters)
			return
		}

		ip = net.ParseIP(ips[0])
	}

	if is, _ := bogon.Is(ip.String()); is {
		result = &AddrResult{Error: "internal address"}
		apiResponse(w, r, result, filters)
		return
	}

	result, err = addrLookup(r.Context(), ip, filters)
	if err != nil {
		logger.Printf("error looking up address %q (%q): %s", addr, ip, err)
		w.WriteHeader(http.StatusServiceUnavailable)
		return
	}

	if err = arc.Set(key, *result); err != nil {
		logger.Printf("unable to add %s to arc cache: %s", addr, err)
	}

	apiResponse(w, r, result, filters)
}

func apiResponse(w http.ResponseWriter, r *http.Request, result *AddrResult, filters []string) {
	var err error

	if len(filters) > 0 {
		if result.Error != "" {
			fmt.Fprintf(w, "err: %s", result.Error)
			return
		}

		base := make(map[string]*json.RawMessage)
		var tmp []byte

		tmp, err = json.Marshal(result)
		if err != nil {
			panic(err)
		}

		if err = json.Unmarshal(tmp, &base); err != nil {
			panic(err)
		}

		out := make([]string, len(filters))
		for i := 0; i < len(filters); i++ {
			out[i] = strings.Replace(fmt.Sprintf("%s", *base[filters[i]]), "\"", "", -1)
		}

		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(strings.Join(out, "|")))
		return
	}

	enc := json.NewEncoder(w)

	if ok, _ := strconv.ParseBool(r.FormValue("pretty")); ok {
		enc.SetIndent("", "  ")
	}

	enc.SetEscapeHTML(false) // Otherwise the map url will get unicoded.
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	err = enc.Encode(result)
	if err != nil {
		logger.Printf("error during json encode for %s: %s", r.RemoteAddr, err)
	}
}

func dbDetailsMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		mcache.RLock()
		if mcache.cache == nil {
			mcache.RUnlock()
			next.ServeHTTP(w, r)
			return
		}

		w.Header().Set("X-Maxmind-Build", fmt.Sprintf("%d-%d", mcache.cache.IPVersion, mcache.cache.BuildEpoch))
		w.Header().Set("X-Maxmind-Type", mcache.cache.DatabaseType)
		mcache.RUnlock()

		next.ServeHTTP(w, r)
	})
}
<commit_msg>fix: proper content-type for filtered requests<commit_after>// Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use
// of this source code is governed by the MIT license that can be found in
// the LICENSE file.

package main

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"strconv"
	"strings"

	"github.com/bluele/gcache"
	"github.com/go-chi/chi"
	bogon "github.com/lrstanley/go-bogon"
)

func registerAPI(r chi.Router) {
	r.Get("/api/{addr}", apiLookup)
	r.Get("/api/{addr}/{filters}", apiLookup)
}

func apiLookup(w http.ResponseWriter, r *http.Request) {
	addr := strings.TrimSpace(chi.URLParam(r, "addr"))
	filters := strings.Split(chi.URLParam(r, "filters"), ",")

	// If they're trying to send us way too many filters (which could cause
	// unwanted extra memory usage/be considered a resource usage attack),
	// we shouldn't handle their request.
	if len(filters) > 20 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "error: too many filters supplied")
		return
	}

	if len(filters) == 1 && filters[0] == "" {
		filters = []string{}
	}

	// Allow users to query themselves without having to have them specify
	// their own IP address. Note that this will not work if you are querying
	// the IP address locally.
	if self := strings.ToLower(addr); self == "self" || self == "me" {
		addr, _, _ = net.SplitHostPort(r.RemoteAddr)
	}

	// This would be the index key used for arc cache, if they request custom
	// filters, we should add that to the key, because those filters may
	// mean that the returned lookup has excluded information, which may
	// cause issues if the same query is returned with no requested filters.
	key := addr
	if len(filters) > 0 {
		key = addr + ":" + strings.Join(filters, ",")
	}

	var result *AddrResult

	query, err := arc.GetIFPresent(key)
	if err == nil {
		resultFromARC, _ := query.(AddrResult)
		result = &resultFromARC
		w.Header().Set("X-Cache", "HIT")
		logger.Printf("query %s fetched from arc cache", addr)

		apiResponse(w, r, result, filters)
		return
	}

	w.Header().Set("X-Cache", "MISS")
	if err != gcache.KeyNotFoundError {
		logger.Printf("unable to get %s off arc stack: %s", addr, err)
	}

	ip := net.ParseIP(addr)
	if ip == nil {
		var ips []string
		ips, err = net.LookupHost(addr)
		if err != nil || len(ips) == 0 {
			logger.Printf("error looking up %q as host address: %s", addr, err)

			result = &AddrResult{Error: fmt.Sprintf("invalid ip/host specified: %s", addr)}
			apiResponse(w, r, result, filters)
			return
		}

		ip = net.ParseIP(ips[0])
	}

	if is, _ := bogon.Is(ip.String()); is {
		result = &AddrResult{Error: "internal address"}
		apiResponse(w, r, result, filters)
		return
	}

	result, err = addrLookup(r.Context(), ip, filters)
	if err != nil {
		logger.Printf("error looking up address %q (%q): %s", addr, ip, err)
		w.WriteHeader(http.StatusServiceUnavailable)
		return
	}

	if err = arc.Set(key, *result); err != nil {
		logger.Printf("unable to add %s to arc cache: %s", addr, err)
	}

	apiResponse(w, r, result, filters)
}

func apiResponse(w http.ResponseWriter, r *http.Request, result *AddrResult, filters []string) {
	var err error

	if len(filters) > 0 {
		if result.Error != "" {
			fmt.Fprintf(w, "err: %s", result.Error)
			return
		}

		base := make(map[string]*json.RawMessage)
		var tmp []byte

		tmp, err = json.Marshal(result)
		if err != nil {
			panic(err)
		}

		if err = json.Unmarshal(tmp, &base); err != nil {
			panic(err)
		}

		out := make([]string, len(filters))
		for i := 0; i < len(filters); i++ {
			out[i] = strings.Replace(fmt.Sprintf("%s", *base[filters[i]]), "\"", "", -1)
		}

		w.Header().Set("Content-Type", "text/plain")
		w.Write([]byte(strings.Join(out, "|")))
		return
	}

	enc := json.NewEncoder(w)

	if ok, _ := strconv.ParseBool(r.FormValue("pretty")); ok {
		enc.SetIndent("", "  ")
	}

	enc.SetEscapeHTML(false) // Otherwise the map url will get unicoded.
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	err = enc.Encode(result)
	if err != nil {
		logger.Printf("error during json encode for %s: %s", r.RemoteAddr, err)
	}
}

func dbDetailsMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		mcache.RLock()
		if mcache.cache == nil {
			mcache.RUnlock()
			next.ServeHTTP(w, r)
			return
		}

		w.Header().Set("X-Maxmind-Build", fmt.Sprintf("%d-%d", mcache.cache.IPVersion, mcache.cache.BuildEpoch))
		w.Header().Set("X-Maxmind-Type", mcache.cache.DatabaseType)
		mcache.RUnlock()

		next.ServeHTTP(w, r)
	})
}
<|endoftext|>


<commit_before>// tweetlib - A fully oauth-authenticated Go Twitter library
//
// Copyright 2011 The Tweetlib 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 tweetlib

type HelpGroup struct {
	*Client
}

// Returns the current configuration used by Twitter including twitter.com
// slugs which are not usernames, maximum photo resolutions, and t.co URL
// lengths.
// See https://dev.twitter.com/docs/api/1.1/get/help/configuration
func (hs *HelpGroup) Configuration() (configuration *Configuration, err error) {
	configuration = &Configuration{}
	err = hs.Call("GET", "help/configuration", nil, configuration)
	return
}

// Returns Twitter's Privacy Policy
// Seehttps://dev.twitter.com/docs/api/1.1/get/help/privacy
func (hs *HelpGroup) PrivacyPolicy() (privacyPolicy string, err error) {
	type pp struct {
		Text string `json:"privacy"`
	}
	ret := &pp{}
	err = hs.Call("GET", "help/privacy", nil, ret)
	privacyPolicy = ret.Text
	return
}

// Returns Twitter's terms of service
// See https://dev.twitter.com/docs/api/1.1/get/help/tos
func (hs *HelpGroup) Tos() (string, error) {
	type tos struct {
		Text string `json:"tos"`
	}
	ret := &tos{}
	err := hs.Call("GET", "help/tos", nil, ret)
	return ret.Text, err
}

// Returns current Twitter's rate limits
// See https://dev.twitter.com/docs/api/1.1/get/application/rate_limit_status
func (hs *HelpGroup) Limits() (limits *Limits, err error) {
	limits = &Limits{}
	err = hs.Call("GET", "application/rate_limit_status", nil, limits)
	return
}
<commit_msg>Added comments<commit_after>// tweetlib - A fully oauth-authenticated Go Twitter library
//
// Copyright 2011 The Tweetlib 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 tweetlib

// Groups help functions
type HelpGroup struct {
	*Client
}

// Returns the current configuration used by Twitter including twitter.com
// slugs which are not usernames, maximum photo resolutions, and t.co URL
// lengths.
// See https://dev.twitter.com/docs/api/1.1/get/help/configuration
func (hs *HelpGroup) Configuration() (configuration *Configuration, err error) {
	configuration = &Configuration{}
	err = hs.Call("GET", "help/configuration", nil, configuration)
	return
}

// Returns Twitter's Privacy Policy
// Seehttps://dev.twitter.com/docs/api/1.1/get/help/privacy
func (hs *HelpGroup) PrivacyPolicy() (privacyPolicy string, err error) {
	type pp struct {
		Text string `json:"privacy"`
	}
	ret := &pp{}
	err = hs.Call("GET", "help/privacy", nil, ret)
	privacyPolicy = ret.Text
	return
}

// Returns Twitter's terms of service
// See https://dev.twitter.com/docs/api/1.1/get/help/tos
func (hs *HelpGroup) Tos() (string, error) {
	type tos struct {
		Text string `json:"tos"`
	}
	ret := &tos{}
	err := hs.Call("GET", "help/tos", nil, ret)
	return ret.Text, err
}

// Returns current Twitter's rate limits
// See https://dev.twitter.com/docs/api/1.1/get/application/rate_limit_status
func (hs *HelpGroup) Limits() (limits *Limits, err error) {
	limits = &Limits{}
	err = hs.Call("GET", "application/rate_limit_status", nil, limits)
	return
}
<|endoftext|>


<commit_before>package util

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"os"
	"path"
	"time"
)

type NetConfig struct {
	Net  string
	Addr string
}

func (c *NetConfig) Listen() (net.Listener, error) {
	return net.Listen(c.Net, c.Addr)
}
func (c *NetConfig) MustListen() net.Listener {
	l, err := net.Listen(c.Net, c.Addr)
	Must(err)
	return l
}
func Must(err error) {
	if err != nil {
		panic(err)
	}
}
func mustPath(path string, err error) string {
	if err != nil {
		panic(err)
	}
	return path
}

var SrcPath = mustPath(os.Executable())
var RootPath = path.Join(path.Dir(SrcPath), "../")
var ResouresPath = path.Join(RootPath, "resources")
var AppDataPath = path.Join(RootPath, "appdata")
var ConfigPath = path.Join(RootPath, "config")

func SetConfigPath(paths ...string) {
	ConfigPath = path.Join(paths...)
}
func MustGetWD() string {
	path, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	return path
}

func joinPath(p string, filepath ...string) string {
	return path.Join(p, path.Join(filepath...))
}
func Resource(filepaths ...string) string {
	return joinPath(ResouresPath, filepaths...)
}
func Config(filepaths ...string) string {
	return joinPath(ConfigPath, filepaths...)
}
func AppData(filepaths ...string) string {
	return joinPath(AppDataPath, filepaths...)
}

var QuitChan = make(chan int)

func WaitingQuit() {
	<-QuitChan
}

func MustListenAndServeHTTP(server *http.Server, netconfig NetConfig, app http.Handler) {
	go func() {
		l := netconfig.MustListen()
		defer l.Close()
		fmt.Println("Listening " + l.Addr().String())
		server.Handler = app
		err := server.Serve(l)
		if err != nil && err != http.ErrServerClosed {
			panic(err)
		}
	}()
}
func MustServeHTTP(server *http.Server, l net.Listener, app http.Handler) {
	go func() {
		fmt.Println("Listening " + l.Addr().String())
		server.Handler = app
		err := server.Serve(l)
		if err != nil && err != http.ErrServerClosed {
			panic(err)
		}
	}()

}
func ShutdownHTTP(Server *http.Server) {
	ShutdownHTTPWithContext(Server, context.Background())

}
func ShutdownHTTPWithTimeout(Server *http.Server, Timeout time.Duration) {
	ctx, _ := context.WithTimeout(context.Background(), Timeout)
	ShutdownHTTPWithContext(Server, ctx)

}
func ShutdownHTTPWithContext(Server *http.Server, ctx context.Context) {
	fmt.Println("Quiting...")
	Server.Shutdown(ctx)
	fmt.Println("Quited.")
}
func Quit() {
	defer func() {
		recover()
	}()
	close(QuitChan)
}
<commit_msg>add uitl.Recover<commit_after>package util

import (
	"context"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"path"
	"runtime/debug"
	"time"
)

type NetConfig struct {
	Net  string
	Addr string
}

func (c *NetConfig) Listen() (net.Listener, error) {
	return net.Listen(c.Net, c.Addr)
}
func (c *NetConfig) MustListen() net.Listener {
	l, err := net.Listen(c.Net, c.Addr)
	Must(err)
	return l
}
func Must(err error) {
	if err != nil {
		panic(err)
	}
}
func mustPath(path string, err error) string {
	if err != nil {
		panic(err)
	}
	return path
}

var SrcPath = mustPath(os.Executable())
var RootPath = path.Join(path.Dir(SrcPath), "../")
var ResouresPath = path.Join(RootPath, "resources")
var AppDataPath = path.Join(RootPath, "appdata")
var ConfigPath = path.Join(RootPath, "config")

func SetConfigPath(paths ...string) {
	ConfigPath = path.Join(paths...)
}
func MustGetWD() string {
	path, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	return path
}

func joinPath(p string, filepath ...string) string {
	return path.Join(p, path.Join(filepath...))
}
func Resource(filepaths ...string) string {
	return joinPath(ResouresPath, filepaths...)
}
func Config(filepaths ...string) string {
	return joinPath(ConfigPath, filepaths...)
}
func AppData(filepaths ...string) string {
	return joinPath(AppDataPath, filepaths...)
}

var QuitChan = make(chan int)

func WaitingQuit() {
	<-QuitChan
}

func MustListenAndServeHTTP(server *http.Server, netconfig NetConfig, app http.Handler) {
	go func() {
		l := netconfig.MustListen()
		defer l.Close()
		fmt.Println("Listening " + l.Addr().String())
		server.Handler = app
		err := server.Serve(l)
		if err != nil && err != http.ErrServerClosed {
			panic(err)
		}
	}()
}
func MustServeHTTP(server *http.Server, l net.Listener, app http.Handler) {
	go func() {
		fmt.Println("Listening " + l.Addr().String())
		server.Handler = app
		err := server.Serve(l)
		if err != nil && err != http.ErrServerClosed {
			panic(err)
		}
	}()

}
func ShutdownHTTP(Server *http.Server) {
	ShutdownHTTPWithContext(Server, context.Background())

}
func ShutdownHTTPWithTimeout(Server *http.Server, Timeout time.Duration) {
	ctx, _ := context.WithTimeout(context.Background(), Timeout)
	ShutdownHTTPWithContext(Server, ctx)

}
func ShutdownHTTPWithContext(Server *http.Server, ctx context.Context) {
	fmt.Println("Quiting...")
	Server.Shutdown(ctx)
	fmt.Println("Quited.")
}

func Recover(args ...interface{}) {
	if r := recover(); r != nil {
		err := r.(error)
		log.Println(err)
		if len(args) > 0 {
			for _, v := range args {
				log.Println(v)
			}
		}
		debug.PrintStack()
	}

}

func Quit() {
	defer func() {
		recover()
	}()
	close(QuitChan)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/hex"
	"encoding/pem"
	"net/http"

	"crypto/x509"
	"io/ioutil"

	"crypto/ecdsa"

	"github.com/gbl08ma/disturbancesmlx/resource"
	"github.com/yarf-framework/yarf"
)

// Static defines a simple resource
type Static struct {
	yarf.Resource
	path  string // Directory to serve static files from.
	strip string // Part of path to strip from http file server handler
}

func (r *Static) WithPath(path string, strip string) *Static {
	r.path = path
	r.strip = strip
	return r
}

// Get implements the static files handler
func (r *Static) Get(c *yarf.Context) error {
	http.StripPrefix(r.strip, http.FileServer(http.Dir(r.path))).ServeHTTP(c.Response, c.Request)

	return nil
}

func APIserver(trustedClientCertPath string) {
	y := yarf.New()

	v1 := yarf.RouteGroup("/v1")

	v1.Add("/networks", new(resource.Network).WithNode(rootSqalxNode))
	v1.Add("/networks/:id", new(resource.Network).WithNode(rootSqalxNode))

	v1.Add("/lines", new(resource.Line).WithNode(rootSqalxNode))
	v1.Add("/lines/:id", new(resource.Line).WithNode(rootSqalxNode))

	v1.Add("/stations", new(resource.Station).WithNode(rootSqalxNode))
	v1.Add("/stations/:id", new(resource.Station).WithNode(rootSqalxNode))
	v1.Add("/stations/:sid/lobbies", new(resource.Lobby).WithNode(rootSqalxNode))

	v1.Add("/lobbies", new(resource.Lobby).WithNode(rootSqalxNode))
	v1.Add("/lobbies/:id", new(resource.Lobby).WithNode(rootSqalxNode))

	v1.Add("/connections", new(resource.Connection).WithNode(rootSqalxNode))
	v1.Add("/connections/:from/:to", new(resource.Connection).WithNode(rootSqalxNode))

	v1.Add("/transfers", new(resource.Transfer).WithNode(rootSqalxNode))
	v1.Add("/transfers/:station/:from/:to", new(resource.Transfer).WithNode(rootSqalxNode))

	v1.Add("/disturbances", new(resource.Disturbance).WithNode(rootSqalxNode))
	v1.Add("/disturbances/:id", new(resource.Disturbance).WithNode(rootSqalxNode))

	v1.Add("/datasets", new(resource.Dataset).WithNode(rootSqalxNode).WithSquirrel(&sdb))
	v1.Add("/datasets/:id", new(resource.Dataset).WithNode(rootSqalxNode).WithSquirrel(&sdb))

	v1.Add("/stats", new(resource.Stats).WithNode(rootSqalxNode).WithCalculator(new(MLcalculator)))
	v1.Add("/stats/:id", new(resource.Stats).WithNode(rootSqalxNode).WithCalculator(new(MLcalculator)))

	v1.Add("/announcements", new(resource.Announcement).WithAnnouncementStore(&annStore))
	v1.Add("/announcements/:source", new(resource.Announcement).WithAnnouncementStore(&annStore))

	v1.Add("/stationkb/*", new(Static).WithPath("stationkb/", "/v1/stationkb/"))

	pubkey := getTrustedClientPublicKey(trustedClientCertPath)

	v1.Add("/pair", new(resource.Pair).
		WithNode(rootSqalxNode).
		WithPublicKey(pubkey).
		WithHashKey(getHashKey()))

	v1.Add("/authtest", new(resource.AuthTest).
		WithNode(rootSqalxNode).
		WithHashKey(getHashKey()))

	v1.Add("/trips", new(resource.Trip).WithNode(rootSqalxNode).WithHashKey(getHashKey()))
	v1.Add("/trips/:id", new(resource.Trip).WithNode(rootSqalxNode).WithHashKey(getHashKey()))

	v1.Add("/feedback", new(resource.Feedback).WithNode(rootSqalxNode).WithHashKey(getHashKey()))

	y.AddGroup(v1)

	y.Logger = webLog
	y.Start(":12000")
}

func getTrustedClientPublicKey(trustedClientCertPath string) *ecdsa.PublicKey {
	certBytes, err := ioutil.ReadFile(trustedClientCertPath)
	if err != nil {
		panic("Error reading trusted client certificate")
	}
	block, _ := pem.Decode([]byte(certBytes))
	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		panic("Error parsing client certificate: " + err.Error())
	}

	return cert.PublicKey.(*ecdsa.PublicKey)
}

func getHashKey() []byte {
	hexkey, present := secrets.Get("secretHMACkey")
	if !present {
		mainLog.Fatal("API secret HMAC key not present in keybox")
	}
	key, err := hex.DecodeString(hexkey)
	if err != nil {
		mainLog.Fatal("Invalid API secret HMAC key specified")
	}
	return key
}
<commit_msg>Delay API responses when debugging<commit_after>package main

import (
	"encoding/hex"
	"encoding/pem"
	"math/rand"
	"net/http"
	"time"

	"crypto/x509"
	"io/ioutil"

	"crypto/ecdsa"

	"github.com/gbl08ma/disturbancesmlx/resource"
	"github.com/yarf-framework/yarf"
)

// Static defines a simple resource
type Static struct {
	yarf.Resource
	path  string // Directory to serve static files from.
	strip string // Part of path to strip from http file server handler
}

func (r *Static) WithPath(path string, strip string) *Static {
	r.path = path
	r.strip = strip
	return r
}

// Get implements the static files handler
func (r *Static) Get(c *yarf.Context) error {
	http.StripPrefix(r.strip, http.FileServer(http.Dir(r.path))).ServeHTTP(c.Response, c.Request)

	return nil
}

func APIserver(trustedClientCertPath string) {
	y := yarf.New()

	v1 := yarf.RouteGroup("/v1")

	v1.Add("/networks", new(resource.Network).WithNode(rootSqalxNode))
	v1.Add("/networks/:id", new(resource.Network).WithNode(rootSqalxNode))

	v1.Add("/lines", new(resource.Line).WithNode(rootSqalxNode))
	v1.Add("/lines/:id", new(resource.Line).WithNode(rootSqalxNode))

	v1.Add("/stations", new(resource.Station).WithNode(rootSqalxNode))
	v1.Add("/stations/:id", new(resource.Station).WithNode(rootSqalxNode))
	v1.Add("/stations/:sid/lobbies", new(resource.Lobby).WithNode(rootSqalxNode))

	v1.Add("/lobbies", new(resource.Lobby).WithNode(rootSqalxNode))
	v1.Add("/lobbies/:id", new(resource.Lobby).WithNode(rootSqalxNode))

	v1.Add("/connections", new(resource.Connection).WithNode(rootSqalxNode))
	v1.Add("/connections/:from/:to", new(resource.Connection).WithNode(rootSqalxNode))

	v1.Add("/transfers", new(resource.Transfer).WithNode(rootSqalxNode))
	v1.Add("/transfers/:station/:from/:to", new(resource.Transfer).WithNode(rootSqalxNode))

	v1.Add("/disturbances", new(resource.Disturbance).WithNode(rootSqalxNode))
	v1.Add("/disturbances/:id", new(resource.Disturbance).WithNode(rootSqalxNode))

	v1.Add("/datasets", new(resource.Dataset).WithNode(rootSqalxNode).WithSquirrel(&sdb))
	v1.Add("/datasets/:id", new(resource.Dataset).WithNode(rootSqalxNode).WithSquirrel(&sdb))

	v1.Add("/stats", new(resource.Stats).WithNode(rootSqalxNode).WithCalculator(new(MLcalculator)))
	v1.Add("/stats/:id", new(resource.Stats).WithNode(rootSqalxNode).WithCalculator(new(MLcalculator)))

	v1.Add("/announcements", new(resource.Announcement).WithAnnouncementStore(&annStore))
	v1.Add("/announcements/:source", new(resource.Announcement).WithAnnouncementStore(&annStore))

	v1.Add("/stationkb/*", new(Static).WithPath("stationkb/", "/v1/stationkb/"))

	pubkey := getTrustedClientPublicKey(trustedClientCertPath)

	v1.Add("/pair", new(resource.Pair).
		WithNode(rootSqalxNode).
		WithPublicKey(pubkey).
		WithHashKey(getHashKey()))

	v1.Add("/authtest", new(resource.AuthTest).
		WithNode(rootSqalxNode).
		WithHashKey(getHashKey()))

	v1.Add("/trips", new(resource.Trip).WithNode(rootSqalxNode).WithHashKey(getHashKey()))
	v1.Add("/trips/:id", new(resource.Trip).WithNode(rootSqalxNode).WithHashKey(getHashKey()))

	v1.Add("/feedback", new(resource.Feedback).WithNode(rootSqalxNode).WithHashKey(getHashKey()))

	y.AddGroup(v1)
	if DEBUG {
		y.Insert(new(DelayMiddleware))
	}

	y.Logger = webLog
	y.Start(":12000")
}

type DelayMiddleware struct {
	yarf.Middleware
}

func (m *DelayMiddleware) PreDispatch(c *yarf.Context) error {
	time.Sleep(500*time.Millisecond + time.Duration(rand.Intn(500))*time.Millisecond)
	return nil
}

func getTrustedClientPublicKey(trustedClientCertPath string) *ecdsa.PublicKey {
	certBytes, err := ioutil.ReadFile(trustedClientCertPath)
	if err != nil {
		panic("Error reading trusted client certificate")
	}
	block, _ := pem.Decode([]byte(certBytes))
	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		panic("Error parsing client certificate: " + err.Error())
	}

	return cert.PublicKey.(*ecdsa.PublicKey)
}

func getHashKey() []byte {
	hexkey, present := secrets.Get("secretHMACkey")
	if !present {
		mainLog.Fatal("API secret HMAC key not present in keybox")
	}
	key, err := hex.DecodeString(hexkey)
	if err != nil {
		mainLog.Fatal("Invalid API secret HMAC key specified")
	}
	return key
}
<|endoftext|>


<commit_before>package task

import (
	"fmt"
	"sort"
	"text/tabwriter"
)

// PrintTasksHelp prints help os tasks that have a description
func (e *Executor) PrintTasksHelp() {
	tasks := e.tasksWithDesc()
	if len(tasks) == 0 {
		return
	}
	e.outf("Available tasks for this project:")

	// Format in tab-separated columns with a tab stop of 8.
	w := tabwriter.NewWriter(e.Stdout, 0, 8, 0, '\t', 0)
	for _, task := range tasks {
		fmt.Fprintln(w, fmt.Sprintf("* %s: \t%s", task, e.Tasks[task].Desc))
	}
	w.Flush()
}

func (e *Executor) tasksWithDesc() (tasks []string) {
	for name, task := range e.Tasks {
		if task.Desc != "" {
			tasks = append(tasks, name)
		}
	}
	sort.Strings(tasks)
	return
}
<commit_msg>Revert "Revert "list: print message with there's no task with description""<commit_after>package task

import (
	"fmt"
	"sort"
	"text/tabwriter"
)

// PrintTasksHelp prints help os tasks that have a description
func (e *Executor) PrintTasksHelp() {
	tasks := e.tasksWithDesc()
	if len(tasks) == 0 {
		e.outf("task: No tasks with description available")
		return
	}
	e.outf("task: Available tasks for this project:")

	// Format in tab-separated columns with a tab stop of 8.
	w := tabwriter.NewWriter(e.Stdout, 0, 8, 0, '\t', 0)
	for _, task := range tasks {
		fmt.Fprintln(w, fmt.Sprintf("* %s: \t%s", task, e.Tasks[task].Desc))
	}
	w.Flush()
}

func (e *Executor) tasksWithDesc() (tasks []string) {
	for name, task := range e.Tasks {
		if task.Desc != "" {
			tasks = append(tasks, name)
		}
	}
	sort.Strings(tasks)
	return
}
<|endoftext|>


<commit_before>package telebot

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

func api_getMe(token string) (User, error) {
	request := "https://api.telegram.org/bot" + token + "/getMe"

	resp, err := http.Get(request)
	if err != nil {
		return User{}, err
	}

	defer resp.Body.Close()
	me_json, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return User{}, err
	}

	var bot_info struct {
		Ok          bool
		Result      User
		Description string
	}

	err = json.Unmarshal(me_json, &bot_info)
	if err != nil {
		return User{}, err
	}

	if bot_info.Ok {
		return bot_info.Result, nil
	} else {
		return User{}, AuthError{bot_info.Description}
	}
}

func api_getUpdates(token string, offset int, updates chan<- Update) error {
	command := fmt.Sprintf("getUpdates?offset=%d", offset)
	request := "https://api.telegram.org/bot" + token + "/" + command

	resp, err := http.Get(request)
	if err != nil {
		return err
	}

	defer resp.Body.Close()
	updates_json, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	var updates_recieved struct {
		Ok          bool
		Result      []Update
		Description string
	}

	err = json.Unmarshal(updates_json, &updates_recieved)
	if err != nil {
		return err
	}

	if !updates_recieved.Ok {
		return FetchError{updates_recieved.Description}
	}

	for _, update := range updates_recieved.Result {
		updates <- update
	}

	return nil
}

func api_sendMessage(token string, recipient User, text string) error {
	resource := "https://api.telegram.org/bot" + token + "/sendMessage"

	params := url.Values{}
	params.Set("chat_id", strconv.FormatInt(int64(recipient.Id), 10))
	params.Set("text", text)

	request := resource + "?" + params.Encode()

	resp, err := http.Get(request)
	if err != nil {
		return err
	}

	defer resp.Body.Close()
	response_json, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	var response_recieved struct {
		Ok          bool
		Description string
	}

	err = json.Unmarshal(response_json, &response_recieved)
	if err != nil {
		return err
	}

	if !response_recieved.Ok {
		return SendError{response_recieved.Description}
	}

	return nil
}
<commit_msg>A little bit refactoring on API calls<commit_after>package telebot

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

func performApiCall(method string, token string, params url.Values) ([]byte, error) {
	url := fmt.Sprintf("https://api.telegram.org/bot%s/%s?%s",
		token, method, params.Encode())

	resp, err := http.Get(url)
	if err != nil {
		return []byte{}, err
	}

	defer resp.Body.Close()
	json, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte{}, err
	}

	return json, nil
}

func api_getMe(token string) (User, error) {
	me_json, err := performApiCall("getMe", token, url.Values{})
	if err != nil {
		return User{}, err
	}

	var bot_info struct {
		Ok          bool
		Result      User
		Description string
	}

	err = json.Unmarshal(me_json, &bot_info)
	if err != nil {
		return User{}, err
	}

	if bot_info.Ok {
		return bot_info.Result, nil
	} else {
		return User{}, AuthError{bot_info.Description}
	}
}

func api_getUpdates(token string, offset int, updates chan<- Update) error {
	params := url.Values{}
	params.Set("offset", strconv.FormatInt(int64(offset), 10))
	updates_json, err := performApiCall("getUpdates", token, params)
	if err != nil {
		return err
	}

	var updates_recieved struct {
		Ok          bool
		Result      []Update
		Description string
	}

	err = json.Unmarshal(updates_json, &updates_recieved)
	if err != nil {
		return err
	}

	if !updates_recieved.Ok {
		return FetchError{updates_recieved.Description}
	}

	for _, update := range updates_recieved.Result {
		updates <- update
	}

	return nil
}

func api_sendMessage(token string, recipient User, text string) error {
	params := url.Values{}
	params.Set("chat_id", strconv.FormatInt(int64(recipient.Id), 10))
	params.Set("text", text)
	response_json, err := performApiCall("sendMessage", token, params)
	if err != nil {
		return err
	}

	var response_recieved struct {
		Ok          bool
		Description string
	}

	err = json.Unmarshal(response_json, &response_recieved)
	if err != nil {
		return err
	}

	if !response_recieved.Ok {
		return SendError{response_recieved.Description}
	}

	return nil
}
<|endoftext|>


<commit_before>// +build !windows

package termbox

import "errors"
import "fmt"
import "os"
import "os/signal"
import "strings"
import "syscall"
import "runtime"

// public API

// Initializes termbox library. This function should be called before any other functions.
// After successful initialization, the library must be finalized using 'Close' function.
//
// Example usage:
//      err := termbox.Init()
//      if err != nil {
//              panic(err)
//      }
//      defer termbox.Close()
func Init() error {
	var err error

	out, err = os.OpenFile("/dev/tty", syscall.O_WRONLY, 0)
	if err != nil {
		return err
	}
	in, err = syscall.Open("/dev/tty", syscall.O_RDONLY, 0)
	if err != nil {
		return err
	}

	err = setup_term()
	if err != nil {
		return fmt.Errorf("termbox: error while reading terminfo data: %v", err)
	}

	signal.Notify(sigwinch, syscall.SIGWINCH)
	signal.Notify(sigio, syscall.SIGIO)

	_, err = fcntl(in, syscall.F_SETFL, syscall.O_ASYNC|syscall.O_NONBLOCK)
	if err != nil {
		return err
	}
	_, err = fcntl(in, syscall.F_SETOWN, syscall.Getpid())
	if runtime.GOOS != "darwin" && err != nil {
		return err
	}
	err = tcgetattr(out.Fd(), &orig_tios)
	if err != nil {
		return err
	}

	tios := orig_tios
	tios.Iflag &^= syscall_IGNBRK | syscall_BRKINT | syscall_PARMRK |
		syscall_ISTRIP | syscall_INLCR | syscall_IGNCR |
		syscall_ICRNL | syscall_IXON
	tios.Oflag &^= syscall_OPOST
	tios.Lflag &^= syscall_ECHO | syscall_ECHONL | syscall_ICANON |
		syscall_ISIG | syscall_IEXTEN
	tios.Cflag &^= syscall_CSIZE | syscall_PARENB
	tios.Cflag |= syscall_CS8
	tios.Cc[syscall_VMIN] = 1
	tios.Cc[syscall_VTIME] = 0

	err = tcsetattr(out.Fd(), &tios)
	if err != nil {
		return err
	}

	out.WriteString(funcs[t_enter_ca])
	out.WriteString(funcs[t_enter_keypad])
	out.WriteString(funcs[t_hide_cursor])
	out.WriteString(funcs[t_clear_screen])

	termw, termh = get_term_size(out.Fd())
	back_buffer.init(termw, termh)
	front_buffer.init(termw, termh)
	back_buffer.clear()
	front_buffer.clear()

	go func() {
		buf := make([]byte, 128)
		for {
			select {
			case <-sigio:
				for {
					n, err := syscall.Read(in, buf)
					if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK {
						break
					}
					input_comm <- input_event{buf[:n], err}
					ie := <-input_comm
					buf = ie.data[:128]
				}
			case <-quit:
				return
			}
		}
	}()

	return nil
}

// used to construct palettes from 24-bit RGB values
type RGB struct{ R, G, B byte }

// used to load various color palettes for 256-color terminals
func SetColorPalette(p []RGB) {
	for n, c := range p {
		out.WriteString(fmt.Sprintf("\033]4;%v;rgb:%02x/%02x/%02x\x1b\\", n, c.R, c.G, c.B))
	}
}

// a preconfigured palette corresponding to XTERM's defaults
var Palette256 []RGB

func init() {
	var r, g, b byte

	// initialize the standard 256 color palette
	Palette256 = make([]RGB, 256)

	// this is the default xterm palette for the first 16 colors
	// pay attention to the blues, which are increased in luma
	// to compensate for human insensitivity to cooler colors

	r, g, b = 205, 205, 238
	Palette256[0] = RGB{0, 0, 0}
	Palette256[1] = RGB{r, 0, 0}
	Palette256[2] = RGB{0, g, 0}
	Palette256[3] = RGB{r, g, 0}
	Palette256[4] = RGB{0, 0, b}
	Palette256[5] = RGB{r, 0, b}
	Palette256[6] = RGB{0, g, b}
	Palette256[7] = RGB{r, g, b}

	r, g, b = 255, 255, 255
	Palette256[8] = RGB{127, 127, 127}
	Palette256[9] = RGB{r, 0, 0}
	Palette256[10] = RGB{0, g, 0}
	Palette256[11] = RGB{r, g, 0}
	Palette256[12] = RGB{92, 92, b}
	Palette256[13] = RGB{r, 0, b}
	Palette256[14] = RGB{0, g, b}
	Palette256[15] = RGB{r, g, b}

	// next we establish a 6x6x6 color cube with no blue
	// correction -- also xterm common, to the point that
	// many users think this is hardcoded
	c := 16

	for r = 0; r < 6; r++ {
		rr := r * 40
		if r > 0 {
			rr += 55
		}
		for g = 0; g < 6; g++ {
			gg := g * 40
			if g > 0 {
				gg += 55
			}
			for b = 0; b < 6; b++ {
				bb := b * 40
				if b > 0 {
					bb += 55
				}
				Palette256[c] = RGB{rr, gg, bb}
				c++
			}
		}
	}

	// and, following the user assumptions, this is
	// a 24 color grey ramp
	var v byte = 8
	for g := 0; g < 24; g++ {
		v += 10
		Palette256[c] = RGB{v, v, v}
		c++
	}
}

// instructs termbox to switch to either ColorMode16 or ColorMode256
func SetColorMode(cm ColorMode) error {
	switch cm {
	case ColorMode16:
		color_mode = cm
		return nil
	case ColorMode256:
		// let it fall through, we need to examine $TERM
	default:
		return errors.New("termbox: invalid color mode requested")
	}

	term := os.Getenv("TERM")
	switch {
	case term == "":
		return errors.New("termbox: TERM environment variable not set")
	case strings.Index(term, "256") == -1:
		return errors.New("termbox: TERM does not contain \"256\"")
	}

	// this is the common palette expected by xterm-256 hackers; it is
	// NOT the only possible one, and a SetColorPalette command might
	// be in order..

	color_mode = cm
	SetColorPalette(Palette256)
	return nil
}

// Finalizes termbox library, should be called after successful initialization
// when termbox's functionality isn't required anymore.
func Close() {
	quit <- 1
	out.WriteString(funcs[t_show_cursor])
	out.WriteString(funcs[t_sgr0])
	out.WriteString(funcs[t_clear_screen])
	out.WriteString(funcs[t_exit_ca])
	out.WriteString(funcs[t_exit_keypad])
	tcsetattr(out.Fd(), &orig_tios)

	out.Close()
	syscall.Close(in)

	// reset the state, so that on next Init() it will work again
	termw = 0
	termh = 0
	input_mode = InputEsc
	out = nil
	in = 0
	lastfg = attr_invalid
	lastbg = attr_invalid
	lastx = coord_invalid
	lasty = coord_invalid
	cursor_x = cursor_hidden
	cursor_y = cursor_hidden
	foreground = ColorDefault
	background = ColorDefault
}

// Synchronizes the internal back buffer with the terminal.
func Flush() error {
	// invalidate cursor position
	lastx = coord_invalid
	lasty = coord_invalid

	update_size_maybe()

	for y := 0; y < front_buffer.height; y++ {
		line_offset := y * front_buffer.width
		for x := 0; x < front_buffer.width; x++ {
			cell_offset := line_offset + x
			back := &back_buffer.cells[cell_offset]
			front := &front_buffer.cells[cell_offset]
			if *back == *front {
				continue
			}
			send_attr(back.Fg, back.Bg)
			send_char(x, y, back.Ch)
			*front = *back
		}
	}
	if !is_cursor_hidden(cursor_x, cursor_y) {
		write_cursor(cursor_x, cursor_y)
	}
	return flush()
}

// Sets the position of the cursor. See also HideCursor().
func SetCursor(x, y int) {
	if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) {
		outbuf.WriteString(funcs[t_show_cursor])
	}

	if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) {
		outbuf.WriteString(funcs[t_hide_cursor])
	}

	cursor_x, cursor_y = x, y
	if !is_cursor_hidden(cursor_x, cursor_y) {
		write_cursor(cursor_x, cursor_y)
	}
}

// The shortcut for SetCursor(-1, -1).
func HideCursor() {
	SetCursor(cursor_hidden, cursor_hidden)
}

// Changes cell's parameters in the internal back buffer at the specified
// position.
func SetCell(x, y int, ch rune, fg, bg Attribute) {
	if x < 0 || x >= back_buffer.width {
		return
	}
	if y < 0 || y >= back_buffer.height {
		return
	}

	back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg}
}

// Returns a slice into the termbox's back buffer. You can get its dimensions
// using 'Size' function. The slice remains valid as long as no 'Clear' or
// 'Flush' function calls were made after call to this function.
func CellBuffer() []Cell {
	return back_buffer.cells
}

// Wait for an event and return it. This is a blocking function call.
func PollEvent() Event {
	var event Event

	// try to extract event from input buffer, return on success
	event.Type = EventKey
	if extract_event(&event) {
		return event
	}

	for {
		select {
		case ev := <-input_comm:
			if ev.err != nil {
				return Event{Type: EventError, Err: ev.err}
			}

			inbuf = append(inbuf, ev.data...)
			input_comm <- ev
			if extract_event(&event) {
				return event
			}
		case <-sigwinch:
			event.Type = EventResize
			event.Width, event.Height = get_term_size(out.Fd())
			return event
		}
	}
	panic("unreachable")
}

// Returns the size of the internal back buffer (which is the same as
// terminal's window size in characters).
func Size() (int, int) {
	return termw, termh
}

// Clears the internal back buffer.
func Clear(fg, bg Attribute) error {
	foreground, background = fg, bg
	err := update_size_maybe()
	back_buffer.clear()
	return err
}

// Sets termbox input mode. Termbox has two input modes:
//
// 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC means KeyEsc.
//
// 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC enables ModAlt modifier for the next keyboard event.
//
// If 'mode' is InputCurrent, returns the current input mode. See also Input*
// constants.
func SetInputMode(mode InputMode) InputMode {
	if mode != InputCurrent {
		input_mode = mode
	}
	return input_mode
}
<commit_msg>Reset palette on close<commit_after>// +build !windows

package termbox

import "errors"
import "fmt"
import "os"
import "os/signal"
import "strings"
import "syscall"
import "runtime"

// public API

// Initializes termbox library. This function should be called before any other functions.
// After successful initialization, the library must be finalized using 'Close' function.
//
// Example usage:
//      err := termbox.Init()
//      if err != nil {
//              panic(err)
//      }
//      defer termbox.Close()
func Init() error {
	var err error

	out, err = os.OpenFile("/dev/tty", syscall.O_WRONLY, 0)
	if err != nil {
		return err
	}
	in, err = syscall.Open("/dev/tty", syscall.O_RDONLY, 0)
	if err != nil {
		return err
	}

	err = setup_term()
	if err != nil {
		return fmt.Errorf("termbox: error while reading terminfo data: %v", err)
	}

	signal.Notify(sigwinch, syscall.SIGWINCH)
	signal.Notify(sigio, syscall.SIGIO)

	_, err = fcntl(in, syscall.F_SETFL, syscall.O_ASYNC|syscall.O_NONBLOCK)
	if err != nil {
		return err
	}
	_, err = fcntl(in, syscall.F_SETOWN, syscall.Getpid())
	if runtime.GOOS != "darwin" && err != nil {
		return err
	}
	err = tcgetattr(out.Fd(), &orig_tios)
	if err != nil {
		return err
	}

	tios := orig_tios
	tios.Iflag &^= syscall_IGNBRK | syscall_BRKINT | syscall_PARMRK |
		syscall_ISTRIP | syscall_INLCR | syscall_IGNCR |
		syscall_ICRNL | syscall_IXON
	tios.Oflag &^= syscall_OPOST
	tios.Lflag &^= syscall_ECHO | syscall_ECHONL | syscall_ICANON |
		syscall_ISIG | syscall_IEXTEN
	tios.Cflag &^= syscall_CSIZE | syscall_PARENB
	tios.Cflag |= syscall_CS8
	tios.Cc[syscall_VMIN] = 1
	tios.Cc[syscall_VTIME] = 0

	err = tcsetattr(out.Fd(), &tios)
	if err != nil {
		return err
	}

	out.WriteString(funcs[t_enter_ca])
	out.WriteString(funcs[t_enter_keypad])
	out.WriteString(funcs[t_hide_cursor])
	out.WriteString(funcs[t_clear_screen])

	termw, termh = get_term_size(out.Fd())
	back_buffer.init(termw, termh)
	front_buffer.init(termw, termh)
	back_buffer.clear()
	front_buffer.clear()

	go func() {
		buf := make([]byte, 128)
		for {
			select {
			case <-sigio:
				for {
					n, err := syscall.Read(in, buf)
					if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK {
						break
					}
					input_comm <- input_event{buf[:n], err}
					ie := <-input_comm
					buf = ie.data[:128]
				}
			case <-quit:
				return
			}
		}
	}()

	return nil
}

// used to construct palettes from 24-bit RGB values
type RGB struct{ R, G, B byte }

// used to load various color palettes for 256-color terminals
func SetColorPalette(p []RGB) {
	for n, c := range p {
		out.WriteString(fmt.Sprintf("\033]4;%v;rgb:%02x/%02x/%02x\x1b\\", n, c.R, c.G, c.B))
	}
}

// a preconfigured palette corresponding to XTERM's defaults
var Palette256 []RGB

func init() {
	var r, g, b byte

	// initialize the standard 256 color palette
	Palette256 = make([]RGB, 256)

	// this is the default xterm palette for the first 16 colors
	// pay attention to the blues, which are increased in luma
	// to compensate for human insensitivity to cooler colors

	r, g, b = 205, 205, 238
	Palette256[0] = RGB{0, 0, 0}
	Palette256[1] = RGB{r, 0, 0}
	Palette256[2] = RGB{0, g, 0}
	Palette256[3] = RGB{r, g, 0}
	Palette256[4] = RGB{0, 0, b}
	Palette256[5] = RGB{r, 0, b}
	Palette256[6] = RGB{0, g, b}
	Palette256[7] = RGB{r, g, b}

	r, g, b = 255, 255, 255
	Palette256[8] = RGB{127, 127, 127}
	Palette256[9] = RGB{r, 0, 0}
	Palette256[10] = RGB{0, g, 0}
	Palette256[11] = RGB{r, g, 0}
	Palette256[12] = RGB{92, 92, b}
	Palette256[13] = RGB{r, 0, b}
	Palette256[14] = RGB{0, g, b}
	Palette256[15] = RGB{r, g, b}

	// next we establish a 6x6x6 color cube with no blue
	// correction -- also xterm common, to the point that
	// many users think this is hardcoded
	c := 16

	for r = 0; r < 6; r++ {
		rr := r * 40
		if r > 0 {
			rr += 55
		}
		for g = 0; g < 6; g++ {
			gg := g * 40
			if g > 0 {
				gg += 55
			}
			for b = 0; b < 6; b++ {
				bb := b * 40
				if b > 0 {
					bb += 55
				}
				Palette256[c] = RGB{rr, gg, bb}
				c++
			}
		}
	}

	// and, following the user assumptions, this is
	// a 24 color grey ramp
	var v byte = 8
	for g := 0; g < 24; g++ {
		v += 10
		Palette256[c] = RGB{v, v, v}
		c++
	}
}

// instructs termbox to switch to either ColorMode16 or ColorMode256
func SetColorMode(cm ColorMode) error {
	switch cm {
	case ColorMode16:
		color_mode = cm
		return nil
	case ColorMode256:
		// let it fall through, we need to examine $TERM
	default:
		return errors.New("termbox: invalid color mode requested")
	}

	term := os.Getenv("TERM")
	switch {
	case term == "":
		return errors.New("termbox: TERM environment variable not set")
	case strings.Index(term, "256") == -1:
		return errors.New("termbox: TERM does not contain \"256\"")
	}

	// this is the common palette expected by xterm-256 hackers; it is
	// NOT the only possible one, and a SetColorPalette command might
	// be in order..

	color_mode = cm
	SetColorPalette(Palette256)
	return nil
}

// Finalizes termbox library, should be called after successful initialization
// when termbox's functionality isn't required anymore.
func Close() {
	quit <- 1
	out.WriteString(funcs[t_show_cursor])
	out.WriteString(funcs[t_sgr0])
	out.WriteString(funcs[t_clear_screen])
	out.WriteString(funcs[t_exit_ca])
	out.WriteString(funcs[t_exit_keypad])
	if color_mode == ColorMode256 {
		SetColorPalette(Palette256)
	}
	tcsetattr(out.Fd(), &orig_tios)

	out.Close()
	syscall.Close(in)

	// reset the state, so that on next Init() it will work again
	termw = 0
	termh = 0
	input_mode = InputEsc
	out = nil
	in = 0
	lastfg = attr_invalid
	lastbg = attr_invalid
	lastx = coord_invalid
	lasty = coord_invalid
	cursor_x = cursor_hidden
	cursor_y = cursor_hidden
	foreground = ColorDefault
	background = ColorDefault
}

// Synchronizes the internal back buffer with the terminal.
func Flush() error {
	// invalidate cursor position
	lastx = coord_invalid
	lasty = coord_invalid

	update_size_maybe()

	for y := 0; y < front_buffer.height; y++ {
		line_offset := y * front_buffer.width
		for x := 0; x < front_buffer.width; x++ {
			cell_offset := line_offset + x
			back := &back_buffer.cells[cell_offset]
			front := &front_buffer.cells[cell_offset]
			if *back == *front {
				continue
			}
			send_attr(back.Fg, back.Bg)
			send_char(x, y, back.Ch)
			*front = *back
		}
	}
	if !is_cursor_hidden(cursor_x, cursor_y) {
		write_cursor(cursor_x, cursor_y)
	}
	return flush()
}

// Sets the position of the cursor. See also HideCursor().
func SetCursor(x, y int) {
	if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) {
		outbuf.WriteString(funcs[t_show_cursor])
	}

	if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) {
		outbuf.WriteString(funcs[t_hide_cursor])
	}

	cursor_x, cursor_y = x, y
	if !is_cursor_hidden(cursor_x, cursor_y) {
		write_cursor(cursor_x, cursor_y)
	}
}

// The shortcut for SetCursor(-1, -1).
func HideCursor() {
	SetCursor(cursor_hidden, cursor_hidden)
}

// Changes cell's parameters in the internal back buffer at the specified
// position.
func SetCell(x, y int, ch rune, fg, bg Attribute) {
	if x < 0 || x >= back_buffer.width {
		return
	}
	if y < 0 || y >= back_buffer.height {
		return
	}

	back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg}
}

// Returns a slice into the termbox's back buffer. You can get its dimensions
// using 'Size' function. The slice remains valid as long as no 'Clear' or
// 'Flush' function calls were made after call to this function.
func CellBuffer() []Cell {
	return back_buffer.cells
}

// Wait for an event and return it. This is a blocking function call.
func PollEvent() Event {
	var event Event

	// try to extract event from input buffer, return on success
	event.Type = EventKey
	if extract_event(&event) {
		return event
	}

	for {
		select {
		case ev := <-input_comm:
			if ev.err != nil {
				return Event{Type: EventError, Err: ev.err}
			}

			inbuf = append(inbuf, ev.data...)
			input_comm <- ev
			if extract_event(&event) {
				return event
			}
		case <-sigwinch:
			event.Type = EventResize
			event.Width, event.Height = get_term_size(out.Fd())
			return event
		}
	}
	panic("unreachable")
}

// Returns the size of the internal back buffer (which is the same as
// terminal's window size in characters).
func Size() (int, int) {
	return termw, termh
}

// Clears the internal back buffer.
func Clear(fg, bg Attribute) error {
	foreground, background = fg, bg
	err := update_size_maybe()
	back_buffer.clear()
	return err
}

// Sets termbox input mode. Termbox has two input modes:
//
// 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC means KeyEsc.
//
// 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC enables ModAlt modifier for the next keyboard event.
//
// If 'mode' is InputCurrent, returns the current input mode. See also Input*
// constants.
func SetInputMode(mode InputMode) InputMode {
	if mode != InputCurrent {
		input_mode = mode
	}
	return input_mode
}
<|endoftext|>