<commit_before>package tty

import (
	"errors"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/elves/elvish/edit/ui"
	"github.com/elves/elvish/sys"
	"golang.org/x/sys/windows"
)

// XXX Put here to make edit package build on Windows.
const DefaultSeqTimeout = 10 * time.Millisecond

type reader struct {
	console   windows.Handle
	eventChan chan Event

	stopEvent   windows.Handle
	stopChan    chan struct{}
	stopAckChan chan struct{}
}

// NewReader creates a new Reader instance.
func newReader(file *os.File) Reader {
	console, err := windows.GetStdHandle(windows.STD_INPUT_HANDLE)
	if err != nil {
		panic(fmt.Errorf("GetStdHandle(STD_INPUT_HANDLE): %v", err))
	}
	stopEvent, err := windows.CreateEvent(nil, 0, 0, nil)
	if err != nil {
		panic(fmt.Errorf("CreateEvent: %v", err))
	}
	return &reader{
		console, make(chan Event), stopEvent, nil, nil}
}

func (r *reader) SetRaw(bool) {
	// NOP on Windows.
}

func (r *reader) EventChan() <-chan Event {
	return r.eventChan
}

func (r *reader) Start() {
	r.stopChan = make(chan struct{})
	r.stopAckChan = make(chan struct{})
	go r.run()
}

var errNr0 = errors.New("ReadConsoleInput reads 0 input")

func (r *reader) run() {
	handles := []windows.Handle{r.console, r.stopEvent}
	for {
		triggered, _, err := sys.WaitForMultipleObjects(handles, false, sys.INFINITE)
		if err != nil {
			r.fatal(err)
			return
		}
		if triggered == 1 {
			<-r.stopChan
			close(r.stopAckChan)
			return
		}

		var buf [1]sys.InputRecord
		nr, err := sys.ReadConsoleInput(r.console, buf[:])
		if nr == 0 {
			r.fatal(errNr0)
			return
		}
		if err != nil {
			r.fatal(err)
			return
		}
		event := convertEvent(buf[0].GetEvent())
		if event != nil {
			r.send(event)
		}
	}
}

func (r *reader) nonFatal(err error) {
	r.send(NonfatalErrorEvent{err})
}

func (r *reader) fatal(err error) {
	if !r.send(FatalErrorEvent{err}) {
		<-r.stopChan
		close(r.stopAckChan)
		r.resetStopEvent()
	}
}

func (r *reader) send(event Event) (stopped bool) {
	select {
	case r.eventChan <- event:
		return false
	case <-r.stopChan:
		close(r.stopAckChan)
		r.resetStopEvent()
		return true
	}
}

func (r *reader) resetStopEvent() {
	err := windows.ResetEvent(r.stopEvent)
	if err != nil {
		panic(err)
	}
}

func (r *reader) Stop() {
	err := windows.SetEvent(r.stopEvent)
	if err != nil {
		log.Println("SetEvent:", err)
	}
	close(r.stopChan)
	<-r.stopAckChan
}

func (r *reader) Close() {
	err := windows.CloseHandle(r.stopEvent)
	if err != nil {
		log.Println("Closing stopEvent handle for reader:", err)
	}
	close(r.eventChan)
}

// A subset of virtual key codes listed in
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
var keyCodeToRune = map[uint16]rune{
	0x08: ui.Backspace, 0x09: ui.Tab,
	0x0d: ui.Enter,
	0x1b: '\x1b',
	0x20: ' ',
	0x23: ui.End, 0x24: ui.Home,
	0x25: ui.Left, 0x26: ui.Up, 0x27: ui.Right, 0x28: ui.Down,
	0x2d: ui.Insert, 0x2e: ui.Delete,
	/* 0x30 - 0x39: digits, same with ASCII */
	/* 0x41 - 0x5a: letters, same with ASCII */
	/* 0x60 - 0x6f: numpads; currently ignored */
	0x70: ui.F1, 0x71: ui.F2, 0x72: ui.F3, 0x73: ui.F4, 0x74: ui.F5, 0x75: ui.F6,
	0x76: ui.F7, 0x77: ui.F8, 0x78: ui.F9, 0x79: ui.F10, 0x7a: ui.F11, 0x7b: ui.F12,
	/* 0x7c - 0x87: F13 - F24; currently ignored */
	0xba: ';', 0xbb: '=', 0xbc: ',', 0xbd: '-', 0xbe: '.', 0xbf: '/', 0xc0: '`',
	0xdb: '[', 0xdc: '\\', 0xdd: ']', 0xde: '\'',
}

// A subset of constants listed in
// https://docs.microsoft.com/en-us/windows/console/key-event-record-str
const (
	leftAlt   = 0x02
	leftCtrl  = 0x08
	rightAlt  = 0x01
	rightCtrl = 0x04
	shift     = 0x10
)

// convertEvent converts the native sys.InputEvent type to a suitable Event
// type. It returns nil if the event should be ignored.
func convertEvent(event sys.InputEvent) Event {
	switch event := event.(type) {
	case *sys.KeyEvent:
		if event.BKeyDown == 0 {
			// Ignore keyup events.
			return nil
		}
		r := rune(event.UChar[0]) + rune(event.UChar[1])<<8
		if event.DwControlKeyState == 0 {
			// No modifier
			// TODO: Deal with surrogate pairs
			if 0x20 <= r && r != 0x7f {
				return KeyEvent(ui.Key{Rune: r})
			}
		} else if event.DwControlKeyState == shift {
			// If only the shift is held down, we try and see if this is a
			// non-functional key by looking if the rune generated is a
			// printable ASCII character.
			if 0x20 <= r && r < 0x7f {
				return KeyEvent(ui.Key{Rune: r})
			}
		}
		r = convertRune(event.WVirtualKeyCode)
		if r == 0 {
			return nil
		}
		mod := convertMod(event.DwControlKeyState)
		return KeyEvent(ui.Key{Rune: r, Mod: mod})
	//case *sys.MouseEvent:
	//case *sys.WindowBufferSizeEvent:
	default:
		// Other events are ignored.
		return nil
	}
}

func convertRune(keyCode uint16) rune {
	r, ok := keyCodeToRune[keyCode]
	if ok {
		return r
	}
	if '0' <= keyCode && keyCode <= '9' || 'A' <= keyCode && keyCode <= 'Z' {
		return rune(keyCode)
	}
	return 0
}

func convertMod(state uint32) ui.Mod {
	mod := ui.Mod(0)
	if state&(leftAlt|rightAlt) != 0 {
		mod |= ui.Alt
	}
	if state&(leftCtrl|rightCtrl) != 0 {
		mod |= ui.Ctrl
	}
	if state&shift != 0 {
		mod |= ui.Shift
	}
	return mod
}
<commit_msg>Fix conversion of keycodes on Windows:<commit_after>package tty

import (
	"errors"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/elves/elvish/edit/ui"
	"github.com/elves/elvish/sys"
	"golang.org/x/sys/windows"
)

// XXX Put here to make edit package build on Windows.
const DefaultSeqTimeout = 10 * time.Millisecond

type reader struct {
	console   windows.Handle
	eventChan chan Event

	stopEvent   windows.Handle
	stopChan    chan struct{}
	stopAckChan chan struct{}
}

// NewReader creates a new Reader instance.
func newReader(file *os.File) Reader {
	console, err := windows.GetStdHandle(windows.STD_INPUT_HANDLE)
	if err != nil {
		panic(fmt.Errorf("GetStdHandle(STD_INPUT_HANDLE): %v", err))
	}
	stopEvent, err := windows.CreateEvent(nil, 0, 0, nil)
	if err != nil {
		panic(fmt.Errorf("CreateEvent: %v", err))
	}
	return &reader{
		console, make(chan Event), stopEvent, nil, nil}
}

func (r *reader) SetRaw(bool) {
	// NOP on Windows.
}

func (r *reader) EventChan() <-chan Event {
	return r.eventChan
}

func (r *reader) Start() {
	r.stopChan = make(chan struct{})
	r.stopAckChan = make(chan struct{})
	go r.run()
}

var errNr0 = errors.New("ReadConsoleInput reads 0 input")

func (r *reader) run() {
	handles := []windows.Handle{r.console, r.stopEvent}
	for {
		triggered, _, err := sys.WaitForMultipleObjects(handles, false, sys.INFINITE)
		if err != nil {
			r.fatal(err)
			return
		}
		if triggered == 1 {
			<-r.stopChan
			close(r.stopAckChan)
			return
		}

		var buf [1]sys.InputRecord
		nr, err := sys.ReadConsoleInput(r.console, buf[:])
		if nr == 0 {
			r.fatal(errNr0)
			return
		}
		if err != nil {
			r.fatal(err)
			return
		}
		event := convertEvent(buf[0].GetEvent())
		if event != nil {
			r.send(event)
		}
	}
}

func (r *reader) nonFatal(err error) {
	r.send(NonfatalErrorEvent{err})
}

func (r *reader) fatal(err error) {
	if !r.send(FatalErrorEvent{err}) {
		<-r.stopChan
		close(r.stopAckChan)
		r.resetStopEvent()
	}
}

func (r *reader) send(event Event) (stopped bool) {
	select {
	case r.eventChan <- event:
		return false
	case <-r.stopChan:
		close(r.stopAckChan)
		r.resetStopEvent()
		return true
	}
}

func (r *reader) resetStopEvent() {
	err := windows.ResetEvent(r.stopEvent)
	if err != nil {
		panic(err)
	}
}

func (r *reader) Stop() {
	err := windows.SetEvent(r.stopEvent)
	if err != nil {
		log.Println("SetEvent:", err)
	}
	close(r.stopChan)
	<-r.stopAckChan
}

func (r *reader) Close() {
	err := windows.CloseHandle(r.stopEvent)
	if err != nil {
		log.Println("Closing stopEvent handle for reader:", err)
	}
	close(r.eventChan)
}

// A subset of virtual key codes listed in
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
var keyCodeToRune = map[uint16]rune{
	0x08: ui.Backspace, 0x09: ui.Tab,
	0x0d: ui.Enter,
	0x1b: '\x1b',
	0x20: ' ',
	0x23: ui.End, 0x24: ui.Home,
	0x25: ui.Left, 0x26: ui.Up, 0x27: ui.Right, 0x28: ui.Down,
	0x2d: ui.Insert, 0x2e: ui.Delete,
	/* 0x30 - 0x39: digits, same with ASCII */
	/* 0x41 - 0x5a: letters, same with ASCII */
	/* 0x60 - 0x6f: numpads; currently ignored */
	0x70: ui.F1, 0x71: ui.F2, 0x72: ui.F3, 0x73: ui.F4, 0x74: ui.F5, 0x75: ui.F6,
	0x76: ui.F7, 0x77: ui.F8, 0x78: ui.F9, 0x79: ui.F10, 0x7a: ui.F11, 0x7b: ui.F12,
	/* 0x7c - 0x87: F13 - F24; currently ignored */
	0xba: ';', 0xbb: '=', 0xbc: ',', 0xbd: '-', 0xbe: '.', 0xbf: '/', 0xc0: '`',
	0xdb: '[', 0xdc: '\\', 0xdd: ']', 0xde: '\'',
}

// A subset of constants listed in
// https://docs.microsoft.com/en-us/windows/console/key-event-record-str
const (
	leftAlt   = 0x02
	leftCtrl  = 0x08
	rightAlt  = 0x01
	rightCtrl = 0x04
	shift     = 0x10
)

// convertEvent converts the native sys.InputEvent type to a suitable Event
// type. It returns nil if the event should be ignored.
func convertEvent(event sys.InputEvent) Event {
	switch event := event.(type) {
	case *sys.KeyEvent:
		if event.BKeyDown == 0 {
			// Ignore keyup events.
			return nil
		}
		r := rune(event.UChar[0]) + rune(event.UChar[1])<<8
		mod := event.DwControlKeyState & (leftAlt | leftCtrl | rightAlt | rightCtrl | shift)
		if mod == 0 {
			// No modifier
			// TODO: Deal with surrogate pairs
			if 0x20 <= r && r != 0x7f {
				return KeyEvent(ui.Key{Rune: r})
			}
		} else if mod == shift {
			// If only the shift is held down, we try and see if this is a
			// non-functional key by looking if the rune generated is a
			// printable ASCII character.
			if 0x20 <= r && r < 0x7f {
				return KeyEvent(ui.Key{Rune: r})
			}
		}
		r = convertRune(event.WVirtualKeyCode)
		if r == 0 {
			return nil
		}
		mod := convertMod(event.DwControlKeyState)
		return KeyEvent(ui.Key{Rune: r, Mod: mod})
	//case *sys.MouseEvent:
	//case *sys.WindowBufferSizeEvent:
	default:
		// Other events are ignored.
		return nil
	}
}

