<commit_before>package main

import (
	"unicode"
	"unicode/utf8"
)

var (
	taboffset = 4
	pageoffset =8
)

//type place int
//
//var (
//	NONE place = iota
//	BOC
//	EOC
//	EOL
//)

type Cursor struct {
	l int // line offset
	o int // cursor offset - When MoveUp or MoveDown, it will calculated from visual offset.
	v int // visual offset - When MoveLeft of MoveRight, it will matched to cursor offset.
	b int // byte offset
	t *Text
	// stick place - will implement later
}

func NewCursor(t *Text) *Cursor {
	return &Cursor{0, 0, 0, 0, t}
}

func (c *Cursor) SetOffsets(b int) {
	c.b = b
	c.v = c.VFromB(b)
	c.o = c.v
}

// Before shifting, visual offset will matched to cursor offset.
func (c *Cursor) ShiftOffsets(b, v int) {
	c.v = c.o
	c.b += b
	c.v += v
	c.o += v
}

// After MoveUp or MoveDown, we need reclaculate cursor offsets (except visual offset).
func (c *Cursor) RecalculateOffsets() {
	c.o = c.OFromV(c.v)
	c.b = c.BFromC(c.o)
}

func (c *Cursor) OFromV(v int) (o int) {
	// Cursor offset cannot go further than line's maximum visual length.
	maxv := c.LineVisualLength()
	if v >  maxv {
		return maxv
	}
	// It's not allowed the cursor is in the middle of multi-length(visual) character.
	// So we need recaculate the cursors offset.
	remain := c.LineData()
	lasto := 0
	for {
		r, rlen := utf8.DecodeRuneInString(remain)
		remain = remain[rlen:]
		lasto = o
		o += RuneVisualLength(r)
		if o==v {
			return o
		} else if o > v {
			return lasto
		}
	}
}

func (c *Cursor) BFromC(o int) (b int) {
	remain := c.LineData()
	for o>0 {
		r, rlen := utf8.DecodeRuneInString(remain)
		remain = remain[rlen:]
		b+= rlen
		o-= RuneVisualLength(r)
	}
	return
}

func BFromC(line string, o int) (b int) {
	remain := line
	for o>0 {
		r, rlen := utf8.DecodeRuneInString(remain)
		remain = remain[rlen:]
		b+= rlen
		o-= RuneVisualLength(r)
	}
	return
}

func (c *Cursor) VFromB(b int) (v int){
	remain := c.LineData()[:b]
	for len(remain) > 0 {
		r, rlen := utf8.DecodeRuneInString(remain)
		remain = remain[rlen:]
		v += RuneVisualLength(r)
	}
	return
}

func (c *Cursor) Position() Point {
	return Point{c.l, c.o}
}

// TODO : relativePosition(p Point) Point ?
func (c *Cursor) PositionInWindow(w *Window) Point {
	return c.Position().Sub(w.min)
}

func (c *Cursor) LineData() string {
	return c.t.lines[c.l].data
}

func (c *Cursor) LineDataUntilCursor() string {
	return c.LineData()[:c.b]
}

func (c *Cursor) LineDataFromCursor() string {
	return c.LineData()[c.b:]
}

func (c *Cursor) ExceededLineLimit() bool {
	return c.b > len(c.LineData())
}

func (c *Cursor) RuneAfter() (rune, int) {
	return utf8.DecodeRuneInString(c.LineData()[c.b:])
}

func (c *Cursor) RuneBefore() (rune, int) {
	return utf8.DecodeLastRuneInString(c.LineData()[:c.b])
}

// should refine after
// may be use dictionary??
func RuneVisualLength(r rune) int {
	if r=='\t' {
		return taboffset
	}
	return 1
}

func (c *Cursor) LineByteLength() int {
	return len(c.LineData())
}

func (c *Cursor) LineVisualLength() int {
	return c.VFromB(c.LineByteLength())
}

func (c *Cursor) AtBol() bool{
	return c.b == 0
}

func (c *Cursor) AtEol() bool{
	return c.b == c.LineByteLength()
}

func (c *Cursor) OnFirstLine() bool{
	return c.l == 0
}

func (c *Cursor) OnLastLine() bool {
	return c.l == len(c.t.lines)-1
}

func (c *Cursor) AtBof() bool {
	return c.OnFirstLine() && c.AtBol()
}

func (c *Cursor) AtEof() bool {
	return c.OnLastLine() && c.AtEol()
}

func (c *Cursor) MoveLeft() {
	if c.AtBof() {
		return
	} else if c.AtBol() {
		c.l--
		c.SetOffsets(c.LineByteLength())
		return
	}
	r, rlen := c.RuneBefore()
	vlen := RuneVisualLength(r)
	c.ShiftOffsets(-rlen, -vlen)
}

func (c *Cursor) MoveRight() {
	if c.AtEof() {
		return
	} else if c.AtEol() || c.ExceededLineLimit(){
		c.l++
		c.SetOffsets(0)
		return
	}
	r, rlen := c.RuneAfter()
	vlen := RuneVisualLength(r)
	c.ShiftOffsets(rlen, vlen)
}

func (c *Cursor) MoveUp() {
	if c.OnFirstLine() {
		return
	}
	c.l--
	c.RecalculateOffsets()
}

func (c *Cursor) MoveDown() {
	if c.OnLastLine() {
		return
	}
	c.l++
	c.RecalculateOffsets()
}

func (c *Cursor) MoveBow() {
	if c.AtBof() {
		return
	}
	// First we should pass every space character.
	for {
		r, _ := c.RuneBefore()
		if unicode.IsLetter(r) || unicode.IsDigit(r) {
			break
		}
		c.MoveLeft()
		if c.AtBof() {
			return
		}
	}
	// Then we will find first space charactor and stop.
	for {
		r, _ := c.RuneBefore()
		if !(unicode.IsLetter(r) || unicode.IsDigit(r)) {
			return
		}
		c.MoveLeft()
		if c.AtBof() {
			return
		}
	}
}

// See moveEow for the algorithm.
func (c *Cursor) MoveEow() {
	if c.AtEof() {
		return
	}
	for {
		r, _ := c.RuneAfter()
		if unicode.IsLetter(r) || unicode.IsDigit(r) {
			break
		}
		c.MoveRight()
		if c.AtEof() {
			return
		}
	}
	for {
		r, _ := c.RuneAfter()
		if !(unicode.IsLetter(r) || unicode.IsDigit(r)) {
			return
		}
		c.MoveRight()
		if c.AtEof() {
			return
		}
	}
}

func (c *Cursor) MoveBol() {
	// if already bol, move cursor to prev line
	if c.AtBol() && !c.OnFirstLine() {
		c.MoveUp()
		return
	}

	remain := c.LineData()
	b := 0 // where line contents start
	for len(remain)>0 {
		r, rlen := utf8.DecodeRuneInString(remain)
		remain = remain[rlen:]
		if !unicode.IsSpace(r) {
			break
		}
		b += rlen
	}
	if c.b > b {
		c.SetOffsets(b)
		return
	}
	c.SetOffsets(0)
}

func (c *Cursor) MoveEol() {
	// if already eol, move to next line
	if c.b == len(c.LineData()) && !c.OnLastLine() {
		c.MoveDown()
	}

	remain := c.LineData()
	b := 0 // where line contents start
	for len(remain)>0 {
		r, rlen := utf8.DecodeRuneInString(remain)
		remain = remain[rlen:]
		if !unicode.IsSpace(r) {
			break
		}
		b += rlen
	}
	if c.b < b {
		c.SetOffsets(b)
		return
	}
	c.SetOffsets(c.LineByteLength())
}

func (c *Cursor) PageUp() {
	for i:=0; i < pageoffset; i++ {
		if c.OnFirstLine() {
			break
		}
		c.MoveUp()
	}
}

func (c *Cursor) PageDown() {
	for i:=0; i < pageoffset; i++ {
		if c.OnLastLine() {
			break
		}
		c.MoveDown()
	}
}

func (c *Cursor) MoveBof() {
	for {
		if c.OnFirstLine() {
			break
		}
		c.MoveUp()
	}
	c.MoveBol()
}

func (c *Cursor) MoveEof() {
	for {
		if c.OnLastLine() {
			break
		}
		c.MoveDown()
	}
	c.MoveEol()
}

func (c *Cursor) SplitLine() {
	c.t.SplitLine(c.l, c.b)
	c.MoveDown()
	c.SetOffsets(0)
}

func (c *Cursor) Insert(r rune) {
	c.t.Insert(r, c.l, c.b)
	c.MoveRight()
}

func (c *Cursor) Delete() {
	if c.AtEof() {
		return
	}
	if c.AtEol() {
		c.t.JoinNextLine(c.l)
		return
	}
	_, rlen := c.RuneAfter()
	c.t.Remove(c.l, c.b, c.b+rlen)
}

func (c *Cursor) Backspace() {
	if c.AtBof() {
		return
	}
	c.MoveLeft()
	c.Delete()
}

func (c *Cursor) DeleteSelection(sel *Selection) {
	min, max := sel.MinMax()
	bmin := Point{min.l, BFromC(c.t.lines[min.l].data, min.o)}
	bmax := Point{max.l, BFromC(c.t.lines[max.l].data, max.o)}
	c.t.RemoveRange(bmin, bmax)
	c.l = min.l
	c.SetOffsets(bmin.o)
}
<commit_msg>minor fix on cursor.MoveEol()<commit_after>package main

import (
	"unicode"
	"unicode/utf8"
)

var (
	taboffset = 4
	pageoffset =8
)

//type place int
//
//var (
//	NONE place = iota
//	BOC
//	EOC
//	EOL
//)

type Cursor struct {
	l int // line offset
	o int // cursor offset - When MoveUp or MoveDown, it will calculated from visual offset.
	v int // visual offset - When MoveLeft of MoveRight, it will matched to cursor offset.
	b int // byte offset
	t *Text
	// stick place - will implement later
}

func NewCursor(t *Text) *Cursor {
	return &Cursor{0, 0, 0, 0, t}
}

func (c *Cursor) SetOffsets(b int) {
	c.b = b
	c.v = c.VFromB(b)
	c.o = c.v
}

// Before shifting, visual offset will matched to cursor offset.
func (c *Cursor) ShiftOffsets(b, v int) {
	c.v = c.o
	c.b += b
	c.v += v
	c.o += v
}

// After MoveUp or MoveDown, we need reclaculate cursor offsets (except visual offset).
func (c *Cursor) RecalculateOffsets() {
	c.o = c.OFromV(c.v)
	c.b = c.BFromC(c.o)
}

func (c *Cursor) OFromV(v int) (o int) {
	// Cursor offset cannot go further than line's maximum visual length.
	maxv := c.LineVisualLength()
	if v >  maxv {
		return maxv
	}
	// It's not allowed the cursor is in the middle of multi-length(visual) character.
	// So we need recaculate the cursors offset.
	remain := c.LineData()
	lasto := 0
	for {
		r, rlen := utf8.DecodeRuneInString(remain)
		remain = remain[rlen:]
		lasto = o
		o += RuneVisualLength(r)
		if o==v {
			return o
		} else if o > v {
			return lasto
		}
	}
}

func (c *Cursor) BFromC(o int) (b int) {
	remain := c.LineData()
	for o>0 {
		r, rlen := utf8.DecodeRuneInString(remain)
		remain = remain[rlen:]
		b+= rlen
		o-= RuneVisualLength(r)
	}
	return
}

func BFromC(line string, o int) (b int) {
	remain := line
	for o>0 {
		r, rlen := utf8.DecodeRuneInString(remain)
		remain = remain[rlen:]
		b+= rlen
		o-= RuneVisualLength(r)
	}
	return
}

func (c *Cursor) VFromB(b int) (v int){
	remain := c.LineData()[:b]
	for len(remain) > 0 {
		r, rlen := utf8.DecodeRuneInString(remain)
		remain = remain[rlen:]
		v += RuneVisualLength(r)
	}
	return
}

func (c *Cursor) Position() Point {
	return Point{c.l, c.o}
}

// TODO : relativePosition(p Point) Point ?
func (c *Cursor) PositionInWindow(w *Window) Point {
	return c.Position().Sub(w.min)
}

func (c *Cursor) LineData() string {
	return c.t.lines[c.l].data
}

func (c *Cursor) LineDataUntilCursor() string {
	return c.LineData()[:c.b]
}

func (c *Cursor) LineDataFromCursor() string {
	return c.LineData()[c.b:]
}

func (c *Cursor) ExceededLineLimit() bool {
	return c.b > len(c.LineData())
}

func (c *Cursor) RuneAfter() (rune, int) {
	return utf8.DecodeRuneInString(c.LineData()[c.b:])
}

func (c *Cursor) RuneBefore() (rune, int) {
	return utf8.DecodeLastRuneInString(c.LineData()[:c.b])
}

// should refine after
// may be use dictionary??
func RuneVisualLength(r rune) int {
	if r=='\t' {
		return taboffset
	}
	return 1
}

func (c *Cursor) LineByteLength() int {
	return len(c.LineData())
}

func (c *Cursor) LineVisualLength() int {
	return c.VFromB(c.LineByteLength())
}

func (c *Cursor) AtBol() bool{
	return c.b == 0
}

func (c *Cursor) AtEol() bool{
	return c.b == c.LineByteLength()
}

func (c *Cursor) OnFirstLine() bool{
	return c.l == 0
}

func (c *Cursor) OnLastLine() bool {
	return c.l == len(c.t.lines)-1
}

func (c *Cursor) AtBof() bool {
	return c.OnFirstLine() && c.AtBol()
}

