<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

// A Post is reflective of the JSON used in the tumblr API.
// It contains a PhotoURL, and, optionally, an array of photos.
// If Photos isn't empty, it typically contains at least one URL which matches PhotoURL.
type Post struct {
	ID            string
	Type          string
	PhotoURL      string `json:"photo-url-1280"`
	Photos        []Post `json:"photos,omitempty"`
	UnixTimestamp int64  `json:"unix-timestamp"`
	PhotoCaption  string `json:"photo-caption"`

	// for regular posts
	RegularBody string `json:"regular-body"`

	// for answer posts
	Answer string

	// for videos
	Video        string `json:"video-player"`
	VideoCaption string `json:"video-caption"` // For links to outside sites.
}

// A Blog is the outer container for Posts. It is necessary for easier JSON deserialization,
// even though it's useless in and of itself.
type Blog struct {
	Posts []Post `json:"posts"`
}

var (
	inlineSearch   = regexp.MustCompile(`(http:\/\/\d{2}\.media\.tumblr\.com\/\w{32}\/tumblr_inline_\w+\.\w+)`) // FIXME: Possibly buggy/unoptimized.
	videoSearch    = regexp.MustCompile(`"hdUrl":"(.*\/tumblr_\w+)"`)                                           // fuck it
	altVideoSearch = regexp.MustCompile(`source src="(.*tumblr_\w+)(?:\/\d+)?" type`)
	gfycatSearch   = regexp.MustCompile(`href="https?:\/\/(?:www\.)?gfycat\.com\/(\w+)`)
)

var PostParseMap = map[string]func(Post) []string{
	"photo":   parsePhotoPost,
	"answer":  parseAnswerPost,
	"regular": parseRegularPost,
	"video":   parseVideoPost,
}

// TrimJS trims the javascript response received from Tumblr.
// The response starts with "var tumblr_api_read = " and ends with ";".
// We need to remove these to parse the response as JSON.
func TrimJS(c []byte) []byte {
	// The length of "var tumblr_api_read = " is 22.
	return c[22 : len(c)-1]
}

func parsePhotoPost(post Post) (URLs []string) {
	if !ignorePhotos {
		if len(post.Photos) == 0 {
			URLs = append(URLs, post.PhotoURL)
		} else {
			for _, photo := range post.Photos {
				URLs = append(URLs, photo.PhotoURL)
			}
		}
	}

	if !ignoreVideos {
		regexResult := gfycatSearch.FindStringSubmatch(post.PhotoCaption)
		if regexResult != nil {
			for _, v := range regexResult[1:] {
				URLs = append(URLs, GetGfycatURL(v))
			}
		}
	}
	return
}

func parseAnswerPost(post Post) (URLs []string) {
	if !ignorePhotos {
		URLs = inlineSearch.FindAllString(post.Answer, -1)
	}
	return
}

func parseRegularPost(post Post) (URLs []string) {
	if !ignorePhotos {
		URLs = inlineSearch.FindAllString(post.RegularBody, -1)
	}
	return
}

