<commit_before>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"github.com/elazarl/goproxy"
	"github.com/howbazaar/loggo"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"
)

type ProxyServer struct {
	goproxy.ProxyHttpServer
	Cache        *Cache
	RequestMutex *RequestMutex
	*Listeners
}

type Listeners struct {
	sync.Mutex
	chans []chan Event
}

type EtchContextData struct {
	CachedContent *bytes.Buffer
}

func ReqMethodIs(method string) goproxy.ReqConditionFunc {
	return func(req *http.Request, ctx *goproxy.ProxyCtx) bool {
		return req.Method == method
	}
}

func StatusCodeIs(code int) goproxy.RespCondition {
	return goproxy.RespConditionFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) bool {
		if resp == nil {
			return false
		}
		return resp.StatusCode == code
	})
}

type RequestMutex struct {
	sync.Mutex
	resChans map[string][]chan *http.Response
}

func NewProxyServer(cacheDir string) *ProxyServer {
	proxy := &ProxyServer{
		ProxyHttpServer: *goproxy.NewProxyHttpServer(),
		Cache:           &Cache{cacheDir},
		RequestMutex:    &RequestMutex{resChans: make(map[string][]chan *http.Response)},
		Listeners:       &Listeners{chans: make([]chan Event, 0)},
	}

	proxy.Setup()

	return proxy
}

func (proxy *ProxyServer) GuardRequest(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	proxy.RequestMutex.Lock()
	chans, ok := proxy.RequestMutex.resChans[req.URL.String()]

	if ok {
		ch := make(chan *http.Response)
		proxy.RequestMutex.resChans[req.URL.String()] = append(chans, ch)
		proxy.RequestMutex.Unlock()

		tracef(ctx, "[%s] Waiting for ongoing request", req.URL)

		res := <-ch

		tracef(ctx, "[%s] Response got from chan: %s", req.URL, res)

		if res != nil {
			return req, res
		}
	} else {
		proxy.RequestMutex.resChans[req.URL.String()] = make([]chan *http.Response, 0)
		proxy.RequestMutex.Unlock()
	}

	return req, nil
}

func (proxy *ProxyServer) PrepareRangedRequest(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	cache := proxy.Cache
	entry := cache.GetEntry(req.URL)

	content, mtime, err := entry.GetContent()

	if err != nil {
		errorf(ctx, "OnRequest: retrieving cache content: %s", err)
		return req, nil
	}

	infof(ctx, "%s: found cache entry", req.URL)

	cachedContent := bytes.NewBuffer(content)
	req.Header.Add("Range", fmt.Sprintf("bytes=%d-", cachedContent.Len()-1))
	req.Header.Add("If-Modified-Since", mtime.Format(time.RFC850))

	_, resp, err := proxy.Tr.DetailedRoundTrip(req)
	if err != nil {
		errorf(ctx, "OnRequest: executing request: %s", err)
		return req, nil
	}

	if resp.StatusCode == http.StatusRequestedRangeNotSatisfiable {
		infof(ctx, "[%s] Got 416: attempting re-fetch", req.URL)

		// clear cache
		req.Header.Del("Range")
		req.Header.Del("If-Modified-Since")

		_, _resp, err := proxy.Tr.DetailedRoundTrip(req)

		if err != nil {
			errorf(ctx, "OnRequest: re-fetch: %s", err)
			return req, nil
		}

		resp = _resp
		cachedContent = new(bytes.Buffer)
	}

	ctx.UserData = &EtchContextData{cachedContent}

	return req, resp
}

func (proxy *ProxyServer) FixStatusCode(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
	switch resp.StatusCode {
	case http.StatusNonAuthoritativeInfo:
		// dat 落ち
		resp.Header.Add("X-Original-Status-Code", fmt.Sprint(resp.StatusCode))
		resp.StatusCode = http.StatusPaymentRequired
	}

	return resp
}

func (proxy *ProxyServer) RestoreCache(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
	if ctx.UserData == nil {
		return proxy.FixStatusCode(resp, ctx)
	}

	userData := ctx.UserData.(*EtchContextData)

	switch resp.StatusCode {
	case http.StatusOK:
		// nop

	case http.StatusPartialContent:
		buf := userData.CachedContent

		// TODO check Content-Range value
		contentRange := resp.Header.Get("Content-Range")
		debugf(ctx, "Content-Range: %s", contentRange)

		// 1 バイトだけキャッシュと重複してるはずなので
		// その部分を整合性チェックに使う
		responseBody := bufio.NewReader(resp.Body)
		firstByte, err := responseBody.ReadByte()

		if err != nil {
			errorf(ctx, "[%s] Reading response: %s", ctx.Req.URL, err)
			return goproxy.NewResponse(
				ctx.Req, goproxy.ContentTypeText, http.StatusInternalServerError, fmt.Sprintf("Reading response: %s", err))
		}

		if buf.Bytes()[buf.Len()-1] != firstByte {
			infof(ctx, "[%s] Cache mismatch; deleting cache", ctx.Req.URL)

			cacheEntry := proxy.Cache.GetEntry(ctx.Req.URL)
			if err := cacheEntry.Delete(); err != nil {
				errorf(ctx, "[%s] Deleting cache failed: %s", ctx.Req.URL, err)
			}

			debugf(ctx, "[%s] Attempting re-fetch", ctx.Req.URL)

			ctx.Req.Header.Del("Range")
			ctx.Req.Header.Del("If-Modified-Since")
			ctx.UserData = nil

			_, _resp, err := proxy.Tr.DetailedRoundTrip(ctx.Req)
			if _resp == nil || err != nil {
				errorf(ctx, "[%s] Re-fetch failed: %s", ctx.Req.URL, err)
				return resp
			}

			return _resp
		}

		// 差分データなのでキャッシュと結合
		io.Copy(buf, responseBody)

		resp.StatusCode = http.StatusOK
		resp.Header.Del("Content-Range")
		resp.Body = ioutil.NopCloser(buf)

	case http.StatusNotModified, // キャッシュから更新なし
		http.StatusNonAuthoritativeInfo: // DAT 落ち

		resp.StatusCode = http.StatusOK
		resp.Body = ioutil.NopCloser(userData.CachedContent)

	default:
		errorf(ctx, "[%s] Unhandled status code: %d", ctx.Req.URL, resp.StatusCode)
	}

	return resp
}

func (proxy *ProxyServer) StoreCache(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
	cache := proxy.Cache

	lastModified := time.Now()
	if lastModifiedString := resp.Header.Get("Last-Modified"); lastModifiedString != "" {
		if _lastModified, err := http.ParseTime(lastModifiedString); err != nil {
			errorf(ctx, `[%s]: Parsing Last-Modified header "%s": %s`, ctx.Req.URL, lastModifiedString, err)
		} else {
			lastModified = _lastModified
		}
	}

	infof(ctx, "[%s] Update cache", ctx.Req.URL)

	lineCount := 0
	if userData, ok := ctx.UserData.(*EtchContextData); ok && userData.CachedContent != nil {
		lineCount = strings.Count(userData.CachedContent.String(), "\n")
	}

	proxy.Listeners.Broadcast(CacheUpdateEvent{URL: resp.Request.URL, Since: lineCount+1})

	cacheEntry := cache.GetEntry(ctx.Req.URL)
	buf := new(bytes.Buffer)
	io.Copy(buf, resp.Body)
	_, err := cacheEntry.FreshenContent(buf.Bytes(), lastModified)
	resp.Body = ioutil.NopCloser(buf)

	if err != nil {
		warningf(ctx, "[%s] FreshenContent failed: %s", ctx.Req.URL, err)
	}

	return resp
}

func (proxy *ProxyServer) UnguardRequest(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
	proxy.RequestMutex.Lock()
	defer proxy.RequestMutex.Unlock()
	if chans := proxy.RequestMutex.resChans[ctx.Req.URL.String()]; chans != nil {
		for _, ch := range chans {
			ch <- resp
		}
		delete(proxy.RequestMutex.resChans, ctx.Req.URL.String())
	}

	return resp
}

func (proxy *ProxyServer) Setup() {
	if logger, _, _ := logConfig(proxy); logger.IsDebugEnabled() {
		proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			debugf(ctx, "Request: %s %s", req.Method, req.URL)
			tracef(ctx, "Request Headers: %+v", req.Header)
			return req, nil
		})
	}

	proxy.OnRequest(ReqMethodIs("GET")).DoFunc(proxy.GuardRequest)
	proxy.OnRequest(ReqMethodIs("GET")).DoFunc(proxy.PrepareRangedRequest)
	proxy.OnResponse(ReqMethodIs("GET")).DoFunc(proxy.RestoreCache)
	proxy.OnResponse(goproxy.ContentTypeIs("text/plain"), ReqMethodIs("GET"), StatusCodeIs(200), goproxy.Not(goproxy.ReqHostIs(""))).DoFunc(proxy.StoreCache)
	proxy.OnResponse().DoFunc(proxy.UnguardRequest)

	if logger, _, _ := logConfig(proxy); logger.IsDebugEnabled() {
		proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
			debugf(ctx, "Response: [%d] %s", resp.StatusCode, resp.Status)
			tracef(ctx, "Response Headers: %+v", resp.Header)
			return resp
		})
	}
}

func (l *Listeners) Broadcast(e Event) {
	for _, ch := range(l.chans) {
		ch <- e
	}
}

func (l *Listeners) Create() chan Event {
	ch := make(chan Event)

	l.Lock()
	defer l.Unlock()

	l.chans = append(l.chans, ch)

	return ch
}

func (l *Listeners) Remove(ch <-chan Event) {
	l.Lock()
	defer l.Unlock()

	l.chans = make([]chan Event, len(l.chans) - 1)
	for _, _ch := range(l.chans) {
		if ch != _ch {
			l.chans = append(l.chans, _ch)
		}
	}
}