func (c *Cursor) AtEof() bool {
	return c.OnLastLine() && c.AtEol()
}

func (c *Cursor) MoveLeft() {
	if c.AtBof() {
		return
	} else if c.AtBol() {
		c.l--
		c.SetOffsets(c.LineByteLength())
		return
	}
	r, rlen := c.RuneBefore()
	vlen := RuneVisualLength(r)
	c.ShiftOffsets(-rlen, -vlen)
}

func (c *Cursor) MoveRight() {
	if c.AtEof() {
		return
	} else if c.AtEol() || c.ExceededLineLimit(){
		c.l++
		c.SetOffsets(0)
		return
	}
	r, rlen := c.RuneAfter()
	vlen := RuneVisualLength(r)
	c.ShiftOffsets(rlen, vlen)
}

func (c *Cursor) MoveUp() {
	if c.OnFirstLine() {
		return
	}
	c.l--
	c.RecalculateOffsets()
}

func (c *Cursor) MoveDown() {
	if c.OnLastLine() {
		return
	}
	c.l++
	c.RecalculateOffsets()
}

func (c *Cursor) MoveBow() {
	if c.AtBof() {
		return
	}
	// First we should pass every space character.
	for {
		r, _ := c.RuneBefore()
		if unicode.IsLetter(r) || unicode.IsDigit(r) {
			break
		}
		c.MoveLeft()
		if c.AtBof() {
			return
		}
	}
	// Then we will find first space charactor and stop.
	for {
		r, _ := c.RuneBefore()
		if !(unicode.IsLetter(r) || unicode.IsDigit(r)) {
			return
		}
		c.MoveLeft()
		if c.AtBof() {
			return
		}
	}
}

// See moveEow for the algorithm.
func (c *Cursor) MoveEow() {
	if c.AtEof() {
		return
	}
	for {
		r, _ := c.RuneAfter()
		if unicode.IsLetter(r) || unicode.IsDigit(r) {
			break
		}
		c.MoveRight()
		if c.AtEof() {
			return
		}
	}
	for {
		r, _ := c.RuneAfter()
		if !(unicode.IsLetter(r) || unicode.IsDigit(r)) {
			return
		}
		c.MoveRight()
		if c.AtEof() {
			return
		}
	}
}

func (c *Cursor) MoveBol() {
	// if already bol, move cursor to prev line
	if c.AtBol() && !c.OnFirstLine() {
		c.MoveUp()
		return
	}

	remain := c.LineData()
	b := 0 // where line contents start
	for len(remain)>0 {
		r, rlen := utf8.DecodeRuneInString(remain)
		remain = remain[rlen:]
		if !unicode.IsSpace(r) {
			break
		}
		b += rlen
	}
	if c.b > b {
		c.SetOffsets(b)
		return
	}
	c.SetOffsets(0)
}

func (c *Cursor) MoveEol() {
	// if already eol, move to next line
	if c.AtEol() && !c.OnLastLine() {
		c.MoveDown()
	}

	remain := c.LineData()
	b := 0 // where line contents start
	for len(remain)>0 {
		r, rlen := utf8.DecodeRuneInString(remain)
		remain = remain[rlen:]
		if !unicode.IsSpace(r) {
			break
		}
		b += rlen
	}
	if c.b < b {
		c.SetOffsets(b)
		return
	}
	c.SetOffsets(c.LineByteLength())
}

func (c *Cursor) PageUp() {
	for i:=0; i < pageoffset; i++ {
		if c.OnFirstLine() {
			break
		}
		c.MoveUp()
	}
}

func (c *Cursor) PageDown() {
	for i:=0; i < pageoffset; i++ {
		if c.OnLastLine() {
			break
		}
		c.MoveDown()
	}
}

func (c *Cursor) MoveBof() {
	for {
		if c.OnFirstLine() {
			break
		}
		c.MoveUp()
	}
	c.MoveBol()
}

func (c *Cursor) MoveEof() {
	for {
		if c.OnLastLine() {
			break
		}
		c.MoveDown()
	}
	c.MoveEol()
}

func (c *Cursor) SplitLine() {
	c.t.SplitLine(c.l, c.b)
	c.MoveDown()
	c.SetOffsets(0)
}

func (c *Cursor) Insert(r rune) {
	c.t.Insert(r, c.l, c.b)
	c.MoveRight()
}

func (c *Cursor) Delete() {
	if c.AtEof() {
		return
	}
	if c.AtEol() {
		c.t.JoinNextLine(c.l)
		return
	}
	_, rlen := c.RuneAfter()
	c.t.Remove(c.l, c.b, c.b+rlen)
}

func (c *Cursor) Backspace() {
	if c.AtBof() {
		return
	}
	c.MoveLeft()
	c.Delete()
}

func (c *Cursor) DeleteSelection(sel *Selection) {
	min, max := sel.MinMax()
	bmin := Point{min.l, BFromC(c.t.lines[min.l].data, min.o)}
	bmax := Point{max.l, BFromC(c.t.lines[max.l].data, max.o)}
	c.t.RemoveRange(bmin, bmax)
	c.l = min.l
	c.SetOffsets(bmin.o)
}
<|endoftext|>


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

/*
Package daemon 0.9.2 for use with Go (golang) services.

Package daemon provides primitives for daemonization of golang services.
This package is not provide implementation of user daemon,
accordingly must have root rights to install/remove service.
In the current implementation is only supported Linux and Mac Os X daemon.

Example:

	// Example of a daemon with echo service
	package main

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

		"github.com/takama/daemon"
	)

	const (

		// name of the service
		name        = "myservice"
		description = "My Echo Service"

		// port which daemon should be listen
		port = ":9977"
	)

  // dependencies that are NOT required by the service, but might be used
  var dependencies = []string{"dummy.service"}

	var stdlog, errlog *log.Logger

	// Service has embedded daemon
	type Service struct {
		daemon.Daemon
	}

	// Manage by daemon commands or run the daemon
	func (service *Service) Manage() (string, error) {

		usage := "Usage: myservice install | remove | start | stop | status"

		// if received any kind of command, do it
		if len(os.Args) > 1 {
			command := os.Args[1]
			switch command {
			case "install":
				return service.Install()
			case "remove":
				return service.Remove()
			case "start":
				return service.Start()
			case "stop":
				return service.Stop()
			case "status":
				return service.Status()
			default:
				return usage, nil
			}
		}

		// Do something, call your goroutines, etc

		// Set up channel on which to send signal notifications.
		// We must use a buffered channel or risk missing the signal
		// if we're not ready to receive when the signal is sent.
		interrupt := make(chan os.Signal, 1)
		signal.Notify(interrupt, os.Interrupt, os.Kill, syscall.SIGTERM)

		// Set up listener for defined host and port
		listener, err := net.Listen("tcp", port)
		if err != nil {
			return "Possibly was a problem with the port binding", err
		}

		// set up channel on which to send accepted connections
		listen := make(chan net.Conn, 100)
		go acceptConnection(listener, listen)

		// loop work cycle with accept connections or interrupt
		// by system signal
		for {
			select {
			case conn := <-listen:
				go handleClient(conn)
			case killSignal := <-interrupt:
				stdlog.Println("Got signal:", killSignal)
				stdlog.Println("Stoping listening on ", listener.Addr())
				listener.Close()
				if killSignal == os.Interrupt {
					return "Daemon was interrupted by system signal", nil
				}
				return "Daemon was killed", nil
			}
		}

		// never happen, but need to complete code
		return usage, nil
	}

	// Accept a client connection and collect it in a channel
	func acceptConnection(listener net.Listener, listen chan<- net.Conn) {
		for {
			conn, err := listener.Accept()
			if err != nil {
				continue
			}
			listen <- conn
		}
	}

	func handleClient(client net.Conn) {
		for {
			buf := make([]byte, 4096)
			numbytes, err := client.Read(buf)
			if numbytes == 0 || err != nil {
				return
			}
			client.Write(buf[:numbytes])
		}
	}

	func init() {
		stdlog = log.New(os.Stdout, "", log.Ldate|log.Ltime)
		errlog = log.New(os.Stderr, "", log.Ldate|log.Ltime)
	}

	func main() {
		srv, err := daemon.New(name, description, dependencies...)
		if err != nil {
			errlog.Println("Error: ", err)
			os.Exit(1)
		}
		service := &Service{srv}
		status, err := service.Manage()
		if err != nil {
			errlog.Println(status, "\nError: ", err)
			os.Exit(1)
		}
		fmt.Println(status)
	}

Go daemon
*/
package daemon

import "strings"

// Daemon interface has a standard set of methods/commands
type Daemon interface {

	// Install the service into the system
	Install(args ...string) (string, error)

	// Remove the service and all corresponding files from the system
	Remove() (string, error)

	// Start the service
	Start() (string, error)

	// Stop the service
	Stop() (string, error)

	// Status - check the service status
	Status() (string, error)
}

// New - Create a new daemon
//
// name: name of the service
//
// description: any explanation, what is the service, its purpose
func New(name, description string, dependencies ...string) (Daemon, error) {
	return newDaemon(strings.Join(strings.Fields(name), "_"), description, dependencies)
}
<commit_msg>Bumped version number to 0.10.0<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by
// license that can be found in the LICENSE file.

/*
Package daemon 0.10.0 for use with Go (golang) services.

Package daemon provides primitives for daemonization of golang services.
This package is not provide implementation of user daemon,
accordingly must have root rights to install/remove service.
In the current implementation is only supported Linux and Mac Os X daemon.

Example:

	// Example of a daemon with echo service
	package main

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

		"github.com/takama/daemon"
	)

	const (

		// name of the service
		name        = "myservice"
		description = "My Echo Service"

		// port which daemon should be listen
		port = ":9977"
	)

  // dependencies that are NOT required by the service, but might be used
  var dependencies = []string{"dummy.service"}

	var stdlog, errlog *log.Logger

	// Service has embedded daemon
	type Service struct {
		daemon.Daemon
	}

	// Manage by daemon commands or run the daemon
	func (service *Service) Manage() (string, error) {

		usage := "Usage: myservice install | remove | start | stop | status"

		// if received any kind of command, do it
		if len(os.Args) > 1 {
			command := os.Args[1]
			switch command {
			case "install":
				return service.Install()
			case "remove":
				return service.Remove()
			case "start":
				return service.Start()
			case "stop":
				return service.Stop()
			case "status":
				return service.Status()
			default:
				return usage, nil
			}
		}

		// Do something, call your goroutines, etc

		// Set up channel on which to send signal notifications.
		// We must use a buffered channel or risk missing the signal
		// if we're not ready to receive when the signal is sent.
		interrupt := make(chan os.Signal, 1)
		signal.Notify(interrupt, os.Interrupt, os.Kill, syscall.SIGTERM)

		// Set up listener for defined host and port
		listener, err := net.Listen("tcp", port)
		if err != nil {
			return "Possibly was a problem with the port binding", err
		}

		// set up channel on which to send accepted connections
		listen := make(chan net.Conn, 100)
		go acceptConnection(listener, listen)

		// loop work cycle with accept connections or interrupt
		// by system signal
		for {
			select {
			case conn := <-listen:
				go handleClient(conn)
			case killSignal := <-interrupt:
				stdlog.Println("Got signal:", killSignal)
				stdlog.Println("Stoping listening on ", listener.Addr())
				listener.Close()
				if killSignal == os.Interrupt {
					return "Daemon was interrupted by system signal", nil
				}
				return "Daemon was killed", nil
			}
		}

		// never happen, but need to complete code
		return usage, nil
	}

	// Accept a client connection and collect it in a channel
	func acceptConnection(listener net.Listener, listen chan<- net.Conn) {
		for {
			conn, err := listener.Accept()
			if err != nil {
				continue
			}
			listen <- conn
		}
	}

	func handleClient(client net.Conn) {
		for {
			buf := make([]byte, 4096)
			numbytes, err := client.Read(buf)
			if numbytes == 0 || err != nil {
				return
			}
			client.Write(buf[:numbytes])
		}
	}

	func init() {
		stdlog = log.New(os.Stdout, "", log.Ldate|log.Ltime)
		errlog = log.New(os.Stderr, "", log.Ldate|log.Ltime)
	}

	func main() {
		srv, err := daemon.New(name, description, dependencies...)
		if err != nil {
			errlog.Println("Error: ", err)
			os.Exit(1)
		}
		service := &Service{srv}
		status, err := service.Manage()
		if err != nil {
			errlog.Println(status, "\nError: ", err)
			os.Exit(1)
		}
		fmt.Println(status)
	}

Go daemon
*/
package daemon

import "strings"

// Daemon interface has a standard set of methods/commands
type Daemon interface {

	// Install the service into the system
	Install(args ...string) (string, error)

	// Remove the service and all corresponding files from the system
	Remove() (string, error)

	// Start the service
	Start() (string, error)

	// Stop the service
	Stop() (string, error)

	// Status - check the service status
	Status() (string, error)
}

// New - Create a new daemon
//
// name: name of the service
//
// description: any explanation, what is the service, its purpose
func New(name, description string, dependencies ...string) (Daemon, error) {
	return newDaemon(strings.Join(strings.Fields(name), "_"), description, dependencies)
}
<|endoftext|>


<commit_before>package hc

import (
	"crypto/sha512"
	"encoding/base64"
	"errors"
	"fmt"
	"net"
	"reflect"

	"github.com/brutella/hc/util"
	"github.com/xiam/to"
)