func convertRune(keyCode uint16) rune {
	r, ok := keyCodeToRune[keyCode]
	if ok {
		return r
	}
	if '0' <= keyCode && keyCode <= '9' {
		return rune(keyCode)
	}
	if 'A' <= keyCode && keyCode <= 'Z' {
		return rune(keyCode - 'A' + 'a')
	}
	return 0
}

func convertMod(state uint32) ui.Mod {
	mod := ui.Mod(0)
	if state&(leftAlt|rightAlt) != 0 {
		mod |= ui.Alt
	}
	if state&(leftCtrl|rightCtrl) != 0 {
		mod |= ui.Ctrl
	}
	if state&shift != 0 {
		mod |= ui.Shift
	}
	return mod
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"regexp"
	"strings"

	"github.com/cespare/diff"
	"github.com/cespare/goclj/parse"
)

func PrintTree(w io.Writer, t *parse.Tree) (err error) {
	bw := &bufWriter{bufio.NewWriter(w)}
	defer func() {
		if e := recover(); e != nil {
			switch e := e.(type) {
			case bufErr:
				err = e
			case fmtErr:
				err = e
			default:
				panic(e)
			}
		}
	}()
	PrintSequence(bw, t.Roots, 0, false)
	return bw.bw.Flush()
}

func PrintNode(w *bufWriter, node parse.Node, indent int) {
	switch node := node.(type) {
	case *parse.BoolNode:
		if node.Val {
			w.WriteString("true")
		} else {
			w.WriteString("false")
		}
	case *parse.CharacterNode:
		w.WriteString(node.Text)
	case *parse.CommentNode:
		w.WriteString(node.Text)
	case *parse.DerefNode:
		w.WriteByte('@')
		PrintNode(w, node.Node, indent+1)
	case *parse.FnLiteralNode:
		w.WriteString("#(")
		PrintSequence(w, node.Nodes, indent+2, true)
		w.WriteString(")")
	case *parse.IgnoreFormNode:
		w.WriteString("#_")
		PrintNode(w, node.Node, indent+2)
	case *parse.KeywordNode:
		w.WriteString(node.Val)
	case *parse.ListNode:
		w.WriteString("(")
		PrintSequence(w, node.Nodes, indent+1, true)
		w.WriteString(")")
	case *parse.MapNode:
		w.WriteString("{")
		PrintSequence(w, node.Nodes, indent+1, false)
		w.WriteString("}")
	case *parse.MetadataNode:
		w.WriteByte('^')
		PrintNode(w, node.Node, indent+1)
	case *parse.NewlineNode:
		panic("should not happen")
	case *parse.NilNode:
		w.WriteString("nil")
	case *parse.NumberNode:
		w.WriteString(node.Val)
	case *parse.QuoteNode:
		w.WriteByte('\'')
		PrintNode(w, node.Node, indent+1)
	case *parse.RegexNode:
		w.WriteString(`#"` + node.Val + `"`)
	case *parse.SetNode:
		w.WriteString("#{")
		PrintSequence(w, node.Nodes, indent+2, false)
		w.WriteString("}")
	case *parse.StringNode:
		w.WriteString(`"` + node.Val + `"`)
	case *parse.SymbolNode:
		w.WriteString(node.Val)
	case *parse.SyntaxQuoteNode:
		w.WriteByte('`')
		PrintNode(w, node.Node, indent+1)
	case *parse.TagNode:
		w.WriteString("#" + node.Val)
	case *parse.UnquoteNode:
		w.WriteByte('~')
		PrintNode(w, node.Node, indent+1)
	case *parse.UnquoteSpliceNode:
		w.WriteString("~@")
		PrintNode(w, node.Node, indent+2)
	case *parse.VarQuoteNode:
		w.WriteString("#'" + node.Val)
	case *parse.VectorNode:
		w.WriteString("[")
		PrintSequence(w, node.Nodes, indent+1, false)
		w.WriteString("]")
	default:
		FmtErrf("%s: unhandled node type %T", node.Position(), node)
	}
}

func PrintSequence(w *bufWriter, nodes []parse.Node, indent int, listIndent bool) {
	newline := false
	subIndent := indent
	for i, n := range nodes {
		if _, ok := n.(*parse.NewlineNode); ok {
			w.WriteByte('\n')
			if listIndent && i == 1 {
				indent++
			}
			subIndent = indent
			newline = true
			continue
		}
		if listIndent && i == 1 {
			indent += ListIndentWidth(nodes[0])
		}
		if newline {
			w.WriteString(strings.Repeat(indentChar, indent))
			newline = false
		} else if i > 0 {
			w.WriteByte(' ')
		}
		PrintNode(w, n, subIndent)
		subIndent += IndentWidth(n)
	}
}

// IndentWidth is the width of a form for the purposes of indenting the next line.
// For 'simple' forms (symbols, keywords, ...) the width includes one extra
// at the end for the following space.
func IndentWidth(node parse.Node) int {
	switch node := node.(type) {
	case *parse.BoolNode:
		if node.Val {
			return 5
		}
		return 6
	case *parse.CharacterNode:
		return 2 // Not going to worry about multiwidth chars
	case *parse.CommentNode:
		return 0
	case *parse.DerefNode:
		return 1 + IndentWidth(node.Node)
	case *parse.KeywordNode:
		return len(node.Val) + 1
	case *parse.ListNode:
		return 2
	case *parse.MapNode:
		return 2
	case *parse.MetadataNode:
		return 1 + IndentWidth(node.Node)
	case *parse.NewlineNode:
		return 0
	case *parse.NilNode:
		return 4
	case *parse.NumberNode:
		return len(node.Val) + 1
	case *parse.SymbolNode:
		return len(node.Val) + 1
	case *parse.QuoteNode:
		return 1 + IndentWidth(node.Node)
	case *parse.StringNode:
		return 3 + len(node.Val)
	case *parse.SyntaxQuoteNode:
		return 1 + IndentWidth(node.Node)
	case *parse.UnquoteNode:
		return 1 + IndentWidth(node.Node)
	case *parse.UnquoteSpliceNode:
		return 1 + IndentWidth(node.Node)
	case *parse.VectorNode:
		return 2
	case *parse.FnLiteralNode:
		return 2
	case *parse.IgnoreFormNode:
		return 2 + IndentWidth(node.Node)
	case *parse.RegexNode:
		return 4 + len(node.Val)
	case *parse.SetNode:
		return 2
	case *parse.VarQuoteNode:
		return 1 + len(node.Val)
	case *parse.TagNode:
		return 1 + len(node.Val)
	}
	panic("unreached")
}

var indentSpecial = regexp.MustCompile(
	`^(def.*|let.*|send.*|when.*|with.*)$`,
)

func ListIndentWidth(node parse.Node) int {
	if node, ok := node.(*parse.SymbolNode); ok {
		switch node.Val {
		case "catch", "doto", "if", "loop", "ns":
			return 1
		}
		if indentSpecial.MatchString(node.Val) {
			return 1
		}
	}
	return IndentWidth(node)
}

type bufWriter struct {
	bw *bufio.Writer
}

type bufErr struct{ error }

func (bw *bufWriter) Write(b []byte) (int, error) {
	n, err := bw.bw.Write(b)
	if err != nil {
		panic(bufErr{err})
	}
	return n, nil
}

func (bw *bufWriter) WriteString(s string) int {
	bw.Write([]byte(s))
	return len(s)
}
func (bw *bufWriter) WriteByte(b byte) int {
	bw.Write([]byte{b})
	return 1
}

type fmtErr string

func (e fmtErr) Error() string { return string(e) }

func FmtErrf(format string, args ...interface{}) {
	panic(fmtErr(fmt.Sprintf(format, args...)))
}

var (
	indentChar string
)

func main() {
	flag.StringVar(&indentChar, "indent-char", " ", "character to use for indenting")
	listDifferent := flag.Bool("l", false, "print files whose formatting differs from cljfmt's")
	writeFile := flag.Bool("w", false, "write result to (source) file instead of stdout")
	flag.Parse()
	if len(indentChar) != 1 {
		fatalf("-indent-char arg must have length 1")
	}
	if flag.NArg() < 1 {
		usage()
	}
	if *listDifferent || *writeFile {
		for _, filename := range flag.Args() {
			if err := writeFormatted(filename, *listDifferent, *writeFile); err != nil {
				fatal(err)
			}
		}
		return
	}

	if flag.NArg() > 1 {
		fatalf("must provide a single file unless -l or -w are given")
	}

	t, err := parse.File(flag.Arg(0), true)
	if err != nil {
		fatal(err)
	}
	if err := PrintTree(os.Stdout, t); err != nil {
		fatal(err)
	}
}

func writeFormatted(filename string, listDifferent, writeFile bool) error {
	tw, err := ioutil.TempFile("", "cljfmt-")
	if err != nil {
		return err
	}
	defer os.Remove(tw.Name())
	defer tw.Close()

	t, err := parse.File(filename, true)
	if err != nil {
		return err
	}
	if err := PrintTree(tw, t); err != nil {
		return err
	}
	tw.Close()
	identical, err := diff.Files(filename, tw.Name())
	if err != nil {
		return err
	}
	if identical {
		return nil
	}
	if listDifferent {
		fmt.Println(filename)
	}
	if writeFile {
		if err := os.Rename(tw.Name(), filename); err != nil {
			return err
		}
	}
	return nil
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: %s [flags] [path ...]\n", os.Args[0])
	os.Exit(1)
}

func fatal(args ...interface{}) {
	fmt.Fprintln(os.Stderr, args...)
	os.Exit(1)
}

func fatalf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
	os.Exit(1)
}
<commit_msg>[cljfmt] Pull out a Printer type<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"regexp"
	"strings"

	"github.com/cespare/diff"
	"github.com/cespare/goclj/parse"
)

type Printer struct {
	*bufWriter
	indentChar byte
}

func NewPrinter(w io.Writer, indentChar byte) *Printer {
	return &Printer{
		bufWriter:  &bufWriter{bufio.NewWriter(w)},
		indentChar: indentChar,
	}
}

func (p *Printer) PrintTree(t *parse.Tree) (err error) {
	defer func() {
		if e := recover(); e != nil {
			switch e := e.(type) {
			case bufErr:
				err = e
			case fmtErr:
				err = e
			default:
				panic(e)
			}
		}
	}()
	p.PrintSequence(t.Roots, 0, false)
	return p.bw.Flush()
}

func (p *Printer) PrintNode(node parse.Node, indent int) {
	switch node := node.(type) {
	case *parse.BoolNode:
		if node.Val {
			p.WriteString("true")
		} else {
			p.WriteString("false")
		}
	case *parse.CharacterNode:
		p.WriteString(node.Text)
	case *parse.CommentNode:
		p.WriteString(node.Text)
	case *parse.DerefNode:
		p.WriteByte('@')
		p.PrintNode(node.Node, indent+1)
	case *parse.FnLiteralNode:
		p.WriteString("#(")
		p.PrintSequence(node.Nodes, indent+2, true)
		p.WriteString(")")
	case *parse.IgnoreFormNode:
		p.WriteString("#_")
		p.PrintNode(node.Node, indent+2)
	case *parse.KeywordNode:
		p.WriteString(node.Val)
	case *parse.ListNode:
		p.WriteString("(")
		p.PrintSequence(node.Nodes, indent+1, true)
		p.WriteString(")")
	case *parse.MapNode:
		p.WriteString("{")
		p.PrintSequence(node.Nodes, indent+1, false)
		p.WriteString("}")
	case *parse.MetadataNode:
		p.WriteByte('^')
		p.PrintNode(node.Node, indent+1)
	case *parse.NewlineNode:
		panic("should not happen")
	case *parse.NilNode:
		p.WriteString("nil")
	case *parse.NumberNode:
		p.WriteString(node.Val)
	case *parse.QuoteNode:
		p.WriteByte('\'')
		p.PrintNode(node.Node, indent+1)
	case *parse.RegexNode:
		p.WriteString(`#"` + node.Val + `"`)
	case *parse.SetNode:
		p.WriteString("#{")
		p.PrintSequence(node.Nodes, indent+2, false)
		p.WriteString("}")
	case *parse.StringNode:
		p.WriteString(`"` + node.Val + `"`)
	case *parse.SymbolNode:
		p.WriteString(node.Val)
	case *parse.SyntaxQuoteNode:
		p.WriteByte('`')
		p.PrintNode(node.Node, indent+1)
	case *parse.TagNode:
		p.WriteString("#" + node.Val)
	case *parse.UnquoteNode:
		p.WriteByte('~')
		p.PrintNode(node.Node, indent+1)
	case *parse.UnquoteSpliceNode:
		p.WriteString("~@")
		p.PrintNode(node.Node, indent+2)
	case *parse.VarQuoteNode:
		p.WriteString("#'" + node.Val)
	case *parse.VectorNode:
		p.WriteString("[")
		p.PrintSequence(node.Nodes, indent+1, false)
		p.WriteString("]")
	default:
		FmtErrf("%s: unhandled node type %T", node.Position(), node)
	}
}