func main() {
	cacheDir := flag.String("cache-dir", "cache", "cache directory")
	port := flag.Int("port", 25252, "proxy port")
	hosts := flag.String("host", "2ch.net,bbspink.com", "hosts to proxy")

	flag.Parse()

	loggo.ConfigureLoggers("<root>=TRACE;proxy=TRACE;cache=TRACE")
	loggo.ReplaceDefaultWriter(loggo.NewSimpleWriter(os.Stderr, &EtchLogFormatter{}))

	proxy := NewProxyServer(*cacheDir)
	control := NewControlServer(proxy)

	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		for _, host := range strings.Split(*hosts, ",") {
			if strings.HasSuffix(req.Host, "."+host) {
				proxy.ServeHTTP(w, req)
				return
			}
		}

		control.ServeHTTP(w, req)
	})

	infof(proxy, "Starting etch at localhost:%d...", *port)
	if err := http.ListenAndServe(fmt.Sprintf(":%d", *port), mux); err != nil {
		errorf(proxy, "%s", err)
		os.Exit(1)
	}
}
<commit_msg>workaround: If-Modified-Since in UTC<commit_after>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"github.com/elazarl/goproxy"
	"github.com/howbazaar/loggo"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"
)

type ProxyServer struct {
	goproxy.ProxyHttpServer
	Cache        *Cache
	RequestMutex *RequestMutex
	*Listeners
}

type Listeners struct {
	sync.Mutex
	chans []chan Event
}

type EtchContextData struct {
	CachedContent *bytes.Buffer
}

func ReqMethodIs(method string) goproxy.ReqConditionFunc {
	return func(req *http.Request, ctx *goproxy.ProxyCtx) bool {
		return req.Method == method
	}
}

func StatusCodeIs(code int) goproxy.RespCondition {
	return goproxy.RespConditionFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) bool {
		if resp == nil {
			return false
		}
		return resp.StatusCode == code
	})
}

type RequestMutex struct {
	sync.Mutex
	resChans map[string][]chan *http.Response
}

func NewProxyServer(cacheDir string) *ProxyServer {
	proxy := &ProxyServer{
		ProxyHttpServer: *goproxy.NewProxyHttpServer(),
		Cache:           &Cache{cacheDir},
		RequestMutex:    &RequestMutex{resChans: make(map[string][]chan *http.Response)},
		Listeners:       &Listeners{chans: make([]chan Event, 0)},
	}

	proxy.Setup()

	return proxy
}

func (proxy *ProxyServer) GuardRequest(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	proxy.RequestMutex.Lock()
	chans, ok := proxy.RequestMutex.resChans[req.URL.String()]

	proxy.Listeners.Broadcast(Event{})

	if ok {
		ch := make(chan *http.Response)
		proxy.RequestMutex.resChans[req.URL.String()] = append(chans, ch)
		proxy.RequestMutex.Unlock()

		tracef(ctx, "[%s] Waiting for ongoing request", req.URL)

		res := <-ch

		tracef(ctx, "[%s] Response got from chan: %s", req.URL, res)

		if res != nil {
			return req, res
		}
	} else {
		proxy.RequestMutex.resChans[req.URL.String()] = make([]chan *http.Response, 0)
		proxy.RequestMutex.Unlock()
	}

	return req, nil
}

func (proxy *ProxyServer) PrepareRangedRequest(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	cache := proxy.Cache
	entry := cache.GetEntry(req.URL)

	content, mtime, err := entry.GetContent()

	if err != nil {
		errorf(ctx, "OnRequest: retrieving cache content: %s", err)
		return req, nil
	}

	infof(ctx, "%s: found cache entry", req.URL)

	cachedContent := bytes.NewBuffer(content)
	req.Header.Add("Range", fmt.Sprintf("bytes=%d-", cachedContent.Len()-1))
	// なんか JST だと うまく 304 を返してくれないサーバがある…
	req.Header.Add("If-Modified-Since", mtime.In(time.UTC).Format(time.RFC1123))

	tracef(ctx, "Request Headers (modified): %+v", req.Header)

	_, resp, err := proxy.Tr.DetailedRoundTrip(req)
	if err != nil {
		errorf(ctx, "OnRequest: executing request: %s", err)
		return req, nil
	}

	if resp.StatusCode == http.StatusRequestedRangeNotSatisfiable {
		infof(ctx, "[%s] Got 416: attempting re-fetch", req.URL)

		// clear cache
		req.Header.Del("Range")
		req.Header.Del("If-Modified-Since")

		_, _resp, err := proxy.Tr.DetailedRoundTrip(req)

		if err != nil {
			errorf(ctx, "OnRequest: re-fetch: %s", err)
			return req, nil
		}

		resp = _resp
		cachedContent = new(bytes.Buffer)
	}

	ctx.UserData = &EtchContextData{cachedContent}

	return req, resp
}

func (proxy *ProxyServer) FixStatusCode(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
	switch resp.StatusCode {
	case http.StatusNonAuthoritativeInfo:
		// dat 落ち
		resp.Header.Add("X-Original-Status-Code", fmt.Sprint(resp.StatusCode))
		resp.StatusCode = http.StatusPaymentRequired
	}

	return resp
}

func (proxy *ProxyServer) RestoreCache(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
	if ctx.UserData == nil {
		return proxy.FixStatusCode(resp, ctx)
	}

	userData := ctx.UserData.(*EtchContextData)

	switch resp.StatusCode {
	case http.StatusOK:
		// nop

	case http.StatusPartialContent:
		buf := userData.CachedContent

		// TODO check Content-Range value
		contentRange := resp.Header.Get("Content-Range")
		debugf(ctx, "Content-Range: %s", contentRange)

		// 1 バイトだけキャッシュと重複してるはずなので
		// その部分を整合性チェックに使う
		responseBody := bufio.NewReader(resp.Body)
		firstByte, err := responseBody.ReadByte()

		if err != nil {
			errorf(ctx, "[%s] Reading response: %s", ctx.Req.URL, err)
			return goproxy.NewResponse(
				ctx.Req, goproxy.ContentTypeText, http.StatusInternalServerError, fmt.Sprintf("Reading response: %s", err))
		}

		if buf.Bytes()[buf.Len()-1] != firstByte {
			infof(ctx, "[%s] Cache mismatch; deleting cache", ctx.Req.URL)

			cacheEntry := proxy.Cache.GetEntry(ctx.Req.URL)
			if err := cacheEntry.Delete(); err != nil {
				errorf(ctx, "[%s] Deleting cache failed: %s", ctx.Req.URL, err)
			}

			debugf(ctx, "[%s] Attempting re-fetch", ctx.Req.URL)

			ctx.Req.Header.Del("Range")
			ctx.Req.Header.Del("If-Modified-Since")
			ctx.UserData = nil

			_, _resp, err := proxy.Tr.DetailedRoundTrip(ctx.Req)
			if _resp == nil || err != nil {
				errorf(ctx, "[%s] Re-fetch failed: %s", ctx.Req.URL, err)
				return resp
			}

			return _resp
		}

		// 差分データなのでキャッシュと結合
		io.Copy(buf, responseBody)

		resp.StatusCode = http.StatusOK
		resp.Header.Del("Content-Range")
		resp.Body = ioutil.NopCloser(buf)

	case http.StatusNotModified, // キャッシュから更新なし
		http.StatusNonAuthoritativeInfo: // DAT 落ち

		resp.StatusCode = http.StatusOK
		resp.Body = ioutil.NopCloser(userData.CachedContent)

	default:
		errorf(ctx, "[%s] Unhandled status code: %d", ctx.Req.URL, resp.StatusCode)
	}

	return resp
}

func (proxy *ProxyServer) StoreCache(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
	cache := proxy.Cache

	lastModified := time.Now()
	if lastModifiedString := resp.Header.Get("Last-Modified"); lastModifiedString != "" {
		if _lastModified, err := http.ParseTime(lastModifiedString); err != nil {
			errorf(ctx, `[%s]: Parsing Last-Modified header "%s": %s`, ctx.Req.URL, lastModifiedString, err)
		} else {
			lastModified = _lastModified
		}
	}

	infof(ctx, "[%s] Update cache", ctx.Req.URL)

	lineCount := 0
	if userData, ok := ctx.UserData.(*EtchContextData); ok && userData.CachedContent != nil {
		lineCount = strings.Count(userData.CachedContent.String(), "\n")
	}

	proxy.Listeners.Broadcast(CacheUpdateEvent{URL: resp.Request.URL, Since: lineCount+1})

	cacheEntry := cache.GetEntry(ctx.Req.URL)
	buf := new(bytes.Buffer)
	io.Copy(buf, resp.Body)
	_, err := cacheEntry.FreshenContent(buf.Bytes(), lastModified)
	resp.Body = ioutil.NopCloser(buf)

	if err != nil {
		warningf(ctx, "[%s] FreshenContent failed: %s", ctx.Req.URL, err)
	}

	return resp
}

func (proxy *ProxyServer) UnguardRequest(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
	proxy.RequestMutex.Lock()
	defer proxy.RequestMutex.Unlock()
	if chans := proxy.RequestMutex.resChans[ctx.Req.URL.String()]; chans != nil {
		for _, ch := range chans {
			ch <- resp
		}
		delete(proxy.RequestMutex.resChans, ctx.Req.URL.String())
	}

	return resp
}

func (proxy *ProxyServer) Setup() {
	if logger, _, _ := logConfig(proxy); logger.IsDebugEnabled() {
		proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			debugf(ctx, "Request: %s %s", req.Method, req.URL)
			tracef(ctx, "Request Headers: %+v", req.Header)
			return req, nil
		})
	}

	proxy.OnRequest(ReqMethodIs("GET")).DoFunc(proxy.GuardRequest)
	proxy.OnRequest(ReqMethodIs("GET")).DoFunc(proxy.PrepareRangedRequest)
	proxy.OnResponse(ReqMethodIs("GET")).DoFunc(proxy.RestoreCache)
	proxy.OnResponse(goproxy.ContentTypeIs("text/plain"), ReqMethodIs("GET"), StatusCodeIs(200), goproxy.Not(goproxy.ReqHostIs(""))).DoFunc(proxy.StoreCache)
	proxy.OnResponse().DoFunc(proxy.UnguardRequest)

	if logger, _, _ := logConfig(proxy); logger.IsDebugEnabled() {
		proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
			debugf(ctx, "Response: [%d] %s", resp.StatusCode, resp.Status)
			tracef(ctx, "Response Headers: %+v", resp.Header)
			return resp
		})
	}
}

func (l *Listeners) Broadcast(e Event) {
	for _, ch := range(l.chans) {
		ch <- e
	}
}