// Config provides  basic cfguration for an IP transport
type Config struct {
	// Path to the storage
	// When empty, the tranport stores the data inside a folder named exactly like the accessory
	StoragePath string

	// Port on which transport is reachable e.g. 12345
	// When empty, the transport uses a random port
	Port string

	// Deprecated: Specifying a static IP is discouraged.
	IP string

	// Pin with has to be entered on iOS client to pair with the accessory
	// When empty, the pin 00102003 is used
	Pin string

	// SetupId used for setup code should be 4 uppercase letters
	SetupId string

	name         string // Accessory name
	id           string // Accessory id
	servePort    int    // Actual port the server listens at (might be differen than Port field)
	version      int64  // Accessory content version (c#)
	categoryId   int    // Accessory category (ci)
	state        int64  // Accessory state (s#)
	protocol     string // Protocol version, default 1.0 (pv)
	discoverable bool   // Flag if accessory is discoverable (sf)
	mfiCompliant bool   // Flag if accessory if Mfi compliant (ff)
	configHash   []byte
}

func defaultConfig(name string) *Config {
	return &Config{
		StoragePath:  name,
		Pin:          "00102003",  // default pin
		Port:         "",          // empty string means that we get port from assigned by the system
		SetupId:      "EASYSETUP", // default setup id
		name:         name,
		id:           util.MAC48Address(util.RandomHexString()),
		version:      1,
		state:        1,
		protocol:     "1.0",
		discoverable: true,
		mfiCompliant: false,
	}
}

// txtRecords returns the config formatted as mDNS txt records
func (cfg Config) txtRecords() map[string]string {
	return map[string]string{
		"pv": cfg.protocol,
		"id": cfg.id,
		"c#": fmt.Sprintf("%d", cfg.version),
		"s#": fmt.Sprintf("%d", cfg.state),
		"sf": fmt.Sprintf("%d", to.Int64(cfg.discoverable)),
		"ff": fmt.Sprintf("%d", to.Int64(cfg.mfiCompliant)),
		"md": cfg.name,
		"ci": fmt.Sprintf("%d", cfg.categoryId),
		"sh": cfg.setupHash(),
	}
}

func (cfg *Config) setupHash() string {
	hashvalue := fmt.Sprintf("%s%s", cfg.SetupId, cfg.id)
	sum := sha512.Sum512([]byte(hashvalue))
	// use only first 4 bytes
	code := []byte{sum[0], sum[1], sum[2], sum[3]}
	encoded := base64.StdEncoding.EncodeToString(code)
	return encoded
}

// loads load the id, version and config hash
func (cfg *Config) load(storage util.Storage) {
	if b, err := storage.Get("uuid"); err == nil && len(b) > 0 {
		cfg.id = string(b)
	}

	if b, err := storage.Get("version"); err == nil && len(b) > 0 {
		cfg.version = to.Int64(string(b))
	}

	if b, err := storage.Get("configHash"); err == nil && len(b) > 0 {
		cfg.configHash = b
	}
}

// save stores the id, version and config
func (cfg *Config) save(storage util.Storage) {
	storage.Set("uuid", []byte(cfg.id))
	storage.Set("version", []byte(fmt.Sprintf("%d", cfg.version)))
	storage.Set("configHash", []byte(cfg.configHash))
}

// merge updates the StoragePath, Pin, Port and IP fields of the receiver from other.
func (cfg *Config) merge(other Config) {
	if dir := other.StoragePath; len(dir) > 0 {
		cfg.StoragePath = dir
	}

	if pin := other.Pin; len(pin) > 0 {
		cfg.Pin = pin
	}

	if port := other.Port; len(port) > 0 {
		cfg.Port = ":" + port
	}

	if ip := other.IP; len(ip) > 0 {
		cfg.IP = ip
	}

	if setupid := other.SetupId; len(setupid) > 0 {
		cfg.SetupId = setupid
	}
}

// updateConfigHash updates configHash of the receiver and increments version
// if new hash is different than old one.
func (cfg *Config) updateConfigHash(hash []byte) {
	if cfg.configHash != nil && reflect.DeepEqual(hash, cfg.configHash) == false {
		cfg.version += 1
	}

	cfg.configHash = hash
}

// getFirstLocalIPAddr returns the first available IP address of the local machine
// This is a fix for Beaglebone Black where net.LookupIP(hostname) return no IP address.
func getFirstLocalIPAddr() (net.IP, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return nil, err
	}

	for _, addr := range addrs {
		var ip net.IP
		switch v := addr.(type) {
		case *net.IPNet:
			ip = v.IP
		case *net.IPAddr:
			ip = v.IP
		}
		if ip == nil || ip.IsLoopback() || ip.IsUnspecified() {
			continue
		}
		ip = ip.To4()
		if ip == nil {
			continue // not an ipv4 address
		}
		return ip, nil
	}

	return nil, errors.New("Could not determine ip address")
}
<commit_msg>Define categoryId as uint8<commit_after>package hc

import (
	"crypto/sha512"
	"encoding/base64"
	"errors"
	"fmt"
	"net"
	"reflect"

	"github.com/brutella/hc/util"
	"github.com/xiam/to"
)

// Config provides  basic cfguration for an IP transport
type Config struct {
	// Path to the storage
	// When empty, the tranport stores the data inside a folder named exactly like the accessory
	StoragePath string

	// Port on which transport is reachable e.g. 12345
	// When empty, the transport uses a random port
	Port string

	// Deprecated: Specifying a static IP is discouraged.
	IP string

	// Pin with has to be entered on iOS client to pair with the accessory
	// When empty, the pin 00102003 is used
	Pin string

	// SetupId used for setup code should be 4 uppercase letters
	SetupId string

	name         string // Accessory name
	id           string // Accessory id
	servePort    int    // Actual port the server listens at (might be differen than Port field)
	version      int64  // Accessory content version (c#)
	categoryId   uint8  // Accessory category (ci)
	state        int64  // Accessory state (s#)
	protocol     string // Protocol version, default 1.0 (pv)
	discoverable bool   // Flag if accessory is discoverable (sf)
	mfiCompliant bool   // Flag if accessory if Mfi compliant (ff)
	configHash   []byte
}

func defaultConfig(name string) *Config {
	return &Config{
		StoragePath:  name,
		Pin:          "00102003",  // default pin
		Port:         "",          // empty string means that we get port from assigned by the system
		SetupId:      "EASYSETUP", // default setup id
		name:         name,
		id:           util.MAC48Address(util.RandomHexString()),
		version:      1,
		state:        1,
		protocol:     "1.0",
		discoverable: true,
		mfiCompliant: false,
	}
}

// txtRecords returns the config formatted as mDNS txt records
func (cfg Config) txtRecords() map[string]string {
	return map[string]string{
		"pv": cfg.protocol,
		"id": cfg.id,
		"c#": fmt.Sprintf("%d", cfg.version),
		"s#": fmt.Sprintf("%d", cfg.state),
		"sf": fmt.Sprintf("%d", to.Int64(cfg.discoverable)),
		"ff": fmt.Sprintf("%d", to.Int64(cfg.mfiCompliant)),
		"md": cfg.name,
		"ci": fmt.Sprintf("%d", cfg.categoryId),
		"sh": cfg.setupHash(),
	}
}

func (cfg *Config) setupHash() string {
	hashvalue := fmt.Sprintf("%s%s", cfg.SetupId, cfg.id)
	sum := sha512.Sum512([]byte(hashvalue))
	// use only first 4 bytes
	code := []byte{sum[0], sum[1], sum[2], sum[3]}
	encoded := base64.StdEncoding.EncodeToString(code)
	return encoded
}

// loads load the id, version and config hash
func (cfg *Config) load(storage util.Storage) {
	if b, err := storage.Get("uuid"); err == nil && len(b) > 0 {
		cfg.id = string(b)
	}

	if b, err := storage.Get("version"); err == nil && len(b) > 0 {
		cfg.version = to.Int64(string(b))
	}

	if b, err := storage.Get("configHash"); err == nil && len(b) > 0 {
		cfg.configHash = b
	}
}

// save stores the id, version and config
func (cfg *Config) save(storage util.Storage) {
	storage.Set("uuid", []byte(cfg.id))
	storage.Set("version", []byte(fmt.Sprintf("%d", cfg.version)))
	storage.Set("configHash", []byte(cfg.configHash))
}

// merge updates the StoragePath, Pin, Port and IP fields of the receiver from other.
func (cfg *Config) merge(other Config) {
	if dir := other.StoragePath; len(dir) > 0 {
		cfg.StoragePath = dir
	}

	if pin := other.Pin; len(pin) > 0 {
		cfg.Pin = pin
	}

	if port := other.Port; len(port) > 0 {
		cfg.Port = ":" + port
	}

	if ip := other.IP; len(ip) > 0 {
		cfg.IP = ip
	}

	if setupid := other.SetupId; len(setupid) > 0 {
		cfg.SetupId = setupid
	}
}

// updateConfigHash updates configHash of the receiver and increments version
// if new hash is different than old one.
func (cfg *Config) updateConfigHash(hash []byte) {
	if cfg.configHash != nil && reflect.DeepEqual(hash, cfg.configHash) == false {
		cfg.version += 1
	}

	cfg.configHash = hash
}

// getFirstLocalIPAddr returns the first available IP address of the local machine
// This is a fix for Beaglebone Black where net.LookupIP(hostname) return no IP address.
func getFirstLocalIPAddr() (net.IP, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return nil, err
	}

	for _, addr := range addrs {
		var ip net.IP
		switch v := addr.(type) {
		case *net.IPNet:
			ip = v.IP
		case *net.IPAddr:
			ip = v.IP
		}
		if ip == nil || ip.IsLoopback() || ip.IsUnspecified() {
			continue
		}
		ip = ip.To4()
		if ip == nil {
			continue // not an ipv4 address
		}
		return ip, nil
	}

	return nil, errors.New("Could not determine ip address")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"strings"
	"io/ioutil"
	"os"
	"os/exec"
	"bytes"
)

// Config is the configuration structure used to instantiate the Google
// provider.
type Config struct {
	Credentials     string
	Project         string
	Region          string
	CredentialsFile string

}

//  TODO: write validation code, currently assumes c.Credentials
//        is either valid json or a file path
func (c *Config) loadAndValidate() (error) {
	var account accountFile

	if c.Credentials != "" {
		// Assume c.Credentials is a JSON string
		if err := parseJSON(&account, c.Credentials); err == nil {
			//  raw account info, write out to a file
			tmpfile, err := ioutil.TempFile("","")
			if err != nil {
				return err
			}
			_, err = tmpfile.WriteString(c.Credentials)
			if err != nil {
				return err
			}
			tmpfile.Close()
			c.CredentialsFile = tmpfile.Name()
			return nil
		} else {
			//  assume we got a file handle and carry on
			return nil
		}
	}
	return fmt.Errorf("Credentials field empty.  That makes it hard to auth, big guy")
}

func (c *Config) cleanupTempAccountFile() {
	if c.Credentials == c.CredentialsFile {
		os.Remove(c.CredentialsFile)
	}
}

//  init function will make sure that gcloud cli is installed,
//  authorized and that dataflow commands are available

func (c *Config) initGcloud() error {
	//  check that gcloud is installed
	_, err := exec.LookPath("gcloud")
	if err != nil {
		return fmt.Errorf("gcloud cli is not installed.  Please install and try again\n")
	}

	//  check that java is installed
	_, err = exec.LookPath("java")
	if err != nil {
		return fmt.Errorf("java jre (at least) is not installed.  Please install and try again\n")
	}

	auth_cmd := exec.Command("gcloud", "auth", "activate-service-account", "--key-file", c.CredentialsFile)
	var stdout, stderr bytes.Buffer
	auth_cmd.Stdout = &stdout
	auth_cmd.Stderr = &stderr
	err = auth_cmd.Run()
	if err != nil {
		return fmt.Errorf("gcloud auth failed with error: %s\n", stderr.String())
	}

	// verify that datacloud functions are installed
	//  this will need to be updated when they come out of alpha
	datacloud_cmd := exec.Command("gcloud", "alpha", "dataflow" , "-h")
	err = datacloud_cmd.Run()
	if err != nil {
		return fmt.Errorf("gcloud dataflow commands not installed.\n")
	}

	return nil
}

//  kubectl is only used when working with pods in a container so we'll check it on its own
func (c *Config) initKubectl(container, zone string) error {
	//  check that kubectl is installed
	_, err := exec.LookPath("kubectl")
	if err != nil {
		return fmt.Errorf("kubectl is not installed.  Please install and try again\n")
	}

	//  project is no longer a cli flag, its only accessible through the config subcommand
	set_proj_cmd := exec.Command("gcloud", "config", "set", "project", c.Project)
	var stdout, stderr bytes.Buffer
	set_proj_cmd.Stdout = &stdout
	set_proj_cmd.Stderr = &stderr
	err = set_proj_cmd.Run()
	if err != nil {
		return fmt.Errorf("Gcloud project set failed: %s\n", stderr.String())
	}

	cred_gen_cmd := exec.Command("gcloud",  "container", "clusters", "get-credentials", container, "--zone=" + zone)
	cred_gen_cmd.Stdout = &stdout
	cred_gen_cmd.Stderr = &stderr
	err = cred_gen_cmd.Run()
	if err != nil {
		return fmt.Errorf("Gcloud container credential fetch failed: %s\n", stderr.String())
	}

	kubectl_check_cmd := exec.Command("kubectl", "config", "view")
	kubectl_check_cmd.Stdout = &stdout
	kubectl_check_cmd.Stderr = &stderr
	err = kubectl_check_cmd.Run()
	if err != nil {
		return fmt.Errorf("Kubectl config view command failed: %q\n", stderr.String())
	}

	return nil
}