func (p *Printer) PrintSequence(nodes []parse.Node, indent int, listIndent bool) {
	newline := false
	subIndent := indent
	for i, n := range nodes {
		if _, ok := n.(*parse.NewlineNode); ok {
			p.WriteByte('\n')
			if listIndent && i == 1 {
				indent++
			}
			subIndent = indent
			newline = true
			continue
		}
		if listIndent && i == 1 {
			indent += ListIndentWidth(nodes[0])
		}
		if newline {
			p.WriteString(strings.Repeat(string(p.indentChar), indent))
			newline = false
		} else if i > 0 {
			p.WriteByte(' ')
		}
		p.PrintNode(n, subIndent)
		subIndent += IndentWidth(n)
	}
}

// IndentWidth is the width of a form for the purposes of indenting the next line.
// For 'simple' forms (symbols, keywords, ...) the width includes one extra
// at the end for the following space.
func IndentWidth(node parse.Node) int {
	switch node := node.(type) {
	case *parse.BoolNode:
		if node.Val {
			return 5
		}
		return 6
	case *parse.CharacterNode:
		return 2 // Not going to worry about multiwidth chars
	case *parse.CommentNode:
		return 0
	case *parse.DerefNode:
		return 1 + IndentWidth(node.Node)
	case *parse.KeywordNode:
		return len(node.Val) + 1
	case *parse.ListNode:
		return 2
	case *parse.MapNode:
		return 2
	case *parse.MetadataNode:
		return 1 + IndentWidth(node.Node)
	case *parse.NewlineNode:
		return 0
	case *parse.NilNode:
		return 4
	case *parse.NumberNode:
		return len(node.Val) + 1
	case *parse.SymbolNode:
		return len(node.Val) + 1
	case *parse.QuoteNode:
		return 1 + IndentWidth(node.Node)
	case *parse.StringNode:
		return 3 + len(node.Val)
	case *parse.SyntaxQuoteNode:
		return 1 + IndentWidth(node.Node)
	case *parse.UnquoteNode:
		return 1 + IndentWidth(node.Node)
	case *parse.UnquoteSpliceNode:
		return 1 + IndentWidth(node.Node)
	case *parse.VectorNode:
		return 2
	case *parse.FnLiteralNode:
		return 2
	case *parse.IgnoreFormNode:
		return 2 + IndentWidth(node.Node)
	case *parse.RegexNode:
		return 4 + len(node.Val)
	case *parse.SetNode:
		return 2
	case *parse.VarQuoteNode:
		return 1 + len(node.Val)
	case *parse.TagNode:
		return 1 + len(node.Val)
	}
	panic("unreached")
}

var indentSpecial = regexp.MustCompile(
	`^(def.*|let.*|send.*|when.*|with.*)$`,
)

func ListIndentWidth(node parse.Node) int {
	if node, ok := node.(*parse.SymbolNode); ok {
		switch node.Val {
		case "catch", "doto", "if", "loop", "ns":
			return 1
		}
		if indentSpecial.MatchString(node.Val) {
			return 1
		}
	}
	return IndentWidth(node)
}

type bufWriter struct {
	bw *bufio.Writer
}

type bufErr struct{ error }

func (bw *bufWriter) Write(b []byte) (int, error) {
	n, err := bw.bw.Write(b)
	if err != nil {
		panic(bufErr{err})
	}
	return n, nil
}

func (bw *bufWriter) WriteString(s string) int {
	bw.Write([]byte(s))
	return len(s)
}
func (bw *bufWriter) WriteByte(b byte) int {
	bw.Write([]byte{b})
	return 1
}

type fmtErr string

func (e fmtErr) Error() string { return string(e) }

func FmtErrf(format string, args ...interface{}) {
	panic(fmtErr(fmt.Sprintf(format, args...)))
}

func main() {
	var (
		indentCharFlag = flag.String("indentchar", " ", "character to use for indenting")
		listDifferent  = flag.Bool("l", false, "print files whose formatting differs from cljfmt's")
		writeFile      = flag.Bool("w", false, "write result to (source) file instead of stdout")
	)
	flag.Parse()
	if len(*indentCharFlag) != 1 {
		fatalf("-indentchar arg must have length 1")
	}
	indentChar := (*indentCharFlag)[0]
	if flag.NArg() < 1 {
		usage()
	}
	if *listDifferent || *writeFile {
		for _, filename := range flag.Args() {
			if err := writeFormatted(filename, indentChar, *listDifferent, *writeFile); err != nil {
				fatal(err)
			}
		}
		return
	}

	if flag.NArg() > 1 {
		fatalf("must provide a single file unless -l or -w are given")
	}

	t, err := parse.File(flag.Arg(0), true)
	if err != nil {
		fatal(err)
	}
	if err := NewPrinter(os.Stdout, indentChar).PrintTree(t); err != nil {
		fatal(err)
	}
}

func writeFormatted(filename string, indentChar byte, listDifferent, writeFile bool) error {
	tw, err := ioutil.TempFile("", "cljfmt-")
	if err != nil {
		return err
	}
	defer os.Remove(tw.Name())
	defer tw.Close()

	t, err := parse.File(filename, true)
	if err != nil {
		return err
	}
	if err := NewPrinter(tw, indentChar).PrintTree(t); err != nil {
		return err
	}
	tw.Close()
	identical, err := diff.Files(filename, tw.Name())
	if err != nil {
		return err
	}
	if identical {
		return nil
	}
	if listDifferent {
		fmt.Println(filename)
	}
	if writeFile {
		if err := os.Rename(tw.Name(), filename); err != nil {
			return err
		}
	}
	return nil
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: %s [flags] [path ...]\n", os.Args[0])
	os.Exit(1)
}

func fatal(args ...interface{}) {
	fmt.Fprintln(os.Stderr, args...)
	os.Exit(1)
}

func fatalf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
	os.Exit(1)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 Google Inc.

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.
*/

// vtcombo: a single binary that contains:
// - a ZK topology server based on an in-memory map.
// - one vtgate instance.
// - many vttablet instances.
// - a vtctld instance so it's easy to see the topology.
package main

import (
	"flag"
	"os"
	"strings"
	"time"

	log "github.com/golang/glog"
	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"

	"github.com/youtube/vitess/go/exit"
	"github.com/youtube/vitess/go/vt/dbconfigs"
	"github.com/youtube/vitess/go/vt/discovery"
	"github.com/youtube/vitess/go/vt/mysqlctl"
	"github.com/youtube/vitess/go/vt/servenv"
	"github.com/youtube/vitess/go/vt/topo"
	"github.com/youtube/vitess/go/vt/topo/memorytopo"
	"github.com/youtube/vitess/go/vt/vtctld"
	"github.com/youtube/vitess/go/vt/vtgate"
	"github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"

	topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
	vttestpb "github.com/youtube/vitess/go/vt/proto/vttest"
)

var (
	protoTopo = flag.String("proto_topo", "", "vttest proto definition of the topology, encoded in compact text format. See vttest.proto for more information.")

	schemaDir = flag.String("schema_dir", "", "Schema base directory. Should contain one directory per keyspace, with a vschema.json file if necessary.")

	ts topo.Server
)

func init() {
	servenv.RegisterDefaultFlags()
}

func main() {
	defer exit.Recover()

	// flag parsing
	dbconfigFlags := dbconfigs.AppConfig | dbconfigs.AllPrivsConfig | dbconfigs.DbaConfig |
		dbconfigs.FilteredConfig | dbconfigs.ReplConfig
	dbconfigs.RegisterFlags(dbconfigFlags)
	mysqlctl.RegisterFlags()
	flag.Parse()

	if *servenv.Version {
		servenv.AppVersion.Print()
		os.Exit(0)
	}

	if len(flag.Args()) > 0 {
		flag.Usage()
		log.Errorf("vtcombo doesn't take any positional arguments")
		exit.Return(1)
	}

	// parse the input topology
	tpb := &vttestpb.VTTestTopology{}
	if err := proto.UnmarshalText(*protoTopo, tpb); err != nil {
		log.Errorf("cannot parse topology: %v", err)
		exit.Return(1)
	}

	// default cell to "test" if unspecified
	if len(tpb.Cells) == 0 {
		tpb.Cells = append(tpb.Cells, "test")
	}

	// set discoverygateway flag to default value
	flag.Set("cells_to_watch", strings.Join(tpb.Cells, ","))

	// vtctld UI requires the cell flag
	flag.Set("cell", tpb.Cells[0])
	flag.Set("enable_realtime_stats", "true")
	flag.Set("log_dir", "$VTDATAROOT/tmp")

	// Create topo server. We use a 'memorytopo' implementation.
	ts = memorytopo.NewServer(tpb.Cells...)
	servenv.Init()
	tabletenv.Init()

	// database configs
	mycnf, err := mysqlctl.NewMycnfFromFlags(0)
	if err != nil {
		log.Errorf("mycnf read failed: %v", err)
		exit.Return(1)
	}
	dbcfgs, err := dbconfigs.Init(mycnf.SocketFile, dbconfigFlags)
	if err != nil {
		log.Warning(err)
	}
	mysqld := mysqlctl.NewMysqld(mycnf, dbcfgs, dbconfigFlags)
	servenv.OnClose(mysqld.Close)

	// tablets configuration and init
	if err := initTabletMap(ts, tpb, mysqld, *dbcfgs, *schemaDir, mycnf); err != nil {
		log.Errorf("initTabletMapProto failed: %v", err)
		exit.Return(1)
	}

	// vtgate configuration and init
	resilientSrvTopoServer := vtgate.NewResilientSrvTopoServer(ts, "ResilientSrvTopoServer")
	healthCheck := discovery.NewHealthCheck(30*time.Second /*connTimeoutTotal*/, 1*time.Millisecond /*retryDelay*/, 1*time.Hour /*healthCheckTimeout*/)
	tabletTypesToWait := []topodatapb.TabletType{
		topodatapb.TabletType_MASTER,
		topodatapb.TabletType_REPLICA,
		topodatapb.TabletType_RDONLY,
	}
	vtgate.Init(context.Background(), healthCheck, ts, resilientSrvTopoServer, tpb.Cells[0], 2 /*retryCount*/, tabletTypesToWait)

	// vtctld configuration and init
	vtctld.InitVtctld(ts)
	vtctld.HandleExplorer("memorytopo", vtctld.NewBackendExplorer(ts.Impl))

	servenv.OnTerm(func() {
		// FIXME(alainjobart): stop vtgate
	})
	servenv.OnClose(func() {
		// We will still use the topo server during lameduck period
		// to update our state, so closing it in OnClose()
		ts.Close()
	})
	servenv.RunDefault()
}
<commit_msg>Dont rewrite log_dir when pasing it.<commit_after>/*
Copyright 2017 Google Inc.

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.
*/

// vtcombo: a single binary that contains:
// - a ZK topology server based on an in-memory map.
// - one vtgate instance.
// - many vttablet instances.
// - a vtctld instance so it's easy to see the topology.
package main