func (l *Listeners) Create() chan Event {
	ch := make(chan Event)

	l.Lock()
	defer l.Unlock()

	l.chans = append(l.chans, ch)

	return ch
}

func (l *Listeners) Remove(ch <-chan Event) {
	l.Lock()
	defer l.Unlock()

	l.chans = make([]chan Event, len(l.chans) - 1)
	for _, _ch := range(l.chans) {
		if ch != _ch {
			l.chans = append(l.chans, _ch)
		}
	}
}

func main() {
	cacheDir := flag.String("cache-dir", "cache", "cache directory")
	port := flag.Int("port", 25252, "proxy port")
	hosts := flag.String("host", "2ch.net,bbspink.com", "hosts to proxy")

	flag.Parse()

	loggo.ConfigureLoggers("<root>=TRACE;proxy=TRACE;cache=TRACE")
	loggo.ReplaceDefaultWriter(loggo.NewSimpleWriter(os.Stderr, &EtchLogFormatter{}))

	proxy := NewProxyServer(*cacheDir)
	control := NewControlServer(proxy)

	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		for _, host := range strings.Split(*hosts, ",") {
			if strings.HasSuffix(req.Host, "."+host) {
				proxy.ServeHTTP(w, req)
				return
			}
		}

		control.ServeHTTP(w, req)
	})

	infof(proxy, "Starting etch at localhost:%d...", *port)
	if err := http.ListenAndServe(fmt.Sprintf(":%d", *port), mux); err != nil {
		errorf(proxy, "%s", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/af83/edwig/api"
	"github.com/af83/edwig/siri"
)

func main() {
	uuidPtr := flag.Bool("testuuid", false, "use the test uuid generator")
	clockPtr := flag.String("testclock", "", "use a fake clock at time given. Format 20060102-1504")
	requestorRefPtr := flag.String("requestor-ref", "Edwig", "Specify requestorRef")

	flag.Parse()

	if *uuidPtr {
		api.SetDefaultUUIDGenerator(api.NewFakeUUIDGenerator())
	}
	if *clockPtr != "" {
		testTime, err := time.Parse("20060102-1504", *clockPtr)
		if err != nil {
			panic(err)
		}
		api.SetDefaultClock(api.NewFakeClockAt(testTime))
	}

	if len(flag.Args()) < 1 {
		fmt.Printf("usage: edwig [-testuuid] [-testclock=<time>] [-requestor-ref=<requestor>]\n             check <url>\n")
		os.Exit(0)
	}

	command := flag.Args()[0]

	var err error
	switch command {
	case "check":
		err = checkStatus(flag.Args()[1], *requestorRefPtr)
	case "api":
		err = api.NewServer("localhost:8080").ListenAndServe()
	}

	if err != nil {
		if _, ok := err.(*siri.SiriError); !ok {
			panic(err)
		}
		log.Println(err)
	}
}

func checkStatus(url string, requestorRef string) error {
	client := siri.NewSOAPClient(url)
	request := &siri.SIRICheckStatusRequest{
		RequestorRef:      requestorRef,
		RequestTimestamp:  api.DefaultClock().Now(),
		MessageIdentifier: "Edwig:Message::6ba7b814-9dad-11d1-0-00c04fd430c8:LOC",
	}

	startTime := time.Now()

	xmlResponse, err := client.CheckStatus(request)
	if err != nil {
		return err
	}

	responseTime := time.Since(startTime)

	// Log
	var logMessage []byte
	if xmlResponse.Status() {
		logMessage = []byte("SIRI OK - status true - ")
	} else {
		logMessage = []byte("SIRI CRITICAL: status false - ")
		if xmlResponse.ErrorType() == "OtherError" {
			logMessage = append(logMessage, fmt.Sprintf("%s %d %s - ", xmlResponse.ErrorType(), xmlResponse.ErrorNumber(), xmlResponse.ErrorText())...)
		} else {
			logMessage = append(logMessage, fmt.Sprintf("%s %s - ", xmlResponse.ErrorType(), xmlResponse.ErrorText())...)
		}
	}
	logMessage = append(logMessage, fmt.Sprintf("%.3f seconds response time", responseTime.Seconds())...)
	log.Println(string(logMessage[:]))

	return nil
}
<commit_msg>add exist status<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/af83/edwig/api"
	"github.com/af83/edwig/siri"
)

func main() {
	uuidPtr := flag.Bool("testuuid", false, "use the test uuid generator")
	clockPtr := flag.String("testclock", "", "use a fake clock at time given. Format 20060102-1504")
	requestorRefPtr := flag.String("requestor-ref", "Edwig", "Specify requestorRef")

	flag.Parse()

	if *uuidPtr {
		api.SetDefaultUUIDGenerator(api.NewFakeUUIDGenerator())
	}
	if *clockPtr != "" {
		testTime, err := time.Parse("20060102-1504", *clockPtr)
		if err != nil {
			panic(err)
		}
		api.SetDefaultClock(api.NewFakeClockAt(testTime))
	}

	if len(flag.Args()) < 1 {
		fmt.Printf("usage: edwig [-testuuid] [-testclock=<time>] [-requestor-ref=<requestor>]\n             <command> [<args>]\n")
		os.Exit(1)
	}

	command := flag.Args()[0]

	var err error
	switch command {
	case "check":
		err = checkStatus(flag.Args()[1], *requestorRefPtr)
	case "api":
		err = api.NewServer("localhost:8080").ListenAndServe()
	}

	if err != nil {
		if _, ok := err.(*siri.SiriError); !ok {
			panic(err)
		}
		log.Println(err)
		os.Exit(2)
	}

	os.Exit(0)
}

func checkStatus(url string, requestorRef string) error {
	client := siri.NewSOAPClient(url)
	request := &siri.SIRICheckStatusRequest{
		RequestorRef:      requestorRef,
		RequestTimestamp:  api.DefaultClock().Now(),
		MessageIdentifier: "Edwig:Message::6ba7b814-9dad-11d1-0-00c04fd430c8:LOC",
	}

	startTime := time.Now()

	xmlResponse, err := client.CheckStatus(request)
	if err != nil {
		return err
	}

	responseTime := time.Since(startTime)

	// Log
	var logMessage []byte
	if xmlResponse.Status() {
		logMessage = []byte("SIRI OK - status true - ")
	} else {
		logMessage = []byte("SIRI CRITICAL: status false - ")
		if xmlResponse.ErrorType() == "OtherError" {
			logMessage = append(logMessage, fmt.Sprintf("%s %d %s - ", xmlResponse.ErrorType(), xmlResponse.ErrorNumber(), xmlResponse.ErrorText())...)
		} else {
			logMessage = append(logMessage, fmt.Sprintf("%s %s - ", xmlResponse.ErrorType(), xmlResponse.ErrorText())...)
		}
	}
	logMessage = append(logMessage, fmt.Sprintf("%.3f seconds response time", responseTime.Seconds())...)
	log.Println(string(logMessage[:]))

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	dl "github.com/openshift/dockerexec/pkg/libdocker"
)

var standardEnvironment = &cli.StringSlice{
	"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
	"HOSTNAME=nsinit",
	"TERM=xterm",
}

var execCommand = cli.Command{
	Name:   "exec",
	Usage:  "execute a new command inside a container",
	Action: execAction,
	Flags: []cli.Flag{
		cli.BoolFlag{Name: "tty,t", Usage: "allocate a TTY to the container"},
		cli.StringFlag{Name: "id", Value: "", Usage: "specify the ID for a container"},
		cli.StringFlag{Name: "user,u", Value: "", Usage: "set the user, uid, and/or gid for the process"},
		cli.StringFlag{Name: "cwd", Value: "", Usage: "set the current working dir"},
		cli.StringSliceFlag{Name: "env", Value: standardEnvironment, Usage: "set environment variables for the process"},
	},
}

func execAction(context *cli.Context) {
	containerId := context.String("id")

	if containerId == "" {
		log.Fatal("Please specify a docker id")
	}

	if len(context.Args()) == 0 {
		log.Fatal("Please specify a command to run in the container")
	}

	execOptions := &dl.DockerExecOptions{
		Args:   context.Args(),
		Env:    context.StringSlice("env"),
		User:   context.String("user"),
		Cwd:    context.String("cwd"),
		Stdin:  os.Stdin,
		Stdout: os.Stdout,
		Stderr: os.Stderr,
		Tty:    context.Bool("tty"),
	}

	retCode, err := dl.RunInContainer(containerId, execOptions)
	if err != nil {
		fatal(err)
	}

	os.Exit(retCode)

}
<commit_msg>Minor cleanup.<commit_after>package main

import (
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	dl "github.com/openshift/dockerexec/pkg/libdocker"
)

var standardEnvironment = &cli.StringSlice{
	"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
	"TERM=xterm",
}

var execCommand = cli.Command{
	Name:   "exec",
	Usage:  "execute a new command inside a container",
	Action: execAction,
	Flags: []cli.Flag{
		cli.BoolFlag{Name: "tty,t", Usage: "allocate a TTY to the container"},
		cli.StringFlag{Name: "id", Value: "", Usage: "specify the ID for a container"},
		cli.StringFlag{Name: "user,u", Value: "", Usage: "set the user, uid, and/or gid for the process"},
		cli.StringFlag{Name: "cwd", Value: "", Usage: "set the current working dir"},
		cli.StringSliceFlag{Name: "env", Value: standardEnvironment, Usage: "set environment variables for the process"},
	},
}

func execAction(context *cli.Context) {
	containerId := context.String("id")

	if containerId == "" {
		log.Fatal("Please specify a docker id")
	}

	if len(context.Args()) == 0 {
		log.Fatal("Please specify a command to run in the container")
	}

	execOptions := &dl.DockerExecOptions{
		Args:   context.Args(),
		Env:    context.StringSlice("env"),
		User:   context.String("user"),
		Cwd:    context.String("cwd"),
		Stdin:  os.Stdin,
		Stdout: os.Stdout,
		Stderr: os.Stderr,
		Tty:    context.Bool("tty"),
	}

	retCode, err := dl.RunInContainer(containerId, execOptions)
	if err != nil {
		fatal(err)
	}

	os.Exit(retCode)
}
<|endoftext|>


<commit_before>// +build linux

package main

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

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/opencontainers/specs"
)