// accountFile represents the structure of the account file JSON file.
type accountFile struct {
	PrivateKeyId string `json:"private_key_id"`
	PrivateKey   string `json:"private_key"`
	ClientEmail  string `json:"client_email"`
	ClientId     string `json:"client_id"`
}

func parseJSON(result interface{}, contents string) error {
	r := strings.NewReader(contents)
	dec := json.NewDecoder(r)

	return dec.Decode(result)
}
<commit_msg>Update config.go<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"strings"
	"io/ioutil"
	"os"
	"os/exec"
	"bytes"
)

// Config is the configuration structure used to instantiate the Google
// provider.
type Config struct {
	Credentials     string
	Project         string
	Region          string
	CredentialsFile string

}

//  TODO: write validation code, currently assumes c.Credentials
//        is either valid json or a file path
func (c *Config) loadAndValidate() (error) {
	var account accountFile

	if c.Credentials != "" {
		// Assume c.Credentials is a JSON string
		if err := parseJSON(&account, c.Credentials); err == nil {
			//  raw account info, write out to a file
			tmpfile, err := ioutil.TempFile("","")
			if err != nil {
				return err
			}
			_, err = tmpfile.WriteString(c.Credentials)
			if err != nil {
				return err
			}
			tmpfile.Close()
			c.CredentialsFile = tmpfile.Name()
			return nil
		} else {
			//  assume we got a file handle and carry on
			return nil
		}
	}
	return fmt.Errorf("Credentials field empty.  That makes it hard to auth, big guy")
}

func (c *Config) cleanupTempAccountFile() {
	if c.Credentials == c.CredentialsFile {
		os.Remove(c.CredentialsFile)
	}
}

//  init function will make sure that gcloud cli is installed,
//  authorized and that dataflow commands are available

func (c *Config) initGcloud() error {
	//  check that gcloud is installed
	_, err := exec.LookPath("gcloud")
	if err != nil {
		return fmt.Errorf("gcloud cli is not installed.  Please install and try again\n")
	}

	//  check that java is installed
	_, err = exec.LookPath("java")
	if err != nil {
		return fmt.Errorf("java jre (at least) is not installed.  Please install and try again\n")
	}

	auth_cmd := exec.Command("gcloud", "--verbosity=debug", "--quiet",  "auth", "activate-service-account", "--key-file", c.CredentialsFile)
	var stdout, stderr bytes.Buffer
	auth_cmd.Stdout = &stdout
	auth_cmd.Stderr = &stderr
	err = auth_cmd.Run()
	if err != nil {
		return fmt.Errorf("gcloud auth failed with error: %s\n", stderr.String())
	}

	// verify that datacloud functions are installed
	//  this will need to be updated when they come out of alpha
	datacloud_cmd := exec.Command("gcloud", "alpha", "dataflow" , "-h")
	err = datacloud_cmd.Run()
	if err != nil {
		return fmt.Errorf("gcloud dataflow commands not installed.\n")
	}

	return nil
}

//  kubectl is only used when working with pods in a container so we'll check it on its own
func (c *Config) initKubectl(container, zone string) error {
	//  check that kubectl is installed
	_, err := exec.LookPath("kubectl")
	if err != nil {
		return fmt.Errorf("kubectl is not installed.  Please install and try again\n")
	}

	//  project is no longer a cli flag, its only accessible through the config subcommand
	set_proj_cmd := exec.Command("gcloud", "config", "set", "project", c.Project)
	var stdout, stderr bytes.Buffer
	set_proj_cmd.Stdout = &stdout
	set_proj_cmd.Stderr = &stderr
	err = set_proj_cmd.Run()
	if err != nil {
		return fmt.Errorf("Gcloud project set failed: %s\n", stderr.String())
	}

	cred_gen_cmd := exec.Command("gcloud", "--verbosity=debug", "--quiet", "container", "clusters", "get-credentials", container, "--zone=" + zone)
	cred_gen_cmd.Stdout = &stdout
	cred_gen_cmd.Stderr = &stderr
	err = cred_gen_cmd.Run()
	if err != nil {
		return fmt.Errorf("Gcloud container credential fetch failed: %s\n", stderr.String())
	}

	kubectl_check_cmd := exec.Command("kubectl", "config", "view")
	kubectl_check_cmd.Stdout = &stdout
	kubectl_check_cmd.Stderr = &stderr
	err = kubectl_check_cmd.Run()
	if err != nil {
		return fmt.Errorf("Kubectl config view command failed: %q\n", stderr.String())
	}

	return nil
}

// accountFile represents the structure of the account file JSON file.
type accountFile struct {
	PrivateKeyId string `json:"private_key_id"`
	PrivateKey   string `json:"private_key"`
	ClientEmail  string `json:"client_email"`
	ClientId     string `json:"client_id"`
}

func parseJSON(result interface{}, contents string) error {
	r := strings.NewReader(contents)
	dec := json.NewDecoder(r)

	return dec.Decode(result)
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"github.com/snormore/gologger"
	"io/ioutil"
)

type Config interface{}

func Register(name string, c Config) error {
	return nil
}

type Configurable struct {
	Config interface{}
}

func Read(filePath string, conf *Config) error {
	logger.Info("Loading configuration from %s...", filePath)
	configJson, err := ioutil.ReadFile(filePath)
	if err != nil {
		return err
	}
	return ReadJson(configJson, conf)
}

func ReadJson(configJson []byte, conf *Config) error {
	return json.Unmarshal(configJson, conf)
}
<commit_msg>Refactor to return Config, error pair.<commit_after>package config

import (
	"encoding/json"
	"github.com/snormore/gologger"
	"io/ioutil"
)

type Config interface{}

func Register(name string, c Config) error {
	return nil
}

type Configurable struct {
	Config interface{}
}

func Init(filePath string) (*Config, error) {
	return Read(filePath)
}

func Read(filePath string) (*Config, error) {
	logger.Info("Loading configuration from %s...", filePath)
	configJson, err := ioutil.ReadFile(filePath)
	if err != nil {
		return nil, err
	}
	return ReadJson(configJson)
}

func ReadJson(configJson []byte) (*Config, error) {
	conf := new(Config)
	err := json.Unmarshal(configJson, conf)
	return conf, err
}
<|endoftext|>


<commit_before>package goini

import (
	"errors"
	"strconv"
	"strings"
)

const (
	COMMENT   = ';'
	SEPARATOR = '='

	ERR_KEY_NOT_EXISTS = "Key not exist"
)

var (
	// Strings accepted as boolean.
	boolString = map[string]bool{
		"t":     true,
		"true":  true,
		"y":     true,
		"yes":   true,
		"on":    true,
		"1":     true,
		"f":     false,
		"false": false,
		"n":     false,
		"no":    false,
		"off":   false,
		"0":     false,
	}
)

// raw value
type rawValue string

func (r rawValue) String() string {
	return string(r)
}

// section
type section string

func (s section) String() string {
	return string(s)
}

//config key
type key string

func (k key) String() string {
	return string(k)
}

// Config is the representation of configuration settings.
type Config struct {
	inheritance map[section]section
	data        map[section]map[key]rawValue
}

type OptionsMap struct {
	data map[key]rawValue
}

func (o *OptionsMap) Len() int {
	return len(o.data)
}

func (cfg *Config) GetSection(s string) *OptionsMap {
	c := &OptionsMap{}
	p, ok := cfg.inheritance[section(s)]
	if !ok {
		c.data = make(map[key]rawValue)
	} else {
		c = cfg.GetSection(string(p))
	}

	for k, v := range cfg.data[section(s)] {
		c.data[k] = v
	}
	return c
}

func (cfg *Config) GetSectionList() []string {
	a := make([]string, len(cfg.data))
	i := 0
	for k := range cfg.data {
		a[i] = string(k)
		i++
	}
	return a
}

// Get value as a string, remove quotes if value was quoted into " or '
func (o *OptionsMap) GetString(k key) (string, error) {
	value, exist := o.data[k]
	if !exist {
		return "", errors.New(ERR_KEY_NOT_EXISTS)
	}
	rawString := string(value)
	if len(rawString) > 1 {
		if rawString[0] == '\'' && rawString[len(rawString)-1] == '\'' {
			rawString = rawString[1 : len(rawString)-1]
		} else if rawString[0] == '"' && rawString[len(rawString)-1] == '"' {
			rawString = rawString[1 : len(rawString)-1]
		}
	}
	return rawString, nil
}

func (o *OptionsMap) GetBool(k key) (bool, error) {
	sv, exists := o.data[k]
	if !exists {
		return false, errors.New(ERR_KEY_NOT_EXISTS)
	}

	value, ok := boolString[strings.ToLower(string(sv))]
	if !ok {
		return false, errors.New("could not parse bool value: " + string(sv))
	}

	return value, nil
}

func (o *OptionsMap) GetInt(k string) (int, error) {
	sv, exists := o.data[key(k)]
	if exists {
		return strconv.Atoi(string(sv))
	}

	return 0, errors.New(ERR_KEY_NOT_EXISTS)
}

func (o *OptionsMap) GetFloat(k string) (float64, error) {
	sv, exitsts := o.data[key(k)]
	if exitsts {
		return strconv.ParseFloat(string(sv), 64)
	}

	return 0, errors.New(ERR_KEY_NOT_EXISTS)
}
<commit_msg>added synonym faunction names<commit_after>package goini

import (
	"errors"
	"strconv"
	"strings"
)

const (
	COMMENT   = ';'
	SEPARATOR = '='

	ERR_KEY_NOT_EXISTS = "Key not exist"
)

var (
	// Strings accepted as boolean.
	boolString = map[string]bool{
		"t":     true,
		"true":  true,
		"y":     true,
		"yes":   true,
		"on":    true,
		"1":     true,
		"f":     false,
		"false": false,
		"n":     false,
		"no":    false,
		"off":   false,
		"0":     false,
	}
)

// raw value
type rawValue string

func (r rawValue) String() string {
	return string(r)
}

// section
type section string

func (s section) String() string {
	return string(s)
}

//config key
type key string

func (k key) String() string {
	return string(k)
}

// Config is the representation of configuration settings.
type Config struct {
	inheritance map[section]section
	data        map[section]map[key]rawValue
}

type OptionsMap struct {
	data map[key]rawValue
}

func (o *OptionsMap) Len() int {
	return len(o.data)
}

func (cfg *Config) GetSection(s string) *OptionsMap {
	c := &OptionsMap{}
	p, ok := cfg.inheritance[section(s)]
	if !ok {
		c.data = make(map[key]rawValue)
	} else {
		c = cfg.GetSection(string(p))
	}

	for k, v := range cfg.data[section(s)] {
		c.data[k] = v
	}
	return c
}

func (cfg *Config) GetSectionList() []string {
	a := make([]string, len(cfg.data))
	i := 0
	for k := range cfg.data {
		a[i] = string(k)
		i++
	}
	return a
}

// Get value as a string, remove quotes if value was quoted into " or '
func (o *OptionsMap) GetString(k string) (string, error) {
	value, exist := o.data[key(k)]
	if !exist {
		return "", errors.New(ERR_KEY_NOT_EXISTS)
	}
	rawString := string(value)
	if len(rawString) > 1 {
		if rawString[0] == '\'' && rawString[len(rawString)-1] == '\'' {
			rawString = rawString[1 : len(rawString)-1]
		} else if rawString[0] == '"' && rawString[len(rawString)-1] == '"' {
			rawString = rawString[1 : len(rawString)-1]
		}
	}
	return rawString, nil
}

// synonym for GetString
func (o *OptionsMap) String(k string) (string, error) {
	return o.GetString(k)
}

// Get value as a bool, uses this mapping from string to bool
// "t":     true,
// "true":  true,
// "y":     true,
// "yes":   true,
// "on":    true,
// "1":     true,
// "f":     false,
// "false": false,
// "n":     false,
// "no":    false,
// "off":   false,
// "0":     false,
func (o *OptionsMap) GetBool(k string) (bool, error) {
	sv, exists := o.data[key(k)]
	if !exists {
		return false, errors.New(ERR_KEY_NOT_EXISTS)
	}

	value, ok := boolString[strings.ToLower(string(sv))]
	if !ok {
		return false, errors.New("could not parse bool value: " + string(sv))
	}

	return value, nil
}

// synonym for GetBool
func (o *OptionsMap) Bool(k string) (bool, error) {
	return o.GetBool(k)
}

// Get value as int
func (o *OptionsMap) GetInt(k string) (int, error) {
	sv, exists := o.data[key(k)]
	if exists {
		return strconv.Atoi(string(sv))
	}

	return 0, errors.New(ERR_KEY_NOT_EXISTS)
}

// synonym for GetInt
func (o *OptionsMap) Int(k string) (int, error) {
	return o.GetInt(k)
}

// Get value as float64
func (o *OptionsMap) GetFloat(k string) (float64, error) {
	sv, exitsts := o.data[key(k)]
	if exitsts {
		return strconv.ParseFloat(string(sv), 64)
	}

	return 0, errors.New(ERR_KEY_NOT_EXISTS)
}

// synonym for GetFloat
func (o *OptionsMap) Float64(k string) (float64, error) {
	return o.GetFloat(k)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"regexp"

	"gopkg.in/yaml.v1"
)

const (
	// LRU = Least recently used
	LRU = "LRU"
	// LFU = Least frequently used
	LFU = "LFU"
)

const (
	defaultThrottlingRate             = 60 // Requests per min
	defaultCacheLimit                 = 0  // No. of bytes
	defaultJpegQuality                = 75
	defaultUploadMaxFileSize          = 5 * 1024 * 1024 // No. of bytes
	defaultAllowCustomTransformations = true
	defaultAllowCustomScale           = true
	defaultAsyncUploads               = false
	defaultAuthorisedGet              = false
	defaultAuthorisedUpload           = false
	defaultLocalPath                  = "local-images"
	defaultCacheStrategy              = LRU
)