func parseVideoPost(post Post) (URLs []string) {
	if !ignoreVideos {
		regextest := videoSearch.FindStringSubmatch(post.Video)
		if regextest == nil { // hdUrl is false. We have to get the other URL.
			regextest = altVideoSearch.FindStringSubmatch(post.Video)
		}

		// If it's still nil, it means it's another embedded video type, like Youtube, Vine or Pornhub.
		// In that case, ignore it and move on. Not my problem.
		if regextest == nil {
			return
		}
		videoURL := strings.Replace(regextest[1], `\`, ``, -1)

		// If there are problems with downloading video, the below part may be the cause.
		// videoURL = strings.Replace(videoURL, `/480`, ``, -1)
		videoURL += ".mp4"

		URLs = append(URLs, videoURL)

		// Here, we get the GfyCat urls from the post.
		regextest = gfycatSearch.FindStringSubmatch(post.VideoCaption)
		if regextest != nil {
			for _, v := range regextest[1:] {
				URLs = append(URLs, GetGfycatURL(v))
			}
		}
	}
	return
}

func parseDataForFiles(post Post) (URLs []string) {
	fn, ok := PostParseMap[post.Type]
	if ok {
		URLs = fn(post)
	}
	return
}

func makeTumblrURL(user *blog, i int) *url.URL {

	base := fmt.Sprintf("http://%s.tumblr.com/api/read/json", user.name)

	tumblrURL, err := url.Parse(base)
	checkFatalError(err, "tumblrURL: ")

	vals := url.Values{}
	vals.Set("num", "50")
	vals.Add("start", strconv.Itoa((i-1)*50))
	// vals.Add("type", "photo")

	if user.tag != "" {
		vals.Add("tagged", user.tag)
	}

	tumblrURL.RawQuery = vals.Encode()
	return tumblrURL
}

func shouldFinishScraping(lim <-chan time.Time, done <-chan struct{}) bool {
	select {
	case <-done:
		return true
	default:
		select {
		case <-done:
			return true
		case <-lim:
			// We get a value from limiter, and proceed to scrape a page.
			return false
		}
	}
}

// strIntLess calculates `strOld < strNew` as a comparison
// without converting to int.
//
// strIntLess assumes that the strings being compared are positive integers.
func strIntLess(strOld, strNew string) bool {
	lenOld, lenNew := len(strOld), len(strNew)

	if lenOld > lenNew {
		return false
	}
	if lenOld < lenNew {
		return true
	}
	return strOld < strNew
}

func scrape(user *blog, limiter <-chan time.Time) <-chan Image {
	var wg sync.WaitGroup
	var IDMutex sync.RWMutex

	var once sync.Once
	imageChannel := make(chan Image, 1000)

	go func() {

		done := make(chan struct{})
		closeDone := func() { close(done) }
		var i int

		// We need to put all of the following into a function because
		// Go evaluates params at defer instead of at execution.
		// That, and it beats writing `defer` multiple times.
		defer func() {
			fmt.Println("Done scraping for", user.name, "(", i-1, "pages )")
			wg.Wait()
			close(imageChannel)
		}()

		for i = 1; ; i++ {
			if shouldFinishScraping(limiter, done) {
				return
			}

			tumblrURL := makeTumblrURL(user, i)

			// fmt.Println(user.name, "is on page", i)
			showProgress(user.name, "is on page", i)
			resp, err := http.Get(tumblrURL.String())

			// XXX: Ugly as shit. This could probably be done better.
			if err != nil {
				i--
				log.Println(user, err)
				continue
			}
			defer resp.Body.Close()

			contents, _ := ioutil.ReadAll(resp.Body)

			// This is returned as pure javascript. We need to filter out the variable and the ending semicolon.
			contents = TrimJS(contents)

			var blog Blog
			err = json.Unmarshal(contents, &blog)
			if err != nil {
				// Goddamnit tumblr, make a consistent API that doesn't
				// fucking return strings AND booleans in the same field
			}

			if len(blog.Posts) == 0 {
				break
			}

			wg.Add(1)

			go func() {
				defer wg.Done()

				checkFatalError(err)
				for _, post := range blog.Posts {

					IDMutex.RLock()
					if strIntLess(user.highestPostID, post.ID) {
						IDMutex.RUnlock()
						IDMutex.Lock()

						// We need to check again because atomicity isn't guaranteed.
						if strIntLess(user.highestPostID, post.ID) {
							user.highestPostID = post.ID
						}

						IDMutex.Unlock()
					} else {
						IDMutex.RUnlock()
					}

					if updateMode && strIntLess(post.ID, user.lastPostID) {
						once.Do(closeDone)
						break
					}

					URLs := parseDataForFiles(post)

					if len(URLs) == 0 {
						continue
					}

					// fmt.Println(URLs)

					for _, URL := range URLs {
						i := Image{
							User:          user.name,
							URL:           URL,
							UnixTimestamp: post.UnixTimestamp,
							ProgressBar:   user.progressBar,
						}

						filename := path.Base(i.URL)
						pathname := path.Join(downloadDirectory, user.name, filename)

						// If there is a file that exists, we skip adding it and move on to the next one.
						// Or, if update mode is enabled, then we can simply stop searching.
						_, err := os.Stat(pathname)
						if err == nil {
							atomic.AddUint64(&alreadyExists, 1)
							// user.progressBar.Increment()
							continue
						}

						atomic.AddInt64(&user.progressBar.Total, 1)
						if useProgressBar {
							user.progressBar.Update()
						}
						atomic.AddUint64(&totalFound, 1)
						imageChannel <- i
					} // Done adding URLs from a single post

				} // Done searching all posts on a page

			}() // Function that asynchronously adds all URLs to download queue

		} // loop that searches blog, page by page

	}() // Function that asynchronously adds all downloadables from a blog to a queue
	return imageChannel
}
<commit_msg>Fix parser failure<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

// A Post is reflective of the JSON used in the tumblr API.
// It contains a PhotoURL, and, optionally, an array of photos.
// If Photos isn't empty, it typically contains at least one URL which matches PhotoURL.
type Post struct {
	ID            string
	Type          string
	PhotoURL      string `json:"photo-url-1280"`
	Photos        []Post `json:"photos,omitempty"`
	UnixTimestamp int64  `json:"unix-timestamp"`
	PhotoCaption  string `json:"photo-caption"`

	// for regular posts
	RegularBody string `json:"regular-body"`

	// for answer posts
	Answer string

	// for videos
	Video        string `json:"video-player"`
	VideoCaption string `json:"video-caption"` // For links to outside sites.
}

// A Blog is the outer container for Posts. It is necessary for easier JSON deserialization,
// even though it's useless in and of itself.
type Blog struct {
	Posts []Post `json:"posts"`
}

var (
	inlineSearch   = regexp.MustCompile(`(http:\/\/\d{2}\.media\.tumblr\.com\/\w{32}\/tumblr_inline_\w+\.\w+)`) // FIXME: Possibly buggy/unoptimized.
	videoSearch    = regexp.MustCompile(`"hdUrl":"(.*\/tumblr_\w+)"`)                                           // fuck it
	altVideoSearch = regexp.MustCompile(`source src="(.*tumblr_\w+)(?:\/\d+)?" type`)
	gfycatSearch   = regexp.MustCompile(`href="https?:\/\/(?:www\.)?gfycat\.com\/(\w+)`)
)

var PostParseMap = map[string]func(Post) []string{
	"photo":   parsePhotoPost,
	"answer":  parseAnswerPost,
	"regular": parseRegularPost,
	"video":   parseVideoPost,
}

// TrimJS trims the javascript response received from Tumblr.
// The response starts with "var tumblr_api_read = " and ends with ";".
// We need to remove these to parse the response as JSON.
func TrimJS(c []byte) []byte {
	// The length of "var tumblr_api_read = " is 22.
	return c[22 : len(c)-2]
}

func parsePhotoPost(post Post) (URLs []string) {
	if !ignorePhotos {
		if len(post.Photos) == 0 {
			URLs = append(URLs, post.PhotoURL)
		} else {
			for _, photo := range post.Photos {
				URLs = append(URLs, photo.PhotoURL)
			}
		}
	}

	if !ignoreVideos {
		regexResult := gfycatSearch.FindStringSubmatch(post.PhotoCaption)
		if regexResult != nil {
			for _, v := range regexResult[1:] {
				URLs = append(URLs, GetGfycatURL(v))
			}
		}
	}
	return
}

func parseAnswerPost(post Post) (URLs []string) {
	if !ignorePhotos {
		URLs = inlineSearch.FindAllString(post.Answer, -1)
	}
	return
}

func parseRegularPost(post Post) (URLs []string) {
	if !ignorePhotos {
		URLs = inlineSearch.FindAllString(post.RegularBody, -1)
	}
	return
}

func parseVideoPost(post Post) (URLs []string) {
	if !ignoreVideos {
		regextest := videoSearch.FindStringSubmatch(post.Video)
		if regextest == nil { // hdUrl is false. We have to get the other URL.
			regextest = altVideoSearch.FindStringSubmatch(post.Video)
		}

		// If it's still nil, it means it's another embedded video type, like Youtube, Vine or Pornhub.
		// In that case, ignore it and move on. Not my problem.
		if regextest == nil {
			return
		}
		videoURL := strings.Replace(regextest[1], `\`, ``, -1)

		// If there are problems with downloading video, the below part may be the cause.
		// videoURL = strings.Replace(videoURL, `/480`, ``, -1)
		videoURL += ".mp4"

		URLs = append(URLs, videoURL)

		// Here, we get the GfyCat urls from the post.
		regextest = gfycatSearch.FindStringSubmatch(post.VideoCaption)
		if regextest != nil {
			for _, v := range regextest[1:] {
				URLs = append(URLs, GetGfycatURL(v))
			}
		}
	}
	return
}

func parseDataForFiles(post Post) (URLs []string) {
	fn, ok := PostParseMap[post.Type]
	if ok {
		URLs = fn(post)
	}
	return
}

func makeTumblrURL(user *blog, i int) *url.URL {

	base := fmt.Sprintf("http://%s.tumblr.com/api/read/json", user.name)

	tumblrURL, err := url.Parse(base)
	checkFatalError(err, "tumblrURL: ")

	vals := url.Values{}
	vals.Set("num", "50")
	vals.Add("start", strconv.Itoa((i-1)*50))
	// vals.Add("type", "photo")

	if user.tag != "" {
		vals.Add("tagged", user.tag)
	}

	tumblrURL.RawQuery = vals.Encode()
	return tumblrURL
}

func shouldFinishScraping(lim <-chan time.Time, done <-chan struct{}) bool {
	select {
	case <-done:
		return true
	default:
		select {
		case <-done:
			return true
		case <-lim:
			// We get a value from limiter, and proceed to scrape a page.
			return false
		}
	}
}

// strIntLess calculates `strOld < strNew` as a comparison
// without converting to int.
//
// strIntLess assumes that the strings being compared are positive integers.
func strIntLess(strOld, strNew string) bool {
	lenOld, lenNew := len(strOld), len(strNew)

	if lenOld > lenNew {
		return false
	}
	if lenOld < lenNew {
		return true
	}
	return strOld < strNew
}

func scrape(user *blog, limiter <-chan time.Time) <-chan Image {
	var wg sync.WaitGroup
	var IDMutex sync.RWMutex

	var once sync.Once
	imageChannel := make(chan Image, 1000)

	go func() {

		done := make(chan struct{})
		closeDone := func() { close(done) }
		var i int

		// We need to put all of the following into a function because
		// Go evaluates params at defer instead of at execution.
		// That, and it beats writing `defer` multiple times.
		defer func() {
			fmt.Println("Done scraping for", user.name, "(", i-1, "pages )")
			wg.Wait()
			close(imageChannel)
		}()

		for i = 1; ; i++ {
			if shouldFinishScraping(limiter, done) {
				return
			}

			tumblrURL := makeTumblrURL(user, i)

			// fmt.Println(user.name, "is on page", i)
			showProgress(user.name, "is on page", i)
			resp, err := http.Get(tumblrURL.String())

			// XXX: Ugly as shit. This could probably be done better.
			if err != nil {
				i--
				log.Println(user, err)
				continue
			}
			defer resp.Body.Close()

			contents, _ := ioutil.ReadAll(resp.Body)

			// This is returned as pure javascript. We need to filter out the variable and the ending semicolon.
			contents = TrimJS(contents)

			var blog Blog
			err = json.Unmarshal(contents, &blog)
			if err != nil {
				// Goddamnit tumblr, make a consistent API that doesn't
				// fucking return strings AND booleans in the same field
			}

			if len(blog.Posts) == 0 {
				break
			}

			wg.Add(1)

			go func() {
				defer wg.Done()

				checkFatalError(err)
				for _, post := range blog.Posts {

					IDMutex.RLock()
					if strIntLess(user.highestPostID, post.ID) {
						IDMutex.RUnlock()
						IDMutex.Lock()

						// We need to check again because atomicity isn't guaranteed.
						if strIntLess(user.highestPostID, post.ID) {
							user.highestPostID = post.ID
						}

						IDMutex.Unlock()
					} else {
						IDMutex.RUnlock()
					}

					if updateMode && strIntLess(post.ID, user.lastPostID) {
						once.Do(closeDone)
						break
					}

					URLs := parseDataForFiles(post)

					if len(URLs) == 0 {
						continue
					}

					// fmt.Println(URLs)

					for _, URL := range URLs {
						i := Image{
							User:          user.name,
							URL:           URL,
							UnixTimestamp: post.UnixTimestamp,
							ProgressBar:   user.progressBar,
						}

						filename := path.Base(i.URL)
						pathname := path.Join(downloadDirectory, user.name, filename)

						// If there is a file that exists, we skip adding it and move on to the next one.
						// Or, if update mode is enabled, then we can simply stop searching.
						_, err := os.Stat(pathname)
						if err == nil {
							atomic.AddUint64(&alreadyExists, 1)
							// user.progressBar.Increment()
							continue
						}

						atomic.AddInt64(&user.progressBar.Total, 1)
						if useProgressBar {
							user.progressBar.Update()
						}
						atomic.AddUint64(&totalFound, 1)
						imageChannel <- i
					} // Done adding URLs from a single post

				} // Done searching all posts on a page

			}() // Function that asynchronously adds all URLs to download queue

		} // loop that searches blog, page by page

	}() // Function that asynchronously adds all downloadables from a blog to a queue
	return imageChannel
}
<|endoftext|>


<commit_before>/* See LICENSE file for license details. */
package main

import (
	"code.google.com/p/go-sqlite/go1/sqlite3"
	"github.com/SlyMarbo/rss"
	"strings"
	"time"
)

/* RSS feed's */
var RSS map[string]string = map[string]string{
	"miau":    "http://www.lets-hack.it/feed/",
	"marmaro": "http://marmaro.de/lue/feed.rss",
	"kuchen":  "https://kuchen.io/feed",
	"g0tmi1k": "https://blog.g0tmi1k.com/atom.xml",
}

var cursor *sqlite3.Conn

func checkErr(err error) {
	if err != nil {
		panic(err)
	}
}

func init_sql() {
	cursor, _ = sqlite3.Open("feedme.db")

	sql := "CREATE TABLE IF NOT EXISTS feed(" +
		"id        INTEGER PRIMARY KEY," +
		"site      VARCHAR(50)," +
		"title     VARCHAR(50) UNIQUE," +
		"link      VARCHAR(100)," +
		"date      INTEGER);"

	cursor.Exec(sql)
}

func insert_sql(site string, title string, link string, date time.Time) {
	sql := sqlite3.NamedArgs{"$site": site, "$title": title, "$link": link, "$date": date}
	cursor.Exec("INSERT INTO feed (site, title, link, date) VALUES($site, $title, $link, $date)", sql)
}

func main() {
	// initialize SQL database
	init_sql()

	for _, url := range RSS {
		feed, err := rss.Fetch(url)
		checkErr(err)

		err = feed.Update()
		checkErr(err)

		for _, element := range feed.Items {
			title := strings.Replace(feed.Title, "'", "\\'", -1)
			insert_sql(title, element.Title, element.Link, element.Date)
		}
	}
}

/* vim: set noet sw=4 sts=4: */
<commit_msg>Refactor ffetch.go<commit_after><|endoftext|>


<commit_before>// Go language actor framework.
//
// Stability: 1 - Experimental
// (see: https://github.com/tristanls/stability-index#stability-1---experimental)
//
// Create an actor behavior:
//    func Print(self actor.Reference, become actor.Become, msg actor.Message) {
//      for _, param := range msg.Params {
//        fmt.Println(param.(string))
//      }
//    }
//
// Create a new actor configuration:
//    func main() {
//      config := actor.Configuration()
//      ...
//
// Create a new actor:
//      ...
//      printer := config.Create(Print)
//      ...
//
// Send a message to an actor:
//      ...
//      printer <- config.CreateMessage("print me")
//      printer <- actor.CreateMessage("print me")
//      ...
//
// Wait for actor configuration to finish:
//      ...
//      <-config.Done
//    }
package actor

import "fmt"

// Process the next message using the Behavior given
type Become func(Behavior)

// Actors are created with behaviors. Each behavior takes a reference to "self",
// a function to "become" another behavior, and a message to respond to.
type Behavior func(Reference, Become, Message)

// Each message is just a slice of data
type Message struct {
  Params []interface{}
}

// Actor references is how messages are sent to actors
type Reference chan<- Message

// Send is here for completeness, use `Reference <- Message` notation instead
func (reference Reference) Send(msg Message) {
  reference <- msg
}

// Block on ActorConfiguration.Done to wait for all actors to finish, for example:
//    config := actor.Configuration()
//    // send some messages to actors
//    <-config.Done
type ActorConfiguration struct {
  Done chan bool
  countChan chan<- int
  messageCount int
}

// Create a new actor as part of the actor configuration
func (configuration *ActorConfiguration) Create(behavior Behavior) Reference {
  messageCounter := make(chan Message)
  reference := make(chan Message)
  go actorBehavior(*configuration, behavior, reference)
  go func() {
    msg := <-messageCounter
    configuration.countChan <- 1
    reference <- msg
  }()
  return messageCounter
}

// CreateMessage is syntactic sugar to create a Message
func (configuration *ActorConfiguration) CreateMessage(params ...interface{}) Message {
  message := Message{Params: make([]interface{}, len(params), len(params))}
  for index, param := range params {
    message.Params[index] = param
  }
  return message
}

// Creates a new actor configuration
func Configuration() ActorConfiguration {
  countChan := make(chan int)
  configuration := ActorConfiguration{Done: make(chan bool), countChan: countChan, messageCount: 0}
  go func() {
    for {
      i := <-countChan
      if (i == 1) {
        configuration.messageCount++
        fmt.Println("trace: messages in flight", configuration.messageCount)
      } else {
        configuration.messageCount--
        fmt.Println("trace: messages in flight", configuration.messageCount)
        if (configuration.messageCount == 0) {
          configuration.Done <- true
          return
        }
      }
    }
  }()
  return configuration
}

// CreateMessage is syntactic sugar to create a Message
func CreateMessage(params ...interface{}) Message {
  message := Message{Params: make([]interface{}, len(params), len(params))}
  for index, param := range params {
    message.Params[index] = param
  }
  return message
}

func actorBehavior(configuration ActorConfiguration, behavior Behavior, reference chan Message) {
  var become func(Behavior)
  become = func(nextBehavior Behavior) {
    become = func(Behavior){} // become can only be called once
    go actorBehavior(configuration, nextBehavior, reference)
    behavior = nil
  }
  for {
    if behavior != nil {
      messageCounter := make(chan Message)
      self := make(chan Message)
      go func() {
        for msg := range messageCounter {
          configuration.countChan <- 1
          self <- msg
        }
        close(self)
      }()
      msg := <-reference
      fmt.Println("trace: <-", msg.Params)
      behavior(messageCounter, become, msg)
      close(messageCounter)
      go func() {
        for selfMsg := range self {
          fmt.Println("trace: self <-", selfMsg.Params)
          reference <- selfMsg
        }
        configuration.countChan <- -1
      }()
    } else {
      return
    }
  }
}<commit_msg>fixed bug where configuration would deadlock; improve godocs; remove debug trace<commit_after>// Go language actor framework.
//
// Stability: 1 - Experimental
// (see: https://github.com/tristanls/stability-index#stability-1---experimental)
//
//    package main
//
//    import "fmt"
//    import "github.com/tristanls/go-actor"
//
//    // create an actor behavior
//    func Print(self actor.Reference, become actor.Become, msg actor.Message) {
//      for _, param := range msg.Params {
//        fmt.Println(param.(string))
//      }
//    }
//
//    // create a new actor configuration
//    func main() {
//      config := actor.Configuration()
//
//      // create a new actor
//      printer := config.Create(Print)
//
//      // send a message to an actor
//      // both ways of creating a message are identical
//      printer <- config.CreateMessage("hello world")
//      printer <- actor.CreateMessage("hello world")
//      printer <- actor.Message{Params: []interface{}{"hello world"}}
//
//      // wait for actor configuration to finish
//      <-config.Done
//    }
package actor

// Become is a function that takes the behavior to handle the next message
type Become func(Behavior)

// Behavior describes how an actor will respond to a message.
// Actors are created with behaviors. Each behavior takes a reference to "self",
// a function to "become" another behavior, and a message to respond to.
// These behaviors are invoked by the library when executing an actor
// configuration.
type Behavior func(Reference, Become, Message)

// Message is just a slice of data
type Message struct {
  Params []interface{}
}

// Reference to an actor is a channel that accepts messages
type Reference chan<- Message

// Send is here for completeness, use `Reference <- Message` notation instead
func (reference Reference) Send(msg Message) {
  reference <- msg
}

// Block on ActorConfiguration.Done to wait for all actors to finish, for example:
//    config := actor.Configuration()
//    // send some messages to actors
//    <-config.Done
type ActorConfiguration struct {
  Done chan bool
  countChan chan<- int
  messageCount int
}

// Create creats a new actor as part of the actor configuration
func (configuration *ActorConfiguration) Create(behavior Behavior) Reference {
  messageCounter := make(chan Message)
  reference := make(chan Message)
  go actorBehavior(*configuration, behavior, reference)
  go func() {
    for msg := range messageCounter {
    	configuration.countChan <- 1
    	reference <- msg
    }
  }()
  return messageCounter
}

// CreateMessage is syntactic sugar to create a Message
func (configuration *ActorConfiguration) CreateMessage(params ...interface{}) Message {
  message := Message{Params: make([]interface{}, len(params), len(params))}
  for index, param := range params {
    message.Params[index] = param
  }
  return message
}

// start starts the configuration message counter that signals when there are
// no more messages in flight and the configuration has stopped executing
func (configuration *ActorConfiguration) start(countChan chan int) {
  go func() {
    for {
      i := <-countChan
      switch i {
      case 1:
      	configuration.messageCount++
      	// fmt.Println("trace: messages in flight", configuration.messageCount)
      default:
        configuration.messageCount--
        // fmt.Println("trace: messages in flight", configuration.messageCount)
        if (configuration.messageCount == 0) {
        	configuration.Done <- true
        	return
        }
      }
    }
  }()
}

// Configuration creates a new actor configuration
func Configuration() ActorConfiguration {
  countChan := make(chan int)
  configuration := ActorConfiguration{Done: make(chan bool), countChan: countChan, messageCount: 0}
  configuration.start(countChan)
  return configuration
}

// CreateMessage is syntactic sugar to create a Message
func CreateMessage(params ...interface{}) Message {
  message := Message{Params: make([]interface{}, len(params), len(params))}
  for index, param := range params {
    message.Params[index] = param
  }
  return message
}

// actorBehavior implements execution of actor behaviors, become semantics,
// and making sure that the configuration doesn't exit while messages are still
// in flight.
func actorBehavior(configuration ActorConfiguration, behavior Behavior, reference chan Message) {
  var become func(Behavior)
  become = func(nextBehavior Behavior) {
    become = func(Behavior){} // become can only be called once
    go actorBehavior(configuration, nextBehavior, reference)
    behavior = nil
  }
  for {
    if behavior != nil {
      messageCounter := make(chan Message)
      self := make(chan Message)
      go func() {
        for msg := range messageCounter {
          configuration.countChan <- 1
          self <- msg
        }
        close(self)
      }()
      msg := <-reference
      // fmt.Println("trace: <-", msg.Params)
      behavior(messageCounter, become, msg)
      close(messageCounter)
      go func() {
        for selfMsg := range self {
          // fmt.Println("trace: self <-", selfMsg.Params)
          reference <- selfMsg
        }
        configuration.countChan <- -1
      }()
    } else {
      return
    }
  }
}<|endoftext|>


<commit_before>package gorethink

import (
	"fmt"
	"strconv"
	"strings"

	p "gopkg.in/dancannon/gorethink.v1/ql2"
)

// A Query represents a query ready to be sent to the database, A Query differs
// from a Term as it contains both a query type and token. These values are used
// by the database to determine if the query is continuing a previous request
// and also allows the driver to identify the response as they can come out of
// order.
type Query struct {
	Type      p.Query_QueryType
	Token     int64
	Term      *Term
	Opts      map[string]interface{}
	builtTerm interface{}
}

func (q *Query) build() []interface{} {
	res := []interface{}{int(q.Type)}
	if q.Term != nil {
		res = append(res, q.builtTerm)
	}

	if len(q.Opts) > 0 {
		res = append(res, q.Opts)
	}

	return res
}

type termsList []Term
type termsObj map[string]Term

// A Term represents a query that is being built. Terms consist of a an array of
// "sub-terms" and a term type. When a Term is a sub-term the first element of
// the terms data is its parent Term.
//
// When built the term becomes a JSON array, for more information on the format
// see http://rethinkdb.com/docs/writing-drivers/.
type Term struct {
	name     string
	rawQuery bool
	rootTerm bool
	termType p.Term_TermType
	data     interface{}
	args     []Term
	optArgs  map[string]Term
	lastErr  error
}

// build takes the query tree and prepares it to be sent as a JSON
// expression
func (t Term) build() (interface{}, error) {
	var err error

	if t.lastErr != nil {
		return nil, t.lastErr
	}

	if t.rawQuery {
		return t.data, nil
	}

	switch t.termType {
	case p.Term_DATUM:
		return t.data, nil
	case p.Term_MAKE_OBJ:
		res := map[string]interface{}{}
		for k, v := range t.optArgs {
			res[k], err = v.build()
			if err != nil {
				return nil, err
			}
		}
		return res, nil
	case p.Term_BINARY:
		if len(t.args) == 0 {
			return map[string]interface{}{
				"$reql_type$": "BINARY",
				"data":        t.data,
			}, nil
		}
	}

	args := make([]interface{}, len(t.args))
	optArgs := make(map[string]interface{}, len(t.optArgs))

	for i, v := range t.args {
		arg, err := v.build()
		if err != nil {
			return nil, err
		}
		args[i] = arg
	}

	for k, v := range t.optArgs {
		optArgs[k], err = v.build()
		if err != nil {
			return nil, err
		}
	}

	ret := []interface{}{int(t.termType)}

	if len(args) > 0 {
		ret = append(ret, args)
	}
	if len(optArgs) > 0 {
		ret = append(ret, optArgs)
	}

	return ret, nil
}

// String returns a string representation of the query tree
func (t Term) String() string {
	switch t.termType {
	case p.Term_MAKE_ARRAY:
		return fmt.Sprintf("[%s]", strings.Join(argsToStringSlice(t.args), ", "))
	case p.Term_MAKE_OBJ:
		return fmt.Sprintf("{%s}", strings.Join(optArgsToStringSlice(t.optArgs), ", "))
	case p.Term_FUNC:
		// Get string representation of each argument
		args := []string{}
		for _, v := range t.args[0].args {
			args = append(args, fmt.Sprintf("var_%d", v.data))
		}

		return fmt.Sprintf("func(%s r.Term) r.Term { return %s }",
			strings.Join(args, ", "),
			t.args[1].String(),
		)
	case p.Term_VAR:
		return fmt.Sprintf("var_%s", t.args[0])
	case p.Term_IMPLICIT_VAR:
		return "r.Row"
	case p.Term_DATUM:
		switch v := t.data.(type) {
		case string:
			return strconv.Quote(v)
		default:
			return fmt.Sprintf("%v", v)
		}
	case p.Term_BINARY:
		if len(t.args) == 0 {
			return fmt.Sprintf("r.binary(<data>)")
		}
	}

	if t.rootTerm {
		return fmt.Sprintf("r.%s(%s)", t.name, strings.Join(allArgsToStringSlice(t.args, t.optArgs), ", "))
	}
	return fmt.Sprintf("%s.%s(%s)", t.args[0].String(), t.name, strings.Join(allArgsToStringSlice(t.args[1:], t.optArgs), ", "))
}

// OptArgs is an interface used to represent a terms optional arguments. All
// optional argument types have a toMap function, the returned map can be encoded
// and sent as part of the query.
type OptArgs interface {
	toMap() map[string]interface{}
}

// WriteResponse is a helper type used when dealing with the response of a
// write query. It is also returned by the RunWrite function.
type WriteResponse struct {
	Errors        int              `gorethink:"errors"`
	Inserted      int              `gorethink:"inserted"`
	Updated       int              `gorethink:"updated"`
	Unchanged     int              `gorethink:"unchanged"`
	Replaced      int              `gorethink:"replaced"`
	Renamed       int              `gorethink:"renamed"`
	Skipped       int              `gorethink:"skipped"`
	Deleted       int              `gorethink:"deleted"`
	Created       int              `gorethink:"created"`
	DBsCreated    int              `gorethink:"dbs_created"`
	TablesCreated int              `gorethink:"tables_created"`
	Dropped       int              `gorethink:"dropped"`
	DBsDropped    int              `gorethink:"dbs_dropped"`
	TablesDropped int              `gorethink:"tables_dropped"`
	GeneratedKeys []string         `gorethink:"generated_keys"`
	FirstError    string           `gorethink:"first_error"` // populated if Errors > 0
	ConfigChanges []ChangeResponse `gorethink:"config_changes"`
	Changes       []ChangeResponse
}

// ChangeResponse is a helper type used when dealing with changefeeds. The type
// contains both the value before the query and the new value.
type ChangeResponse struct {
	NewValue interface{} `gorethink:"new_val"`
	OldValue interface{} `gorethink:"old_val"`
}

// RunOpts contains the optional arguments for the Run function.
type RunOpts struct {
	DB             interface{} `gorethink:"db,omitempty"`
	Db             interface{} `gorethink:"db,omitempty"` // Deprecated
	Profile        interface{} `gorethink:"profile,omitempty"`
	UseOutdated    interface{} `gorethink:"use_outdated,omitempty"` // Deprecated
	ArrayLimit     interface{} `gorethink:"array_limit,omitempty"`
	TimeFormat     interface{} `gorethink:"time_format,omitempty"`
	GroupFormat    interface{} `gorethink:"group_format,omitempty"`
	BinaryFormat   interface{} `gorethink:"binary_format,omitempty"`
	GeometryFormat interface{} `gorethink:"geometry_format,omitempty"`

	MinBatchRows              interface{} `gorethink:"min_batch_rows,omitempty"`
	MaxBatchRows              interface{} `gorethink:"max_batch_rows,omitempty"`
	MaxBatchBytes             interface{} `gorethink:"max_batch_bytes,omitempty"`
	MaxBatchSeconds           interface{} `gorethink:"max_batch_seconds,omitempty"`
	FirstBatchScaledownFactor interface{} `gorethink:"first_batch_scaledown_factor,omitempty"`
}

func (o *RunOpts) toMap() map[string]interface{} {
	return optArgsToMap(o)
}

// Run runs a query using the given connection.
//
//	rows, err := query.Run(sess)
//	if err != nil {
//		// error
//	}
//
//  var doc MyDocumentType
//	for rows.Next(&doc) {
//      // Do something with document
//	}
func (t Term) Run(s *Session, optArgs ...RunOpts) (*Cursor, error) {
	opts := map[string]interface{}{}
	if len(optArgs) >= 1 {
		opts = optArgs[0].toMap()
	}

	q, err := s.newQuery(t, opts)
	if err != nil {
		return nil, err
	}

	return s.Query(q)
}

// RunWrite runs a query using the given connection but unlike Run automatically
// scans the result into a variable of type WriteResponse. This function should be used
// if you are running a write query (such as Insert,  Update, TableCreate, etc...).
//
// If an error occurs when running the write query the first error is returned.
//
//	res, err := r.DB("database").Table("table").Insert(doc).RunWrite(sess)
func (t Term) RunWrite(s *Session, optArgs ...RunOpts) (WriteResponse, error) {
	var response WriteResponse

	res, err := t.Run(s, optArgs...)
	if err != nil {
		return response, err
	}
	defer res.Close()

	if err = res.One(&response); err != nil {
		return response, err
	}

	if response.Errors > 0 {
		return response, fmt.Errorf(response.FirstError)
	}

	return response, nil
}

// ExecOpts contains the optional arguments for the Exec function and  inherits
// its options from RunOpts, the only difference is the addition of the NoReply
// field.
//
// When NoReply is true it causes the driver not to wait to receive the result
// and return immediately.
type ExecOpts struct {
	DB             interface{} `gorethink:"db,omitempty"`
	Db             interface{} `gorethink:"db,omitempty"` // Deprecated
	Profile        interface{} `gorethink:"profile,omitempty"`
	UseOutdated    interface{} `gorethink:"use_outdated,omitempty"` // Deprecated
	ArrayLimit     interface{} `gorethink:"array_limit,omitempty"`
	TimeFormat     interface{} `gorethink:"time_format,omitempty"`
	GroupFormat    interface{} `gorethink:"group_format,omitempty"`
	BinaryFormat   interface{} `gorethink:"binary_format,omitempty"`
	GeometryFormat interface{} `gorethink:"geometry_format,omitempty"`

	MinBatchRows              interface{} `gorethink:"min_batch_rows,omitempty"`
	MaxBatchRows              interface{} `gorethink:"max_batch_rows,omitempty"`
	MaxBatchBytes             interface{} `gorethink:"max_batch_bytes,omitempty"`
	MaxBatchSeconds           interface{} `gorethink:"max_batch_seconds,omitempty"`
	FirstBatchScaledownFactor interface{} `gorethink:"first_batch_scaledown_factor,omitempty"`

	NoReply interface{} `gorethink:"noreply,omitempty"`
}

func (o *ExecOpts) toMap() map[string]interface{} {
	return optArgsToMap(o)
}

// Exec runs the query but does not return the result. Exec will still wait for
// the response to be received unless the NoReply field is true.
//
//	err := r.DB("database").Table("table").Insert(doc).Exec(sess, r.ExecOpts{
//		NoReply: true,
//	})
func (t Term) Exec(s *Session, optArgs ...ExecOpts) error {
	opts := map[string]interface{}{}
	if len(optArgs) >= 1 {
		opts = optArgs[0].toMap()
	}

	q, err := s.newQuery(t, opts)
	if err != nil {
		return err
	}

	return s.Exec(q)
}
<commit_msg>Fix panic<commit_after>package gorethink

import (
	"fmt"
	"strconv"
	"strings"

	p "gopkg.in/dancannon/gorethink.v1/ql2"
)

// A Query represents a query ready to be sent to the database, A Query differs
// from a Term as it contains both a query type and token. These values are used
// by the database to determine if the query is continuing a previous request
// and also allows the driver to identify the response as they can come out of
// order.
type Query struct {
	Type      p.Query_QueryType
	Token     int64
	Term      *Term
	Opts      map[string]interface{}
	builtTerm interface{}
}

func (q *Query) build() []interface{} {
	res := []interface{}{int(q.Type)}
	if q.Term != nil {
		res = append(res, q.builtTerm)
	}

	if len(q.Opts) > 0 {
		res = append(res, q.Opts)
	}

	return res
}

type termsList []Term
type termsObj map[string]Term

// A Term represents a query that is being built. Terms consist of a an array of
// "sub-terms" and a term type. When a Term is a sub-term the first element of
// the terms data is its parent Term.
//
// When built the term becomes a JSON array, for more information on the format
// see http://rethinkdb.com/docs/writing-drivers/.
type Term struct {
	name     string
	rawQuery bool
	rootTerm bool
	termType p.Term_TermType
	data     interface{}
	args     []Term
	optArgs  map[string]Term
	lastErr  error
}

// build takes the query tree and prepares it to be sent as a JSON
// expression
func (t Term) build() (interface{}, error) {
	var err error

	if t.lastErr != nil {
		return nil, t.lastErr
	}

	if t.rawQuery {
		return t.data, nil
	}

	switch t.termType {
	case p.Term_DATUM:
		return t.data, nil
	case p.Term_MAKE_OBJ:
		res := map[string]interface{}{}
		for k, v := range t.optArgs {
			res[k], err = v.build()
			if err != nil {
				return nil, err
			}
		}
		return res, nil
	case p.Term_BINARY:
		if len(t.args) == 0 {
			return map[string]interface{}{
				"$reql_type$": "BINARY",
				"data":        t.data,
			}, nil
		}
	}

	args := make([]interface{}, len(t.args))
	optArgs := make(map[string]interface{}, len(t.optArgs))

	for i, v := range t.args {
		arg, err := v.build()
		if err != nil {
			return nil, err
		}
		args[i] = arg
	}

	for k, v := range t.optArgs {
		optArgs[k], err = v.build()
		if err != nil {
			return nil, err
		}
	}

	ret := []interface{}{int(t.termType)}

	if len(args) > 0 {
		ret = append(ret, args)
	}
	if len(optArgs) > 0 {
		ret = append(ret, optArgs)
	}

	return ret, nil
}

// String returns a string representation of the query tree
func (t Term) String() string {
	switch t.termType {
	case p.Term_MAKE_ARRAY:
		return fmt.Sprintf("[%s]", strings.Join(argsToStringSlice(t.args), ", "))
	case p.Term_MAKE_OBJ:
		return fmt.Sprintf("{%s}", strings.Join(optArgsToStringSlice(t.optArgs), ", "))
	case p.Term_FUNC:
		// Get string representation of each argument
		args := []string{}
		for _, v := range t.args[0].args {
			args = append(args, fmt.Sprintf("var_%d", v.data))
		}

		return fmt.Sprintf("func(%s r.Term) r.Term { return %s }",
			strings.Join(args, ", "),
			t.args[1].String(),
		)
	case p.Term_VAR:
		return fmt.Sprintf("var_%s", t.args[0])
	case p.Term_IMPLICIT_VAR:
		return "r.Row"
	case p.Term_DATUM:
		switch v := t.data.(type) {
		case string:
			return strconv.Quote(v)
		default:
			return fmt.Sprintf("%v", v)
		}
	case p.Term_BINARY:
		if len(t.args) == 0 {
			return fmt.Sprintf("r.binary(<data>)")
		}
	}

	if t.rootTerm {
		return fmt.Sprintf("r.%s(%s)", t.name, strings.Join(allArgsToStringSlice(t.args, t.optArgs), ", "))
	}

	if t.args == nil {
		return "r"
	}

	return fmt.Sprintf("%s.%s(%s)", t.args[0].String(), t.name, strings.Join(allArgsToStringSlice(t.args[1:], t.optArgs), ", "))
}

// OptArgs is an interface used to represent a terms optional arguments. All
// optional argument types have a toMap function, the returned map can be encoded
// and sent as part of the query.
type OptArgs interface {
	toMap() map[string]interface{}
}

// WriteResponse is a helper type used when dealing with the response of a
// write query. It is also returned by the RunWrite function.
type WriteResponse struct {
	Errors        int              `gorethink:"errors"`
	Inserted      int              `gorethink:"inserted"`
	Updated       int              `gorethink:"updated"`
	Unchanged     int              `gorethink:"unchanged"`
	Replaced      int              `gorethink:"replaced"`
	Renamed       int              `gorethink:"renamed"`
	Skipped       int              `gorethink:"skipped"`
	Deleted       int              `gorethink:"deleted"`
	Created       int              `gorethink:"created"`
	DBsCreated    int              `gorethink:"dbs_created"`
	TablesCreated int              `gorethink:"tables_created"`
	Dropped       int              `gorethink:"dropped"`
	DBsDropped    int              `gorethink:"dbs_dropped"`
	TablesDropped int              `gorethink:"tables_dropped"`
	GeneratedKeys []string         `gorethink:"generated_keys"`
	FirstError    string           `gorethink:"first_error"` // populated if Errors > 0
	ConfigChanges []ChangeResponse `gorethink:"config_changes"`
	Changes       []ChangeResponse
}

// ChangeResponse is a helper type used when dealing with changefeeds. The type
// contains both the value before the query and the new value.
type ChangeResponse struct {
	NewValue interface{} `gorethink:"new_val"`
	OldValue interface{} `gorethink:"old_val"`
}

// RunOpts contains the optional arguments for the Run function.
type RunOpts struct {
	DB             interface{} `gorethink:"db,omitempty"`
	Db             interface{} `gorethink:"db,omitempty"` // Deprecated
	Profile        interface{} `gorethink:"profile,omitempty"`
	UseOutdated    interface{} `gorethink:"use_outdated,omitempty"` // Deprecated
	ArrayLimit     interface{} `gorethink:"array_limit,omitempty"`
	TimeFormat     interface{} `gorethink:"time_format,omitempty"`
	GroupFormat    interface{} `gorethink:"group_format,omitempty"`
	BinaryFormat   interface{} `gorethink:"binary_format,omitempty"`
	GeometryFormat interface{} `gorethink:"geometry_format,omitempty"`

	MinBatchRows              interface{} `gorethink:"min_batch_rows,omitempty"`
	MaxBatchRows              interface{} `gorethink:"max_batch_rows,omitempty"`
	MaxBatchBytes             interface{} `gorethink:"max_batch_bytes,omitempty"`
	MaxBatchSeconds           interface{} `gorethink:"max_batch_seconds,omitempty"`
	FirstBatchScaledownFactor interface{} `gorethink:"first_batch_scaledown_factor,omitempty"`
}

func (o *RunOpts) toMap() map[string]interface{} {
	return optArgsToMap(o)
}

// Run runs a query using the given connection.
//
//	rows, err := query.Run(sess)
//	if err != nil {
//		// error
//	}
//
//  var doc MyDocumentType
//	for rows.Next(&doc) {
//      // Do something with document
//	}
func (t Term) Run(s *Session, optArgs ...RunOpts) (*Cursor, error) {
	opts := map[string]interface{}{}
	if len(optArgs) >= 1 {
		opts = optArgs[0].toMap()
	}

	q, err := s.newQuery(t, opts)
	if err != nil {
		return nil, err
	}

	return s.Query(q)
}

// RunWrite runs a query using the given connection but unlike Run automatically
// scans the result into a variable of type WriteResponse. This function should be used
// if you are running a write query (such as Insert,  Update, TableCreate, etc...).
//
// If an error occurs when running the write query the first error is returned.
//
//	res, err := r.DB("database").Table("table").Insert(doc).RunWrite(sess)
func (t Term) RunWrite(s *Session, optArgs ...RunOpts) (WriteResponse, error) {
	var response WriteResponse

	res, err := t.Run(s, optArgs...)
	if err != nil {
		return response, err
	}
	defer res.Close()

	if err = res.One(&response); err != nil {
		return response, err
	}

	if response.Errors > 0 {
		return response, fmt.Errorf(response.FirstError)
	}

	return response, nil
}

// ExecOpts contains the optional arguments for the Exec function and  inherits
// its options from RunOpts, the only difference is the addition of the NoReply
// field.
//
// When NoReply is true it causes the driver not to wait to receive the result
// and return immediately.
type ExecOpts struct {
	DB             interface{} `gorethink:"db,omitempty"`
	Db             interface{} `gorethink:"db,omitempty"` // Deprecated
	Profile        interface{} `gorethink:"profile,omitempty"`
	UseOutdated    interface{} `gorethink:"use_outdated,omitempty"` // Deprecated
	ArrayLimit     interface{} `gorethink:"array_limit,omitempty"`
	TimeFormat     interface{} `gorethink:"time_format,omitempty"`
	GroupFormat    interface{} `gorethink:"group_format,omitempty"`
	BinaryFormat   interface{} `gorethink:"binary_format,omitempty"`
	GeometryFormat interface{} `gorethink:"geometry_format,omitempty"`

	MinBatchRows              interface{} `gorethink:"min_batch_rows,omitempty"`
	MaxBatchRows              interface{} `gorethink:"max_batch_rows,omitempty"`
	MaxBatchBytes             interface{} `gorethink:"max_batch_bytes,omitempty"`
	MaxBatchSeconds           interface{} `gorethink:"max_batch_seconds,omitempty"`
	FirstBatchScaledownFactor interface{} `gorethink:"first_batch_scaledown_factor,omitempty"`

	NoReply interface{} `gorethink:"noreply,omitempty"`
}

func (o *ExecOpts) toMap() map[string]interface{} {
	return optArgsToMap(o)
}

// Exec runs the query but does not return the result. Exec will still wait for
// the response to be received unless the NoReply field is true.
//
//	err := r.DB("database").Table("table").Insert(doc).Exec(sess, r.ExecOpts{
//		NoReply: true,
//	})
func (t Term) Exec(s *Session, optArgs ...ExecOpts) error {
	opts := map[string]interface{}{}
	if len(optArgs) >= 1 {
		opts = optArgs[0].toMap()
	}

	q, err := s.newQuery(t, opts)
	if err != nil {
		return err
	}

	return s.Exec(q)
}
<|endoftext|>


<commit_before>package ds

import (
	"context"
	"time"

	"cloud.google.com/go/datastore"
)

// Query is the function for set datastore query
type Query func(q *datastore.Query) *datastore.Query

// Query run Get All
// dst is *[]*Model
func (client *Client) Query(ctx context.Context, kind KindGetter, dst interface{}, qs ...Query) error {
	q := datastore.NewQuery(kind.Kind())
	for _, setter := range qs {
		q = setter(q)
	}

	keys, err := client.GetAll(ctx, q, dst)
	if err != nil {
		return err
	}
	setKeys(keys, dst)
	return nil
}

// QueryFirst run Get to get the first result
func (client *Client) QueryFirst(ctx context.Context, dst interface{}, qs ...Query) error {
	q := datastore.NewQuery(dst.(KindGetter).Kind())
	for _, setter := range qs {
		q = setter(q)
	}

	key, err := client.Run(ctx, q).Next(dst)
	if err != nil {
		return err
	}

	if x, ok := dst.(KeySetter); ok {
		x.SetKey(key)
	}
	return nil
}

// QueryKeys queries only key
func (client *Client) QueryKeys(ctx context.Context, kind KindGetter, qs ...Query) ([]*datastore.Key, error) {
	q := datastore.NewQuery(kind.Kind())
	for _, setter := range qs {
		q = setter(q)
	}
	q = q.KeysOnly()

	keys, err := client.GetAll(ctx, q, nil)
	if err != nil {
		return nil, err
	}
	return keys, nil
}

// Query Helper functions

// Filter func
func Filter(filterStr string, value interface{}) Query {
	return func(q *datastore.Query) *datastore.Query {
		return q.Filter(filterStr, value)
	}
}

// CreateAfter quries is model created after (or equals) given time
func CreateAfter(t time.Time, equals bool) Query {
	p := "CreatedAt >"
	if equals {
		p += "="
	}
	return Filter(p, t)
}

// UpdateBefore queries is model updated before (or equals) given time
func UpdateBefore(t time.Time, equals bool) Query {
	p := "UpdatedAt <"
	if equals {
		p += "="
	}
	return Filter(p, t)
}

// UpdateAfter queries is model updated after (or equals) given time
func UpdateAfter(t time.Time, equals bool) Query {
	p := "UpdatedAt >"
	if equals {
		p += "="
	}
	return Filter(p, t)
}

// Offset adds offset to query
func Offset(offset int) Query {
	return func(q *datastore.Query) *datastore.Query {
		return q.Offset(offset)
	}
}

// Limit adds limit to query
func Limit(limit int) Query {
	return func(q *datastore.Query) *datastore.Query {
		return q.Limit(limit)
	}
}

// Namespace adds namespace to query
func Namespace(ns string) Query {
	return func(q *datastore.Query) *datastore.Query {
		return q.Namespace(ns)
	}
}

// Order adds order to query
func Order(fieldName string) Query {
	return func(q *datastore.Query) *datastore.Query {
		return q.Order(fieldName)
	}
}

// Project adds order to query
func Project(fieldNames ...string) Query {
	return func(q *datastore.Query) *datastore.Query {
		return q.Project(fieldNames...)
	}
}
<commit_msg>Add transaction query<commit_after>package ds

import (
	"context"
	"time"

	"cloud.google.com/go/datastore"
)

// Query is the function for set datastore query
type Query func(q *datastore.Query) *datastore.Query

// Query run Get All
// dst is *[]*Model
func (client *Client) Query(ctx context.Context, kind KindGetter, dst interface{}, qs ...Query) error {
	q := datastore.NewQuery(kind.Kind())
	for _, setter := range qs {
		q = setter(q)
	}

	keys, err := client.GetAll(ctx, q, dst)
	if err != nil {
		return err
	}
	setKeys(keys, dst)
	return nil
}

// QueryFirst run Get to get the first result
func (client *Client) QueryFirst(ctx context.Context, dst interface{}, qs ...Query) error {
	q := datastore.NewQuery(dst.(KindGetter).Kind())
	for _, setter := range qs {
		q = setter(q)
	}

	key, err := client.Run(ctx, q).Next(dst)
	if err != nil {
		return err
	}

	if x, ok := dst.(KeySetter); ok {
		x.SetKey(key)
	}
	return nil
}

// QueryKeys queries only key
func (client *Client) QueryKeys(ctx context.Context, kind KindGetter, qs ...Query) ([]*datastore.Key, error) {
	q := datastore.NewQuery(kind.Kind())
	for _, setter := range qs {
		q = setter(q)
	}
	q = q.KeysOnly()

	keys, err := client.GetAll(ctx, q, nil)
	if err != nil {
		return nil, err
	}
	return keys, nil
}

// Query Helper functions

// Filter func
func Filter(filterStr string, value interface{}) Query {
	return func(q *datastore.Query) *datastore.Query {
		return q.Filter(filterStr, value)
	}
}

// CreateAfter quries is model created after (or equals) given time
func CreateAfter(t time.Time, equals bool) Query {
	p := "CreatedAt >"
	if equals {
		p += "="
	}
	return Filter(p, t)
}

// UpdateBefore queries is model updated before (or equals) given time
func UpdateBefore(t time.Time, equals bool) Query {
	p := "UpdatedAt <"
	if equals {
		p += "="
	}
	return Filter(p, t)
}

// UpdateAfter queries is model updated after (or equals) given time
func UpdateAfter(t time.Time, equals bool) Query {
	p := "UpdatedAt >"
	if equals {
		p += "="
	}
	return Filter(p, t)
}

// Offset adds offset to query
func Offset(offset int) Query {
	return func(q *datastore.Query) *datastore.Query {
		return q.Offset(offset)
	}
}

// Limit adds limit to query
func Limit(limit int) Query {
	return func(q *datastore.Query) *datastore.Query {
		return q.Limit(limit)
	}
}

// Namespace adds namespace to query
func Namespace(ns string) Query {
	return func(q *datastore.Query) *datastore.Query {
		return q.Namespace(ns)
	}
}

// Order adds order to query
func Order(fieldName string) Query {
	return func(q *datastore.Query) *datastore.Query {
		return q.Order(fieldName)
	}
}

// Project adds order to query
func Project(fieldNames ...string) Query {
	return func(q *datastore.Query) *datastore.Query {
		return q.Project(fieldNames...)
	}
}

// Transaction adds transaction to query
func Transaction(t *datastore.Transaction) Query {
	return func(q *datastore.Query) *datastore.Query {
		return q.Transaction(t)
	}
}
<|endoftext|>


<commit_before>package forge

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
)

// Section struct holds a map of values
type Section struct {
	comments []string
	includes []string
	parent   *Section
	values   map[string]Value
}

// NewSection will create and initialize a new Section
func NewSection() *Section {
	return &Section{
		comments: make([]string, 0),
		includes: make([]string, 0),
		values:   make(map[string]Value),
	}
}

func newChildSection(parent *Section) *Section {
	return &Section{
		comments: make([]string, 0),
		includes: make([]string, 0),
		parent:   parent,
		values:   make(map[string]Value),
	}
}

// AddComment will append a new comment into the section
func (section *Section) AddComment(comment string) {
	section.comments = append(section.comments, comment)
}

// AddInclude will append a new filename into the section
func (section *Section) AddInclude(filename string) {
	section.includes = append(section.includes, filename)
}

// GetComments will return all the comments were defined for this Section
func (section *Section) GetComments() []string {
	return section.comments
}

// GetIncludes will return the filenames of all the includes were parsed for this Section
func (section *Section) GetIncludes() []string {
	return section.includes
}

// GetType will respond with the ValueType of this Section (hint, always SECTION)
func (section *Section) GetType() ValueType {
	return SECTION
}

// GetValue retrieves the raw underlying value stored in this Section
func (section *Section) GetValue() interface{} {
	return section.values
}

// UpdateValue updates the raw underlying value stored in this Section
func (section *Section) UpdateValue(value interface{}) error {
	switch value.(type) {
	case map[string]Value:
		section.values = value.(map[string]Value)
		return nil
	}

	msg := fmt.Sprintf("unsupported type, %s must be of type `map[string]Value`", value)
	return errors.New(msg)
}

// AddSection adds a new child section to this Section with the provided name
func (section *Section) AddSection(name string) *Section {
	childSection := newChildSection(section)
	section.values[name] = childSection
	return childSection
}

// Exists returns true when a value stored under the key exists
func (section *Section) Exists(name string) bool {
	_, err := section.Get(name)
	return err == nil
}

// Get the value (Primative or Section) stored under the name
// will respond with an error if the value does not exist
func (section *Section) Get(name string) (Value, error) {
	value, ok := section.values[name]
	var err error
	if ok == false {
		err = errors.New("value does not exist")
	}
	return value, err
}

// GetBoolean will try to get the value stored under name as a bool
// will respond with an error if the value does not exist or cannot be converted to a bool
func (section *Section) GetBoolean(name string) (bool, error) {
	value, err := section.Get(name)
	if err != nil {
		return false, err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsBoolean()
	case *Section:
		return true, nil
	}

	return false, errors.New("could not convert unknown value to boolean")
}

// GetFloat will try to get the value stored under name as a float64
// will respond with an error if the value does not exist or cannot be converted to a float64
func (section *Section) GetFloat(name string) (float64, error) {
	value, err := section.Get(name)
	if err != nil {
		return float64(0), err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsFloat()
	}

	return float64(0), errors.New("could not convert non-primative value to float")
}

// GetInteger will try to get the value stored under name as a int64
// will respond with an error if the value does not exist or cannot be converted to a int64
func (section *Section) GetInteger(name string) (int64, error) {
	value, err := section.Get(name)
	if err != nil {
		return int64(0), err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsInteger()
	}

	return int64(0), errors.New("could not convert non-primative value to integer")
}

// GetSection will try to get the value stored under name as a Section
// will respond with an error if the value does not exist or is not a Section
func (section *Section) GetSection(name string) (*Section, error) {
	value, err := section.Get(name)
	if err != nil {
		return nil, err
	}

	if value.GetType() == SECTION {
		return value.(*Section), nil
	}
	return nil, errors.New("could not fetch value as section")
}

// GetString will try to get the value stored under name as a string
// will respond with an error if the value does not exist or cannot be converted to a string
func (section *Section) GetString(name string) (string, error) {
	value, err := section.Get(name)
	if err != nil {
		return "", err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsString()
	}

	return "", errors.New("could not convert non-primative value to string")
}

// GetParent will get the parent section associated with this Section or nil
// if it does not have one
func (section *Section) GetParent() *Section {
	return section.parent
}

// HasParent will return true if this Section has a parent
func (section *Section) HasParent() bool {
	return section.parent != nil
}

// Set will set a value (Primative or Section) to the provided name
func (section *Section) Set(name string, value Value) {
	section.values[name] = value
}

// SetBoolean will set the value for name as a bool
func (section *Section) SetBoolean(name string, value bool) {
	current, err := section.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		section.values[name] = NewBoolean(value)
	}
}

// SetFloat will set the value for name as a float64
func (section *Section) SetFloat(name string, value float64) {
	current, err := section.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		section.values[name] = NewFloat(value)
	}
}

// SetInteger will set the value for name as a int64
func (section *Section) SetInteger(name string, value int64) {
	current, err := section.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		section.values[name] = NewInteger(value)
	}
}

// SetNull will set the value for name as nil
func (section *Section) SetNull(name string) {
	current, err := section.Get(name)

	// Already is a Null, nothing to do
	if err == nil && current.GetType() == NULL {
		return
	}
	section.Set(name, NewNull())
}

// SetString will set the value for name as a string
func (section *Section) SetString(name string, value string) {
	current, err := section.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		section.Set(name, NewString(value))
	}
}

// Resolve will recursively try to fetch the provided value and will respond
// with an error if the name does not exist or tries to be resolved through
// a non-section value
func (section *Section) Resolve(name string) (Value, error) {
	// Used only in error state return value
	var value Value

	parts := strings.Split(name, ".")
	if len(parts) == 0 {
		return value, errors.New("no name provided")
	}

	var current Value
	current = section
	for _, part := range parts {
		if current.GetType() != SECTION {
			return value, errors.New("trying to resolve value from non-section")
		}

		nextCurrent, err := current.(*Section).Get(part)
		if err != nil {
			return value, errors.New("could not find value in section")
		}
		current = nextCurrent
	}
	return current, nil
}

// ToJSON will convert this Section and all it's underlying values and Sections
// into JSON as a []byte
func (section *Section) ToJSON() ([]byte, error) {
	data := section.ToMap()
	return json.Marshal(data)
}

// ToMap will convert this Section and all it's underlying values and Sections into
// a map[string]interface{}
func (section *Section) ToMap() map[string]interface{} {
	output := make(map[string]interface{})

	for key, value := range section.values {
		if value.GetType() == SECTION {
			output[key] = value.(*Section).ToMap()
		} else {
			output[key] = value.GetValue()
		}
	}
	return output
}
<commit_msg>add Keys method to Section<commit_after>package forge

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
)

// Section struct holds a map of values
type Section struct {
	comments []string
	includes []string
	parent   *Section
	values   map[string]Value
}

// NewSection will create and initialize a new Section
func NewSection() *Section {
	return &Section{
		comments: make([]string, 0),
		includes: make([]string, 0),
		values:   make(map[string]Value),
	}
}

func newChildSection(parent *Section) *Section {
	return &Section{
		comments: make([]string, 0),
		includes: make([]string, 0),
		parent:   parent,
		values:   make(map[string]Value),
	}
}

// AddComment will append a new comment into the section
func (section *Section) AddComment(comment string) {
	section.comments = append(section.comments, comment)
}

// AddInclude will append a new filename into the section
func (section *Section) AddInclude(filename string) {
	section.includes = append(section.includes, filename)
}

// GetComments will return all the comments were defined for this Section
func (section *Section) GetComments() []string {
	return section.comments
}

// GetIncludes will return the filenames of all the includes were parsed for this Section
func (section *Section) GetIncludes() []string {
	return section.includes
}

// GetType will respond with the ValueType of this Section (hint, always SECTION)
func (section *Section) GetType() ValueType {
	return SECTION
}

// GetValue retrieves the raw underlying value stored in this Section
func (section *Section) GetValue() interface{} {
	return section.values
}

// UpdateValue updates the raw underlying value stored in this Section
func (section *Section) UpdateValue(value interface{}) error {
	switch value.(type) {
	case map[string]Value:
		section.values = value.(map[string]Value)
		return nil
	}

	msg := fmt.Sprintf("unsupported type, %s must be of type `map[string]Value`", value)
	return errors.New(msg)
}

// AddSection adds a new child section to this Section with the provided name
func (section *Section) AddSection(name string) *Section {
	childSection := newChildSection(section)
	section.values[name] = childSection
	return childSection
}

// Exists returns true when a value stored under the key exists
func (section *Section) Exists(name string) bool {
	_, err := section.Get(name)
	return err == nil
}

// Get the value (Primative or Section) stored under the name
// will respond with an error if the value does not exist
func (section *Section) Get(name string) (Value, error) {
	value, ok := section.values[name]
	var err error
	if ok == false {
		err = errors.New("value does not exist")
	}
	return value, err
}

// GetBoolean will try to get the value stored under name as a bool
// will respond with an error if the value does not exist or cannot be converted to a bool
func (section *Section) GetBoolean(name string) (bool, error) {
	value, err := section.Get(name)
	if err != nil {
		return false, err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsBoolean()
	case *Section:
		return true, nil
	}

	return false, errors.New("could not convert unknown value to boolean")
}

// GetFloat will try to get the value stored under name as a float64
// will respond with an error if the value does not exist or cannot be converted to a float64
func (section *Section) GetFloat(name string) (float64, error) {
	value, err := section.Get(name)
	if err != nil {
		return float64(0), err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsFloat()
	}

	return float64(0), errors.New("could not convert non-primative value to float")
}

// GetInteger will try to get the value stored under name as a int64
// will respond with an error if the value does not exist or cannot be converted to a int64
func (section *Section) GetInteger(name string) (int64, error) {
	value, err := section.Get(name)
	if err != nil {
		return int64(0), err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsInteger()
	}

	return int64(0), errors.New("could not convert non-primative value to integer")
}

// GetSection will try to get the value stored under name as a Section
// will respond with an error if the value does not exist or is not a Section
func (section *Section) GetSection(name string) (*Section, error) {
	value, err := section.Get(name)
	if err != nil {
		return nil, err
	}

	if value.GetType() == SECTION {
		return value.(*Section), nil
	}
	return nil, errors.New("could not fetch value as section")
}

// GetString will try to get the value stored under name as a string
// will respond with an error if the value does not exist or cannot be converted to a string
func (section *Section) GetString(name string) (string, error) {
	value, err := section.Get(name)
	if err != nil {
		return "", err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsString()
	}

	return "", errors.New("could not convert non-primative value to string")
}

// GetParent will get the parent section associated with this Section or nil
// if it does not have one
func (section *Section) GetParent() *Section {
	return section.parent
}

// HasParent will return true if this Section has a parent
func (section *Section) HasParent() bool {
	return section.parent != nil
}

// Keys will return back a list of all setting names in this Section
func (section *Section) Keys() []string {
	keys := make([]string, 0)
	for key, _ := range section.values {
		keys = append(keys, key)
	}

	return keys
}

// Set will set a value (Primative or Section) to the provided name
func (section *Section) Set(name string, value Value) {
	section.values[name] = value
}

// SetBoolean will set the value for name as a bool
func (section *Section) SetBoolean(name string, value bool) {
	current, err := section.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		section.values[name] = NewBoolean(value)
	}
}

// SetFloat will set the value for name as a float64
func (section *Section) SetFloat(name string, value float64) {
	current, err := section.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		section.values[name] = NewFloat(value)
	}
}

// SetInteger will set the value for name as a int64
func (section *Section) SetInteger(name string, value int64) {
	current, err := section.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		section.values[name] = NewInteger(value)
	}
}

// SetNull will set the value for name as nil
func (section *Section) SetNull(name string) {
	current, err := section.Get(name)

	// Already is a Null, nothing to do
	if err == nil && current.GetType() == NULL {
		return
	}
	section.Set(name, NewNull())
}

// SetString will set the value for name as a string
func (section *Section) SetString(name string, value string) {
	current, err := section.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		section.Set(name, NewString(value))
	}
}

// Resolve will recursively try to fetch the provided value and will respond
// with an error if the name does not exist or tries to be resolved through
// a non-section value
func (section *Section) Resolve(name string) (Value, error) {
	// Used only in error state return value
	var value Value

	parts := strings.Split(name, ".")
	if len(parts) == 0 {
		return value, errors.New("no name provided")
	}

	var current Value
	current = section
	for _, part := range parts {
		if current.GetType() != SECTION {
			return value, errors.New("trying to resolve value from non-section")
		}

		nextCurrent, err := current.(*Section).Get(part)
		if err != nil {
			return value, errors.New("could not find value in section")
		}
		current = nextCurrent
	}
	return current, nil
}

// ToJSON will convert this Section and all it's underlying values and Sections
// into JSON as a []byte
func (section *Section) ToJSON() ([]byte, error) {
	data := section.ToMap()
	return json.Marshal(data)
}

// ToMap will convert this Section and all it's underlying values and Sections into
// a map[string]interface{}
func (section *Section) ToMap() map[string]interface{} {
	output := make(map[string]interface{})

	for key, value := range section.values {
		if value.GetType() == SECTION {
			output[key] = value.(*Section).ToMap()
		} else {
			output[key] = value.GetValue()
		}
	}
	return output
}
<|endoftext|>


<commit_before>package qmd

import (
	"fmt"

	"github.com/bitly/go-nsq"
)

type QueueConfig struct {
	HostNSQDAddr string   `toml:"host_nsqd_address"`
	NSQDAddrs    []string `toml:"nsqd_addresses"`
	LookupdAddrs []string `toml:"lookupd_addresses"`
}

func (qc *QueueConfig) Clean() error {
	if len(qc.LookupdAddrs) == 0 {
		if len(qc.NSQDAddrs) == 0 {
			return fmt.Errorf("Both LookupdAddresses and NSQDAddresses are missing")
		}
	}
	return nil
}

func ConnectConsumer(qc *QueueConfig, consumer *nsq.Consumer) error {
	var err error

	// Connect consumers to NSQLookupd
	if qc.LookupdAddrs != nil && len(qc.LookupdAddrs) != 0 {
		lg.Info("Connecting Consumer to the following NSQLookupds %s", qc.LookupdAddrs)
		err = consumer.ConnectToNSQLookupds(qc.LookupdAddrs)
		if err != nil {
			return err
		}
	}
	// Connect consumers to NSQD
	if qc.NSQDAddrs != nil && len(qc.NSQDAddrs) != 0 {
		lg.Info("Connecting Consumer to the following NSQDs %s", qc.NSQDAddrs)
		err = consumer.ConnectToNSQDs(qc.NSQDAddrs)
		if err != nil {
			return err
		}
	}
	return fmt.Errorf("Couldn't connect to either NSQDs or NSQLookupds")
}
<commit_msg>Fixed wrong return type<commit_after>package qmd

import (
	"fmt"

	"github.com/bitly/go-nsq"
)

type QueueConfig struct {
	HostNSQDAddr string   `toml:"host_nsqd_address"`
	NSQDAddrs    []string `toml:"nsqd_addresses"`
	LookupdAddrs []string `toml:"lookupd_addresses"`
}

func (qc *QueueConfig) Clean() error {
	if len(qc.LookupdAddrs) == 0 {
		if len(qc.NSQDAddrs) == 0 {
			return fmt.Errorf("Both LookupdAddresses and NSQDAddresses are missing")
		}
	}
	return nil
}

func ConnectConsumer(qc *QueueConfig, consumer *nsq.Consumer) error {
	var err error

	// Connect consumers to NSQLookupd
	if qc.LookupdAddrs != nil && len(qc.LookupdAddrs) != 0 {
		lg.Info("Connecting Consumer to the following NSQLookupds %s", qc.LookupdAddrs)
		err = consumer.ConnectToNSQLookupds(qc.LookupdAddrs)
		if err != nil {
			return err
		}
	}
	// Connect consumers to NSQD
	if qc.NSQDAddrs != nil && len(qc.NSQDAddrs) != 0 {
		lg.Info("Connecting Consumer to the following NSQDs %s", qc.NSQDAddrs)
		err = consumer.ConnectToNSQDs(qc.NSQDAddrs)
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package dbr

import (
	"bytes"
)

// Quoter is the quiter to use for quoting text; use Mysql quoting by default
var Quoter = MysqlQuoter{}

// Interface for driver-swappable quoting
type quoter interface {
	writeQuotedColumn()
}

// MysqlQuoter implements Mysql-specific quoting
type MysqlQuoter struct{}

func (q MysqlQuoter) writeQuotedColumn(column string, sql *bytes.Buffer) {
	sql.WriteRune('`')
	sql.WriteString(column)
	sql.WriteRune('`')
}
<commit_msg>CLEANUP: Fix typo<commit_after>package dbr

import (
	"bytes"
)

// Quoter is the quoter to use for quoting text; use Mysql quoting by default
var Quoter = MysqlQuoter{}

// Interface for driver-swappable quoting
type quoter interface {
	writeQuotedColumn()
}

// MysqlQuoter implements Mysql-specific quoting
type MysqlQuoter struct{}

func (q MysqlQuoter) writeQuotedColumn(column string, sql *bytes.Buffer) {
	sql.WriteRune('`')
	sql.WriteString(column)
	sql.WriteRune('`')
}
<|endoftext|>


<commit_before>// Go Port:
// Copyright (c) 2013 Dmitry Chestnykh <dmitry@codingrobots.com>
//
// Original:
// Copyright (c) 2008 Ryan Grove <ryan@wonko.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of this project nor the names of its contributors may be
//     used to endorse or promote products derived from this software without
//     specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Package cssmin minifies CSS. It's a port of Ryan Grove's cssmin from Ruby.
package cssmin

import (
	"bytes"
	"fmt"
	"regexp"
	"strconv"
)

var (
	rcomments      = regexp.MustCompile(`\/\*[\s\S]*?\*\/`)
	rwhitespace    = regexp.MustCompile(`\s+`)
	rbmh           = regexp.MustCompile(`"\\"\}\\""`)
	runspace1      = regexp.MustCompile(`(?:^|\})[^\{:]+\s+:+[^\{]*\{`)
	runspace2      = regexp.MustCompile(`\s+([!\{\};:>+\(\)\],])`)
	runspace3      = regexp.MustCompile(`([!\{\}:;>+\(\[,])\s+`)
	rsemicolons    = regexp.MustCompile(`([^;\}])\}`)
	runits         = regexp.MustCompile(`(?i)([\s:])([+-]?0)(?:%|em|ex|px|in|cm|mm|pt|pc)`)
	rfourzero      = regexp.MustCompile(`:(?:0 )+0;`)
	rleadzero      = regexp.MustCompile(`(:|\s)0+\.(\d+)`)
	rrgb           = regexp.MustCompile(`rgb\s*\(\s*([0-9,\s]+)\s*\)`)
	rdigits        = regexp.MustCompile(`\d+`)
	rcompresshex   = regexp.MustCompile(`(?i)([^"'=\s])(\s?)\s*#([0-9a-f]){6}`)
	rhexval        = regexp.MustCompile(`[0-9a-f]{2}`)
	remptyrules    = regexp.MustCompile(`[^\}]+\{;\}\n`)
	rmediaspace    = regexp.MustCompile(`\band\(`)
	rredsemicolons = regexp.MustCompile(`;+\}`)
)

func Minify(css []byte) (minified []byte) {
	// Remove comments.
	css = rcomments.ReplaceAll(css, []byte{})

	// Compress all runs of whitespace to a single space to make things easier
	// to work with.
	css = rwhitespace.ReplaceAll(css, []byte(" "))

	// Replace box model hacks with placeholders.
	css = rbmh.ReplaceAll(css, []byte("___BMH___"))

	// Remove unnecessary spaces, but be careful not to turn "p :link {...}"
	// into "p:link{...}".
	css = runspace1.ReplaceAllFunc(css, func(match []byte) []byte {
		return bytes.Replace(match, []byte(":"), []byte("___PSEUDOCLASSCOLON___"), -1)
	})
	css = runspace2.ReplaceAll(css, []byte("$1"))
	css = bytes.Replace(css, []byte("___PSEUDOCLASSCOLON___"), []byte(":"), -1)
	css = runspace3.ReplaceAll(css, []byte("$1"))

	// Add missing semicolons.
	css = rsemicolons.ReplaceAll(css, []byte("$1;}"))

	// Replace 0(%, em, ex, px, in, cm, mm, pt, pc) with just 0.
	css = runits.ReplaceAll(css, []byte("$1$2"))

	// Replace 0 0 0 0; with 0.
	css = rfourzero.ReplaceAll(css, []byte(":0;"))

	// Replace background-position:0; with background-position:0 0;
	css = bytes.Replace(css, []byte("background-position:0;"), []byte("background-position:0 0;"), -1)

	// Replace 0.6 with .6, but only when preceded by : or a space.
	css = rleadzero.ReplaceAll(css, []byte("$1.$2"))

	// Convert rgb color values to hex values.
	css = rrgb.ReplaceAllFunc(css, func(match []byte) (out []byte) {
		out = []byte{'#'}
		for _, v := range rdigits.FindAll(match, -1) {
			d, err := strconv.Atoi(string(v))
			if err != nil {
				return match
			}
			out = append(out, []byte(fmt.Sprintf("%02x", d))...)
		}
		return out
	})

	// Compress color hex values, making sure not to touch values used in IE
	// filters, since they would break.
	css = rcompresshex.ReplaceAllFunc(css, func(match []byte) (out []byte) {
		vals := rhexval.FindAll(match, -1)
		if len(vals) != 3 {
			return match
		}
		compressible := true
		for _, v := range vals {
			if v[0] != v[1] {
				compressible = false
			}
		}
		if !compressible {
			return match
		}
		out = append(out, match[:bytes.IndexByte(match, '#')+1]...)
		return append(out, vals[0][0], vals[1][0], vals[2][0])
	})

	// Remove empty rules.
	css = remptyrules.ReplaceAll(css, []byte{})

	// Re-insert box model hacks.
	css = bytes.Replace(css, []byte("___BMH___"), []byte(`"\"}\""`), -1)

	// Put the space back in for media queries
	css = rmediaspace.ReplaceAll(css, []byte("and ("))

	// Prevent redundant semicolons.
	css = rredsemicolons.ReplaceAll(css, []byte("}"))

	return bytes.TrimSpace(css)
}
<commit_msg>Space out license from the package doc string.<commit_after>// Go Port:
// Copyright (c) 2013 Dmitry Chestnykh <dmitry@codingrobots.com>
//
// Original:
// Copyright (c) 2008 Ryan Grove <ryan@wonko.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of this project nor the names of its contributors may be
//     used to endorse or promote products derived from this software without
//     specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Package cssmin minifies CSS. It's a port of Ryan Grove's cssmin from Ruby.
package cssmin

import (
	"bytes"
	"fmt"
	"regexp"
	"strconv"
)

var (
	rcomments      = regexp.MustCompile(`\/\*[\s\S]*?\*\/`)
	rwhitespace    = regexp.MustCompile(`\s+`)
	rbmh           = regexp.MustCompile(`"\\"\}\\""`)
	runspace1      = regexp.MustCompile(`(?:^|\})[^\{:]+\s+:+[^\{]*\{`)
	runspace2      = regexp.MustCompile(`\s+([!\{\};:>+\(\)\],])`)
	runspace3      = regexp.MustCompile(`([!\{\}:;>+\(\[,])\s+`)
	rsemicolons    = regexp.MustCompile(`([^;\}])\}`)
	runits         = regexp.MustCompile(`(?i)([\s:])([+-]?0)(?:%|em|ex|px|in|cm|mm|pt|pc)`)
	rfourzero      = regexp.MustCompile(`:(?:0 )+0;`)
	rleadzero      = regexp.MustCompile(`(:|\s)0+\.(\d+)`)
	rrgb           = regexp.MustCompile(`rgb\s*\(\s*([0-9,\s]+)\s*\)`)
	rdigits        = regexp.MustCompile(`\d+`)
	rcompresshex   = regexp.MustCompile(`(?i)([^"'=\s])(\s?)\s*#([0-9a-f]){6}`)
	rhexval        = regexp.MustCompile(`[0-9a-f]{2}`)
	remptyrules    = regexp.MustCompile(`[^\}]+\{;\}\n`)
	rmediaspace    = regexp.MustCompile(`\band\(`)
	rredsemicolons = regexp.MustCompile(`;+\}`)
)

func Minify(css []byte) (minified []byte) {
	// Remove comments.
	css = rcomments.ReplaceAll(css, []byte{})

	// Compress all runs of whitespace to a single space to make things easier
	// to work with.
	css = rwhitespace.ReplaceAll(css, []byte(" "))

	// Replace box model hacks with placeholders.
	css = rbmh.ReplaceAll(css, []byte("___BMH___"))

	// Remove unnecessary spaces, but be careful not to turn "p :link {...}"
	// into "p:link{...}".
	css = runspace1.ReplaceAllFunc(css, func(match []byte) []byte {
		return bytes.Replace(match, []byte(":"), []byte("___PSEUDOCLASSCOLON___"), -1)
	})
	css = runspace2.ReplaceAll(css, []byte("$1"))
	css = bytes.Replace(css, []byte("___PSEUDOCLASSCOLON___"), []byte(":"), -1)
	css = runspace3.ReplaceAll(css, []byte("$1"))

	// Add missing semicolons.
	css = rsemicolons.ReplaceAll(css, []byte("$1;}"))

	// Replace 0(%, em, ex, px, in, cm, mm, pt, pc) with just 0.
	css = runits.ReplaceAll(css, []byte("$1$2"))

	// Replace 0 0 0 0; with 0.
	css = rfourzero.ReplaceAll(css, []byte(":0;"))

	// Replace background-position:0; with background-position:0 0;
	css = bytes.Replace(css, []byte("background-position:0;"), []byte("background-position:0 0;"), -1)

	// Replace 0.6 with .6, but only when preceded by : or a space.
	css = rleadzero.ReplaceAll(css, []byte("$1.$2"))

	// Convert rgb color values to hex values.
	css = rrgb.ReplaceAllFunc(css, func(match []byte) (out []byte) {
		out = []byte{'#'}
		for _, v := range rdigits.FindAll(match, -1) {
			d, err := strconv.Atoi(string(v))
			if err != nil {
				return match
			}
			out = append(out, []byte(fmt.Sprintf("%02x", d))...)
		}
		return out
	})

	// Compress color hex values, making sure not to touch values used in IE
	// filters, since they would break.
	css = rcompresshex.ReplaceAllFunc(css, func(match []byte) (out []byte) {
		vals := rhexval.FindAll(match, -1)
		if len(vals) != 3 {
			return match
		}
		compressible := true
		for _, v := range vals {
			if v[0] != v[1] {
				compressible = false
			}
		}
		if !compressible {
			return match
		}
		out = append(out, match[:bytes.IndexByte(match, '#')+1]...)
		return append(out, vals[0][0], vals[1][0], vals[2][0])
	})

	// Remove empty rules.
	css = remptyrules.ReplaceAll(css, []byte{})

	// Re-insert box model hacks.
	css = bytes.Replace(css, []byte("___BMH___"), []byte(`"\"}\""`), -1)

	// Put the space back in for media queries
	css = rmediaspace.ReplaceAll(css, []byte("and ("))

	// Prevent redundant semicolons.
	css = rredsemicolons.ReplaceAll(css, []byte("}"))

	return bytes.TrimSpace(css)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/nsf/termbox-go"
)

type cursor struct {
	x     int
	y     int
	color termbox.Attribute
}

var mainCursor = &cursor{
	x:     0,
	y:     0,
	color: termbox.ColorWhite,
}

func (c *cursor) placeLifeForms(list lifeFormList) lifeFormList {
	c.placeCursor(0, 0)
	for {
		event := termbox.PollEvent()
		if event.Key == termbox.KeyEnter {
			return list
		}
		switch event.Ch {
		case 'h':
			c.placeCursor(c.x-1, c.y)
		case 'j':
			c.placeCursor(c.x, c.y+1)
		case 'k':
			c.placeCursor(c.x, c.y-1)
		case 'l':
			c.placeCursor(c.x+1, c.y)
		case 'c':
			buffer := termbox.CellBuffer()
			w, _ := termbox.Size()
			if buffer[c.x+(c.y*w)].Ch != lifeFormSymbol {
				list = newLifeForm(c.x, c.y, list)
				termbox.SetCell(c.x, c.y, lifeFormSymbol, termbox.ColorBlue, c.color)
			}
			termbox.Flush()
		}
	}
	return list
}

func (c *cursor) placeCursor(x, y int) bool {
	buffer := termbox.CellBuffer()
	w, h := termbox.Size()
	if x < 0 || y < 0 || y >= h || x >= w {
		return false
	}
	termbox.SetCell(c.x, c.y, buffer[c.x+(c.y*w)].Ch, buffer[c.x+(c.y*w)].Fg, termbox.ColorDefault)
	termbox.SetCell(x, y, buffer[x+(y*w)].Ch, buffer[x+(y*w)].Fg, c.color)
	c.x = x
	c.y = y
	termbox.Flush()
	return true
}
<commit_msg>fixing up things<commit_after>package main

import (
	"github.com/nsf/termbox-go"
)

type cursor struct {
	x     int
	y     int
	color termbox.Attribute
}

var mainCursor = &cursor{
	x:     0,
	y:     0,
	color: termbox.ColorWhite,
}

func (c *cursor) placeLifeForms(list lifeFormList) lifeFormList {
	c.placeCursor(0, 0)
	for {
		event := termbox.PollEvent()
		if event.Key == termbox.KeyEnter {
			return list
		}
		switch event.Ch {
		case 'h':
			c.placeCursor(c.x-1, c.y)
		case 'j':
			c.placeCursor(c.x, c.y+1)
		case 'k':
			c.placeCursor(c.x, c.y-1)
		case 'l':
			c.placeCursor(c.x+1, c.y)
		case 'c':
			buffer := termbox.CellBuffer()
			w, _ := termbox.Size()
			if buffer[c.x+(c.y*w)].Ch != lifeFormSymbol {
				list = newLifeForm(c.x, c.y, list)
				termbox.SetCell(c.x, c.y, lifeFormSymbol, termbox.ColorBlue, c.color)
			}
			termbox.Flush()
		}
	}
}

func (c *cursor) placeCursor(x, y int) bool {
	buffer := termbox.CellBuffer()
	w, h := termbox.Size()
	if x < 0 || y < 0 || y >= h || x >= w {
		return false
	}
	termbox.SetCell(c.x, c.y, buffer[c.x+(c.y*w)].Ch, buffer[c.x+(c.y*w)].Fg, termbox.ColorDefault)
	termbox.SetCell(x, y, buffer[x+(y*w)].Ch, buffer[x+(y*w)].Fg, c.color)
	c.x = x
	c.y = y
	termbox.Flush()
	return true
}
<|endoftext|>


<commit_before>// Package godaemon runs a program as a Unix daemon.
package godaemon

// Copyright (c) 2013 VividCortex, Inc. All rights reserved.
// Please see the LICENSE file for applicable license terms.

import (
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
	"syscall"
	"time"
)

// The name of the env var that indicates what stage of daemonization we're at.
const stageVar = "__DAEMON_STAGE"

// DaemonAttr describes the options that apply to daemonization
type DaemonAttr struct {
	CaptureOutput bool // whether to capture stdout/stderr
}

/*
MakeDaemon turns the process into a daemon. But given the lack of Go's
support for fork(), MakeDaemon() is forced to run the process all over again,
from the start. Hence, this should probably be your first call after main
begins, unless you understand the effects of calling from somewhere else.
Keep in mind that the PID changes after this function is called, given
that it only returns in the child; the parent will exit without returning.

Options are provided as a DaemonAttr structure. In particular, setting the
CaptureOutput member to true will make the function return two io.Reader
streams to read the process' standard output and standard error, respectively.
That's useful if you want to capture things you'd normally lose given the
lack of console output for a daemon. Some libraries can write error conditions
to standard error or make use of Go's log package, that defaults to standard
error too. Having these streams allows you to capture them as required. (Note
that this function takes no action whatsoever on any of the streams.)

NOTE: If you use them, make sure NOT to take one of these readers and write
the data back again to standard output/error, or you'll end up with a loop.

Daemonizing is a 3-stage process. In stage 0, the program increments the
magical environment variable and starts a copy of itself that's a session
leader, with its STDIN, STDOUT, and STDERR disconnected from any tty. It
then exits.

In stage 1, the (new copy of) the program starts another copy that's not
a session leader, and then exits.

In stage 2, the (new copy of) the program chdir's to /, then sets the umask
and reestablishes the original value for the environment variable.
*/
func MakeDaemon(attrs *DaemonAttr) (io.Reader, io.Reader) {
	stage, advanceStage, resetEnv := getStage()

	// These will be redirected to logfile if we can "fork" and want to
	// capture output.
	stdout, stderr := os.Stdout, os.Stderr

	// getExecutablePath() is OS-specific.
	procName, err := GetExecutablePath()

	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}

	// If getExecutablePath() returns "" but no error, determinating the
	// executable path is not implemented on the host OS, so daemonization
	// is not supported.
	if len(procName) > 0 {
		if stage == 0 || stage == 1 {
			// Descriptors 0, 1 and 2 are fixed in the "os" package. If we close
			// them, the process may choose to open something else there, with bad
			// consequences if some write to os.Stdout or os.Stderr follows (even
			// from Go's library itself, through the default log package). We thus
			// reserve these descriptors to avoid that.
			nullDev, err := os.OpenFile("/dev/null", 0, 0)

			if err != nil {
				fmt.Fprintf(os.Stderr, "unable to open /dev/null: ", err, "\n")
				os.Exit(1)
			}

			files := make([]*os.File, 3, 5)
			files[0] = nullDev // stdin
			// FDs should not be changed; we're using literals (as opposed to
			// constants) on purpose to discourage such a practice.

			if stage == 1 && attrs.CaptureOutput {
				files = files[0:5]

				// stdout: write at fd:1, read at fd:3
				if files[3], files[1], err = os.Pipe(); err != nil {
					fmt.Fprintf(os.Stderr, "unable to create stdout pipe: ", err, "\n")
					os.Exit(1)
				}

				// stderr: write at fd:2, read at fd:4
				if files[4], files[2], err = os.Pipe(); err != nil {
					fmt.Fprintf(os.Stderr, "unable to create stderr pipe: ", err, "\n")
					os.Exit(1)
				}
			} else {
				files[1], files[2] = nullDev, nullDev
			}

			advanceStage()
			dir, _ := os.Getwd()
			attrs := os.ProcAttr{Dir: dir, Env: os.Environ(), Files: files}

			if stage == 0 {
				sysattrs := syscall.SysProcAttr{Setsid: true}
				attrs.Sys = &sysattrs
			}

			proc, err := os.StartProcess(procName, os.Args, &attrs)

			if err != nil {
				fmt.Fprintf(os.Stderr, "can't create process %s\n", procName)
				os.Exit(1)
			}

			proc.Release()
			os.Exit(0)
		}

		os.Chdir("/")
		syscall.Umask(0)
		resetEnv()

		if attrs.CaptureOutput {
			stdout = os.NewFile(uintptr(3), "stdout")
			stderr = os.NewFile(uintptr(4), "stderr")
		}
	}
	return stdout, stderr
}

// Daemonize is equivalent to MakeDaemon(&DaemonAttr{}). It is kept only for
// backwards API compatibility, but it's usage is otherwise discouraged. Use
// MakeDaemon() instead. The child parameter, previously used to tell whether
// to reset the environment or not (see MakeDaemon()), is currently ignored.
// The environment is reset in all cases.
func Daemonize(child ...bool) {
	MakeDaemon(&DaemonAttr{})
}

// Returns the current stage in the "daemonization process", that's kept in
// an environment variable. The variable is instrumented with a digital
// signature, to avoid misbehavior if it was present in the user's
// environment. The original value is restored after the last stage, so that
// there's no final effect on the environment the application receives.
func getStage() (stage int, advanceStage func(), resetEnv func()) {
	var origValue string
	stage = 0

	daemonStage := os.Getenv(stageVar)
	stageTag := strings.SplitN(daemonStage, ":", 2)
	stageInfo := strings.SplitN(stageTag[0], "/", 3)

	if len(stageInfo) == 3 {
		stageStr, tm, check := stageInfo[0], stageInfo[1], stageInfo[2]

		hash := sha1.New()
		hash.Write([]byte(stageStr + "/" + tm + "/"))

		if check != hex.EncodeToString(hash.Sum([]byte{})) {
			// This whole chunk is original data
			origValue = daemonStage
		} else {
			stage, _ = strconv.Atoi(stageStr)

			if len(stageTag) == 2 {
				origValue = stageTag[1]
			}
		}
	} else {
		origValue = daemonStage
	}

	advanceStage = func() {
		base := fmt.Sprintf("%d/%09d/", stage+1, time.Now().Nanosecond())
		hash := sha1.New()
		hash.Write([]byte(base))

		tag := base + hex.EncodeToString(hash.Sum([]byte{}))

		if err := os.Setenv(stageVar, tag+":"+origValue); err != nil {
			fmt.Fprintf(os.Stderr, "can't set %s (stage %d)\n", stageVar, stage)
			os.Exit(1)
		}
	}

	resetEnv = func() {
		if err := os.Setenv(stageVar, origValue); err != nil {
			fmt.Fprintf(os.Stderr, "can't reset %s\n", stageVar)
			os.Exit(1)
		}
	}

	return stage, advanceStage, resetEnv
}
<commit_msg>Fix possible incorrect return of os.Stdout, os.Stderr as readers; fixes #5<commit_after>// Package godaemon runs a program as a Unix daemon.
package godaemon

// Copyright (c) 2013 VividCortex, Inc. All rights reserved.
// Please see the LICENSE file for applicable license terms.

import (
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
	"syscall"
	"time"
)

// The name of the env var that indicates what stage of daemonization we're at.
const stageVar = "__DAEMON_STAGE"

// DaemonAttr describes the options that apply to daemonization
type DaemonAttr struct {
	CaptureOutput bool // whether to capture stdout/stderr
}

/*
MakeDaemon turns the process into a daemon. But given the lack of Go's
support for fork(), MakeDaemon() is forced to run the process all over again,
from the start. Hence, this should probably be your first call after main
begins, unless you understand the effects of calling from somewhere else.
Keep in mind that the PID changes after this function is called, given
that it only returns in the child; the parent will exit without returning.

Options are provided as a DaemonAttr structure. In particular, setting the
CaptureOutput member to true will make the function return two io.Reader
streams to read the process' standard output and standard error, respectively.
That's useful if you want to capture things you'd normally lose given the
lack of console output for a daemon. Some libraries can write error conditions
to standard error or make use of Go's log package, that defaults to standard
error too. Having these streams allows you to capture them as required. (Note
that this function takes no action whatsoever on any of the streams.)

NOTE: If you use them, make sure NOT to take one of these readers and write
the data back again to standard output/error, or you'll end up with a loop.

Daemonizing is a 3-stage process. In stage 0, the program increments the
magical environment variable and starts a copy of itself that's a session
leader, with its STDIN, STDOUT, and STDERR disconnected from any tty. It
then exits.

In stage 1, the (new copy of) the program starts another copy that's not
a session leader, and then exits.

In stage 2, the (new copy of) the program chdir's to /, then sets the umask
and reestablishes the original value for the environment variable.
*/
func MakeDaemon(attrs *DaemonAttr) (io.Reader, io.Reader) {
	stage, advanceStage, resetEnv := getStage()

	// getExecutablePath() is OS-specific.
	procName, err := GetExecutablePath()

	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}

	// If getExecutablePath() returns "" but no error, determinating the
	// executable path is not implemented on the host OS, so daemonization
	// is not supported.
	if len(procName) > 0 {
		if stage == 0 || stage == 1 {
			// Descriptors 0, 1 and 2 are fixed in the "os" package. If we close
			// them, the process may choose to open something else there, with bad
			// consequences if some write to os.Stdout or os.Stderr follows (even
			// from Go's library itself, through the default log package). We thus
			// reserve these descriptors to avoid that.
			nullDev, err := os.OpenFile("/dev/null", 0, 0)

			if err != nil {
				fmt.Fprintf(os.Stderr, "unable to open /dev/null: ", err, "\n")
				os.Exit(1)
			}

			files := make([]*os.File, 3, 5)
			files[0] = nullDev // stdin
			// FDs should not be changed; we're using literals (as opposed to
			// constants) on purpose to discourage such a practice.

			if stage == 1 && attrs.CaptureOutput {
				files = files[0:5]

				// stdout: write at fd:1, read at fd:3
				if files[3], files[1], err = os.Pipe(); err != nil {
					fmt.Fprintf(os.Stderr, "unable to create stdout pipe: ", err, "\n")
					os.Exit(1)
				}

				// stderr: write at fd:2, read at fd:4
				if files[4], files[2], err = os.Pipe(); err != nil {
					fmt.Fprintf(os.Stderr, "unable to create stderr pipe: ", err, "\n")
					os.Exit(1)
				}
			} else {
				files[1], files[2] = nullDev, nullDev
			}

			advanceStage()
			dir, _ := os.Getwd()
			attrs := os.ProcAttr{Dir: dir, Env: os.Environ(), Files: files}

			if stage == 0 {
				sysattrs := syscall.SysProcAttr{Setsid: true}
				attrs.Sys = &sysattrs
			}

			proc, err := os.StartProcess(procName, os.Args, &attrs)

			if err != nil {
				fmt.Fprintf(os.Stderr, "can't create process %s\n", procName)
				os.Exit(1)
			}

			proc.Release()
			os.Exit(0)
		}

		os.Chdir("/")
		syscall.Umask(0)
		resetEnv()

		var stdout, stderr *os.File
		if attrs.CaptureOutput {
			stdout = os.NewFile(uintptr(3), "stdout")
			stderr = os.NewFile(uintptr(4), "stderr")
		}
		return stdout, stderr
	} else {
		// This is a corner case: determining the executable path is not
		// supported on this OS.
		fmt.Fprintf(os.Stderr, "%v: can't determine full path to executable\n", os.Args[0])
		os.Exit(1)
	}
	// Should never get here.
	return nil, nil
}

// Daemonize is equivalent to MakeDaemon(&DaemonAttr{}). It is kept only for
// backwards API compatibility, but it's usage is otherwise discouraged. Use
// MakeDaemon() instead. The child parameter, previously used to tell whether
// to reset the environment or not (see MakeDaemon()), is currently ignored.
// The environment is reset in all cases.
func Daemonize(child ...bool) {
	MakeDaemon(&DaemonAttr{})
}

// Returns the current stage in the "daemonization process", that's kept in
// an environment variable. The variable is instrumented with a digital
// signature, to avoid misbehavior if it was present in the user's
// environment. The original value is restored after the last stage, so that
// there's no final effect on the environment the application receives.
func getStage() (stage int, advanceStage func(), resetEnv func()) {
	var origValue string
	stage = 0

	daemonStage := os.Getenv(stageVar)
	stageTag := strings.SplitN(daemonStage, ":", 2)
	stageInfo := strings.SplitN(stageTag[0], "/", 3)

	if len(stageInfo) == 3 {
		stageStr, tm, check := stageInfo[0], stageInfo[1], stageInfo[2]

		hash := sha1.New()
		hash.Write([]byte(stageStr + "/" + tm + "/"))

		if check != hex.EncodeToString(hash.Sum([]byte{})) {
			// This whole chunk is original data
			origValue = daemonStage
		} else {
			stage, _ = strconv.Atoi(stageStr)

			if len(stageTag) == 2 {
				origValue = stageTag[1]
			}
		}
	} else {
		origValue = daemonStage
	}

	advanceStage = func() {
		base := fmt.Sprintf("%d/%09d/", stage+1, time.Now().Nanosecond())
		hash := sha1.New()
		hash.Write([]byte(base))

		tag := base + hex.EncodeToString(hash.Sum([]byte{}))

		if err := os.Setenv(stageVar, tag+":"+origValue); err != nil {
			fmt.Fprintf(os.Stderr, "can't set %s (stage %d)\n", stageVar, stage)
			os.Exit(1)
		}
	}

	resetEnv = func() {
		if err := os.Setenv(stageVar, origValue); err != nil {
			fmt.Fprintf(os.Stderr, "can't reset %s\n", stageVar)
			os.Exit(1)
		}
	}

	return stage, advanceStage, resetEnv
}
<|endoftext|>