var execCommand = cli.Command{
	Name:  "exec",
	Usage: "execute new process inside the container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "console",
			Value: "",
			Usage: "specify the pty slave path for use with the container",
		},
	},
	Action: func(context *cli.Context) {
		config, err := loadProcessConfig(context.Args().First())
		if err != nil {
			fatal(err)
		}
		if os.Geteuid() != 0 {
			logrus.Fatal("runc should be run as root")
		}
		if config.Args == nil {
			logrus.Fatal("args missing in process configuration")
		}
		status, err := execProcess(context, config)
		if err != nil {
			logrus.Fatalf("exec failed: %v", err)
		}
		os.Exit(status)
	},
}

func execProcess(context *cli.Context, config *specs.Process) (int, error) {
	container, err := getContainer(context)
	if err != nil {
		return -1, err
	}
	process := newProcess(*config)

	rootuid, err := container.Config().HostUID()
	if err != nil {
		return -1, err
	}
	tty, err := newTty(config.Terminal, process, rootuid, context.String("console"))
	if err != nil {
		return -1, err
	}
	handler := newSignalHandler(tty)
	defer handler.Close()
	if err := container.Start(process); err != nil {
		return -1, err
	}
	return handler.forward(process)
}

// loadProcessConfig loads the process configuration from the provided path.
func loadProcessConfig(path string) (*specs.Process, error) {
	f, err := os.Open(path)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("JSON configuration file for %s not found", path)
		}
		return nil, err
	}
	defer f.Close()
	var s *specs.Process
	if err := json.NewDecoder(f).Decode(&s); err != nil {
		return nil, err
	}
	return s, nil
}
<commit_msg>update exec to pass args and --tty on run<commit_after>// +build linux

package main

import (
	"fmt"
	"os"
	"path"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
)

var execCommand = cli.Command{
	Name:  "exec",
	Usage: "execute new process inside the container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "console",
			Usage: "specify the pty slave path for use with the container",
		},
		cli.StringFlag{
			Name:  "cwd",
			Usage: "current working directory in the container",
		},
		cli.StringSliceFlag{
			Name:  "env, e",
			Usage: "set environment variables",
		},
		cli.BoolFlag{
			Name:  "tty, t",
			Usage: "allocate a pseudo-TTY",
		},
		cli.StringFlag{
			Name:  "user, u",
			Usage: "UID (format: <uid>[:<gid>])",
		},
	},
	Action: func(context *cli.Context) {
		if len(context.Args()) == 0 {
			logrus.Fatal("pass the command")
		}
		if os.Geteuid() != 0 {
			logrus.Fatal("runc should be run as root")
		}
		status, err := execProcess(context, context.Args())
		if err != nil {
			logrus.Fatalf("exec failed: %v", err)
		}
		os.Exit(status)
	},
}

func execProcess(context *cli.Context, args []string) (int, error) {
	container, err := getContainer(context)
	if err != nil {
		return -1, err
	}

	bundle := container.Config().Rootfs
	if err := os.Chdir(path.Dir(bundle)); err != nil {
		return -1, err
	}
	spec, _, err := loadSpec(specConfig, runtimeConfig)
	if err != nil {
		return -1, err
	}

	p := spec.Process

	// override the cwd, if passed
	if context.String("cwd") != "" {
		p.Cwd = context.String("cwd")
	}

	// append the passed env variables
	for _, e := range context.StringSlice("env") {
		p.Env = append(p.Env, e)
	}

	// set the tty
	if context.IsSet("tty") {
		p.Terminal = context.Bool("tty")
	}

	// override the user, if passed
	if context.String("user") != "" {
		u := strings.SplitN(context.String("user"), ":", 2)
		if len(u) > 1 {
			gid, err := strconv.Atoi(u[1])
			if err != nil {
				return -1, fmt.Errorf("parsing %s as int for gid failed: %v", u[1], err)
			}
			p.User.GID = uint32(gid)
		}
		uid, err := strconv.Atoi(u[0])
		if err != nil {
			return -1, fmt.Errorf("parsing %s as int for uid failed: %v", u[0], err)
		}
		p.User.UID = uint32(uid)
	}

	process := newProcess(p)
	process.Args = args
	rootuid, err := container.Config().HostUID()
	if err != nil {
		return -1, err
	}

	tty, err := newTty(p.Terminal, process, rootuid, context.String("console"))
	if err != nil {
		return -1, err
	}

	handler := newSignalHandler(tty)
	defer handler.Close()
	if err := container.Start(process); err != nil {
		return -1, err
	}

	return handler.forward(process)
}
<|endoftext|>


<commit_before>// Package querybuilder parses ResourceD query and turn it into Cassandra + Lucene query.
package querybuilder

import (
	"fmt"
	"strings"

	"github.com/resourced/resourced-master/libstring"
)

// Parse parses ResourceD query and turns it into Cassandra + Lucene query.
func Parse(input string, skipFields []string) string {
	return parseAnd(input, skipFields)
}

// parseAnd parses and conjunctive operator.
func parseAnd(input string, skipFields []string) string {
	pgQueryParts := make([]string, 0)

	// normalize variation of AND
	input = strings.Replace(input, " AND ", " and ", -1)

	statements := strings.Split(input, " and ")
	for _, statement := range statements {
		pgStatement := parseStatement(statement, skipFields)
		if pgStatement != "" {
			pgQueryParts = append(pgQueryParts, pgStatement)
		}
	}

	if len(pgQueryParts) > 1 {
		return fmt.Sprintf(`{type: "boolean", must: [%v]}`, strings.Join(pgQueryParts, ","))

	} else if len(pgQueryParts) == 1 {
		return pgQueryParts[0]
	}

	return ""
}

// parseStatement parses ResourceD statement and turns it into postgres statement.
func parseStatement(statement string, skipFields []string) string {
	statement = strings.TrimSpace(statement)

	// Querying tags.
	// There can only be 1 operator for tags: "="
	if strings.HasPrefix(statement, "Tags") || strings.HasPrefix(statement, "tags") {
		if strings.Contains(statement, "=") {
			parts := strings.Split(statement, "=")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			fieldTags := strings.Replace(parts[0], ".", "$", 1)
			fieldTags = strings.TrimSpace(fieldTags)

			foundMasterTagsToSkip := false
			for _, skipField := range skipFields {
				if skipField == "master_tags" {
					foundMasterTagsToSkip = true
					break
				}
			}

			// Skip master_tags
			if foundMasterTagsToSkip {
				return fmt.Sprintf(`{type: "match", field: "%v", value: "%v"}`, fieldTags, data)
			}

			// Not skipping master_tags
			fieldMasterTags := strings.Replace(fieldTags, "tags$", "master_tags$", 1)

			return fmt.Sprintf(`{type: "boolean", should: [{type: "match", field: "%v", value: "%v"},{type: "match", field: "%v", value: "%v"}]}`, fieldTags, data, fieldMasterTags, data)
		}
	}

	// Querying hostname.
	// Operators:
	// "="        : Exact match.
	// "^"        : Starts with, case sensitive.
	// "~"        : Matches regular expression, case sensitive.
	// "contains" : Contains the following values.
	// "wildcard" : Perform wildcard search.
	if strings.HasPrefix(statement, "Hostname") || strings.HasPrefix(statement, "hostname") {
		if strings.Contains(statement, "=") {
			parts := strings.Split(statement, "=")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "match", "hostname", data)

		} else if strings.Contains(statement, "contains") {
			parts := strings.Split(statement, "contains")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			dataSlice := strings.Split(data, ",")
			for i, datum := range dataSlice {
				dataSlice[i] = `"` + strings.TrimSpace(datum) + `"`
			}

			return fmt.Sprintf(`{type: "%v", field: "%v", values: "%v"}`, "contains", "hostname", strings.Join(dataSlice, `,`))

		} else if strings.Contains(statement, "wildcard") {
			parts := strings.Split(statement, "wildcard")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "wildcard", "hostname", data)

		} else if strings.Contains(statement, "^") {
			parts := strings.Split(statement, "^")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "prefix", "hostname", data)

		} else if strings.Contains(statement, "~") {
			parts := strings.Split(statement, "~")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "regexp", "hostname", data)
		}
	}

	// Querying filename.
	// Operators:
	// "="        : Exact match.
	// "^"        : Starts with, case sensitive.
	// "~"        : Matches regular expression, case sensitive.
	// "contains" : Contains the following values.
	// "wildcard" : Perform wildcard search.
	if strings.HasPrefix(statement, "Filename") || strings.HasPrefix(statement, "filename") {
		if strings.Contains(statement, "=") {
			parts := strings.Split(statement, "=")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "match", "filename", data)

		} else if strings.Contains(statement, "contains") {
			parts := strings.Split(statement, "contains")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			dataSlice := strings.Split(data, ",")
			for i, datum := range dataSlice {
				dataSlice[i] = `"` + strings.TrimSpace(datum) + `"`
			}

			return fmt.Sprintf(`{type: "%v", field: "%v", values: "%v"}`, "contains", "filename", strings.Join(dataSlice, `,`))

		} else if strings.Contains(statement, "wildcard") {
			parts := strings.Split(statement, "wildcard")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "wildcard", "filename", data)

		} else if strings.Contains(statement, "^") {
			parts := strings.Split(statement, "^")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "prefix", "filename", data)

		} else if strings.Contains(statement, "~") {
			parts := strings.Split(statement, "~")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "regexp", "filename", data)
		}
	}

	// Querying data.
	// Operators for floating point data: >=, <=, =, <, >
	//     Expected output: (data #>> '{/free,Memory.TotalGB}')::float8
	// Operators for string data:
	//     "="        : Exact match.
	//     "^"        : Starts with, case sensitive.
	//     "~"        : Matches regular expression, case sensitive.
	//     "contains" : Contains the following values.
	//     "wildcard" : Perform wildcard search.
	if strings.HasPrefix(statement, "/") {
		operator := ""

		for _, op := range []string{">=", "<=", "=", "<", ">", "wildcard", "^", "~"} {
			if strings.Contains(statement, op) {
				operator = op
				break
			}
		}

		if operator != "" {
			parts := strings.Split(statement, operator)

			field := parts[0]
			field = strings.TrimSpace(field)

			value := parts[len(parts)-1]
			value = strings.TrimSpace(value)
			value = libstring.StripChars(value, `"'`)

			if operator == "=" {
				return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "match", "data_string$"+field, value)
			}

			if operator == ">" {
				return fmt.Sprintf(`{type: "%v", field: "%v", lower: %v}`, "range", "data_float$"+field, value)
			}
			if operator == ">=" {
				return fmt.Sprintf(`{type: "%v", field: "%v", lower: %v, include_lower: true}`, "range", "data_float$"+field, value)
			}

			if operator == "<" {
				return fmt.Sprintf(`{type: "%v", field: "%v", upper: %v}`, "range", "data_float$"+field, value)
			}
			if operator == "<=" {
				return fmt.Sprintf(`{type: "%v", field: "%v", upper: %v, include_upper: true}`, "range", "data_float$"+field, value)
			}

			if operator == "~" {
				return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "regexp", "data_string$"+field, value)
			}

			if operator == "^" {
				return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "prefix", "data_string$"+field, value)
			}

			if operator == "wildcard" {
				return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "wildcard", "data_string$"+field, value)
			}

			return ""
		}
	}

	// Querying logline.
	// Operators:
	// "search" : Full text search.
	if strings.HasPrefix(statement, "Logline") || strings.HasPrefix(statement, "logline") {
		if strings.Contains(statement, "search") {
			parts := strings.Split(statement, "search")

			searchQuery := parts[len(parts)-1]
			searchQuery = strings.TrimSpace(searchQuery)
			searchQuery = libstring.StripChars(searchQuery, `"'`)

			return fmt.Sprintf(`{type: "phrase", field: "logline", value: "%v", slop: 1}`, searchQuery)
		}
	}

	return ""
}
<commit_msg>Escape forward slash<commit_after>// Package querybuilder parses ResourceD query and turn it into Cassandra + Lucene query.
package querybuilder