var (
	// Config is a global configuration object
	Config Configuration
)

// Configuration specifies server configuration options
type Configuration struct {
	throttlingRate, cacheLimit, jpegQuality, uploadMaxFileSize                                  int
	allowCustomTransformations, allowCustomScale, asyncUploads, authorisedGet, authorisedUpload bool
	localPath, cacheStrategy                                                                    string
	transformations                                                                             map[string]Transformation
	eagerTransformations                                                                        []Transformation
}

func configInit(configFilePath string) error {
	Config = Configuration{defaultThrottlingRate, defaultCacheLimit, defaultJpegQuality, defaultUploadMaxFileSize, defaultAllowCustomTransformations, defaultAllowCustomScale, defaultAsyncUploads, defaultAuthorisedGet, defaultAuthorisedUpload, defaultLocalPath, defaultCacheStrategy, make(map[string]Transformation), make([]Transformation, 0)}

	if configFilePath == "" {
		return nil
	}

	data, err := ioutil.ReadFile(configFilePath)
	if err != nil {
		return err
	}

	m := make(map[interface{}]interface{})
	err = yaml.Unmarshal([]byte(data), &m)

	throttlingRate, ok := m["throttling-rate"].(int)
	if ok && throttlingRate >= 0 {
		Config.throttlingRate = throttlingRate
	}

	jpegQuality, ok := m["jpeg-quality"].(int)
	if ok && jpegQuality >= 1 && jpegQuality <= 100 {
		Config.jpegQuality = jpegQuality
	}

	uploadMaxFileSize, ok := m["upload-max-file-size"].(int)
	if ok && uploadMaxFileSize > 0 {
		Config.uploadMaxFileSize = uploadMaxFileSize
	}

	allowCustomTransformations, ok := m["allow-custom-transformations"].(bool)
	if ok {
		Config.allowCustomTransformations = allowCustomTransformations
	}

	allowCustomScale, ok := m["allow-custom-scale"].(bool)
	if ok {
		Config.allowCustomScale = allowCustomScale
	}

	asyncUploads, ok := m["async-uploads"].(bool)
	if ok {
		Config.asyncUploads = asyncUploads
	}

	authorisation, ok := m["authorisation"].(map[interface{}]interface{})
	if ok {
		get, ok := authorisation["get"].(bool)
		if ok {
			Config.authorisedGet = get
		}
		upload, ok := authorisation["upload"].(bool)
		if ok {
			Config.authorisedUpload = upload
		}
	}

	localPath, ok := m["local-path"].(string)
	if ok {
		Config.localPath = localPath
	}

	cache, ok := m["cache"].(map[interface{}]interface{})
	if ok {
		limit, ok := cache["limit"].(int)
		if ok {
			Config.cacheLimit = limit
		}

		strategy, ok := cache["strategy"].(string)
		if ok && (strategy == LRU || strategy == LFU) {
			Config.cacheStrategy = strategy
		}
	}

	transformations, ok := m["transformations"].([]interface{})
	if ok {
		for _, transformationInterface := range transformations {
			transformation, ok := transformationInterface.(map[interface{}]interface{})
			if ok {
				parametersStr, ok := transformation["parameters"].(string)
				if ok {
					params, err := parseParameters(parametersStr)
					if err != nil {
						return fmt.Errorf("invalid transformation parameters: %s (%s)", parametersStr, err)
					}
					name, ok := transformation["name"].(string)
					if ok {
						if !isValidTransformationName(name) {
							return fmt.Errorf("invalid transformation name: %s", name)
						}

						t := Transformation{&params, nil}

						watermarkMap, ok := transformation["watermark"].(map[interface{}]interface{})
						if ok {
							imagePath, ok := watermarkMap["source"].(string)
							if !ok {
								return fmt.Errorf("a watermark needs to have a source specified")
							}
							// x and y will default to 0 if not found in config
							x := watermarkMap["x-pos"].(int)
							y := watermarkMap["y-pos"].(int)
							t.watermark = &Watermark{imagePath, x, y}
						}

						Config.transformations[name] = t

						eager, ok := transformation["eager"].(bool)
						if ok && eager {
							Config.eagerTransformations = append(Config.eagerTransformations, t)
						}
					}
				}
			}
		}
	}

	return nil
}

var (
	transformationNameConfigRe = regexp.MustCompile("^([0-9A-Za-z-]+)$")
)

func isValidTransformationName(name string) bool {
	return transformationNameConfigRe.MatchString(name)
}
<commit_msg>Removed a lot of nesting from config parsing<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"regexp"

	"gopkg.in/yaml.v1"
)

const (
	// LRU = Least recently used
	LRU = "LRU"
	// LFU = Least frequently used
	LFU = "LFU"
)

const (
	defaultThrottlingRate             = 60 // Requests per min
	defaultCacheLimit                 = 0  // No. of bytes
	defaultJpegQuality                = 75
	defaultUploadMaxFileSize          = 5 * 1024 * 1024 // No. of bytes
	defaultAllowCustomTransformations = true
	defaultAllowCustomScale           = true
	defaultAsyncUploads               = false
	defaultAuthorisedGet              = false
	defaultAuthorisedUpload           = false
	defaultLocalPath                  = "local-images"
	defaultCacheStrategy              = LRU
)

var (
	// Config is a global configuration object
	Config Configuration
)

// Configuration specifies server configuration options
type Configuration struct {
	throttlingRate, cacheLimit, jpegQuality, uploadMaxFileSize                                  int
	allowCustomTransformations, allowCustomScale, asyncUploads, authorisedGet, authorisedUpload bool
	localPath, cacheStrategy                                                                    string
	transformations                                                                             map[string]Transformation
	eagerTransformations                                                                        []Transformation
}

func configInit(configFilePath string) error {
	Config = Configuration{defaultThrottlingRate, defaultCacheLimit, defaultJpegQuality, defaultUploadMaxFileSize, defaultAllowCustomTransformations, defaultAllowCustomScale, defaultAsyncUploads, defaultAuthorisedGet, defaultAuthorisedUpload, defaultLocalPath, defaultCacheStrategy, make(map[string]Transformation), make([]Transformation, 0)}

	if configFilePath == "" {
		return nil
	}

	data, err := ioutil.ReadFile(configFilePath)
	if err != nil {
		return err
	}

	m := make(map[interface{}]interface{})
	err = yaml.Unmarshal([]byte(data), &m)

	throttlingRate, ok := m["throttling-rate"].(int)
	if ok && throttlingRate >= 0 {
		Config.throttlingRate = throttlingRate
	}

	jpegQuality, ok := m["jpeg-quality"].(int)
	if ok && jpegQuality >= 1 && jpegQuality <= 100 {
		Config.jpegQuality = jpegQuality
	}

	uploadMaxFileSize, ok := m["upload-max-file-size"].(int)
	if ok && uploadMaxFileSize > 0 {
		Config.uploadMaxFileSize = uploadMaxFileSize
	}

	allowCustomTransformations, ok := m["allow-custom-transformations"].(bool)
	if ok {
		Config.allowCustomTransformations = allowCustomTransformations
	}

	allowCustomScale, ok := m["allow-custom-scale"].(bool)
	if ok {
		Config.allowCustomScale = allowCustomScale
	}

	asyncUploads, ok := m["async-uploads"].(bool)
	if ok {
		Config.asyncUploads = asyncUploads
	}

	authorisation, ok := m["authorisation"].(map[interface{}]interface{})
	if ok {
		get, ok := authorisation["get"].(bool)
		if ok {
			Config.authorisedGet = get
		}
		upload, ok := authorisation["upload"].(bool)
		if ok {
			Config.authorisedUpload = upload
		}
	}

	localPath, ok := m["local-path"].(string)
	if ok {
		Config.localPath = localPath
	}

	cache, ok := m["cache"].(map[interface{}]interface{})
	if ok {
		limit, ok := cache["limit"].(int)
		if ok {
			Config.cacheLimit = limit
		}

		strategy, ok := cache["strategy"].(string)
		if ok && (strategy == LRU || strategy == LFU) {
			Config.cacheStrategy = strategy
		}
	}

	transformations, ok := m["transformations"].([]interface{})
	if !ok {
		return nil
	}

	for _, transformationInterface := range transformations {
		transformation, ok := transformationInterface.(map[interface{}]interface{})
		if !ok {
			continue
		}

		parametersStr, ok := transformation["parameters"].(string)
		if !ok {
			continue
		}

		params, err := parseParameters(parametersStr)
		if err != nil {
			return fmt.Errorf("invalid transformation parameters: %s (%s)", parametersStr, err)
		}

		name, ok := transformation["name"].(string)
		if !ok {
			continue
		}
		if !isValidTransformationName(name) {
			return fmt.Errorf("invalid transformation name: %s", name)
		}

		t := Transformation{&params, nil}

		watermarkMap, ok := transformation["watermark"].(map[interface{}]interface{})
		if ok {
			imagePath, ok := watermarkMap["source"].(string)
			if !ok {
				return fmt.Errorf("a watermark needs to have a source specified")
			}
			// x and y will default to 0 if not found in config
			x := watermarkMap["x-pos"].(int)
			y := watermarkMap["y-pos"].(int)
			t.watermark = &Watermark{imagePath, x, y}
		}

		Config.transformations[name] = t

		eager, ok := transformation["eager"].(bool)
		if ok && eager {
			Config.eagerTransformations = append(Config.eagerTransformations, t)
		}
	}

	return nil
}

var (
	transformationNameConfigRe = regexp.MustCompile("^([0-9A-Za-z-]+)$")
)

func isValidTransformationName(name string) bool {
	return transformationNameConfigRe.MatchString(name)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"os"
)

type ServerConf struct {
	Default   bool         `json:"default"`
	Host      string       `json:"host"`
	Port      int          `json:"port"`
	Bin       string       `json:"bin"`
	Resources ResourceConf `json:"resources"`
	Target    string       `json:"target"`
	Startup   []string     `json:"startup"`
	Builder   []string     `json:"builder"`
	Workspace string       `json:"workspace"`
	GOROOT    string       `json:"GOROOT"`
	GOPATH    []string     `json:"GOPATH"`
}

type ResourceConf struct {
	Ignore string   `json:"ignore"`
	Paths  []string `json:"paths"`
}

type Config struct {
	Port   int          `json:"port"` //proxy port
	GOROOT string       `json:"GOROOT"`
	GOPATH []string     `json:"GOPATH"`
	Server []ServerConf `json:"server"`
}

func LoadConfig(configFile string) (*Config, error) {
	r, err := os.Open(configFile)

	if err != nil {
		return nil, fmt.Errorf("Unable to read configution file: %s\n%s", configFile, err.Error())
	}

	conf := new(Config)

	dec := json.NewDecoder(r)
	if err := dec.Decode(&conf); err != nil {
		return nil, fmt.Errorf("Unable to parse configution file: %s\n%s", configFile, err.Error())
	}

	return conf, nil
}
<commit_msg>Close file handle<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"
)

type ServerConf struct {
	Default   bool         `json:"default"`
	Host      string       `json:"host"`
	Port      int          `json:"port"`
	Bin       string       `json:"bin"`
	Resources ResourceConf `json:"resources"`
	Target    string       `json:"target"`
	Startup   []string     `json:"startup"`
	Builder   []string     `json:"builder"`
	Workspace string       `json:"workspace"`
	GOROOT    string       `json:"GOROOT"`
	GOPATH    []string     `json:"GOPATH"`
}

type ResourceConf struct {
	Ignore string   `json:"ignore"`
	Paths  []string `json:"paths"`
}

type Config struct {
	Port   int          `json:"port"` //proxy port
	GOROOT string       `json:"GOROOT"`
	GOPATH []string     `json:"GOPATH"`
	Server []ServerConf `json:"server"`
}

func LoadConfig(configFile string) (*Config, error) {
	r, err := os.Open(configFile)

	if err != nil {
		return nil, fmt.Errorf("Unable to read configution file: %s\n%s", configFile, err.Error())
	}

	defer r.Close()

	conf := new(Config)

	dec := json.NewDecoder(r)
	if err := dec.Decode(&conf); err != nil {
		return nil, fmt.Errorf("Unable to parse configution file: %s\n%s", configFile, err.Error())
	}

	return conf, nil
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
)

type Settings struct {
	Token      string `json:"token"`
	ProjectId  string `json:"project_id"`
	Host       string `json:"host"`
	Protocol   string `json:"protocol"`
	Port       int    `json:"port"`
	ApiVersion string `json:"api_version"`
}

var (
	presets = map[string]Settings{
		"worker": Settings{Protocol: "https", Port: 443, ApiVersion: "1", Host: "worker-aws-us-east-1.iron.io"},
		"mq":     Settings{Protocol: "https", Port: 443, ApiVersion: "1", Host: "mq-aws-us-east-1.iron.io"},
		"cache":  Settings{Protocol: "https", Port: 443, ApiVersion: "1", Host: "cache-aws-us-east-1.iron.io"},
	}
)