import (
	"flag"
	"os"
	"strings"
	"time"

	log "github.com/golang/glog"
	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"

	"github.com/youtube/vitess/go/exit"
	"github.com/youtube/vitess/go/vt/dbconfigs"
	"github.com/youtube/vitess/go/vt/discovery"
	"github.com/youtube/vitess/go/vt/mysqlctl"
	"github.com/youtube/vitess/go/vt/servenv"
	"github.com/youtube/vitess/go/vt/topo"
	"github.com/youtube/vitess/go/vt/topo/memorytopo"
	"github.com/youtube/vitess/go/vt/vtctld"
	"github.com/youtube/vitess/go/vt/vtgate"
	"github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"

	topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
	vttestpb "github.com/youtube/vitess/go/vt/proto/vttest"
)

var (
	protoTopo = flag.String("proto_topo", "", "vttest proto definition of the topology, encoded in compact text format. See vttest.proto for more information.")

	schemaDir = flag.String("schema_dir", "", "Schema base directory. Should contain one directory per keyspace, with a vschema.json file if necessary.")

	ts topo.Server
)

func init() {
	servenv.RegisterDefaultFlags()
}

func main() {
	defer exit.Recover()

	// flag parsing
	dbconfigFlags := dbconfigs.AppConfig | dbconfigs.AllPrivsConfig | dbconfigs.DbaConfig |
		dbconfigs.FilteredConfig | dbconfigs.ReplConfig
	dbconfigs.RegisterFlags(dbconfigFlags)
	mysqlctl.RegisterFlags()
	flag.Parse()

	if *servenv.Version {
		servenv.AppVersion.Print()
		os.Exit(0)
	}

	if len(flag.Args()) > 0 {
		flag.Usage()
		log.Errorf("vtcombo doesn't take any positional arguments")
		exit.Return(1)
	}

	// parse the input topology
	tpb := &vttestpb.VTTestTopology{}
	if err := proto.UnmarshalText(*protoTopo, tpb); err != nil {
		log.Errorf("cannot parse topology: %v", err)
		exit.Return(1)
	}

	// default cell to "test" if unspecified
	if len(tpb.Cells) == 0 {
		tpb.Cells = append(tpb.Cells, "test")
	}

	// set discoverygateway flag to default value
	flag.Set("cells_to_watch", strings.Join(tpb.Cells, ","))

	// vtctld UI requires the cell flag
	flag.Set("cell", tpb.Cells[0])
	flag.Set("enable_realtime_stats", "true")
	if flag.Lookup("log_dir") == nil {
		flag.Set("log_dir", "$VTDATAROOT/tmp")
	}

	// Create topo server. We use a 'memorytopo' implementation.
	ts = memorytopo.NewServer(tpb.Cells...)
	servenv.Init()
	tabletenv.Init()

	// database configs
	mycnf, err := mysqlctl.NewMycnfFromFlags(0)
	if err != nil {
		log.Errorf("mycnf read failed: %v", err)
		exit.Return(1)
	}
	dbcfgs, err := dbconfigs.Init(mycnf.SocketFile, dbconfigFlags)
	if err != nil {
		log.Warning(err)
	}
	mysqld := mysqlctl.NewMysqld(mycnf, dbcfgs, dbconfigFlags)
	servenv.OnClose(mysqld.Close)

	// tablets configuration and init
	if err := initTabletMap(ts, tpb, mysqld, *dbcfgs, *schemaDir, mycnf); err != nil {
		log.Errorf("initTabletMapProto failed: %v", err)
		exit.Return(1)
	}

	// vtgate configuration and init
	resilientSrvTopoServer := vtgate.NewResilientSrvTopoServer(ts, "ResilientSrvTopoServer")
	healthCheck := discovery.NewHealthCheck(30*time.Second /*connTimeoutTotal*/, 1*time.Millisecond /*retryDelay*/, 1*time.Hour /*healthCheckTimeout*/)
	tabletTypesToWait := []topodatapb.TabletType{
		topodatapb.TabletType_MASTER,
		topodatapb.TabletType_REPLICA,
		topodatapb.TabletType_RDONLY,
	}
	vtgate.Init(context.Background(), healthCheck, ts, resilientSrvTopoServer, tpb.Cells[0], 2 /*retryCount*/, tabletTypesToWait)

	// vtctld configuration and init
	vtctld.InitVtctld(ts)
	vtctld.HandleExplorer("memorytopo", vtctld.NewBackendExplorer(ts.Impl))

	servenv.OnTerm(func() {
		// FIXME(alainjobart): stop vtgate
	})
	servenv.OnClose(func() {
		// We will still use the topo server during lameduck period
		// to update our state, so closing it in OnClose()
		ts.Close()
	})
	servenv.RunDefault()
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

// +build !windows
// socket_nix.go

package libkb

import (
	"fmt"
	"net"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/keybase/client/go/logger"
)

// Though I've never seen this come up in production, it definitely comes up
// in systests that multiple go-routines might race over the current
// working directory as they do the (chdir && dial) dance below. Make sure
// a lock is held whenever operating on sockets, so that two racing goroutines
// can't conflict here.
var bindLock sync.Mutex

func (s SocketInfo) BindToSocket() (ret net.Listener, err error) {

	// Lock so that multiple goroutines can't race over current working dir.
	// See note above.
	bindLock.Lock()
	defer bindLock.Unlock()

	bindFile := s.bindFile
	what := fmt.Sprintf("SocketInfo#BindToSocket(unix:%s)", bindFile)
	defer Trace(s.log, what, func() error { return err })()

	if err := MakeParentDirs(s.log, bindFile); err != nil {
		return nil, err
	}

	// Path can't be longer than N characters.
	// In this case Chdir to the file directory first and use a local path.
	// On many Linuxes, N=108, on some N=106, and on macOS N=104.
	// N=104 is the lowest I know of.
	// It's the length of the path buffer in sockaddr_un.
	// And there may be a null-terminator in there, not sure, so make it 103 for good luck.
	// https://github.com/golang/go/issues/6895#issuecomment-98006662
	// https://gist.github.com/mlsteele/16dc5b6eb3d112b914183928c9af71b8#file-un-h-L79
	// We could always Chdir, but then this function would be non-threadsafe more of the time.
	// Pick your poison.
	if len(bindFile) >= 103 {
		prevWd, err := os.Getwd()
		if err != nil {
			return nil, fmt.Errorf("Error getting working directory: %s", err)
		}
		s.log.Debug("| Changing current working directory because path for binding is too long")
		dir := filepath.Dir(bindFile)
		s.log.Debug("| Chdir(%s)", dir)
		if err := os.Chdir(dir); err != nil {
			return nil, fmt.Errorf("Path can't be longer than 108 characters (failed to chdir): %s", err)
		}

		defer func() {
			s.log.Debug("| Chdir(%s)", prevWd)
			os.Chdir(prevWd)
		}()

		bindFile = filepath.Base(bindFile)
	}

	s.log.Info("| net.Listen on unix:%s", bindFile)
	ret, err = net.Listen("unix", bindFile)
	if err != nil {
		s.log.Warning("net.Listen failed with: %s", err.Error())
	}
	return ret, err
}

func (s SocketInfo) DialSocket() (net.Conn, error) {
	errs := []error{}
	for _, file := range s.dialFiles {
		ret, err := s.dialSocket(file)
		if err == nil {
			return ret, nil
		}
		errs = append(errs, err)
	}
	return nil, CombineErrors(errs...)
}

func (s SocketInfo) dialSocket(dialFile string) (ret net.Conn, err error) {

	// Lock so that multiple goroutines can't race over current working dir.
	// See note above.
	bindLock.Lock()
	defer bindLock.Unlock()

	what := fmt.Sprintf("SocketInfo#dialSocket(unix:%s)", dialFile)
	defer Trace(s.log, what, func() error { return err })()

	if dialFile == "" {
		return nil, fmt.Errorf("Can't dial empty path")
	}

	// Path can't be longer than 108 characters.
	// In this case Chdir to the file directory first.
	// https://github.com/golang/go/issues/6895#issuecomment-98006662
	if len(dialFile) >= 108 {
		prevWd, err := os.Getwd()
		if err != nil {
			return nil, fmt.Errorf("Error getting working directory: %s", err)
		}
		s.log.Warning("| Changing current working directory because path for dialing is too long")
		dir := filepath.Dir(dialFile)
		s.log.Debug("| os.Chdir(%s)", dir)
		if err := os.Chdir(dir); err != nil {
			return nil, fmt.Errorf("Path can't be longer than 108 characters (failed to chdir): %s", err)
		}
		defer os.Chdir(prevWd)
		dialFile = filepath.Base(dialFile)
	}

	s.log.Debug("| net.Dial(unix:%s)", dialFile)
	return net.Dial("unix", dialFile)
}

func NewSocket(g *GlobalContext) (ret Socket, err error) {
	var dialFiles []string
	dialFiles, err = g.Env.GetSocketDialFiles()
	if err != nil {
		return
	}
	var bindFile string
	bindFile, err = g.Env.GetSocketBindFile()
	if err != nil {
		return
	}
	log := g.Log
	if log == nil {
		log = logger.NewNull()
	}
	ret = SocketInfo{
		log:       log,
		dialFiles: dialFiles,
		bindFile:  bindFile,
	}
	return
}

func NewSocketWithFiles(
	log logger.Logger, bindFile string, dialFiles []string) Socket {
	return SocketInfo{
		log:       log,
		bindFile:  bindFile,
		dialFiles: dialFiles,
	}
}

// net.errClosing isn't exported, so do this.. UGLY!
func IsSocketClosedError(e error) bool {
	return strings.HasSuffix(e.Error(), "use of closed network connection")
}
<commit_msg>make this path limit consistent (#13893)<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

// +build !windows
// socket_nix.go

package libkb

import (
	"fmt"
	"net"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/keybase/client/go/logger"
)

// Though I've never seen this come up in production, it definitely comes up
// in systests that multiple go-routines might race over the current
// working directory as they do the (chdir && dial) dance below. Make sure
// a lock is held whenever operating on sockets, so that two racing goroutines
// can't conflict here.
var bindLock sync.Mutex

func (s SocketInfo) BindToSocket() (ret net.Listener, err error) {

	// Lock so that multiple goroutines can't race over current working dir.
	// See note above.
	bindLock.Lock()
	defer bindLock.Unlock()

	bindFile := s.bindFile
	what := fmt.Sprintf("SocketInfo#BindToSocket(unix:%s)", bindFile)
	defer Trace(s.log, what, func() error { return err })()

	if err := MakeParentDirs(s.log, bindFile); err != nil {
		return nil, err
	}

	// Path can't be longer than N characters.
	// In this case Chdir to the file directory first and use a local path.
	// On many Linuxes, N=108, on some N=106, and on macOS N=104.
	// N=104 is the lowest I know of.
	// It's the length of the path buffer in sockaddr_un.
	// And there may be a null-terminator in there, not sure, so make it 103 for good luck.
	// https://github.com/golang/go/issues/6895#issuecomment-98006662
	// https://gist.github.com/mlsteele/16dc5b6eb3d112b914183928c9af71b8#file-un-h-L79
	// We could always Chdir, but then this function would be non-threadsafe more of the time.
	// Pick your poison.
	if len(bindFile) >= 103 {
		prevWd, err := os.Getwd()
		if err != nil {
			return nil, fmt.Errorf("Error getting working directory: %s", err)
		}
		s.log.Debug("| Changing current working directory because path for binding is too long")
		dir := filepath.Dir(bindFile)
		s.log.Debug("| Chdir(%s)", dir)
		if err := os.Chdir(dir); err != nil {
			return nil, fmt.Errorf("Path can't be longer than 108 characters (failed to chdir): %s", err)
		}

		defer func() {
			s.log.Debug("| Chdir(%s)", prevWd)
			os.Chdir(prevWd)
		}()

		bindFile = filepath.Base(bindFile)
	}

	s.log.Info("| net.Listen on unix:%s", bindFile)
	ret, err = net.Listen("unix", bindFile)
	if err != nil {
		s.log.Warning("net.Listen failed with: %s", err.Error())
	}
	return ret, err
}

func (s SocketInfo) DialSocket() (net.Conn, error) {
	errs := []error{}
	for _, file := range s.dialFiles {
		ret, err := s.dialSocket(file)
		if err == nil {
			return ret, nil
		}
		errs = append(errs, err)
	}
	return nil, CombineErrors(errs...)
}

func (s SocketInfo) dialSocket(dialFile string) (ret net.Conn, err error) {

	// Lock so that multiple goroutines can't race over current working dir.
	// See note above.
	bindLock.Lock()
	defer bindLock.Unlock()

	what := fmt.Sprintf("SocketInfo#dialSocket(unix:%s)", dialFile)
	defer Trace(s.log, what, func() error { return err })()

	if dialFile == "" {
		return nil, fmt.Errorf("Can't dial empty path")
	}

	// Path can't be longer than 103 characters.
	// In this case Chdir to the file directory first.
	// https://github.com/golang/go/issues/6895#issuecomment-98006662
	if len(dialFile) >= 103 {
		prevWd, err := os.Getwd()
		if err != nil {
			return nil, fmt.Errorf("Error getting working directory: %s", err)
		}
		s.log.Warning("| Changing current working directory because path for dialing is too long")
		dir := filepath.Dir(dialFile)
		s.log.Debug("| os.Chdir(%s)", dir)
		if err := os.Chdir(dir); err != nil {
			return nil, fmt.Errorf("Path can't be longer than 108 characters (failed to chdir): %s", err)
		}
		defer os.Chdir(prevWd)
		dialFile = filepath.Base(dialFile)
	}

	s.log.Debug("| net.Dial(unix:%s)", dialFile)
	return net.Dial("unix", dialFile)
}

func NewSocket(g *GlobalContext) (ret Socket, err error) {
	var dialFiles []string
	dialFiles, err = g.Env.GetSocketDialFiles()
	if err != nil {
		return
	}
	var bindFile string
	bindFile, err = g.Env.GetSocketBindFile()
	if err != nil {
		return
	}
	log := g.Log
	if log == nil {
		log = logger.NewNull()
	}
	ret = SocketInfo{
		log:       log,
		dialFiles: dialFiles,
		bindFile:  bindFile,
	}
	return
}

func NewSocketWithFiles(
	log logger.Logger, bindFile string, dialFiles []string) Socket {
	return SocketInfo{
		log:       log,
		bindFile:  bindFile,
		dialFiles: dialFiles,
	}
}

// net.errClosing isn't exported, so do this.. UGLY!
func IsSocketClosedError(e error) bool {
	return strings.HasSuffix(e.Error(), "use of closed network connection")
}
<|endoftext|>


<commit_before>package plugins

import (
    "github.com/bwmarrin/discordgo"
    "github.com/Seklfreak/Robyul2/helpers"
    "golang.org/x/oauth2/google"
    "io/ioutil"
    "google.golang.org/api/drive/v3"
    "golang.org/x/net/context"
    "github.com/Seklfreak/Robyul2/logger"
    "google.golang.org/api/googleapi"
    "fmt"
    "time"
    "math/rand"
    "github.com/Seklfreak/Robyul2/cache"
    "strings"
    "github.com/bradfitz/slice"
    rethink "github.com/gorethink/gorethink"
    "github.com/dustin/go-humanize"
    "github.com/Seklfreak/Robyul2/metrics"
)

type RandomPictures struct{}

// @TODO: Metrics

type DB_RandomPictures_Source struct {
    ID               string            `gorethink:"id,omitempty"`
    GuildID          string            `gorethink:"guildid"`
    PostToChannelIDs []string          `gorethink:"post_to_channelids"`
    DriveFolderIDs   []string          `gorethink:"drive_folderids"`
    Aliases          []string          `gorethink:"aliases"`
}

var (
    filesCache   map[string][]*drive.File
    driveService *drive.Service
)

const (
    driveSearchText string = "\"%s\" in parents and (mimeType = \"image/gif\" or mimeType = \"image/jpeg\" or mimeType = \"image/png\")"
    driveFieldsText string = "nextPageToken, files(id, name, size, modifiedTime, imageMediaMetadata)"
)

func (rp *RandomPictures) Commands() []string {
    return []string{
        "randompictures",
        "rapi",
        "rp",
        "pic",
    }
}

func (rp *RandomPictures) Init(session *discordgo.Session) {
    filesCache = make(map[string][]*drive.File, 0)
    // Set up Google Drive Client
    ctx := context.Background()
    authJson, err := ioutil.ReadFile(helpers.GetConfig().Path("google.client_credentials_json_location").Data().(string))
    helpers.Relax(err)
    config, err := google.JWTConfigFromJSON(authJson, drive.DriveReadonlyScope)
    helpers.Relax(err)
    client := config.Client(ctx)
    driveService, err = drive.New(client)
    helpers.Relax(err)
    // initial random generator
    rand.Seed(time.Now().Unix())

    go func() {
        defer helpers.Recover()

        for {
            var rpSources []DB_RandomPictures_Source
            cursor, err := rethink.Table("randompictures_sources").Run(helpers.GetDB())
            helpers.Relax(err)
            err = cursor.All(&rpSources)
            helpers.Relax(err)
            if err != nil && err != rethink.ErrEmptyResult {
                helpers.Relax(err)
            }

            logger.INFO.L("randompictures", "gathering google drive picture cache")
            for _, sourceEntry := range rpSources {
                filesCache[sourceEntry.ID] = rp.getFileCache(sourceEntry)
                rp.updateImagesCachedMetric()
            }

            time.Sleep(12 * time.Hour)
        }
    }()
    logger.PLUGIN.L("randompictures", "Started files cache loop (12h)")

    go func() {
        defer helpers.Recover()

        for {
            var rpSources []DB_RandomPictures_Source
            cursor, err := rethink.Table("randompictures_sources").Run(helpers.GetDB())
            helpers.Relax(err)
            err = cursor.All(&rpSources)
            helpers.Relax(err)
            if err != nil && err != rethink.ErrEmptyResult {
                helpers.Relax(err)
            }

            for _, sourceEntry := range rpSources {
                if len(sourceEntry.PostToChannelIDs) > 0 {
                    if _, ok := filesCache[sourceEntry.ID]; ok {
                        if len(filesCache[sourceEntry.ID]) > 0 {
                            for _, postToChannelID := range sourceEntry.PostToChannelIDs {
                                randomItem := filesCache[sourceEntry.ID][rand.Intn(len(filesCache[sourceEntry.ID]))]
                                go func() {
                                    defer helpers.Recover()
                                    rp.postItem(postToChannelID, randomItem)
                                }()
                            }
                        }
                    }
                }
            }

            time.Sleep(1 * time.Hour)
        }
    }()
    logger.PLUGIN.L("randompictures", "Started post loop (1h)")
}

func (rp *RandomPictures) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
    switch command {
    case "pic": // [p]pic [<name>]
        session.ChannelTyping(msg.ChannelID)
        channel, err := session.Channel(msg.ChannelID)
        helpers.Relax(err)
        var matchEntry DB_RandomPictures_Source
        postedPic := false

        var rpSources []DB_RandomPictures_Source
        cursor, err := rethink.Table("randompictures_sources").Run(helpers.GetDB())
        helpers.Relax(err)
        err = cursor.All(&rpSources)
        helpers.Relax(err)
        if err != nil && err != rethink.ErrEmptyResult {
            helpers.Relax(err)
        }

        if content != "" { // match <name>
            for _, sourceEntry := range rpSources {
                if sourceEntry.GuildID == channel.GuildID {
                    for _, alias := range sourceEntry.Aliases {
                        if strings.ToLower(alias) == strings.ToLower(content) {
                            matchEntry = sourceEntry
                        }
                    }
                }
            }
        } else { // match roles
            guildRoles, err := session.GuildRoles(channel.GuildID)
            helpers.Relax(err)
            targetMember, err := session.GuildMember(channel.GuildID, msg.Author.ID)
            helpers.Relax(err)
            slice.Sort(guildRoles, func(i, j int) bool {
                return guildRoles[i].Position > guildRoles[j].Position
            })
            for _, guildRole := range guildRoles {
                for _, userRole := range targetMember.Roles {
                    if guildRole.ID == userRole {
                        for _, sourceEntry := range rpSources {
                            if sourceEntry.GuildID == channel.GuildID {
                                for _, alias := range sourceEntry.Aliases {
                                    if strings.Contains(strings.ToLower(guildRole.Name), strings.ToLower(alias)) {
                                        matchEntry = sourceEntry
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        if matchEntry.ID != "" {
            if _, ok := filesCache[matchEntry.ID]; ok {
                if len(filesCache[matchEntry.ID]) > 0 {
                    randomItem := filesCache[matchEntry.ID][rand.Intn(len(filesCache[matchEntry.ID]))]
                    rp.postItem(msg.ChannelID, randomItem)
                    postedPic = true
                    break
                }
            }
        }
        if postedPic == false {
            session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.randompictures.pic-no-picture"))
        }
        return
    default:
        args := strings.Split(content, " ")
        if len(args) > 0 {
            switch args[0] {
            case "new-config": // [p]randompictures new-config
                helpers.RequireBotAdmin(msg, func() {
                    session.ChannelTyping(msg.ChannelID)

                    insert := rethink.Table("randompictures_sources").Insert(DB_RandomPictures_Source{})
                    _, err := insert.RunWrite(helpers.GetDB())
                    helpers.Relax(err)

                    _, err = session.ChannelMessageSend(msg.ChannelID, "Created a new entry in the Database. Please fill it manually.")
                    helpers.Relax(err)
                })
                return
            case "list": // [p]randompictures list
                helpers.RequireBotAdmin(msg, func() {
                    session.ChannelTyping(msg.ChannelID)

                    var rpSources []DB_RandomPictures_Source
                    cursor, err := rethink.Table("randompictures_sources").Run(helpers.GetDB())
                    helpers.Relax(err)
                    err = cursor.All(&rpSources)
                    helpers.Relax(err)

                    if err == rethink.ErrEmptyResult || len(rpSources) <= 0 {
                        session.ChannelMessageSend(msg.ChannelID, helpers.GetTextF("plugins.randompictures.list-no-entries-error"))
                        return
                    } else if err != nil {
                        helpers.Relax(err)
                    }

                    listText := ":cloud: Sources set up:\n"
                    totalSources := 0
                    totalCachedImages := 0
                    for _, rpSource := range rpSources {
                        if rpSource.GuildID == "" {
                            continue
                        }
                        rpSourceGuild, err := session.Guild(rpSource.GuildID)
                        helpers.Relax(err)
                        cacheText := "No cache yet"
                        if _, ok := filesCache[rpSource.ID]; ok {
                            cacheText = fmt.Sprintf("%s images cached", humanize.Comma(int64(len(filesCache[rpSource.ID]))))
                            totalCachedImages += len(filesCache[rpSource.ID])
                        }
                        listText += fmt.Sprintf(":arrow_forward: `%s`: on %s (#%s), %d Aliases, %d Folders, %d Channels, %s\n",
                            rpSource.ID, rpSourceGuild.Name, rpSourceGuild.ID, len(rpSource.Aliases), len(rpSource.DriveFolderIDs), len(rpSource.PostToChannelIDs), cacheText)
                        totalSources += 1
                    }
                    listText += fmt.Sprintf("Found **%d** Sources in total and **%s** Cached Images.", totalSources, humanize.Comma(int64(totalCachedImages)))

                    _, err = session.ChannelMessageSend(msg.ChannelID, listText)
                    helpers.Relax(err)
                })
                return
            case "refresh": // [p]randompictures refresh <source id>
                helpers.RequireBotAdmin(msg, func() {
                    session.ChannelTyping(msg.ChannelID)
                    if len(args) < 2 {
                        _, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("bot.arguments.too-few"))
                        helpers.Relax(err)
                        return
                    }

                    var rpSources []DB_RandomPictures_Source
                    cursor, err := rethink.Table("randompictures_sources").Run(helpers.GetDB())
                    helpers.Relax(err)
                    err = cursor.All(&rpSources)
                    helpers.Relax(err)

                    if err == rethink.ErrEmptyResult || len(rpSources) <= 0 {
                        session.ChannelMessageSend(msg.ChannelID, helpers.GetTextF("plugins.randompictures.list-no-entries-error"))
                        return
                    } else if err != nil {
                        helpers.Relax(err)
                    }

                    for _, rpSource := range rpSources {
                        if rpSource.ID == args[1] {
                            session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.randompictures.refresh-started"))
                            filesCache[rpSource.ID] = rp.getFileCache(rpSource)
                            rp.updateImagesCachedMetric()
                            _, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.randompictures.refresh-success"))
                            helpers.Relax(err)
                            return
                        }
                    }

                    _, err = session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.randompictures.refresh-not-found-error"))
                    helpers.Relax(err)
                    return
                })
            }
        }
    }

}

func (rp *RandomPictures) getFileCache(sourceEntry DB_RandomPictures_Source) []*drive.File {
    var allFiles []*drive.File

    for _, driveFolderID := range sourceEntry.DriveFolderIDs {
        logger.INFO.L("randompictures", fmt.Sprintf("getting google drive picture cache Folder #%s for Entry #%s", driveFolderID, sourceEntry.ID))
        result, err := driveService.Files.List().Q(fmt.Sprintf(driveSearchText, driveFolderID)).Fields(googleapi.Field(driveFieldsText)).PageSize(1000).Do()
        helpers.Relax(err)
        for _, file := range result.Files {
            allFiles = append(allFiles, file)
        }

        for {
            if result.NextPageToken == "" {
                break
            }
            result, err = driveService.Files.List().Q(fmt.Sprintf(driveSearchText, driveFolderID)).Fields(googleapi.Field(driveFieldsText)).PageSize(1000).PageToken(result.NextPageToken).Do()
            helpers.Relax(err)
            for _, file := range result.Files {
                allFiles = append(allFiles, file)
            }
        }
    }
    return allFiles
}

func (rp *RandomPictures) updateImagesCachedMetric() {
    var totalImages int64
    for _, imagesCached := range filesCache {
        totalImages += int64(len(imagesCached))
    }
    metrics.RandomPictureSourcesImagesCachedCount.Set(totalImages)
}

func (rp *RandomPictures) postItem(channelID string, file *drive.File) {
    result, err := driveService.Files.Get(file.Id).Download()
    helpers.Relax(err)
    defer result.Body.Close()

    _, err = cache.GetSession().ChannelFileSendWithMessage(channelID, fmt.Sprintf("`%s`", file.Name), file.Name, result.Body)
    helpers.Relax(err)
}
<commit_msg>[RandomPictures] fixes role check<commit_after>package plugins

import (
    "github.com/bwmarrin/discordgo"
    "github.com/Seklfreak/Robyul2/helpers"
    "golang.org/x/oauth2/google"
    "io/ioutil"
    "google.golang.org/api/drive/v3"
    "golang.org/x/net/context"
    "github.com/Seklfreak/Robyul2/logger"
    "google.golang.org/api/googleapi"
    "fmt"
    "time"
    "math/rand"
    "github.com/Seklfreak/Robyul2/cache"
    "strings"
    "github.com/bradfitz/slice"
    rethink "github.com/gorethink/gorethink"
    "github.com/dustin/go-humanize"
    "github.com/Seklfreak/Robyul2/metrics"
)

type RandomPictures struct{}

// @TODO: Metrics

type DB_RandomPictures_Source struct {
    ID               string            `gorethink:"id,omitempty"`
    GuildID          string            `gorethink:"guildid"`
    PostToChannelIDs []string          `gorethink:"post_to_channelids"`
    DriveFolderIDs   []string          `gorethink:"drive_folderids"`
    Aliases          []string          `gorethink:"aliases"`
}

var (
    filesCache   map[string][]*drive.File
    driveService *drive.Service
)

const (
    driveSearchText string = "\"%s\" in parents and (mimeType = \"image/gif\" or mimeType = \"image/jpeg\" or mimeType = \"image/png\")"
    driveFieldsText string = "nextPageToken, files(id, name, size, modifiedTime)"
)

func (rp *RandomPictures) Commands() []string {
    return []string{
        "randompictures",
        "rapi",
        "rp",
        "pic",
    }
}

func (rp *RandomPictures) Init(session *discordgo.Session) {
    filesCache = make(map[string][]*drive.File, 0)
    // Set up Google Drive Client
    ctx := context.Background()
    authJson, err := ioutil.ReadFile(helpers.GetConfig().Path("google.client_credentials_json_location").Data().(string))
    helpers.Relax(err)
    config, err := google.JWTConfigFromJSON(authJson, drive.DriveReadonlyScope)
    helpers.Relax(err)
    client := config.Client(ctx)
    driveService, err = drive.New(client)
    helpers.Relax(err)
    // initial random generator
    rand.Seed(time.Now().Unix())

    go func() {
        defer helpers.Recover()

        for {
            var rpSources []DB_RandomPictures_Source
            cursor, err := rethink.Table("randompictures_sources").Run(helpers.GetDB())
            helpers.Relax(err)
            err = cursor.All(&rpSources)
            helpers.Relax(err)
            if err != nil && err != rethink.ErrEmptyResult {
                helpers.Relax(err)
            }

            logger.INFO.L("randompictures", "gathering google drive picture cache")
            for _, sourceEntry := range rpSources {
                filesCache[sourceEntry.ID] = rp.getFileCache(sourceEntry)
                rp.updateImagesCachedMetric()
            }

            time.Sleep(12 * time.Hour)
        }
    }()
    logger.PLUGIN.L("randompictures", "Started files cache loop (12h)")

    go func() {
        defer helpers.Recover()

        for {
            var rpSources []DB_RandomPictures_Source
            cursor, err := rethink.Table("randompictures_sources").Run(helpers.GetDB())
            helpers.Relax(err)
            err = cursor.All(&rpSources)
            helpers.Relax(err)
            if err != nil && err != rethink.ErrEmptyResult {
                helpers.Relax(err)
            }

            for _, sourceEntry := range rpSources {
                if len(sourceEntry.PostToChannelIDs) > 0 {
                    if _, ok := filesCache[sourceEntry.ID]; ok {
                        if len(filesCache[sourceEntry.ID]) > 0 {
                            for _, postToChannelID := range sourceEntry.PostToChannelIDs {
                                randomItem := filesCache[sourceEntry.ID][rand.Intn(len(filesCache[sourceEntry.ID]))]
                                go func() {
                                    defer helpers.Recover()
                                    rp.postItem(postToChannelID, randomItem)
                                }()
                            }
                        }
                    }
                }
            }

            time.Sleep(1 * time.Hour)
        }
    }()
    logger.PLUGIN.L("randompictures", "Started post loop (1h)")
}

func (rp *RandomPictures) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
    switch command {
    case "pic": // [p]pic [<name>]
        session.ChannelTyping(msg.ChannelID)
        channel, err := session.Channel(msg.ChannelID)
        helpers.Relax(err)
        var matchEntry DB_RandomPictures_Source
        postedPic := false

        var rpSources []DB_RandomPictures_Source
        cursor, err := rethink.Table("randompictures_sources").Run(helpers.GetDB())
        helpers.Relax(err)
        err = cursor.All(&rpSources)
        helpers.Relax(err)
        if err != nil && err != rethink.ErrEmptyResult {
            helpers.Relax(err)
        }

        if content != "" { // match <name>
            for _, sourceEntry := range rpSources {
                if sourceEntry.GuildID == channel.GuildID {
                    for _, alias := range sourceEntry.Aliases {
                        if strings.ToLower(alias) == strings.ToLower(content) {
                            matchEntry = sourceEntry
                        }
                    }
                }
            }
        } else { // match roles
            guildRoles, err := session.GuildRoles(channel.GuildID)
            helpers.Relax(err)
            targetMember, err := session.GuildMember(channel.GuildID, msg.Author.ID)
            helpers.Relax(err)
            slice.Sort(guildRoles, func(i, j int) bool {
                return guildRoles[i].Position > guildRoles[j].Position
            })
        CheckRoles:
            for _, guildRole := range guildRoles {
                for _, userRole := range targetMember.Roles {
                    if guildRole.ID == userRole {
                        for _, sourceEntry := range rpSources {
                            if sourceEntry.GuildID == channel.GuildID {
                                for _, alias := range sourceEntry.Aliases {
                                    if strings.Contains(strings.ToLower(guildRole.Name), strings.ToLower(alias)) {
                                        matchEntry = sourceEntry
                                        break CheckRoles
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        if matchEntry.ID != "" {
            if _, ok := filesCache[matchEntry.ID]; ok {
                if len(filesCache[matchEntry.ID]) > 0 {
                    randomItem := filesCache[matchEntry.ID][rand.Intn(len(filesCache[matchEntry.ID]))]
                    rp.postItem(msg.ChannelID, randomItem)
                    postedPic = true
                    break
                }
            }
        }
        if postedPic == false {
            session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.randompictures.pic-no-picture"))
        }
        return
    default:
        args := strings.Split(content, " ")
        if len(args) > 0 {
            switch args[0] {
            case "new-config": // [p]randompictures new-config
                helpers.RequireBotAdmin(msg, func() {
                    session.ChannelTyping(msg.ChannelID)

                    insert := rethink.Table("randompictures_sources").Insert(DB_RandomPictures_Source{})
                    _, err := insert.RunWrite(helpers.GetDB())
                    helpers.Relax(err)

                    _, err = session.ChannelMessageSend(msg.ChannelID, "Created a new entry in the Database. Please fill it manually.")
                    helpers.Relax(err)
                })
                return
            case "list": // [p]randompictures list
                helpers.RequireBotAdmin(msg, func() {
                    session.ChannelTyping(msg.ChannelID)

                    var rpSources []DB_RandomPictures_Source
                    cursor, err := rethink.Table("randompictures_sources").Run(helpers.GetDB())
                    helpers.Relax(err)
                    err = cursor.All(&rpSources)
                    helpers.Relax(err)

                    if err == rethink.ErrEmptyResult || len(rpSources) <= 0 {
                        session.ChannelMessageSend(msg.ChannelID, helpers.GetTextF("plugins.randompictures.list-no-entries-error"))
                        return
                    } else if err != nil {
                        helpers.Relax(err)
                    }

                    listText := ":cloud: Sources set up:\n"
                    totalSources := 0
                    totalCachedImages := 0
                    for _, rpSource := range rpSources {
                        if rpSource.GuildID == "" {
                            continue
                        }
                        rpSourceGuild, err := session.Guild(rpSource.GuildID)
                        helpers.Relax(err)
                        cacheText := "No cache yet"
                        if _, ok := filesCache[rpSource.ID]; ok {
                            cacheText = fmt.Sprintf("%s images cached", humanize.Comma(int64(len(filesCache[rpSource.ID]))))
                            totalCachedImages += len(filesCache[rpSource.ID])
                        }
                        listText += fmt.Sprintf(":arrow_forward: `%s`: on %s (#%s), %d Aliases, %d Folders, %d Channels, %s\n",
                            rpSource.ID, rpSourceGuild.Name, rpSourceGuild.ID, len(rpSource.Aliases), len(rpSource.DriveFolderIDs), len(rpSource.PostToChannelIDs), cacheText)
                        totalSources += 1
                    }
                    listText += fmt.Sprintf("Found **%d** Sources in total and **%s** Cached Images.", totalSources, humanize.Comma(int64(totalCachedImages)))

                    for _, page := range helpers.Pagify(listText, "\n") {
                        _, err = session.ChannelMessageSend(msg.ChannelID, page)
                        helpers.Relax(err)
                    }
                })
                return
            case "refresh": // [p]randompictures refresh <source id>
                helpers.RequireBotAdmin(msg, func() {
                    session.ChannelTyping(msg.ChannelID)
                    if len(args) < 2 {
                        _, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("bot.arguments.too-few"))
                        helpers.Relax(err)
                        return
                    }

                    var rpSources []DB_RandomPictures_Source
                    cursor, err := rethink.Table("randompictures_sources").Run(helpers.GetDB())
                    helpers.Relax(err)
                    err = cursor.All(&rpSources)
                    helpers.Relax(err)

                    if err == rethink.ErrEmptyResult || len(rpSources) <= 0 {
                        session.ChannelMessageSend(msg.ChannelID, helpers.GetTextF("plugins.randompictures.list-no-entries-error"))
                        return
                    } else if err != nil {
                        helpers.Relax(err)
                    }

                    for _, rpSource := range rpSources {
                        if rpSource.ID == args[1] {
                            session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.randompictures.refresh-started"))
                            filesCache[rpSource.ID] = rp.getFileCache(rpSource)
                            rp.updateImagesCachedMetric()
                            _, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.randompictures.refresh-success"))
                            helpers.Relax(err)
                            return
                        }
                    }

                    _, err = session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.randompictures.refresh-not-found-error"))
                    helpers.Relax(err)
                    return
                })
            }
        }
    }

}

func (rp *RandomPictures) getFileCache(sourceEntry DB_RandomPictures_Source) []*drive.File {
    var allFiles []*drive.File

    for _, driveFolderID := range sourceEntry.DriveFolderIDs {
        logger.INFO.L("randompictures", fmt.Sprintf("getting google drive picture cache Folder #%s for Entry #%s", driveFolderID, sourceEntry.ID))
        result, err := driveService.Files.List().Q(fmt.Sprintf(driveSearchText, driveFolderID)).Fields(googleapi.Field(driveFieldsText)).PageSize(1000).Do()
        helpers.Relax(err)
        for _, file := range result.Files {
            allFiles = append(allFiles, file)
        }

        for {
            if result.NextPageToken == "" {
                break
            }
            result, err = driveService.Files.List().Q(fmt.Sprintf(driveSearchText, driveFolderID)).Fields(googleapi.Field(driveFieldsText)).PageSize(1000).PageToken(result.NextPageToken).Do()
            helpers.Relax(err)
            for _, file := range result.Files {
                allFiles = append(allFiles, file)
            }
        }
    }
    return allFiles
}

func (rp *RandomPictures) updateImagesCachedMetric() {
    var totalImages int64
    for _, imagesCached := range filesCache {
        totalImages += int64(len(imagesCached))
    }
    metrics.RandomPictureSourcesImagesCachedCount.Set(totalImages)
}

func (rp *RandomPictures) postItem(channelID string, file *drive.File) {
    result, err := driveService.Files.Get(file.Id).Download()
    helpers.Relax(err)
    defer result.Body.Close()

    _, err = cache.GetSession().ChannelFileSendWithMessage(channelID, fmt.Sprintf("`%s`", file.Name), file.Name, result.Body)
    helpers.Relax(err)
}
<|endoftext|>


<commit_before>// Copyright 2015 FactomProject Authors. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package anchor

import (
	"os"

	"github.com/FactomProject/FactomCode/factomlog"
	"github.com/FactomProject/FactomCode/util"
)

var (
	logcfg     = util.ReadConfig().Log
	logPath    = logcfg.LogPath
	logLevel   = logcfg.LogLevel
	logfile, _ = os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0660)
)

// setup subsystem loggers
var (
	anchorLog = factomlog.New(logfile, logLevel, "anchor")
)
<commit_msg>Minor text change<commit_after>// Copyright 2015 FactomProject Authors. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package anchor

import (
	"os"

	"github.com/FactomProject/FactomCode/factomlog"
	"github.com/FactomProject/FactomCode/util"
)

var (
	logcfg     = util.ReadConfig().Log
	logPath    = logcfg.LogPath
	logLevel   = logcfg.LogLevel
	logfile, _ = os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0660)
)

// setup subsystem loggers
var (
	anchorLog = factomlog.New(logfile, logLevel, "ANCH")
)
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 cmac

import (
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"testing"
)

func TestXor(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type XorTest struct{}

func init() { RegisterTestSuite(&XorTest{}) }

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *XorTest) LengthsNotEqual() {
	a := []byte{0x00}
	b := []byte{0x00, 0x11}
	f := func() { xor(a, b) }
	ExpectThat(f, Panics(HasSubstr("length")))
}

func (t *XorTest) NilBuffers() {
	a := []byte(nil)
	b := []byte(nil)
	result := xor(a, b)

	AssertNe(nil, result)
	ExpectThat(result, DeepEquals([]byte{}))
}

func (t *XorTest) EmptyBuffers() {
	a := []byte{}
	b := []byte{}
	result := xor(a, b)

	AssertNe(nil, result)
	ExpectThat(result, DeepEquals([]byte{}))
}

func (t *XorTest) OneByteBuffers() {
	var a, b, expected []byte

	a = []byte{fromBinary("00000000")}
	b = []byte{fromBinary("00000000")}
	expected = []byte{fromBinary("00000000")}
	ExpectThat(xor(a, b), DeepEquals(expected))

	a = []byte{fromBinary("11111111")}
	b = []byte{fromBinary("11111111")}
	expected = []byte{fromBinary("00000000")}
	ExpectThat(xor(a, b), DeepEquals(expected))

	a = []byte{fromBinary("11111111")}
	b = []byte{fromBinary("00000000")}
	expected = []byte{fromBinary("11111111")}
	ExpectThat(xor(a, b), DeepEquals(expected))

	a = []byte{fromBinary("00000000")}
	b = []byte{fromBinary("11111111")}
	expected = []byte{fromBinary("11111111")}
	ExpectThat(xor(a, b), DeepEquals(expected))

	a = []byte{fromBinary("10100100")}
	b = []byte{fromBinary("11111111")}
	expected = []byte{fromBinary("01011011")}
	ExpectThat(xor(a, b), DeepEquals(expected))
}

func (t *XorTest) MultiByteBuffers() {
	ExpectEq("TODO", "")
}
<commit_msg>XorTest.MultiByteBuffers<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 cmac

import (
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"testing"
)

func TestXor(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type XorTest struct{}

func init() { RegisterTestSuite(&XorTest{}) }

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *XorTest) LengthsNotEqual() {
	a := []byte{0x00}
	b := []byte{0x00, 0x11}
	f := func() { xor(a, b) }
	ExpectThat(f, Panics(HasSubstr("length")))
}

func (t *XorTest) NilBuffers() {
	a := []byte(nil)
	b := []byte(nil)
	result := xor(a, b)

	AssertNe(nil, result)
	ExpectThat(result, DeepEquals([]byte{}))
}

func (t *XorTest) EmptyBuffers() {
	a := []byte{}
	b := []byte{}
	result := xor(a, b)

	AssertNe(nil, result)
	ExpectThat(result, DeepEquals([]byte{}))
}

func (t *XorTest) OneByteBuffers() {
	var a, b, expected []byte

	a = []byte{fromBinary("00000000")}
	b = []byte{fromBinary("00000000")}
	expected = []byte{fromBinary("00000000")}
	ExpectThat(xor(a, b), DeepEquals(expected))

	a = []byte{fromBinary("11111111")}
	b = []byte{fromBinary("11111111")}
	expected = []byte{fromBinary("00000000")}
	ExpectThat(xor(a, b), DeepEquals(expected))

	a = []byte{fromBinary("11111111")}
	b = []byte{fromBinary("00000000")}
	expected = []byte{fromBinary("11111111")}
	ExpectThat(xor(a, b), DeepEquals(expected))

	a = []byte{fromBinary("00000000")}
	b = []byte{fromBinary("11111111")}
	expected = []byte{fromBinary("11111111")}
	ExpectThat(xor(a, b), DeepEquals(expected))

	a = []byte{fromBinary("10100100")}
	b = []byte{fromBinary("11111111")}
	expected = []byte{fromBinary("01011011")}
	ExpectThat(xor(a, b), DeepEquals(expected))
}

func (t *XorTest) MultiByteBuffers() {
	var a, b, expected []byte

	a = []byte{fromBinary("00000000"), fromBinary("00000000")}
	b = []byte{fromBinary("00000000"), fromBinary("00000000")}
	expected = []byte{fromBinary("00000000"), fromBinary("00000000")}
	ExpectThat(xor(a, b), DeepEquals(expected))

	a = []byte{fromBinary("11111111"), fromBinary("11111111")}
	b = []byte{fromBinary("11111111"), fromBinary("11111111")}
	expected = []byte{fromBinary("00000000"), fromBinary("00000000")}
	ExpectThat(xor(a, b), DeepEquals(expected))

	a = []byte{fromBinary("00000000"), fromBinary("11111111")}
	b = []byte{fromBinary("11111111"), fromBinary("00000000")}
	expected = []byte{fromBinary("00000000"), fromBinary("00000000")}
	ExpectThat(xor(a, b), DeepEquals(expected))
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"log"
	"os"
	"strconv"

	"github.com/boltdb/bolt"
	"github.com/codegangsta/cli"
)

func main() {
	log.SetFlags(0)
	NewApp().Run(os.Args)
}

// NewApp creates an Application instance.
func NewApp() *cli.App {
	app := cli.NewApp()
	app.Name = "bolt"
	app.Usage = "BoltDB toolkit"
	app.Commands = []cli.Command{
		{
			Name:   "get",
			Usage:  "Retrieve a value for given key in a bucket",
			Action: GetCommand,
		},
		{
			Name:   "set",
			Usage:  "Sets a value for given key in a bucket",
			Action: SetCommand,
		},
		{
			Name:   "keys",
			Usage:  "Retrieve a list of all keys in a bucket",
			Action: KeysCommand,
		},
		{
			Name:   "buckets",
			Usage:  "Retrieves a list of all buckets",
			Action: BucketsCommand,
		},
		{
			Name:   "pages",
			Usage:  "Dumps page information for a database",
			Action: PagesCommand,
		},
	}
	return app
}

// GetCommand retrieves the value for a given bucket/key.
func GetCommand(c *cli.Context) {
	path, name, key := c.Args().Get(0), c.Args().Get(1), c.Args().Get(2)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		fatal(err)
		return
	}

	db, err := bolt.Open(path, 0600)
	if err != nil {
		fatal(err)
		return
	}
	defer db.Close()

	err = db.With(func(tx *bolt.Tx) error {
		// Find bucket.
		b := tx.Bucket(name)
		if b == nil {
			fatalf("bucket not found: %s", name)
			return nil
		}

		// Find value for a given key.
		value := b.Get([]byte(key))
		if value == nil {
			fatalf("key not found: %s", key)
			return nil
		}

		logger.Println(string(value))
		return nil
	})
	if err != nil {
		fatal(err)
		return
	}
}

// SetCommand sets the value for a given key in a bucket.
func SetCommand(c *cli.Context) {
	path, name, key, value := c.Args().Get(0), c.Args().Get(1), c.Args().Get(2), c.Args().Get(3)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		fatal(err)
		return
	}

	db, err := bolt.Open(path, 0600)
	if err != nil {
		fatal(err)
		return
	}
	defer db.Close()

	err = db.Do(func(tx *bolt.Tx) error {
		// Find bucket.
		b := tx.Bucket(name)
		if b == nil {
			fatalf("bucket not found: %s", name)
			return nil
		}

		// Set value for a given key.
		return b.Put([]byte(key), []byte(value))
	})
	if err != nil {
		fatal(err)
		return
	}
}

// KeysCommand retrieves a list of keys for a given bucket.
func KeysCommand(c *cli.Context) {
	path, name := c.Args().Get(0), c.Args().Get(1)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		fatal(err)
		return
	}

	db, err := bolt.Open(path, 0600)
	if err != nil {
		fatal(err)
		return
	}
	defer db.Close()

	err = db.With(func(tx *bolt.Tx) error {
		// Find bucket.
		b := tx.Bucket(name)
		if b == nil {
			fatalf("bucket not found: %s", name)
			return nil
		}

		// Iterate over each key.
		return b.ForEach(func(key, _ []byte) error {
			logger.Println(string(key))
			return nil
		})
	})
	if err != nil {
		fatal(err)
		return
	}
}

// BucketsCommand retrieves a list of all buckets.
func BucketsCommand(c *cli.Context) {
	path := c.Args().Get(0)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		fatal(err)
		return
	}

	db, err := bolt.Open(path, 0600)
	if err != nil {
		fatal(err)
		return
	}
	defer db.Close()

	err = db.With(func(tx *bolt.Tx) error {
		for _, b := range tx.Buckets() {
			logger.Println(b.Name())
		}
		return nil
	})
	if err != nil {
		fatal(err)
		return
	}
}

// PagesCommand prints a list of all pages in a database.
func PagesCommand(c *cli.Context) {
	path := c.Args().Get(0)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		fatal(err)
		return
	}

	db, err := bolt.Open(path, 0600)
	if err != nil {
		fatal(err)
		return
	}
	defer db.Close()

	logger.Println("ID       TYPE       ITEMS  OVRFLW")
	logger.Println("======== ========== ====== ======")

	db.Do(func(tx *bolt.Tx) error {
		var id int
		for {
			p, err := tx.Page(id)
			if err != nil {
				fatalf("page error: %d: %s", id, err)
			} else if p == nil {
				break
			}

			var overflow string
			if p.OverflowCount > 0 {
				overflow = strconv.Itoa(p.OverflowCount)
			}
			logger.Printf("%-8d %-10s %-6d %-6s", p.ID, p.Type, p.Count, overflow)
			id += 1 + p.OverflowCount
		}
		return nil
	})
}

var logger = log.New(os.Stderr, "", 0)
var logBuffer *bytes.Buffer

func fatal(v ...interface{}) {
	logger.Print(v...)
	if !testMode {
		os.Exit(1)
	}
}

func fatalf(format string, v ...interface{}) {
	logger.Printf(format, v...)
	if !testMode {
		os.Exit(1)
	}
}

func fatalln(v ...interface{}) {
	logger.Println(v...)
	if !testMode {
		os.Exit(1)
	}
}

// LogBuffer returns the contents of the log.
// This only works while the CLI is in test mode.
func LogBuffer() string {
	if logBuffer != nil {
		return logBuffer.String()
	}
	return ""
}

var testMode bool

// SetTestMode sets whether the CLI is running in test mode and resets the logger.
func SetTestMode(value bool) {
	testMode = value
	if testMode {
		logBuffer = bytes.NewBuffer(nil)
		logger = log.New(logBuffer, "", 0)
	} else {
		logger = log.New(os.Stderr, "", 0)
	}
}
<commit_msg>Fix print.<commit_after>package main

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"strconv"

	"github.com/boltdb/bolt"
	"github.com/codegangsta/cli"
)

func main() {
	log.SetFlags(0)
	NewApp().Run(os.Args)
}

// NewApp creates an Application instance.
func NewApp() *cli.App {
	app := cli.NewApp()
	app.Name = "bolt"
	app.Usage = "BoltDB toolkit"
	app.Commands = []cli.Command{
		{
			Name:   "get",
			Usage:  "Retrieve a value for given key in a bucket",
			Action: GetCommand,
		},
		{
			Name:   "set",
			Usage:  "Sets a value for given key in a bucket",
			Action: SetCommand,
		},
		{
			Name:   "keys",
			Usage:  "Retrieve a list of all keys in a bucket",
			Action: KeysCommand,
		},
		{
			Name:   "buckets",
			Usage:  "Retrieves a list of all buckets",
			Action: BucketsCommand,
		},
		{
			Name:   "pages",
			Usage:  "Dumps page information for a database",
			Action: PagesCommand,
		},
	}
	return app
}

// GetCommand retrieves the value for a given bucket/key.
func GetCommand(c *cli.Context) {
	path, name, key := c.Args().Get(0), c.Args().Get(1), c.Args().Get(2)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		fatal(err)
		return
	}

	db, err := bolt.Open(path, 0600)
	if err != nil {
		fatal(err)
		return
	}
	defer db.Close()

	err = db.With(func(tx *bolt.Tx) error {
		// Find bucket.
		b := tx.Bucket(name)
		if b == nil {
			fatalf("bucket not found: %s", name)
			return nil
		}

		// Find value for a given key.
		value := b.Get([]byte(key))
		if value == nil {
			fatalf("key not found: %s", key)
			return nil
		}

		println(string(value))
		return nil
	})
	if err != nil {
		fatal(err)
		return
	}
}

// SetCommand sets the value for a given key in a bucket.
func SetCommand(c *cli.Context) {
	path, name, key, value := c.Args().Get(0), c.Args().Get(1), c.Args().Get(2), c.Args().Get(3)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		fatal(err)
		return
	}

	db, err := bolt.Open(path, 0600)
	if err != nil {
		fatal(err)
		return
	}
	defer db.Close()

	err = db.Do(func(tx *bolt.Tx) error {
		// Find bucket.
		b := tx.Bucket(name)
		if b == nil {
			fatalf("bucket not found: %s", name)
			return nil
		}

		// Set value for a given key.
		return b.Put([]byte(key), []byte(value))
	})
	if err != nil {
		fatal(err)
		return
	}
}

// KeysCommand retrieves a list of keys for a given bucket.
func KeysCommand(c *cli.Context) {
	path, name := c.Args().Get(0), c.Args().Get(1)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		fatal(err)
		return
	}

	db, err := bolt.Open(path, 0600)
	if err != nil {
		fatal(err)
		return
	}
	defer db.Close()

	err = db.With(func(tx *bolt.Tx) error {
		// Find bucket.
		b := tx.Bucket(name)
		if b == nil {
			fatalf("bucket not found: %s", name)
			return nil
		}

		// Iterate over each key.
		return b.ForEach(func(key, _ []byte) error {
			println(string(key))
			return nil
		})
	})
	if err != nil {
		fatal(err)
		return
	}
}

// BucketsCommand retrieves a list of all buckets.
func BucketsCommand(c *cli.Context) {
	path := c.Args().Get(0)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		fatal(err)
		return
	}

	db, err := bolt.Open(path, 0600)
	if err != nil {
		fatal(err)
		return
	}
	defer db.Close()

	err = db.With(func(tx *bolt.Tx) error {
		for _, b := range tx.Buckets() {
			println(b.Name())
		}
		return nil
	})
	if err != nil {
		fatal(err)
		return
	}
}

// PagesCommand prints a list of all pages in a database.
func PagesCommand(c *cli.Context) {
	path := c.Args().Get(0)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		fatal(err)
		return
	}

	db, err := bolt.Open(path, 0600)
	if err != nil {
		fatal(err)
		return
	}
	defer db.Close()

	println("ID       TYPE       ITEMS  OVRFLW")
	println("======== ========== ====== ======")

	db.Do(func(tx *bolt.Tx) error {
		var id int
		for {
			p, err := tx.Page(id)
			if err != nil {
				fatalf("page error: %d: %s", id, err)
			} else if p == nil {
				break
			}

			var overflow string
			if p.OverflowCount > 0 {
				overflow = strconv.Itoa(p.OverflowCount)
			}
			printf("%-8d %-10s %-6d %-6s\n", p.ID, p.Type, p.Count, overflow)
			id += 1 + p.OverflowCount
		}
		return nil
	})
}

var logger = log.New(os.Stderr, "", 0)
var logBuffer *bytes.Buffer

func print(v ...interface{}) {
	if testMode {
		logger.Print(v...)
	} else {
		fmt.Print(v...)
	}
}

func printf(format string, v ...interface{}) {
	if testMode {
		logger.Printf(format, v...)
	} else {
		fmt.Printf(format, v...)
	}
}

func println(v ...interface{}) {
	if testMode {
		logger.Println(v...)
	} else {
		fmt.Println(v...)
	}
}

func fatal(v ...interface{}) {
	logger.Print(v...)
	if !testMode {
		os.Exit(1)
	}
}

func fatalf(format string, v ...interface{}) {
	logger.Printf(format, v...)
	if !testMode {
		os.Exit(1)
	}
}

func fatalln(v ...interface{}) {
	logger.Println(v...)
	if !testMode {
		os.Exit(1)
	}
}

// LogBuffer returns the contents of the log.
// This only works while the CLI is in test mode.
func LogBuffer() string {
	if logBuffer != nil {
		return logBuffer.String()
	}
	return ""
}

var testMode bool

// SetTestMode sets whether the CLI is running in test mode and resets the logger.
func SetTestMode(value bool) {
	testMode = value
	if testMode {
		logBuffer = bytes.NewBuffer(nil)
		logger = log.New(logBuffer, "", 0)
	} else {
		logger = log.New(os.Stderr, "", 0)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/mainflux/mainflux/coap"
	"github.com/mainflux/mainflux/coap/nats"

	broker "github.com/nats-io/go-nats"
	"go.uber.org/zap"
)

const (
	port       int    = 5683
	defNatsURL string = broker.DefaultURL
	envNatsURL string = "COAP_ADAPTER_NATS_URL"
)

type config struct {
	Port    int
	NatsURL string
}

func main() {
	cfg := &config{
		NatsURL: env(envNatsURL, defNatsURL),
		Port:    port,
	}

	logger, _ := zap.NewProduction()
	defer logger.Sync()

	nc := connectToNats(cfg, logger)
	defer nc.Close()

	repo := nats.NewMessageRepository(nc)
	ca := adapter.NewCoAPAdapter(logger, repo)

	nc.Subscribe("msg.http", ca.BridgeHandler)
	nc.Subscribe("msg.mqtt", ca.BridgeHandler)

	errs := make(chan error, 2)

	go func() {
		coapAddr := fmt.Sprintf(":%d", cfg.Port)
		logger.Info("CoAP adapter started.")
		errs <- ca.Serve(coapAddr)
	}()

	go func() {
		c := make(chan os.Signal)
		signal.Notify(c, syscall.SIGINT)
		errs <- fmt.Errorf("%s", <-c)
	}()

	c := <-errs
	logger.Info("CoAP adapter terminated.", zap.String("error", c.Error()))
}

func env(key, fallback string) string {
	value := os.Getenv(key)
	if value == "" {
		return fallback
	}

	return value
}

func connectToNats(cfg *config, logger *zap.Logger) *broker.Conn {
	nc, err := broker.Connect(cfg.NatsURL)
	if err != nil {
		logger.Error("Cannot connect to NATS.", zap.Error(err))
		os.Exit(1)
	}

	return nc
}
<commit_msg>Register CoAP adapter to Consul<commit_after>package main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"

	kitconsul "github.com/go-kit/kit/sd/consul"
	stdconsul "github.com/hashicorp/consul/api"
	"github.com/mainflux/mainflux/coap"
	"github.com/mainflux/mainflux/coap/nats"
	broker "github.com/nats-io/go-nats"
	uuid "github.com/satori/go.uuid"

	"go.uber.org/zap"
)

const (
	port    int    = 9003
	natsKey string = "nats"
)

var (
	kv     *stdconsul.KV
	logger *zap.Logger
)

func main() {
	logger, _ = zap.NewProduction()
	defer logger.Sync()

	consulAddr := os.Getenv("CONSUL_ADDR")
	if consulAddr == "" {
		logger.Fatal("Cannot start the service: CONSUL_ADDR not set.")
	}

	consul, err := stdconsul.NewClient(&stdconsul.Config{
		Address: consulAddr,
	})

	if err != nil {
		status := fmt.Sprintf("Cannot connect to Consul due to %s", err)
		logger.Fatal(status)
	}

	kv = consul.KV()

	asr := &stdconsul.AgentServiceRegistration{
		ID:                uuid.NewV4().String(),
		Name:              "coap-adapter",
		Tags:              []string{},
		Port:              port,
		Address:           "",
		EnableTagOverride: false,
	}

	sd := kitconsul.NewClient(consul)
	if err = sd.Register(asr); err != nil {
		status := fmt.Sprintf("Cannot register service due to %s", err)
		logger.Fatal(status)
	}

	nc, err := broker.Connect(get(natsKey))
	if err != nil {
		logger.Fatal("Cannot connect to NATS.", zap.Error(err))
	}
	defer nc.Close()

	repo := nats.NewMessageRepository(nc)
	ca := adapter.NewCoAPAdapter(logger, repo)

	nc.Subscribe("msg.http", ca.BridgeHandler)
	nc.Subscribe("msg.mqtt", ca.BridgeHandler)

	errChan := make(chan error, 10)

	go func() {
		p := fmt.Sprintf(":%d", port)
		logger.Info("CoAP adapter started.")
		errChan <- ca.Serve(p)
	}()

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	for {
		select {
		case err := <-errChan:
			status := fmt.Sprintf("CoAP adapter terminated due to %s", err)
			logger.Fatal(status)
		case <-sigChan:
			logger.Info("CoAP adapter terminated.")
		}
	}
}

func get(key string) string {
	pair, _, err := kv.Get(key, nil)
	if err != nil {
		status := fmt.Sprintf("Cannot retrieve %s due to %s", key, err)
		logger.Fatal(status)
	}

	return string(pair.Value)
}
<|endoftext|>