import (
	"fmt"
	"strings"

	"github.com/resourced/resourced-master/libstring"
)

// Parse parses ResourceD query and turns it into Cassandra + Lucene query.
func Parse(input string, skipFields []string) string {
	return parseAnd(input, skipFields)
}

// parseAnd parses and conjunctive operator.
func parseAnd(input string, skipFields []string) string {
	pgQueryParts := make([]string, 0)

	// normalize variation of AND
	input = strings.Replace(input, " AND ", " and ", -1)

	statements := strings.Split(input, " and ")
	for _, statement := range statements {
		pgStatement := parseStatement(statement, skipFields)
		if pgStatement != "" {
			pgQueryParts = append(pgQueryParts, pgStatement)
		}
	}

	if len(pgQueryParts) > 1 {
		return fmt.Sprintf(`{type: "boolean", must: [%v]}`, strings.Join(pgQueryParts, ","))

	} else if len(pgQueryParts) == 1 {
		return pgQueryParts[0]
	}

	return ""
}

// parseStatement parses ResourceD statement and turns it into postgres statement.
func parseStatement(statement string, skipFields []string) string {
	statement = strings.TrimSpace(statement)

	// Querying tags.
	// There can only be 1 operator for tags: "="
	if strings.HasPrefix(statement, "Tags") || strings.HasPrefix(statement, "tags") {
		if strings.Contains(statement, "=") {
			parts := strings.Split(statement, "=")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			fieldTags := strings.Replace(parts[0], ".", "$", 1)
			fieldTags = strings.TrimSpace(fieldTags)

			foundMasterTagsToSkip := false
			for _, skipField := range skipFields {
				if skipField == "master_tags" {
					foundMasterTagsToSkip = true
					break
				}
			}

			// Skip master_tags
			if foundMasterTagsToSkip {
				return fmt.Sprintf(`{type: "match", field: "%v", value: "%v"}`, fieldTags, data)
			}

			// Not skipping master_tags
			fieldMasterTags := strings.Replace(fieldTags, "tags$", "master_tags$", 1)

			return fmt.Sprintf(`{type: "boolean", should: [{type: "match", field: "%v", value: "%v"},{type: "match", field: "%v", value: "%v"}]}`, fieldTags, data, fieldMasterTags, data)
		}
	}

	// Querying hostname.
	// Operators:
	// "="        : Exact match.
	// "^"        : Starts with, case sensitive.
	// "~"        : Matches regular expression, case sensitive.
	// "contains" : Contains the following values.
	// "wildcard" : Perform wildcard search.
	if strings.HasPrefix(statement, "Hostname") || strings.HasPrefix(statement, "hostname") {
		if strings.Contains(statement, "=") {
			parts := strings.Split(statement, "=")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "match", "hostname", data)

		} else if strings.Contains(statement, "contains") {
			parts := strings.Split(statement, "contains")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			dataSlice := strings.Split(data, ",")
			for i, datum := range dataSlice {
				dataSlice[i] = `"` + strings.TrimSpace(datum) + `"`
			}

			return fmt.Sprintf(`{type: "%v", field: "%v", values: "%v"}`, "contains", "hostname", strings.Join(dataSlice, `,`))

		} else if strings.Contains(statement, "wildcard") {
			parts := strings.Split(statement, "wildcard")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "wildcard", "hostname", data)

		} else if strings.Contains(statement, "^") {
			parts := strings.Split(statement, "^")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "prefix", "hostname", data)

		} else if strings.Contains(statement, "~") {
			parts := strings.Split(statement, "~")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "regexp", "hostname", data)
		}
	}

	// Querying filename.
	// Operators:
	// "="        : Exact match.
	// "^"        : Starts with, case sensitive.
	// "~"        : Matches regular expression, case sensitive.
	// "contains" : Contains the following values.
	// "wildcard" : Perform wildcard search.
	if strings.HasPrefix(statement, "Filename") || strings.HasPrefix(statement, "filename") {
		if strings.Contains(statement, "=") {
			parts := strings.Split(statement, "=")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "match", "filename", data)

		} else if strings.Contains(statement, "contains") {
			parts := strings.Split(statement, "contains")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			dataSlice := strings.Split(data, ",")
			for i, datum := range dataSlice {
				dataSlice[i] = `"` + strings.TrimSpace(datum) + `"`
			}

			return fmt.Sprintf(`{type: "%v", field: "%v", values: "%v"}`, "contains", "filename", strings.Join(dataSlice, `,`))

		} else if strings.Contains(statement, "wildcard") {
			parts := strings.Split(statement, "wildcard")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "wildcard", "filename", data)

		} else if strings.Contains(statement, "^") {
			parts := strings.Split(statement, "^")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "prefix", "filename", data)

		} else if strings.Contains(statement, "~") {
			parts := strings.Split(statement, "~")

			data := parts[len(parts)-1]
			data = strings.TrimSpace(data)
			data = libstring.StripChars(data, `"'`)

			return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "regexp", "filename", data)
		}
	}

	// Querying data.
	// Operators for floating point data: >=, <=, =, <, >
	//     Expected output: (data #>> '{/free,Memory.TotalGB}')::float8
	// Operators for string data:
	//     "="        : Exact match.
	//     "^"        : Starts with, case sensitive.
	//     "~"        : Matches regular expression, case sensitive.
	//     "contains" : Contains the following values.
	//     "wildcard" : Perform wildcard search.
	if strings.HasPrefix(statement, "/") {
		operator := ""

		for _, op := range []string{">=", "<=", "=", "<", ">", "wildcard", "^", "~"} {
			if strings.Contains(statement, op) {
				operator = op
				break
			}
		}

		if operator != "" {
			parts := strings.Split(statement, operator)

			field := parts[0]
			field = strings.TrimSpace(field)

			if strings.HasPrefix(field, "/") {
				field = strings.Replace(field, "/", `\/`, 1)
			}

			value := parts[len(parts)-1]
			value = strings.TrimSpace(value)
			value = libstring.StripChars(value, `"'`)

			if operator == "=" {
				return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "match", "data_string$"+field, value)
			}

			if operator == ">" {
				return fmt.Sprintf(`{type: "%v", field: "%v", lower: %v}`, "range", "data_float$"+field, value)
			}
			if operator == ">=" {
				return fmt.Sprintf(`{type: "%v", field: "%v", lower: %v, include_lower: true}`, "range", "data_float$"+field, value)
			}

			if operator == "<" {
				return fmt.Sprintf(`{type: "%v", field: "%v", upper: %v}`, "range", "data_float$"+field, value)
			}
			if operator == "<=" {
				return fmt.Sprintf(`{type: "%v", field: "%v", upper: %v, include_upper: true}`, "range", "data_float$"+field, value)
			}

			if operator == "~" {
				return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "regexp", "data_string$"+field, value)
			}

			if operator == "^" {
				return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "prefix", "data_string$"+field, value)
			}

			if operator == "wildcard" {
				return fmt.Sprintf(`{type: "%v", field: "%v", value: "%v"}`, "wildcard", "data_string$"+field, value)
			}

			return ""
		}
	}

	// Querying logline.
	// Operators:
	// "search" : Full text search.
	if strings.HasPrefix(statement, "Logline") || strings.HasPrefix(statement, "logline") {
		if strings.Contains(statement, "search") {
			parts := strings.Split(statement, "search")

			searchQuery := parts[len(parts)-1]
			searchQuery = strings.TrimSpace(searchQuery)
			searchQuery = libstring.StripChars(searchQuery, `"'`)

			return fmt.Sprintf(`{type: "phrase", field: "logline", value: "%v", slop: 1}`, searchQuery)
		}
	}

	return ""
}
<|endoftext|>


<commit_before>package fann

/*
#include <fann.h>

typedef unsigned int* uint_ptr;

const unsigned int* go2uintArray(unsigned int* arr, int n) {
	return malloc(sizeof(unsigned int) * n);
}

*/
import "C"

type TrainData struct {
	object *C.struct_fann_train_data
}

func ReadFromFile(fn string) {
}

type Ann struct {
	object *C.struct_fann
}

func CreateStandart(numLayers uint, layers []uint32) (*Ann) {
	var ann Ann
	ann.object = C.fann_create_standard_array(C.uint(numLayers), (*C.uint)(&layers[0]))
	return &ann
}

func (*Ann) Foo() {

}
/*
func CreateSparse(concentration float32, num_layers uint, layers []int) {
}
*/
<commit_msg>Added TrainOnFile<commit_after>package fann

/*
#include <fann.h>

typedef unsigned int* uint_ptr;

const unsigned int* go2uintArray(unsigned int* arr, int n) {
	return malloc(sizeof(unsigned int) * n);
}

*/
import "C"
import "unsafe"
type TrainData struct {
	object *C.struct_fann_train_data
}