// fullProduct is like "iron_worker" and "iron_mq", not "worker" or "mq", to
// keep some flexibility in future.
func Config(fullProduct string) (settings Settings) {
	pair := strings.SplitN(fullProduct, "_", 2)
	if len(pair) != 2 {
		panic("Invalid product name, has to use prefix.")
	}
	family, product := pair[0], pair[1]

	base, found := presets[product]

	if !found {
		base = Settings{
			Protocol:   "https",
			Port:       443,
			ApiVersion: "1",
			Host:       product + "-aws-us-east-1.iron.io",
		}
	}

	// The global configuration file sets the defaults according to the file hierarchy.
	(&base).globalConfig(family, product)
	fmt.Println(base)

	// The global environment variables overwrite the global configuration file’s values.
	(&base).globalEnv(family, product)
	fmt.Println(base)

	// The product-specific environment variables overwrite everything before them.
	(&base).productEnv(family, product)
	fmt.Println(base)

	// The local configuration file overwrites everything before it according to the file hierarchy.
	(&base).localConfig(family, product)
	fmt.Println(base)

	// The configuration file specified when instantiating the client library overwrites everything before it according to the file hierarchy.
	(&base).forceConfig(family, product)
	fmt.Println(base)

	// The arguments passed when instantiating the client library overwrite everything before them.
	(&base).passedConfig(family, product)
	fmt.Println(base)

	return base
}

func (s *Settings) globalConfig(family, product string) {
	u, err := user.Current()
	if err != nil {
		panic(err.Error())
	}

	path := filepath.Join(u.HomeDir, ".iron.json")
	s.commonConfigFile(family, product, path)
}

// The environment variables the scheme looks for are all of the same formula:
// the camel-cased product name is switched to an underscore (“IronWorker”
// becomes “iron_worker”) and converted to be all capital letters. For the
// global environment variables, “IRON” is used by itself. The value being
// loaded is then joined by an underscore to the name, and again capitalised.
// For example, to retrieve the OAuth token, the client looks for “IRON_TOKEN”.
func (s *Settings) globalEnv(family, product string) {
	eFamily := strings.ToUpper(family) + "_"
	s.commonEnv(eFamily)
}

// In the case of product-specific variables (which override global variables),
// it would be “IRON_WORKER_TOKEN” (for IronWorker).
func (s *Settings) productEnv(family, product string) {
	eProduct := strings.ToUpper(family) + "_" + strings.ToUpper(product) + "_"
	s.commonEnv(eProduct)
}

func (s *Settings) localConfig(family, product string) {
	s.commonConfigFile(family, product, "iron.json")
}
func (s *Settings) forceConfig(family, product string)  {}
func (s *Settings) passedConfig(family, product string) {}

func (s *Settings) commonEnv(prefix string) {
	if token := os.Getenv(prefix + "TOKEN"); token != "" {
		s.Token = token
	}
	if pid := os.Getenv(prefix + "PROJECT_ID"); pid != "" {
		s.ProjectId = pid
	}
	if host := os.Getenv(prefix + "HOST"); host != "" {
		s.Host = host
	}
	if prot := os.Getenv(prefix + "PROTOCOL"); prot != "" {
		s.Protocol = prot
	}
	if port := os.Getenv(prefix + "PORT"); port != "" {
		n, err := strconv.ParseInt(port, 10, 32)
		if err != nil {
			panic(err)
		}
		s.Port = int(n)
	}
	if vers := os.Getenv(prefix + "API_VERSION"); vers != "" {
		s.ApiVersion = vers
	}
}

func (s *Settings) commonConfigFile(family, product, path string) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return
	}

	data := map[string]interface{}{}
	err = json.Unmarshal(content, &data)
	if err != nil {
		panic("Invalid JSON in " + path + ": " + err.Error())
	}

	s.commonConfigMap(data)

	ipData, found := data[family+"_"+product]
	if found {
		pData := ipData.(map[string]interface{})
		s.commonConfigMap(pData)
	}
}

func (s *Settings) commonConfigMap(data map[string]interface{}) {
	if token, found := data["token"]; found {
		s.Token = token.(string)
	}
	if projectId, found := data["project_id"]; found {
		s.ProjectId = projectId.(string)
	}
	if host, found := data["host"]; found {
		s.Host = host.(string)
	}
	if prot, found := data["protocol"]; found {
		s.Protocol = prot.(string)
	}
	if port, found := data["port"]; found {
		s.Port = int(port.(float64))
	}
	if vers, found := data["api_version"]; found {
		s.ApiVersion = vers.(string)
	}
}
<commit_msg>also allow calling UseConfigFile to let libs respect passed config files<commit_after>package config

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
)

type Settings struct {
	Token      string `json:"token"`
	ProjectId  string `json:"project_id"`
	Host       string `json:"host"`
	Protocol   string `json:"protocol"`
	Port       int    `json:"port"`
	ApiVersion string `json:"api_version"`
}

var (
	presets = map[string]Settings{
		"worker": Settings{Protocol: "https", Port: 443, ApiVersion: "1", Host: "worker-aws-us-east-1.iron.io"},
		"mq":     Settings{Protocol: "https", Port: 443, ApiVersion: "1", Host: "mq-aws-us-east-1.iron.io"},
		"cache":  Settings{Protocol: "https", Port: 443, ApiVersion: "1", Host: "cache-aws-us-east-1.iron.io"},
	}
)

// fullProduct is like "iron_worker" and "iron_mq", not "worker" or "mq", to
// keep some flexibility in future.
func Config(fullProduct string) (settings Settings) {
	pair := strings.SplitN(fullProduct, "_", 2)
	if len(pair) != 2 {
		panic("Invalid product name, has to use prefix.")
	}
	family, product := pair[0], pair[1]

	base, found := presets[product]

	if !found {
		base = Settings{
			Protocol:   "https",
			Port:       443,
			ApiVersion: "1",
			Host:       product + "-aws-us-east-1.iron.io",
		}
	}

	(&base).globalConfig(family, product)
	(&base).globalEnv(family, product)
	(&base).productEnv(family, product)
	(&base).localConfig(family, product)

	return base
}

func (s *Settings) globalConfig(family, product string) {
	if u, err := user.Current(); err == nil {
		path := filepath.Join(u.HomeDir, ".iron.json")
		s.UseConfigFile(family, product, path)
	}
}

// The environment variables the scheme looks for are all of the same formula:
// the camel-cased product name is switched to an underscore (“IronWorker”
// becomes “iron_worker”) and converted to be all capital letters. For the
// global environment variables, “IRON” is used by itself. The value being
// loaded is then joined by an underscore to the name, and again capitalised.
// For example, to retrieve the OAuth token, the client looks for “IRON_TOKEN”.
func (s *Settings) globalEnv(family, product string) {
	eFamily := strings.ToUpper(family) + "_"
	s.commonEnv(eFamily)
}

// In the case of product-specific variables (which override global variables),
// it would be “IRON_WORKER_TOKEN” (for IronWorker).
func (s *Settings) productEnv(family, product string) {
	eProduct := strings.ToUpper(family) + "_" + strings.ToUpper(product) + "_"
	s.commonEnv(eProduct)
}

func (s *Settings) localConfig(family, product string) {
	s.UseConfigFile(family, product, "iron.json")
}

func (s *Settings) commonEnv(prefix string) {
	if token := os.Getenv(prefix + "TOKEN"); token != "" {
		s.Token = token
	}
	if pid := os.Getenv(prefix + "PROJECT_ID"); pid != "" {
		s.ProjectId = pid
	}
	if host := os.Getenv(prefix + "HOST"); host != "" {
		s.Host = host
	}
	if prot := os.Getenv(prefix + "PROTOCOL"); prot != "" {
		s.Protocol = prot
	}
	if port := os.Getenv(prefix + "PORT"); port != "" {
		n, err := strconv.ParseInt(port, 10, 32)
		if err != nil {
			panic(err)
		}
		s.Port = int(n)
	}
	if vers := os.Getenv(prefix + "API_VERSION"); vers != "" {
		s.ApiVersion = vers
	}
}

func (s *Settings) UseConfigFile(family, product, path string) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return
	}

	data := map[string]interface{}{}
	err = json.Unmarshal(content, &data)
	if err != nil {
		panic("Invalid JSON in " + path + ": " + err.Error())
	}

	s.commonConfigMap(data)

	ipData, found := data[family+"_"+product]
	if found {
		pData := ipData.(map[string]interface{})
		s.commonConfigMap(pData)
	}
}

func (s *Settings) commonConfigMap(data map[string]interface{}) {
	if token, found := data["token"]; found {
		s.Token = token.(string)
	}
	if projectId, found := data["project_id"]; found {
		s.ProjectId = projectId.(string)
	}
	if host, found := data["host"]; found {
		s.Host = host.(string)
	}
	if prot, found := data["protocol"]; found {
		s.Protocol = prot.(string)
	}
	if port, found := data["port"]; found {
		s.Port = int(port.(float64))
	}
	if vers, found := data["api_version"]; found {
		s.ApiVersion = vers.(string)
	}
}
<|endoftext|>


<commit_before>package amalgam

import (
	"fmt"
	"log"
	"os"
	"os/user"
	"path/filepath"

	_ "github.com/lib/pq"
	"github.com/namsral/flag"
)

var (
	Listen     = ":8000"
	DbName     = ""
	DbHost     = "127.0.0.1"
	DbPort     = 5432
	DbUser     = "user"
	DbPass     = ""
	Verbosity  = 4
	Secret     = ""
	Config     = ""
	CreateConf = false

	FLAGSET *flag.FlagSet = nil

	Confs map[string]interface{}
)

func StringFlag(
	varName *string,
	name string,
	defVal string,
	description string,
) {
	Confs[name] = defVal
	FLAGSET.StringVar(varName, name, defVal, description)
}

func IntFlag(
	varName *int,
	name string,
	defVal int,
	description string,
) {
	Confs[name] = defVal
	FLAGSET.IntVar(varName, name, defVal, description)
}

func BoolFlag(
	varName *bool,
	name string,
	defVal bool,
	description string,
) {
	Confs[name] = defVal
	FLAGSET.BoolVar(varName, name, defVal, description)
}

//func CreateFlag(
//	varName interface{},
//	name string,
//	defVal interface{},
//	description string,
//) {
//	var intVal int
//	var stringVal string
//	var ok = false
//
//	stringVal, ok = defVal.(string)
//	if !ok {
//		intVal, ok = defVal.(int)
//		if !ok {
//			panic("Unhandled flag type!")
//		}
//
//		Confs[name] = defVal
//
//		v, ok := varName.(*int)
//		if !ok {
//			panic("wrong pointer type")
//		}
//		FLAGSET.IntVar(v, name, intVal, description)
//
//	} else {
//		Confs[name] = defVal
//
//		v, ok := varName.(*string)
//		if !ok {
//			panic("wrong pointer type")
//		}
//		FLAGSET.StringVar(v, name, stringVal, description)
//	}
//}

func init() {
	filename, _ := os.Executable()
	Config = filepath.Base(filename) + ".conf"

	Confs = make(map[string]interface{})

	user, err := user.Current()
	if err == nil {
		DbUser = user.Username
	}

	f := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
	FLAGSET = f

	StringFlag(&Listen, "listen", Listen, "http address")
	IntFlag(
		&Verbosity, "verbosity", Verbosity,
		"logging verbosity, 0: none, 4: all",
	)
	StringFlag(&DbName, "dbname", DbName, "database name")
	StringFlag(&DbHost, "dbhost", DbHost, "database host")
	IntFlag(&DbPort, "dbport", DbPort, "database port")
	StringFlag(&DbUser, "dbuser", DbUser, "database user")
	StringFlag(&DbPass, "dbpass", DbPass, "database password")
	StringFlag(&Secret, "secret", Secret, "django secret key")
	BoolFlag(&CreateConf, "create-conf", CreateConf, "")
}

func Init() {
	if err := FLAGSET.Parse(os.Args[1:]); err != nil {
		log.Fatal(err)
	}

	if CreateConf {
		n, err := filepath.Abs(filepath.Dir(os.Args[0]))
		if err != nil {
			log.Fatal(err)
		}

		prodDir := filepath.Dir(n)

		exName, _ := os.Executable()

		confFile := filepath.Join(prodDir, filepath.Base(exName)+".conf")

		if _, err := os.Stat(confFile); err == nil {
			panic("conf files already present!")
		} else {
			writeConfFile(confFile)
		}
	}

	StringFlag(&Config, "config", Config, "config file")

	if err := FLAGSET.Parse(os.Args[1:]); err != nil {
		log.Fatal(err)
	}

	log.Println("config_parsed", "args", os.Args[1:], "flags", FLAGSET.Args())

}

func writeConfFile(confFile string) {
	f, err := os.Create(confFile)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	for key, val := range Confs {
		var stringVal string
		var intVal int
		var ok bool

		stringVal, ok = val.(string)
		if ok {
			f.WriteString(fmt.Sprintf("%s %s\n", key, stringVal))
		}

		intVal, ok = val.(int)
		if ok {
			f.WriteString(fmt.Sprintf("%s %d\n", key, intVal))
		}
	}

}
<commit_msg>conf file generation mods<commit_after>package amalgam

import (
	"fmt"
	"log"
	"os"
	"os/user"
	"path/filepath"

	_ "github.com/lib/pq"
	"github.com/namsral/flag"
)

var (
	Listen     = ":8000"
	DbName     = ""
	DbHost     = "127.0.0.1"
	DbPort     = 5432
	DbUser     = "user"
	DbPass     = ""
	Verbosity  = 4
	Secret     = ""
	Config     = ""
	CreateConf = false

	FLAGSET *flag.FlagSet = nil

	Confs map[string]interface{}
)

func StringFlag(
	varName *string,
	name string,
	defVal string,
	description string,
) {
	Confs[name] = defVal
	FLAGSET.StringVar(varName, name, defVal, description)
}

func IntFlag(
	varName *int,
	name string,
	defVal int,
	description string,
) {
	Confs[name] = defVal
	FLAGSET.IntVar(varName, name, defVal, description)
}