func ReadFromFile(fn string) {
}

type Ann struct {
	object *C.struct_fann
}

func CreateStandart(numLayers uint, layers []uint32) (*Ann) {
	var ann Ann
	ann.object = C.fann_create_standard_array(C.uint(numLayers), (*C.uint)(&layers[0]))
	return &ann
}

func (ann *Ann) TrainOnFile(filename string, maxEpoches uint32, epochBetweenReports uint32, desiredError float32) {
	cfn := C.CString(filename)
	defer C.free(unsafe.Pointer(cfn))
	C.fann_train_on_file(ann.object, cfn, C.uint(maxEpoches), C.uint(epochBetweenReports), C.float(desiredError));
}

func (*Ann) Foo() {

}
/*
func CreateSparse(concentration float32, num_layers uint, layers []int) {
}
*/
<|endoftext|>


<commit_before>package spvwallet

import (
	"encoding/json"
	"github.com/OpenBazaar/wallet-interface"
	"golang.org/x/net/proxy"
	"net"
	"net/http"
	"time"
)

type httpClient interface {
	Get(string) (*http.Response, error)
}

type feeCache struct {
	fees        *Fees
	lastUpdated time.Time
}

type Fees struct {
	FastestFee  uint64
	HalfHourFee uint64
	HourFee     uint64
}

type FeeProvider struct {
	maxFee      uint64
	priorityFee uint64
	normalFee   uint64
	economicFee uint64
	feeAPI      string

	httpClient httpClient

	cache *feeCache
}

func NewFeeProvider(maxFee, priorityFee, normalFee, economicFee uint64, feeAPI string, proxy proxy.Dialer) *FeeProvider {
	fp := FeeProvider{
		maxFee:      maxFee,
		priorityFee: priorityFee,
		normalFee:   normalFee,
		economicFee: economicFee,
		feeAPI:      feeAPI,
		cache:       new(feeCache),
	}
	dial := net.Dial
	if proxy != nil {
		dial = proxy.Dial
	}
	tbTransport := &http.Transport{Dial: dial}
	httpClient := &http.Client{Transport: tbTransport, Timeout: time.Second * 10}
	fp.httpClient = httpClient
	return &fp
}

func (fp *FeeProvider) GetFeePerByte(feeLevel wallet.FeeLevel) uint64 {
	defaultFee := func() uint64 {
		switch feeLevel {
		case wallet.PRIOIRTY:
			return fp.priorityFee
		case wallet.NORMAL:
			return fp.normalFee
		case wallet.ECONOMIC:
			return fp.economicFee
		case wallet.FEE_BUMP:
			return fp.priorityFee * 2
		default:
			return fp.normalFee
		}
	}
	if fp.feeAPI == "" {
		return defaultFee()
	}
	fees := new(Fees)
	if time.Since(fp.cache.lastUpdated) > time.Minute {
		resp, err := fp.httpClient.Get(fp.feeAPI)
		if err != nil {
			return defaultFee()
		}

		defer resp.Body.Close()

		err = json.NewDecoder(resp.Body).Decode(&fees)
		if err != nil {
			return defaultFee()
		}
		fp.cache.lastUpdated = time.Now()
		fp.cache.fees = fees
	} else {
		fees = fp.cache.fees
	}
	switch feeLevel {
	case wallet.PRIOIRTY:
		if fees.FastestFee > fp.maxFee || fees.FastestFee == 0 {
			return fp.maxFee
		} else {
			return fees.FastestFee
		}
	case wallet.NORMAL:
		if fees.HalfHourFee > fp.maxFee || fees.HalfHourFee == 0 {
			return fp.maxFee
		} else {
			return fees.HalfHourFee
		}
	case wallet.ECONOMIC:
		if fees.HourFee > fp.maxFee || fees.HourFee == 0 {
			return fp.maxFee
		} else {
			return fees.HourFee
		}
	case wallet.FEE_BUMP:
		if (fees.FastestFee*2) > fp.maxFee || fees.FastestFee == 0 {
			return fp.maxFee
		} else {
			return fees.FastestFee * 2
		}
	default:
		return fp.normalFee
	}
}
<commit_msg>Update FeeProvider with new API format<commit_after>package spvwallet

import (
	"encoding/json"
	"github.com/OpenBazaar/wallet-interface"
	"golang.org/x/net/proxy"
	"net"
	"net/http"
	"time"
)

type httpClient interface {
	Get(string) (*http.Response, error)
}

type feeCache struct {
	fees        *Fees
	lastUpdated time.Time
}

type Fees struct {
	Priority uint64 `json:"priority"`
	Normal   uint64 `json:"normal"`
	Economic uint64 `json:"economic"`
}

type FeeProvider struct {
	maxFee      uint64
	priorityFee uint64
	normalFee   uint64
	economicFee uint64
	feeAPI      string

	httpClient httpClient

	cache *feeCache
}

func NewFeeProvider(maxFee, priorityFee, normalFee, economicFee uint64, feeAPI string, proxy proxy.Dialer) *FeeProvider {
	fp := FeeProvider{
		maxFee:      maxFee,
		priorityFee: priorityFee,
		normalFee:   normalFee,
		economicFee: economicFee,
		feeAPI:      feeAPI,
		cache:       new(feeCache),
	}
	dial := net.Dial
	if proxy != nil {
		dial = proxy.Dial
	}
	tbTransport := &http.Transport{Dial: dial}
	httpClient := &http.Client{Transport: tbTransport, Timeout: time.Second * 10}
	fp.httpClient = httpClient
	return &fp
}

func (fp *FeeProvider) GetFeePerByte(feeLevel wallet.FeeLevel) uint64 {
	defaultFee := func() uint64 {
		switch feeLevel {
		case wallet.PRIOIRTY:
			return fp.priorityFee
		case wallet.NORMAL:
			return fp.normalFee
		case wallet.ECONOMIC:
			return fp.economicFee
		case wallet.FEE_BUMP:
			return fp.priorityFee * 2
		default:
			return fp.normalFee
		}
	}
	if fp.feeAPI == "" {
		return defaultFee()
	}
	fees := new(Fees)
	if time.Since(fp.cache.lastUpdated) > time.Minute {
		resp, err := fp.httpClient.Get(fp.feeAPI)
		if err != nil {
			return defaultFee()
		}

		defer resp.Body.Close()

		err = json.NewDecoder(resp.Body).Decode(&fees)
		if err != nil {
			return defaultFee()
		}
		fp.cache.lastUpdated = time.Now()
		fp.cache.fees = fees
	} else {
		fees = fp.cache.fees
	}
	switch feeLevel {
	case wallet.PRIOIRTY:
		if fees.Priority > fp.maxFee || fees.Priority == 0 {
			return fp.maxFee
		} else {
			return fees.Priority
		}
	case wallet.NORMAL:
		if fees.Normal > fp.maxFee || fees.Normal == 0 {
			return fp.maxFee
		} else {
			return fees.Normal
		}
	case wallet.ECONOMIC:
		if fees.Economic > fp.maxFee || fees.Economic == 0 {
			return fp.maxFee
		} else {
			return fees.Economic
		}
	case wallet.FEE_BUMP:
		if (fees.Priority*2) > fp.maxFee || fees.Priority == 0 {
			return fp.maxFee
		} else {
			return fees.Priority * 2
		}
	default:
		return fp.normalFee
	}
}
<|endoftext|>


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

package com

import (
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"os"
	"path"
)

// Storage unit constants.
const (
	Byte  = 1
	KByte = Byte * 1024
	MByte = KByte * 1024
	GByte = MByte * 1024
	TByte = GByte * 1024
	PByte = TByte * 1024
	EByte = PByte * 1024
)

func logn(n, b float64) float64 {
	return math.Log(n) / math.Log(b)
}

func humanateBytes(s uint64, base float64, sizes []string) string {
	if s < 10 {
		return fmt.Sprintf("%dB", s)
	}
	e := math.Floor(logn(float64(s), base))
	suffix := sizes[int(e)]
	val := float64(s) / math.Pow(base, math.Floor(e))
	f := "%.0f"
	if val < 10 {
		f = "%.1f"
	}

	return fmt.Sprintf(f+"%s", val, suffix)
}

// HumaneFileSize calculates the file size and generate user-friendly string.
func HumaneFileSize(s int64) string {
	sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"}
	return humanateBytes(uint64(s), 1024, sizes)
}

// FileMTime returns file modified time and possible error.
func FileMTime(file string) (int64, error) {
	f, err := os.Stat(file)
	if err != nil {
		return 0, err
	}
	return f.ModTime().Unix(), nil
}

// FileSize returns file size in bytes and possible error.
func FileSize(file string) (int64, error) {
	f, err := os.Stat(file)
	if err != nil {
		return 0, err
	}
	return f.Size(), nil
}

// Copy copies file from source to target path.
func Copy(src, dest string) error {
	// Gather file information to set back later.
	si, err := os.Lstat(src)
	if err != nil {
		return err
	}

	// Handle symbolic link.
	if si.Mode()&os.ModeSymlink != 0 {
		target, err := os.Readlink(src)
		if err != nil {
			return err
		}
		// NOTE: os.Chmod and os.Chtimes don't recoganize symbolic link,
		// which will lead "no such file or directory" error.
		return os.Symlink(target, dest)
	}

	sr, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sr.Close()

	dw, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer dw.Close()

	if _, err = io.Copy(dw, sr); err != nil {
		return err
	}

	// Set back file information.
	if err = os.Chtimes(dest, si.ModTime(), si.ModTime()); err != nil {
		return err
	}
	return os.Chmod(dest, si.Mode())
}

// WriteFile writes data to a file named by filename.
// If the file does not exist, WriteFile creates it
// and its upper level paths.
func WriteFile(filename string, data []byte) error {
	os.MkdirAll(path.Dir(filename), os.ModePerm)
	return ioutil.WriteFile(filename, data, 0655)
}

// IsFile returns true if given path is a file,
// or returns false when it's a directory or does not exist.
func IsFile(filePath string) bool {
	f, e := os.Stat(filePath)
	if e != nil {
		return false
	}
	return !f.IsDir()
}

// IsExist checks whether a file or directory exists.
// It returns false when the file or directory does not exist.
func IsExist(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsExist(err)
}
<commit_msg>Update<commit_after>// Copyright 2013 com authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package com

import (
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"os"
	"path"
)

// Storage unit constants.
const (
	Byte  = 1
	KByte = Byte * 1024
	MByte = KByte * 1024
	GByte = MByte * 1024
	TByte = GByte * 1024
	PByte = TByte * 1024
	EByte = PByte * 1024
)

func logn(n, b float64) float64 {
	return math.Log(n) / math.Log(b)
}

func humanateBytes(s uint64, base float64, sizes []string) string {
	if s < 10 {
		return fmt.Sprintf("%dB", s)
	}
	e := math.Floor(logn(float64(s), base))
	suffix := sizes[int(e)]
	val := float64(s) / math.Pow(base, math.Floor(e))
	f := "%.0f"
	if val < 10 {
		f = "%.1f"
	}

	return fmt.Sprintf(f+"%s", val, suffix)
}

// HumaneFileSize calculates the file size and generate user-friendly string.
func HumaneFileSize(s uint64) string {
	sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"}
	return humanateBytes(s, 1024, sizes)
}

// FileMTime returns file modified time and possible error.
func FileMTime(file string) (int64, error) {
	f, err := os.Stat(file)
	if err != nil {
		return 0, err
	}
	return f.ModTime().Unix(), nil
}

// FileSize returns file size in bytes and possible error.
func FileSize(file string) (int64, error) {
	f, err := os.Stat(file)
	if err != nil {
		return 0, err
	}
	return f.Size(), nil
}

// Copy copies file from source to target path.
func Copy(src, dest string) error {
	// Gather file information to set back later.
	si, err := os.Lstat(src)
	if err != nil {
		return err
	}

	// Handle symbolic link.
	if si.Mode()&os.ModeSymlink != 0 {
		target, err := os.Readlink(src)
		if err != nil {
			return err
		}
		// NOTE: os.Chmod and os.Chtimes don't recoganize symbolic link,
		// which will lead "no such file or directory" error.
		return os.Symlink(target, dest)
	}

	sr, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sr.Close()

	dw, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer dw.Close()

	if _, err = io.Copy(dw, sr); err != nil {
		return err
	}

	// Set back file information.
	if err = os.Chtimes(dest, si.ModTime(), si.ModTime()); err != nil {
		return err
	}
	return os.Chmod(dest, si.Mode())
}

// WriteFile writes data to a file named by filename.
// If the file does not exist, WriteFile creates it
// and its upper level paths.
func WriteFile(filename string, data []byte) error {
	os.MkdirAll(path.Dir(filename), os.ModePerm)
	return ioutil.WriteFile(filename, data, 0655)
}

// IsFile returns true if given path is a file,
// or returns false when it's a directory or does not exist.
func IsFile(filePath string) bool {
	f, e := os.Stat(filePath)
	if e != nil {
		return false
	}
	return !f.IsDir()
}

// IsExist checks whether a file or directory exists.
// It returns false when the file or directory does not exist.
func IsExist(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsExist(err)
}
<|endoftext|>


<commit_before>package rw

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
)

const (
	B  = 1
	KB = 1024 * B
	MB = 1024 * KB
	GB = 1024 * MB
	TB = 1024 * GB
	PB = 1024 * TB
)

// BUG(rjeczalik): LimitFile creates a writer that always truncates
// the file if it exists - the append support is missing.

// NOTE: limitedFile's writer is always doing copy-on-write - it
// writes to a temporary files that are renamed on a successful
// Close() call.
//
// NOTE: It is not safe to call Read() and/or Write() methods
// concurrently - synchronization is left up to the caller.
type limitedFile struct {
	path    string
	n       int
	written int
	limit   int
	r       *os.File
	w       *os.File
	err     error
	tmp     string
	cow     []string
	single  bool
}

var _ io.ReadWriteCloser = (*limitedFile)(nil)

func LimitReader(path string) io.ReadCloser {
	return &limitedFile{
		path: path,
	}
}

func LimitWriter(path string, limit int) io.WriteCloser {
	return &limitedFile{
		path:  path,
		limit: limit,
	}
}

func (f *limitedFile) Read(p []byte) (int, error) {
	if f.r == nil && f.n == 0 {
		var err error
		switch f.r, err = os.Open(f.path); {
		case os.IsNotExist(err):
			var e error
			if f.r, e = os.Open(f.path + ".1"); e != nil {
				return 0, err // return original error
			}
			f.n++
		case err == nil:
			f.single = true
		default:
			return 0, err
		}
	}
	n, err := f.r.Read(p)
	if err == io.EOF {
		_ = f.r.Close()
		var e error
		if f.r, e = os.Open(fmt.Sprintf("%s.%d", f.path, f.n+1)); e != nil {
			if os.IsNotExist(e) {
				return n, err
			}
			return n, e
		}
		f.n++
		err = nil
	}
	return n, err
}

func (f *limitedFile) mktmp() (*os.File, error) {
	return ioutil.TempFile(filepath.Split(f.path))
}

func (f *limitedFile) Write(p []byte) (int, error) {
	if f.w == nil && f.n == 0 {
		var err error
		if f.w, err = f.mktmp(); err != nil {
			return 0, err
		}
	}
	start, end := 0, min(len(p), f.limit-f.written)
	for {
		start, end = end, min(len(p), start+f.limit-f.written)
		n, err := f.w.Write(p[start:end])
		if err != nil {
			return n, err
		}
		f.written += n
		if f.written >= f.limit {
			if err = f.w.Close(); err != nil {
				return n, err
			}
			f.cow = append(f.cow, f.w.Name())
			if f.w, err = f.mktmp(); err != nil {
				return n, err
			}
			f.written = 0
		}
		if end == len(p) {
			break
		}
	}
	return len(p), nil
}

func (f *limitedFile) Close() error {
	var err error
	if f.w != nil {
		err = nonil(err, f.w.Close())
		f.cow = append(f.cow, f.w.Name())
	}
	if f.r != nil {
		err = nonil(err, f.r.Close())
	}
	if err != nil {
		return err
	}
	if len(f.cow) == 1 {
		err = nonil(err, os.Rename(f.cow[0], f.path))
	} else {
		for i, tmp := range f.cow {
			path := fmt.Sprintf("%s.%d", f.path, i+1)
			err = nonil(err, os.Rename(tmp, path))
		}
		if f.single && err == nil {
			err = nonil(err, os.Remove(f.path))
		}
	}
	return err
}
<commit_msg>rw: do not migrate to multipart files<commit_after>package rw

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
)

const (
	B  = 1
	KB = 1024 * B
	MB = 1024 * KB
	GB = 1024 * MB
	TB = 1024 * GB
	PB = 1024 * TB
)

// BUG(rjeczalik): LimitFile creates a writer that always truncates
// the file if it exists - the append support is missing.

// NOTE: limitedFile's writer is always doing copy-on-write - it
// writes to a temporary files that are renamed on a successful
// Close() call.
//
// NOTE: It is not safe to call Read() and/or Write() methods
// concurrently - synchronization is left up to the caller.
type limitedFile struct {
	path    string
	n       int
	written int
	limit   int
	r       *os.File
	w       *os.File
	err     error
	tmp     string
	cow     []string
}

var _ io.ReadWriteCloser = (*limitedFile)(nil)

func LimitReader(path string) io.ReadCloser {
	return &limitedFile{
		path: path,
	}
}

func LimitWriter(path string, limit int) io.WriteCloser {
	return &limitedFile{
		path:  path,
		limit: limit,
	}
}

func (f *limitedFile) Read(p []byte) (int, error) {
	if f.r == nil && f.n == 0 {
		var err error
		switch f.r, err = os.Open(f.path); {
		case os.IsNotExist(err):
			var e error
			if f.r, e = os.Open(f.path + ".1"); e != nil {
				return 0, err // return original error
			}
			f.n++
		case err == nil:
		default:
			return 0, err
		}
	}
	n, err := f.r.Read(p)
	if err == io.EOF {
		_ = f.r.Close()
		var e error
		if f.r, e = os.Open(fmt.Sprintf("%s.%d", f.path, f.n+1)); e != nil {
			if os.IsNotExist(e) {
				return n, err
			}
			return n, e
		}
		f.n++
		err = nil
	}
	return n, err
}

func (f *limitedFile) mktmp() (*os.File, error) {
	return ioutil.TempFile(filepath.Split(f.path))
}

func (f *limitedFile) Write(p []byte) (int, error) {
	if f.w == nil && f.n == 0 {
		var err error
		if f.w, err = f.mktmp(); err != nil {
			return 0, err
		}
	}
	start, end := 0, min(len(p), f.limit-f.written)
	for {
		start, end = end, min(len(p), start+f.limit-f.written)
		n, err := f.w.Write(p[start:end])
		if err != nil {
			return n, err
		}
		f.written += n
		if f.written >= f.limit {
			if err = f.w.Close(); err != nil {
				return n, err
			}
			f.cow = append(f.cow, f.w.Name())
			if f.w, err = f.mktmp(); err != nil {
				return n, err
			}
			f.written = 0
		}
		if end == len(p) {
			break
		}
	}
	return len(p), nil
}

func (f *limitedFile) Close() error {
	var err error
	if f.w != nil {
		err = nonil(err, f.w.Close())
		f.cow = append(f.cow, f.w.Name())
	}
	if f.r != nil {
		err = nonil(err, f.r.Close())
	}
	if err != nil {
		return err
	}
	if len(f.cow) == 1 {
		err = nonil(err, os.Rename(f.cow[0], f.path))
	} else {
		for i, tmp := range f.cow {
			path := fmt.Sprintf("%s.%d", f.path, i+1)
			err = nonil(err, os.Rename(tmp, path))
		}
	}
	return err
}
<|endoftext|>


<commit_before>package styx

import "aqwari.net/net/styx/styxproto"

type file interface{}

// An fcall is a 9P message that contains a fid, and represents
// an operation on a file.
type fcall interface {
	styxproto.Msg
	Fid() uint32
}
<commit_msg>remove unused types<commit_after><|endoftext|>


<commit_before>package glfw3

//#include "glfw/include/GLFW/glfw3.h"
//void glfwSetErrorCallbackCB();
import "C"

import (
	"fmt"
)