func BoolFlag(
	varName *bool,
	name string,
	defVal bool,
	description string,
) {
	Confs[name] = defVal
	FLAGSET.BoolVar(varName, name, defVal, description)
}

func init() {
	filename, _ := os.Executable()
	Config = filepath.Base(filename) + ".conf"

	Confs = make(map[string]interface{})

	user, err := user.Current()
	if err == nil {
		DbUser = user.Username
	}

	f := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
	FLAGSET = f

	StringFlag(&Listen, "listen", Listen, "http address")
	IntFlag(
		&Verbosity, "verbosity", Verbosity,
		"logging verbosity, 0: none, 4: all",
	)
	StringFlag(&DbName, "dbname", DbName, "database name")
	StringFlag(&DbHost, "dbhost", DbHost, "database host")
	IntFlag(&DbPort, "dbport", DbPort, "database port")
	StringFlag(&DbUser, "dbuser", DbUser, "database user")
	StringFlag(&DbPass, "dbpass", DbPass, "database password")
	StringFlag(&Secret, "secret", Secret, "django secret key")
	BoolFlag(&CreateConf, "create-conf", CreateConf, "")
}

func Init() {
	if err := FLAGSET.Parse(os.Args[1:]); err != nil {
		log.Fatal(err)
	}

	if CreateConf {
		n, err := filepath.Abs(filepath.Dir(os.Args[0]))
		if err != nil {
			log.Fatal(err)
		}

		prodDir := filepath.Dir(n)

		exName, _ := os.Executable()

		confFile := filepath.Join(prodDir, filepath.Base(exName)+".conf")

		if _, err := os.Stat(confFile); err == nil {
			panic("conf files already present!")
		} else {
			writeConfFile(confFile)
		}
	}

	StringFlag(&Config, "config", Config, "config file")

	if err := FLAGSET.Parse(os.Args[1:]); err != nil {
		log.Fatal(err)
	}

	log.Println("config_parsed", "args", os.Args[1:], "flags", FLAGSET.Args())

}

func writeConfFile(confFile string) {
	f, err := os.Create(confFile)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	for key, val := range Confs {
		var stringVal string
		var intVal int
		var ok bool

		stringVal, ok = val.(string)
		if ok {
			f.WriteString(fmt.Sprintf("%s %s\n", key, stringVal))
		}

		intVal, ok = val.(int)
		if ok {
			f.WriteString(fmt.Sprintf("%s %d\n", key, intVal))
		}
	}

}
<|endoftext|>


<commit_before>package main

import "runtime"

const (
	KiteVersion = "0.0.1"

	// Name is the user facing name for this binary. Internally we call it
	// klientctl to avoid confusion.
	Name = "kd"

	// KlientName is the user facing name for klient.
	KlientName = "Koding Service Connector"

	// KlientAddress is url of locally running klient to connect to send
	// user commands.
	KlientAddress = "http://127.0.0.1:56789/kite"

	// KiteHome is full path to the kite key that we will use to authenticate
	// to the given klient.
	KiteHome = "/etc/kite"

	// KlientDirectory is full path to directory that holds klient.
	KlientDirectory = "/opt/kite/klient"

	// KlientctlDirectory is full path to directory that holds klientctl.
	KlientctlDirectory = "/usr/local/bin"

	// KontrolUrl is the url to connect to authenticate local klient and get
	// list of machines.
	KontrolUrl = "https://koding.com/kontrol/kite"

	// Version is the current version of klientctl. This number is used
	// by CheckUpdate to determine if current version is behind or equal to latest
	// version on S3 bucket.
	Version = 1

	osName = runtime.GOOS

	// S3UpdateLocation is publically accessible url to check for new updates.
	S3UpdateLocation = "https://koding-kd.s3.amazonaws.com/latest-version.txt"

	// S3KlientctlPath is publically accessible url for latest version of klient.
	// Each OS has its own version of binary, identifiable by OS suffix.
	S3KlientPath = "https://koding-kd.s3.amazonaws.com/klient-" + osName

	// S3KlientctlPath is publically accessible url for latest version of
	// klientctl. Each OS has its own version of binary, identifiable by suffix.
	S3KlientctlPath = "https://koding-kd.s3.amazonaws.com/klientctl-" + osName

	// SSHDefaultKeyDir is the default directory that stores users ssh key pairs.
	SSHDefaultKeyDir = ".ssh"

	// SSHDefaultKeyDir is the default name of the ssh key pair.
	SSHDefaultKeyName = "kd-ssh-key"
)
<commit_msg>klientctl: Changed name of "Koding Service Connector" to "KD Daemon"<commit_after>package main

import "runtime"

const (
	KiteVersion = "0.0.1"

	// Name is the user facing name for this binary. Internally we call it
	// klientctl to avoid confusion.
	Name = "kd"

	// KlientName is the user facing name for klient.
	KlientName = "KD Daemon"

	// KlientAddress is url of locally running klient to connect to send
	// user commands.
	KlientAddress = "http://127.0.0.1:56789/kite"

	// KiteHome is full path to the kite key that we will use to authenticate
	// to the given klient.
	KiteHome = "/etc/kite"

	// KlientDirectory is full path to directory that holds klient.
	KlientDirectory = "/opt/kite/klient"

	// KlientctlDirectory is full path to directory that holds klientctl.
	KlientctlDirectory = "/usr/local/bin"

	// KontrolUrl is the url to connect to authenticate local klient and get
	// list of machines.
	KontrolUrl = "https://koding.com/kontrol/kite"

	// Version is the current version of klientctl. This number is used
	// by CheckUpdate to determine if current version is behind or equal to latest
	// version on S3 bucket.
	Version = 1

	osName = runtime.GOOS

	// S3UpdateLocation is publically accessible url to check for new updates.
	S3UpdateLocation = "https://koding-kd.s3.amazonaws.com/latest-version.txt"

	// S3KlientctlPath is publically accessible url for latest version of klient.
	// Each OS has its own version of binary, identifiable by OS suffix.
	S3KlientPath = "https://koding-kd.s3.amazonaws.com/klient-" + osName

	// S3KlientctlPath is publically accessible url for latest version of
	// klientctl. Each OS has its own version of binary, identifiable by suffix.
	S3KlientctlPath = "https://koding-kd.s3.amazonaws.com/klientctl-" + osName

	// SSHDefaultKeyDir is the default directory that stores users ssh key pairs.
	SSHDefaultKeyDir = ".ssh"

	// SSHDefaultKeyDir is the default name of the ssh key pair.
	SSHDefaultKeyName = "kd-ssh-key"
)
<|endoftext|>


<commit_before>/*
CompileDaemon is a very simple compile daemon for Go.

CompileDaemon watches your .go files in a directory and invokes `go build`
if a file changes.

Examples

In its simplest form, the defaults will do. With the current working directory set
to the source directory you can simply…

    $ CompileDaemon

… and it will recompile your code whenever you save a source file.

If you want it to also run your program each time it builds you might add…

    $ CompileDaemon -command="./MyProgram -my-options"

… and it will also keep a copy of your program running. Killing the old one and
starting a new one each time you build.

You may find that you need to exclude some directories and files from
monitoring, such as a .git repository or emacs temporary files…

    $ CompileDaemon -exclude-dir=.git -exclude=".#*"

If you want to monitor files other than .go and .c files you might…

    $ CompileDaemon -include=Makefile -include="*.less" -include="*.tmpl"

Options

There are command line options.

	FILE SELECTION
	-directory=XXX    – which directory to monitor for changes
	-recursive=XXX    – look into subdirectories
	-exclude-dir=XXX  – exclude directories matching glob pattern XXX
	-exlude=XXX       – exclude files whose basename matches glob pattern XXX
	-include=XXX      – include files whose basename matches glob pattern XXX
	-pattern=XXX      – include files whose path matches regexp XXX

	MISC
	-color            - enable colorized output
	-log-prefix       - Enable/disable stdout/stderr labelling for the child process
	-graceful-kill    - On supported platforms, send the child process a SIGTERM to
	                    allow it to exit gracefully if possible.
	ACTIONS
	-build=CCC        – Execute CCC to rebuild when a file changes
	-command=CCC      – Run command CCC after a successful build, stops previous command first

*/
package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/fatih/color"
	"github.com/howeyc/fsnotify"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"
	"time"
)

// Milliseconds to wait for the next job to begin after a file change
const WorkDelay = 900

// Default pattern to match files which trigger a build
const FilePattern = `(.+\.go|.+\.c)$`

type globList []string

func (g *globList) String() string {
	return fmt.Sprint(*g)
}
func (g *globList) Set(value string) error {
	*g = append(*g, value)
	return nil
}
func (g *globList) Matches(value string) bool {
	for _, v := range *g {
		if match, err := filepath.Match(v, value); err != nil {
			log.Fatalf("Bad pattern \"%s\": %s", v, err.Error())
		} else if match {
			return true
		}
	}
	return false
}

var (
	flag_directory    = flag.String("directory", ".", "Directory to watch for changes")
	flag_pattern      = flag.String("pattern", FilePattern, "Pattern of watched files")
	flag_command      = flag.String("command", "", "Command to run and restart after build")
	flag_recursive    = flag.Bool("recursive", true, "Watch all dirs. recursively")
	flag_build        = flag.String("build", "go build", "Command to rebuild after changes")
	flag_color        = flag.Bool("color", false, "Colorize output for CompileDaemon status messages")
	flag_logprefix    = flag.Bool("log-prefix", true, "Print log timestamps and subprocess stderr/stdout output")
	flag_gracefulkill = flag.Bool("graceful-kill", false, "Gracefully attempt to kill the child process by sending a SIGTERM first")

	// initialized in main() due to custom type.
	flag_excludedDirs globList
	flag_excludedFiles globList
	flag_includedFiles globList
)

func okColor(format string, args ...interface{}) string {
	if *flag_color {
		return color.GreenString(format, args...)
	} else {
		return fmt.Sprintf(format, args...)
	}
}

func failColor(format string, args ...interface{}) string {
	if *flag_color {
		return color.RedString(format, args...)
	} else {
		return fmt.Sprintf(format, args...)
	}
}

// Run `go build` and print the output if something's gone wrong.
func build() bool {
	log.Println(okColor("Running build command!"))

	args := strings.Split(*flag_build, " ")
	if len(args) == 0 {
		// If the user has specified and empty then we are done.
		return true
	}

	cmd := exec.Command(args[0], args[1:]...)

	cmd.Dir = *flag_directory

	output, err := cmd.CombinedOutput()

	if err == nil {
		log.Println(okColor("Build ok."))
	} else {
		log.Println(failColor("Error while building:\n"), failColor(string(output)))
	}

	return err == nil
}

func matchesPattern(pattern *regexp.Regexp, file string) bool {
	return pattern.MatchString(file)
}

// Accept build jobs and start building when there are no jobs rushing in.
// The inrush protection is WorkDelay milliseconds long, in this period
// every incoming job will reset the timer.
func builder(jobs <-chan string, buildDone chan<- struct{}) {
	createThreshold := func() <-chan time.Time {
		return time.After(time.Duration(WorkDelay * time.Millisecond))
	}

	threshold := createThreshold()

	for {
		select {
		case <-jobs:
			threshold = createThreshold()
		case <-threshold:
			if build() {
				buildDone <- struct{}{}
			}
		}
	}
}

func logger(pipeChan <-chan io.ReadCloser) {
	dumper := func(pipe io.ReadCloser, prefix string) {
		reader := bufio.NewReader(pipe)

	readloop:
		for {
			line, err := reader.ReadString('\n')

			if err != nil {
				break readloop
			}

			if *flag_logprefix {
				log.Print(prefix, " ", line)
			} else {
				log.Print(line)
			}
		}
	}

	for {
		pipe := <-pipeChan
		go dumper(pipe, "stdout:")

		pipe = <-pipeChan
		go dumper(pipe, "stderr:")
	}
}

// Start the supplied command and return stdout and stderr pipes for logging.
func startCommand(command string) (cmd *exec.Cmd, stdout io.ReadCloser, stderr io.ReadCloser, err error) {
	args := strings.Split(command, " ")
	cmd = exec.Command(args[0], args[1:]...)

	if stdout, err = cmd.StdoutPipe(); err != nil {
		err = fmt.Errorf("can't get stdout pipe for command: %s", err)
		return
	}

	if stderr, err = cmd.StderrPipe(); err != nil {
		err = fmt.Errorf("can't get stderr pipe for command: %s", err)
		return
	}

	if err = cmd.Start(); err != nil {
		err = fmt.Errorf("can't start command: %s", err)
		return
	}

	return
}

// Run the command in the given string and restart it after
// a message was received on the buildDone channel.
func runner(command string, buildDone <-chan struct{}) {
	var currentProcess *os.Process
	pipeChan := make(chan io.ReadCloser)

	go logger(pipeChan)

	for {
		<-buildDone

		if currentProcess != nil {
			killProcess(currentProcess)
		}

		log.Println(okColor("Restarting the given command."))
		cmd, stdoutPipe, stderrPipe, err := startCommand(command)

		if err != nil {
			log.Fatal(failColor("Could not start command:", err))
		}

		pipeChan <- stdoutPipe
		pipeChan <- stderrPipe

		currentProcess = cmd.Process
	}
}

func killProcess(process *os.Process) {
	if *flag_gracefulkill {
		killProcessGracefully(process)
	} else {
		killProcessHard(process)
	}
}

func killProcessHard(process *os.Process) {
	log.Println(okColor("Hard stopping the current process.."))

	if err := process.Kill(); err != nil {
		log.Fatal(failColor("Could not kill child process. Aborting due to danger of infinite forks."))
	}

	if _, err := process.Wait(); err != nil {
		log.Fatal(failColor("Could not wait for child process. Aborting due to danger of infinite forks."))
	}
}