// ErrorCode corresponds to an error code.
type ErrorCode int

// Error codes that are translated to panics and the programmer should not
// expect to handle.
const (
	notInitialized   ErrorCode = C.GLFW_NOT_INITIALIZED    // GLFW has not been initialized.
	noCurrentContext ErrorCode = C.GLFW_NO_CURRENT_CONTEXT // No context is current.
	invalidEnum      ErrorCode = C.GLFW_INVALID_ENUM       // One of the enum parameters for the function was given an invalid enum.
	invalidValue     ErrorCode = C.GLFW_INVALID_VALUE      // One of the parameters for the function was given an invalid value.
	outOfMemory      ErrorCode = C.GLFW_OUT_OF_MEMORY      // A memory allocation failed.
)

// Error codes.
const (
	APIUnavailable     ErrorCode = C.GLFW_API_UNAVAILABLE     // GLFW could not find support for the requested client API on the system.
	VersionUnavailable ErrorCode = C.GLFW_VERSION_UNAVAILABLE // The requested client API version is not available.
	PlatformError      ErrorCode = C.GLFW_PLATFORM_ERROR      // A platform-specific error occurred that does not match any of the more specific categories.
	FormatUnavailable  ErrorCode = C.GLFW_FORMAT_UNAVAILABLE  // The clipboard did not contain data in the requested format.
)

// GLFWError holds error code and description.
type GLFWError struct {
	Code ErrorCode
	Desc string
}

// Note: There are many cryptic caveats to proper error handling here.
// See: https://github.com/go-gl/glfw3/pull/86

// Holds the value of the last error.
var lastError = make(chan *GLFWError, 1)

//export goErrorCB
func goErrorCB(code C.int, desc *C.char) {
	flushErrors()
	err := &GLFWError{ErrorCode(code), C.GoString(desc)}
	select {
	case lastError <- err:
	default:
		fmt.Println("GLFW: An uncaught error has occurred:", err)
		fmt.Println("GLFW: Please report this bug in the Go package immediately.")
	}
}

// Error prints the error code and description in a readable format.
func (e *GLFWError) Error() string {
	return fmt.Sprintf("Error %d: %s", e.Code, e.Desc)
}

// Set the glfw callback internally
func init() {
	C.glfwSetErrorCallbackCB()
}

// flushErrors is called by Terminate before it actually calls C.glfwTerminate,
// this ensures that any uncaught errors buffered in lastError are printed
// before the program exits.
func flushErrors() {
	err := fetchError()
	if err != nil {
		fmt.Println("GLFW: An uncaught error has occurred:", err)
		fmt.Println("GLFW: Please report this bug in the Go package immediately.")
	}
}

// fetchError is called by various functions to retrieve the error that might
// have occurred from a generic GLFW operation. It returns nil if no error is
// present.
func fetchError() error {
	select {
	case err := <-lastError:
		switch err.Code {
		case outOfMemory, invalidEnum, invalidValue, notInitialized, noCurrentContext:
			panic(err)
		default:
			return err
		}
	default:
		return nil
	}
}
<commit_msg>Rename GLFWError to just Error.<commit_after>package glfw3

//#include "glfw/include/GLFW/glfw3.h"
//void glfwSetErrorCallbackCB();
import "C"

import (
	"fmt"
)

// ErrorCode corresponds to an error code.
type ErrorCode int

// Error codes that are translated to panics and the programmer should not
// expect to handle.
const (
	notInitialized   ErrorCode = C.GLFW_NOT_INITIALIZED    // GLFW has not been initialized.
	noCurrentContext ErrorCode = C.GLFW_NO_CURRENT_CONTEXT // No context is current.
	invalidEnum      ErrorCode = C.GLFW_INVALID_ENUM       // One of the enum parameters for the function was given an invalid enum.
	invalidValue     ErrorCode = C.GLFW_INVALID_VALUE      // One of the parameters for the function was given an invalid value.
	outOfMemory      ErrorCode = C.GLFW_OUT_OF_MEMORY      // A memory allocation failed.
)

// Error codes.
const (
	APIUnavailable     ErrorCode = C.GLFW_API_UNAVAILABLE     // GLFW could not find support for the requested client API on the system.
	VersionUnavailable ErrorCode = C.GLFW_VERSION_UNAVAILABLE // The requested client API version is not available.
	PlatformError      ErrorCode = C.GLFW_PLATFORM_ERROR      // A platform-specific error occurred that does not match any of the more specific categories.
	FormatUnavailable  ErrorCode = C.GLFW_FORMAT_UNAVAILABLE  // The clipboard did not contain data in the requested format.
)

// Error holds error code and description.
type Error struct {
	Code ErrorCode
	Desc string
}

// Note: There are many cryptic caveats to proper error handling here.
// See: https://github.com/go-gl/glfw3/pull/86

// Holds the value of the last error.
var lastError = make(chan *Error, 1)

//export goErrorCB
func goErrorCB(code C.int, desc *C.char) {
	flushErrors()
	err := &Error{ErrorCode(code), C.GoString(desc)}
	select {
	case lastError <- err:
	default:
		fmt.Println("GLFW: An uncaught error has occurred:", err)
		fmt.Println("GLFW: Please report this bug in the Go package immediately.")
	}
}

// Error prints the error code and description in a readable format.
func (e *Error) Error() string {
	return fmt.Sprintf("Error %d: %s", e.Code, e.Desc)
}

// Set the glfw callback internally
func init() {
	C.glfwSetErrorCallbackCB()
}

// flushErrors is called by Terminate before it actually calls C.glfwTerminate,
// this ensures that any uncaught errors buffered in lastError are printed
// before the program exits.
func flushErrors() {
	err := fetchError()
	if err != nil {
		fmt.Println("GLFW: An uncaught error has occurred:", err)
		fmt.Println("GLFW: Please report this bug in the Go package immediately.")
	}
}

// fetchError is called by various functions to retrieve the error that might
// have occurred from a generic GLFW operation. It returns nil if no error is
// present.
func fetchError() error {
	select {
	case err := <-lastError:
		switch err.Code {
		case outOfMemory, invalidEnum, invalidValue, notInitialized, noCurrentContext:
			panic(err)
		default:
			return err
		}
	default:
		return nil
	}
}
<|endoftext|>


<commit_before>package glfw3

//#include "glfw/include/GLFW/glfw3.h"
//void glfwSetErrorCallbackCB();
import "C"

import (
	"fmt"
)

// ErrorCode corresponds to an error code.
type ErrorCode int

// Error codes.
const (
	NotInitialized     ErrorCode = C.GLFW_NOT_INITIALIZED     // GLFW has not been initialized.
	NoCurrentContext   ErrorCode = C.GLFW_NO_CURRENT_CONTEXT  // No context is current.
	InvalidEnum        ErrorCode = C.GLFW_INVALID_ENUM        // One of the enum parameters for the function was given an invalid enum.
	InvalidValue       ErrorCode = C.GLFW_INVALID_VALUE       // One of the parameters for the function was given an invalid value.
	OutOfMemory        ErrorCode = C.GLFW_OUT_OF_MEMORY       // A memory allocation failed.
	APIUnavailable     ErrorCode = C.GLFW_API_UNAVAILABLE     // GLFW could not find support for the requested client API on the system.
	VersionUnavailable ErrorCode = C.GLFW_VERSION_UNAVAILABLE // The requested client API version is not available.
	PlatformError      ErrorCode = C.GLFW_PLATFORM_ERROR      // A platform-specific error occurred that does not match any of the more specific categories.
	FormatUnavailable  ErrorCode = C.GLFW_FORMAT_UNAVAILABLE  // The clipboard did not contain data in the requested format.
)

// GlfwError holds error code and description.
type GlfwError struct {
	Code ErrorCode
	Desc string
}

// Holds the value of the last error
var lastError = make(chan *GlfwError, 1)

//export goErrorCB
func goErrorCB(code C.int, desc *C.char) {
	lastError <- &GlfwError{ErrorCode(code), C.GoString(desc)}
}

// Error prints the error code and description in a readable format.
func (e *GlfwError) Error() string {
	return fmt.Sprintf("Error %d: %s", e.Code, e.Desc)
}

// Set the glfw callback internally
func init() {
	C.glfwSetErrorCallbackCB()
}
<commit_msg>Update to #74<commit_after>package glfw3

//#include "glfw/include/GLFW/glfw3.h"
//void glfwSetErrorCallbackCB();
import "C"

import (
	"fmt"
)

// ErrorCode corresponds to an error code.
type ErrorCode int

// Error codes.
const (
	NotInitialized     ErrorCode = C.GLFW_NOT_INITIALIZED     // GLFW has not been initialized.
	NoCurrentContext   ErrorCode = C.GLFW_NO_CURRENT_CONTEXT  // No context is current.
	InvalidEnum        ErrorCode = C.GLFW_INVALID_ENUM        // One of the enum parameters for the function was given an invalid enum.
	InvalidValue       ErrorCode = C.GLFW_INVALID_VALUE       // One of the parameters for the function was given an invalid value.
	OutOfMemory        ErrorCode = C.GLFW_OUT_OF_MEMORY       // A memory allocation failed.
	APIUnavailable     ErrorCode = C.GLFW_API_UNAVAILABLE     // GLFW could not find support for the requested client API on the system.
	VersionUnavailable ErrorCode = C.GLFW_VERSION_UNAVAILABLE // The requested client API version is not available.
	PlatformError      ErrorCode = C.GLFW_PLATFORM_ERROR      // A platform-specific error occurred that does not match any of the more specific categories.
	FormatUnavailable  ErrorCode = C.GLFW_FORMAT_UNAVAILABLE  // The clipboard did not contain data in the requested format.
)

// GlfwError holds error code and description.
type GLFWError struct {
	Code ErrorCode
	Desc string
}

// Holds the value of the last error
var lastError = make(chan *GLFWError, 1)

//export goErrorCB
func goErrorCB(code C.int, desc *C.char) {
	lastError <- &GLFWError{ErrorCode(code), C.GoString(desc)}
}

// Error prints the error code and description in a readable format.
func (e *GLFWError) Error() string {
	return fmt.Sprintf("Error %d: %s", e.Code, e.Desc)
}

// Set the glfw callback internally
func init() {
	C.glfwSetErrorCallbackCB()
}
<|endoftext|>