func killProcessGracefully(process *os.Process) {
	done := make(chan error, 1)
	go func() {
		log.Println(okColor("Gracefully stopping the current process.."))
		if err := terminateGracefully(process); err != nil {
			done <- err
			return
		}
		_, err := process.Wait()
		done <- err
	}()

	select {
	case <-time.After(3 * time.Second):
		log.Println(failColor("Could not gracefully stop the current process, proceeding to hard stop."))
		killProcessHard(process)
		<-done
	case err := <-done:
		if err != nil {
			log.Fatal(failColor("Could not kill child process. Aborting due to danger of infinite forks."))
		}
	}
}

func flusher(buildDone <-chan struct{}) {
	for {
		<-buildDone
	}
}

func main() {
	flag.Var(&flag_excludedDirs, "exclude-dir", " Don't watch directories matching this name")
	flag.Var(&flag_excludedFiles, "exclude", " Don't watch files matching this name")
	flag.Var(&flag_includedFiles, "include", " Watch files matching this name")

	flag.Parse()

	if !*flag_logprefix {
		log.SetFlags(0)
	}

	if *flag_directory == "" {
		fmt.Fprintf(os.Stderr, "-directory=... is required.\n")
		os.Exit(1)
	}

	if *flag_gracefulkill && !gracefulTerminationPossible() {
		log.Fatal("Graceful termination is not supported on your platform.")
	}

	watcher, err := fsnotify.NewWatcher()

	if err != nil {
		log.Fatal(err)
	}

	defer watcher.Close()

	if *flag_recursive == true {
		err = filepath.Walk(*flag_directory, func(path string, info os.FileInfo, err error) error {
			if err == nil && info.IsDir() {
				if flag_excludedDirs.Matches(info.Name()) {
					return filepath.SkipDir
				} else {
					return watcher.Watch(path)
				}
			}
			return err
		})

		if err != nil {
			log.Fatal("filepath.Walk():", err)
		}

	} else {
		if err := watcher.Watch(*flag_directory); err != nil {
			log.Fatal("watcher.Watch():", err)
		}
	}

	pattern := regexp.MustCompile(*flag_pattern)
	jobs := make(chan string)
	buildDone := make(chan struct{})

	go builder(jobs, buildDone)

	if *flag_command != "" {
		go runner(*flag_command, buildDone)
	} else {
		go flusher(buildDone)
	}

	for {
		select {
		case ev := <-watcher.Event:
			if ev.Name != "" {
				base := filepath.Base(ev.Name)

				if flag_includedFiles.Matches(base) || matchesPattern(pattern, ev.Name) {
					if !flag_excludedFiles.Matches(base) {
						jobs <- ev.Name
					}
				}
			}

		case err := <-watcher.Error:
			if v, ok := err.(*os.SyscallError); ok {
				if v.Err == syscall.EINTR {
					continue
				}
				log.Fatal("watcher.Error: SyscallError:", v)
			}
			log.Fatal("watcher.Error:", err)
		}
	}
}
<commit_msg>Added option to stop command before build<commit_after>/*
CompileDaemon is a very simple compile daemon for Go.

CompileDaemon watches your .go files in a directory and invokes `go build`
if a file changes.

Examples

In its simplest form, the defaults will do. With the current working directory set
to the source directory you can simply…

    $ CompileDaemon

… and it will recompile your code whenever you save a source file.

If you want it to also run your program each time it builds you might add…

    $ CompileDaemon -command="./MyProgram -my-options"

… and it will also keep a copy of your program running. Killing the old one and
starting a new one each time you build.

You may find that you need to exclude some directories and files from
monitoring, such as a .git repository or emacs temporary files…

    $ CompileDaemon -exclude-dir=.git -exclude=".#*"

If you want to monitor files other than .go and .c files you might…

    $ CompileDaemon -include=Makefile -include="*.less" -include="*.tmpl"

Options

There are command line options.

	FILE SELECTION
	-directory=XXX    – which directory to monitor for changes
	-recursive=XXX    – look into subdirectories
	-exclude-dir=XXX  – exclude directories matching glob pattern XXX
	-exlude=XXX       – exclude files whose basename matches glob pattern XXX
	-include=XXX      – include files whose basename matches glob pattern XXX
	-pattern=XXX      – include files whose path matches regexp XXX

	MISC
	-color            - enable colorized output
	-log-prefix       - Enable/disable stdout/stderr labelling for the child process
	-graceful-kill    - On supported platforms, send the child process a SIGTERM to
	                    allow it to exit gracefully if possible.
	ACTIONS
	-build=CCC        – Execute CCC to rebuild when a file changes
	-command=CCC      – Run command CCC after a successful build, stops previous command first

*/
package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/fatih/color"
	"github.com/howeyc/fsnotify"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"
	"time"
)

// Milliseconds to wait for the next job to begin after a file change
const WorkDelay = 900

// Default pattern to match files which trigger a build
const FilePattern = `(.+\.go|.+\.c)$`

type globList []string

func (g *globList) String() string {
	return fmt.Sprint(*g)
}
func (g *globList) Set(value string) error {
	*g = append(*g, value)
	return nil
}
func (g *globList) Matches(value string) bool {
	for _, v := range *g {
		if match, err := filepath.Match(v, value); err != nil {
			log.Fatalf("Bad pattern \"%s\": %s", v, err.Error())
		} else if match {
			return true
		}
	}
	return false
}

var (
	flag_directory    = flag.String("directory", ".", "Directory to watch for changes")
	flag_pattern      = flag.String("pattern", FilePattern, "Pattern of watched files")
	flag_command      = flag.String("command", "", "Command to run and restart after build")
	flag_command_stop  = flag.Bool("command-stop", false, "Stop command before building")
	flag_recursive    = flag.Bool("recursive", true, "Watch all dirs. recursively")
	flag_build        = flag.String("build", "go build", "Command to rebuild after changes")
	flag_color        = flag.Bool("color", false, "Colorize output for CompileDaemon status messages")
	flag_logprefix    = flag.Bool("log-prefix", true, "Print log timestamps and subprocess stderr/stdout output")
	flag_gracefulkill = flag.Bool("graceful-kill", false, "Gracefully attempt to kill the child process by sending a SIGTERM first")

	// initialized in main() due to custom type.
	flag_excludedDirs globList
	flag_excludedFiles globList
	flag_includedFiles globList
)

func okColor(format string, args ...interface{}) string {
	if *flag_color {
		return color.GreenString(format, args...)
	} else {
		return fmt.Sprintf(format, args...)
	}
}

func failColor(format string, args ...interface{}) string {
	if *flag_color {
		return color.RedString(format, args...)
	} else {
		return fmt.Sprintf(format, args...)
	}
}

// Run `go build` and print the output if something's gone wrong.
func build() bool {
	log.Println(okColor("Running build command!"))

	args := strings.Split(*flag_build, " ")
	if len(args) == 0 {
		// If the user has specified and empty then we are done.
		return true
	}

	cmd := exec.Command(args[0], args[1:]...)

	cmd.Dir = *flag_directory

	output, err := cmd.CombinedOutput()

	if err == nil {
		log.Println(okColor("Build ok."))
	} else {
		log.Println(failColor("Error while building:\n"), failColor(string(output)))
	}

	return err == nil
}

func matchesPattern(pattern *regexp.Regexp, file string) bool {
	return pattern.MatchString(file)
}

// Accept build jobs and start building when there are no jobs rushing in.
// The inrush protection is WorkDelay milliseconds long, in this period
// every incoming job will reset the timer.
func builder(jobs <-chan string, buildDone chan<- struct{}) {
	createThreshold := func() <-chan time.Time {
		return time.After(time.Duration(WorkDelay * time.Millisecond))
	}

	threshold := createThreshold()

	for {
		select {
		case <-jobs:
			threshold = createThreshold()
		case <-threshold:
			if *flag_command_stop {
				buildDone <- struct{}{}
			}
			if build() {
				buildDone <- struct{}{}
			}
		}
	}
}

func logger(pipeChan <-chan io.ReadCloser) {
	dumper := func(pipe io.ReadCloser, prefix string) {
		reader := bufio.NewReader(pipe)

	readloop:
		for {
			line, err := reader.ReadString('\n')

			if err != nil {
				break readloop
			}

			if *flag_logprefix {
				log.Print(prefix, " ", line)
			} else {
				log.Print(line)
			}
		}
	}

	for {
		pipe := <-pipeChan
		go dumper(pipe, "stdout:")

		pipe = <-pipeChan
		go dumper(pipe, "stderr:")
	}
}

// Start the supplied command and return stdout and stderr pipes for logging.
func startCommand(command string) (cmd *exec.Cmd, stdout io.ReadCloser, stderr io.ReadCloser, err error) {
	args := strings.Split(command, " ")
	cmd = exec.Command(args[0], args[1:]...)

	if stdout, err = cmd.StdoutPipe(); err != nil {
		err = fmt.Errorf("can't get stdout pipe for command: %s", err)
		return
	}

	if stderr, err = cmd.StderrPipe(); err != nil {
		err = fmt.Errorf("can't get stderr pipe for command: %s", err)
		return
	}

	if err = cmd.Start(); err != nil {
		err = fmt.Errorf("can't start command: %s", err)
		return
	}

	return
}

// Run the command in the given string and restart it after
// a message was received on the buildDone channel.
func runner(command string, buildDone <-chan struct{}) {
	var currentProcess *os.Process
	pipeChan := make(chan io.ReadCloser)

	go logger(pipeChan)

	for {
		<-buildDone

		if currentProcess != nil {
			killProcess(currentProcess)
		}
		if *flag_command_stop {
			log.Println(okColor("Command stopped. Waiting for build to complete."))
			<-buildDone
		}

		log.Println(okColor("Restarting the given command."))
		cmd, stdoutPipe, stderrPipe, err := startCommand(command)

		if err != nil {
			log.Fatal(failColor("Could not start command:", err))
		}

		pipeChan <- stdoutPipe
		pipeChan <- stderrPipe

		currentProcess = cmd.Process
	}
}

func killProcess(process *os.Process) {
	if *flag_gracefulkill {
		killProcessGracefully(process)
	} else {
		killProcessHard(process)
	}
}

func killProcessHard(process *os.Process) {
	log.Println(okColor("Hard stopping the current process.."))

	if err := process.Kill(); err != nil {
		log.Fatal(failColor("Could not kill child process. Aborting due to danger of infinite forks."))
	}

	if _, err := process.Wait(); err != nil {
		log.Fatal(failColor("Could not wait for child process. Aborting due to danger of infinite forks."))
	}
}

func killProcessGracefully(process *os.Process) {
	done := make(chan error, 1)
	go func() {
		log.Println(okColor("Gracefully stopping the current process.."))
		if err := terminateGracefully(process); err != nil {
			done <- err
			return
		}
		_, err := process.Wait()
		done <- err
	}()

	select {
	case <-time.After(3 * time.Second):
		log.Println(failColor("Could not gracefully stop the current process, proceeding to hard stop."))
		killProcessHard(process)
		<-done
	case err := <-done:
		if err != nil {
			log.Fatal(failColor("Could not kill child process. Aborting due to danger of infinite forks."))
		}
	}
}

func flusher(buildDone <-chan struct{}) {
	for {
		if *flag_command_stop {
			<-buildDone
		}
		<-buildDone
	}
}

func main() {
	flag.Var(&flag_excludedDirs, "exclude-dir", " Don't watch directories matching this name")
	flag.Var(&flag_excludedFiles, "exclude", " Don't watch files matching this name")
	flag.Var(&flag_includedFiles, "include", " Watch files matching this name")

	flag.Parse()

	if !*flag_logprefix {
		log.SetFlags(0)
	}

	if *flag_directory == "" {
		fmt.Fprintf(os.Stderr, "-directory=... is required.\n")
		os.Exit(1)
	}

	if *flag_gracefulkill && !gracefulTerminationPossible() {
		log.Fatal("Graceful termination is not supported on your platform.")
	}

	watcher, err := fsnotify.NewWatcher()

	if err != nil {
		log.Fatal(err)
	}

	defer watcher.Close()

	if *flag_recursive == true {
		err = filepath.Walk(*flag_directory, func(path string, info os.FileInfo, err error) error {
			if err == nil && info.IsDir() {
				if flag_excludedDirs.Matches(info.Name()) {
					return filepath.SkipDir
				} else {
					return watcher.Watch(path)
				}
			}
			return err
		})

		if err != nil {
			log.Fatal("filepath.Walk():", err)
		}

	} else {
		if err := watcher.Watch(*flag_directory); err != nil {
			log.Fatal("watcher.Watch():", err)
		}
	}

	pattern := regexp.MustCompile(*flag_pattern)
	jobs := make(chan string)
	buildDone := make(chan struct{})

	go builder(jobs, buildDone)

	if *flag_command != "" {
		go runner(*flag_command, buildDone)
	} else {
		go flusher(buildDone)
	}

	for {
		select {
		case ev := <-watcher.Event:
			if ev.Name != "" {
				base := filepath.Base(ev.Name)

				if flag_includedFiles.Matches(base) || matchesPattern(pattern, ev.Name) {
					if !flag_excludedFiles.Matches(base) {
						jobs <- ev.Name
					}
				}
			}

		case err := <-watcher.Error:
			if v, ok := err.(*os.SyscallError); ok {
				if v.Err == syscall.EINTR {
					continue
				}
				log.Fatal("watcher.Error: SyscallError:", v)
			}
			log.Fatal("watcher.Error:", err)
		}
	}
}
<|endoftext|>