<commit_before>package plexapi

import (
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"sync"

	"code.senomas.com/go/util"
	log "github.com/Sirupsen/logrus"
)

// Server struct
type Server struct {
	api               *API
	host              string
	XMLName           xml.Name `xml:"Server"`
	AccessToken       string   `xml:"accessToken,attr"`
	Name              string   `xml:"name,attr"`
	Address           string   `xml:"address,attr"`
	Port              int      `xml:"port,attr"`
	Version           string   `xml:"version,attr"`
	Scheme            string   `xml:"scheme,attr"`
	Host              string   `xml:"host,attr"`
	LocalAddresses    string   `xml:"localAddresses,attr"`
	MachineIdentifier string   `xml:"machineIdentifier,attr"`
	CreatedAt         int      `xml:"createdAt,attr"`
	UpdatedAt         int      `xml:"updatedAt,attr"`
	Owned             int      `xml:"owned,attr"`
	Synced            int      `xml:"synced,attr"`
}

func (server *Server) setHeader(req *http.Request) {
	req.Header.Add("X-Plex-Product", "plex-sync")
	req.Header.Add("X-Plex-Version", "1.0.0")
	req.Header.Add("X-Plex-Client-Identifier", "donkey")
	if server.api.userInfo.Token != "" {
		req.Header.Add("X-Plex-Token", server.api.userInfo.Token)
	}
}

func (server *Server) getContainer(url string) (container MediaContainer, err error) {
	log.WithField("url", url).Debugf("GET")
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.WithField("url", url).WithError(err).Errorf("http.GET")
		return container, err
	}
	server.setHeader(req)

	resp, err := server.api.client.Do(req)
	if err != nil {
		return container, err
	}
	defer resp.Body.Close()

	log.WithField("url", url).WithField("status", resp.Status).Debugf("RESP")

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.WithField("url", url).WithError(err).Errorf("RESP")
		return container, err
	}

	err = xml.Unmarshal(body, &container)
	return container, err
}

// Check func
func (server *Server) Check() {
	var urls []string
	urls = append(urls, fmt.Sprintf("https://%s:32400", server.Address))
	urls = append(urls, fmt.Sprintf("https://%s:32400", server.Host))
	for _, la := range strings.Split(server.LocalAddresses, ",") {
		urls = append(urls, fmt.Sprintf("https://%s:32400", la))
	}
	log.Debugf("CHECK %s", util.JSONPrettyPrint(urls))

	var checkOnce util.CheckOnce
	var wg sync.WaitGroup

	out := make(chan string)
	defer close(out)

	for _, pu := range urls {
		url := pu
		wg.Add(1)
		go func() error {
			defer wg.Done()

			if !checkOnce.IsDone() {
				log.Debugf("CHECK %s %s", server.Name, url)
				req, err := http.NewRequest("GET", url, nil)
				if err != nil {
					log.Debugf("ERROR GET %s '%s': %v", server.Name, url, err)
					return err
				}
				server.setHeader(req)
				resp, err := server.api.client.Do(req)
				if err != nil {
					log.Debugf("ERROR GET %s '%s': %v", server.Name, url, err)
					return err
				}
				defer resp.Body.Close()

				log.Debugf("RESP %s '%s' %s", server.Name, url, resp.Status)

				if resp.StatusCode == 200 {
					checkOnce.Done(func() {
						out <- url
					})
				}
			}
			return nil
		}()
	}

	go func() {
		wg.Wait()
		checkOnce.Done(func() {
			out <- ""
		})
	}()

	server.host = <-out
	log.Debugf("URL %s  %s", server.Name, server.host)
}

// Perform func
func (server *Server) Perform(cmd, path string) error {
	if server.host != "" {
		url := server.host + path
		log.WithField("url", url).Debugf(cmd)
		req, err := http.NewRequest(cmd, url, nil)
		if err != nil {
			log.WithField("url", url).WithError(err).Errorf("http.%s", cmd)
			return err
		}
		server.setHeader(req)

		resp, err := server.api.client.Do(req)
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		log.WithField("url", url).WithField("status", resp.Status).Debugf("RESP")

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.WithField("url", url).WithError(err).Errorf("RESP")
			return err
		}

		log.Debugf("RESP BODY\n%s", body)
		return err
	}
	return fmt.Errorf("NO HOST")
}

// GetContainer func
func (server *Server) GetContainer(path string) (container MediaContainer, err error) {
	if server.host != "" {
		container, err = server.getContainer(server.host + path)
		return container, err
	}
	return container, fmt.Errorf("NO HOST")
}

// GetDirectories func
func (server *Server) GetDirectories() (directories []Directory, err error) {
	container, err := server.GetContainer("/library/sections")
	if err != nil {
		return directories, err
	}
	for _, d := range container.Directories {
		nd := d
		// nd.server = server
		directories = append(directories, nd)
	}
	return directories, err
}

// GetVideos func
func (server *Server) GetVideos(wg *sync.WaitGroup, out chan<- interface{}) {
	cs := make(chan MediaContainer)
	dirs := make(chan Directory)

	wg.Add(1)
	go func() {
		container, err := server.GetContainer("/library/sections")
		if err == nil {
			cs <- container
		} else {
			wg.Done()
		}
	}()
	for i, il := 0, server.api.HTTP.WorkerSize; i < il; i++ {
		go func() {
			for c := range cs {
				func() {
					defer wg.Done()
					for _, d := range c.Directories {
						if d.Type == "movie" {
							wg.Add(1)
							d.Paths = c.Paths
							d.Keys = c.Keys
							d.Keys = append(d.Keys, KeyInfo{Key: d.Key, Type: d.Type, Title: d.Title})
							dirs <- d
						}
					}
					for _, v := range c.Videos {
						// if v.GUID == "" {
						// 	meta, err := server.GetMeta(v)
						// 	if err != nil {
						// 		log.Fatal("GetMeta ", err)
						// 	}
						// 	v = meta
						// }
						v.server = server
						v.Paths = c.Paths
						v.Keys = c.Keys
						var idx []string
						for _, px := range v.Media.Parts {
							for _, kk := range v.Paths {
								if strings.HasPrefix(px.File, kk) {
									idx = append(idx, px.File[len(kk):])
								}
							}
						}
						if len(idx) > 0 {
							v.FID = strings.Join(idx, ":")
							v.FID = strings.Replace(v.FID, "\\", "/", -1)
						} else {
							v.FID = v.GUID
						}
						wg.Add(1)
						out <- v
					}
				}()
			}
		}()
		go func() {
			for d := range dirs {
				func() {
					defer wg.Done()

					if strings.HasPrefix(d.Key, "/library/") {
						cc, err := server.GetContainer(d.Key)
						if err == nil {
							wg.Add(1)
							cc.Keys = d.Keys
							cc.Paths = d.Paths
							for _, l := range d.Locations {
								if l.Path != "" {
									cc.Paths = append(cc.Paths, l.Path)
								}
							}
							cs <- cc
						}
					} else {
						cc, err := server.GetContainer(fmt.Sprintf("/library/sections/%v/all", d.Key))
						if err == nil {
							wg.Add(1)
							cc.Keys = d.Keys
							cc.Paths = d.Paths
							for _, l := range d.Locations {
								if l.Path != "" {
									cc.Paths = append(cc.Paths, l.Path)
								}
							}
							cs <- cc
						}
					}
				}()
			}
		}()
	}
}

// GetMeta func
func (server *Server) GetMeta(video Video) (meta Video, err error) {
	if video.GUID != "" {
		return video, nil
	}
	var mc MediaContainer
	mc, err = server.GetContainer(video.Key)
	if err != nil {
		return video, err
	}
	return mc.Videos[0], nil
}

// MarkWatched func
func (server *Server) MarkWatched(key string) error {
	url := server.host + "/:/scrobble?identifier=com.plexapp.plugins.library&key=" + key
	log.WithField("url", url).WithField("server", server.Name).Debug("MarkWatched.GET")
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	server.setHeader(req)

	resp, err := server.api.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	log.WithField("url", url).WithField("server", server.Name).Debugf("MarkWatched.RESULT\n%s", body)

	return err
}

// MarkUnwatched func
func (server *Server) MarkUnwatched(key string) error {
	url := server.host + "/:/unscrobble?identifier=com.plexapp.plugins.library&key=" + key
	log.WithField("url", url).WithField("server", server.Name).Debug("MarkUnwatched.GET")
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	server.setHeader(req)

	resp, err := server.api.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

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

	log.WithField("url", url).WithField("server", server.Name).Debugf("MarkUnwatched.RESULT\n%s", body)

	return err
}

// SetViewOffset func
func (server *Server) SetViewOffset(key, offset string) error {
	url := server.host + "/:/progress?key=" + key + "&identifier=com.plexapp.plugins.library&time=" + offset
	log.WithField("url", url).WithField("server", server.Name).Debug("SetViewOffset.GET")
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	server.setHeader(req)

	resp, err := server.api.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

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

	log.WithField("url", url).WithField("server", server.Name).Debugf("SetViewOffset.RESULT\n%s", body)

	return err
}
<commit_msg>test remote access<commit_after>package plexapi

import (
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"sync"

	"code.senomas.com/go/util"
	log "github.com/Sirupsen/logrus"
)

// Server struct
type Server struct {
	api               *API
	host              string
	XMLName           xml.Name `xml:"Server"`
	AccessToken       string   `xml:"accessToken,attr"`
	Name              string   `xml:"name,attr"`
	Address           string   `xml:"address,attr"`
	Port              int      `xml:"port,attr"`
	Version           string   `xml:"version,attr"`
	Scheme            string   `xml:"scheme,attr"`
	Host              string   `xml:"host,attr"`
	LocalAddresses    string   `xml:"localAddresses,attr"`
	MachineIdentifier string   `xml:"machineIdentifier,attr"`
	CreatedAt         int      `xml:"createdAt,attr"`
	UpdatedAt         int      `xml:"updatedAt,attr"`
	Owned             int      `xml:"owned,attr"`
	Synced            int      `xml:"synced,attr"`
}

func (server *Server) setHeader(req *http.Request) {
	req.Header.Add("X-Plex-Product", "plex-sync")
	req.Header.Add("X-Plex-Version", "1.0.0")
	req.Header.Add("X-Plex-Client-Identifier", "donkey")
	if server.api.userInfo.Token != "" {
		req.Header.Add("X-Plex-Token", server.api.userInfo.Token)
	}
}

func (server *Server) getContainer(url string) (container MediaContainer, err error) {
	log.WithField("url", url).Debugf("GET")
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.WithField("url", url).WithError(err).Errorf("http.GET")
		return container, err
	}
	server.setHeader(req)

	resp, err := server.api.client.Do(req)
	if err != nil {
		return container, err
	}
	defer resp.Body.Close()

	log.WithField("url", url).WithField("status", resp.Status).Debugf("RESP")

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.WithField("url", url).WithError(err).Errorf("RESP")
		return container, err
	}

	err = xml.Unmarshal(body, &container)
	return container, err
}

// Check func
func (server *Server) Check() {
	var urls []string
	urls = append(urls, fmt.Sprintf("https://%s:%v", server.Address, server.Port))
	for _, la := range strings.Split(server.LocalAddresses, ",") {
		urls = append(urls, fmt.Sprintf("https://%s:32400", la))
	}

	var checkOnce util.CheckOnce
	var wg sync.WaitGroup

	out := make(chan string)
	defer close(out)

	for _, pu := range urls {
		url := pu
		wg.Add(1)
		go func() error {
			defer wg.Done()

			if !checkOnce.IsDone() {
				log.Debugf("CHECK %s %s", server.Name, url)
				c, err := server.getContainer(url)
				if err != nil {
					log.Debugf("ERROR GET %s '%s': %v", server.Name, url, err)
					return err
				}

				if c.FriendlyName != server.Name {
					log.Fatal("WRONG SERVER ", c.FriendlyName, "  ", server.Name)
				}
				// log.Debugf("RESP BODY\n%s", util.JSONPrettyPrint(c))
				checkOnce.Done(func() {
					out <- url
				})
			}
			return nil
		}()
	}

	go func() {
		wg.Wait()
		checkOnce.Done(func() {
			out <- ""
		})
	}()

	server.host = <-out
	log.Debugf("URL %s  %s", server.Name, server.host)
}

// Perform func
func (server *Server) Perform(cmd, path string) error {
	if server.host != "" {
		url := server.host + path
		log.WithField("url", url).Debugf(cmd)
		req, err := http.NewRequest(cmd, url, nil)
		if err != nil {
			log.WithField("url", url).WithError(err).Errorf("http.%s", cmd)
			return err
		}
		server.setHeader(req)

		resp, err := server.api.client.Do(req)
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		log.WithField("url", url).WithField("status", resp.Status).Debugf("RESP")

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.WithField("url", url).WithError(err).Errorf("RESP")
			return err
		}

		log.Debugf("RESP BODY\n%s", body)
		return err
	}
	return fmt.Errorf("NO HOST")
}

// GetContainer func
func (server *Server) GetContainer(path string) (container MediaContainer, err error) {
	if server.host != "" {
		container, err = server.getContainer(server.host + path)
		return container, err
	}
	return container, fmt.Errorf("NO HOST")
}

// GetDirectories func
func (server *Server) GetDirectories() (directories []Directory, err error) {
	container, err := server.GetContainer("/library/sections")
	if err != nil {
		return directories, err
	}
	for _, d := range container.Directories {
		nd := d
		// nd.server = server
		directories = append(directories, nd)
	}
	return directories, err
}

// GetVideos func
func (server *Server) GetVideos(wg *sync.WaitGroup, out chan<- interface{}) {
	cs := make(chan MediaContainer)
	dirs := make(chan Directory)

	wg.Add(1)
	go func() {
		container, err := server.GetContainer("/library/sections")
		if err == nil {
			cs <- container
		} else {
			wg.Done()
		}
	}()
	for i, il := 0, server.api.HTTP.WorkerSize; i < il; i++ {
		go func() {
			for c := range cs {
				func() {
					defer wg.Done()
					for _, d := range c.Directories {
						if d.Type == "movie" {
							wg.Add(1)
							d.Paths = c.Paths
							d.Keys = c.Keys
							d.Keys = append(d.Keys, KeyInfo{Key: d.Key, Type: d.Type, Title: d.Title})
							dirs <- d
						}
					}
					for _, v := range c.Videos {
						// if v.GUID == "" {
						// 	meta, err := server.GetMeta(v)
						// 	if err != nil {
						// 		log.Fatal("GetMeta ", err)
						// 	}
						// 	v = meta
						// }
						v.server = server
						v.Paths = c.Paths
						v.Keys = c.Keys
						var idx []string
						for _, px := range v.Media.Parts {
							for _, kk := range v.Paths {
								if strings.HasPrefix(px.File, kk) {
									idx = append(idx, px.File[len(kk):])
								}
							}
						}
						if len(idx) > 0 {
							v.FID = strings.Join(idx, ":")
							v.FID = strings.Replace(v.FID, "\\", "/", -1)
						} else {
							v.FID = v.GUID
						}
						wg.Add(1)
						out <- v
					}
				}()
			}
		}()
		go func() {
			for d := range dirs {
				func() {
					defer wg.Done()

					if strings.HasPrefix(d.Key, "/library/") {
						cc, err := server.GetContainer(d.Key)
						if err == nil {
							wg.Add(1)
							cc.Keys = d.Keys
							cc.Paths = d.Paths
							for _, l := range d.Locations {
								if l.Path != "" {
									cc.Paths = append(cc.Paths, l.Path)
								}
							}
							cs <- cc
						}
					} else {
						cc, err := server.GetContainer(fmt.Sprintf("/library/sections/%v/all", d.Key))
						if err == nil {
							wg.Add(1)
							cc.Keys = d.Keys
							cc.Paths = d.Paths
							for _, l := range d.Locations {
								if l.Path != "" {
									cc.Paths = append(cc.Paths, l.Path)
								}
							}
							cs <- cc
						}
					}
				}()
			}
		}()
	}
}

// GetMeta func
func (server *Server) GetMeta(video Video) (meta Video, err error) {
	if video.GUID != "" {
		return video, nil
	}
	var mc MediaContainer
	mc, err = server.GetContainer(video.Key)
	if err != nil {
		return video, err
	}
	return mc.Videos[0], nil
}

// MarkWatched func
func (server *Server) MarkWatched(key string) error {
	url := server.host + "/:/scrobble?identifier=com.plexapp.plugins.library&key=" + key
	log.WithField("url", url).WithField("server", server.Name).Debug("MarkWatched.GET")
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	server.setHeader(req)

	resp, err := server.api.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	log.WithField("url", url).WithField("server", server.Name).Debugf("MarkWatched.RESULT\n%s", body)

	return err
}

// MarkUnwatched func
func (server *Server) MarkUnwatched(key string) error {
	url := server.host + "/:/unscrobble?identifier=com.plexapp.plugins.library&key=" + key
	log.WithField("url", url).WithField("server", server.Name).Debug("MarkUnwatched.GET")
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	server.setHeader(req)

	resp, err := server.api.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

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

	log.WithField("url", url).WithField("server", server.Name).Debugf("MarkUnwatched.RESULT\n%s", body)

	return err
}

// SetViewOffset func
func (server *Server) SetViewOffset(key, offset string) error {
	url := server.host + "/:/progress?key=" + key + "&identifier=com.plexapp.plugins.library&time=" + offset
	log.WithField("url", url).WithField("server", server.Name).Debug("SetViewOffset.GET")
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	server.setHeader(req)

	resp, err := server.api.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

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

	log.WithField("url", url).WithField("server", server.Name).Debugf("SetViewOffset.RESULT\n%s", body)

	return err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/ungerik/go-rest"
	"net/url"
	"os/exec"
)

func main() {
	// See RunServer below
	stopServerChan := make(chan struct{})

	fmt.Printf("Starting REST server\n")

	rest.HandleGET("/get", func(in url.Values)  string {
		args := []string{"get"}
		fmt.Println(in)
		if in.Get("relay") != "" {
                        args = append(args,"--relay")
			args = append(args,(fmt.Sprintf("%s", in.Get("relay"))))
		}
		fmt.Println(args)
		cmd := exec.Command("./relay_control.py", args...)
		out, err := cmd.Output()

		if err != nil {
			println(err.Error())
			return ""
		}

		return fmt.Sprintf(string(out))
	})

	rest.HandlePOST("/set", func(in url.Values)  string {
		if in.Get("state") == "" {
			println("State param is mandatory.")
			return ""
		}
		fmt.Println(in)
		args := []string{"set","--state", fmt.Sprintf("%s", in.Get("state"))}
		if in.Get("relay") != "" {
			args = append(args,"--relay")
			args = append(args, fmt.Sprintf("%s", in.Get("relay")))
		}
		fmt.Println(args)
		cmd := exec.Command("./relay_control.py", args...)
		out, err := cmd.Output()

		if err != nil {
			println(err.Error())
			return ""
		}

		return fmt.Sprintf(string(out))
	})

	rest.HandleGET("/close", func() string {
		stopServerChan <- struct{}{}
		return "Stopping REST server..."
	})

	rest.RunServer("0.0.0.0:8080", stopServerChan)
}
<commit_msg>update python script path<commit_after>package main

import (
	"fmt"
	"github.com/ungerik/go-rest"
	"net/url"
	"os/exec"
)

func main() {
	// See RunServer below
	stopServerChan := make(chan struct{})

	fmt.Printf("Starting REST server\n")

	rest.HandleGET("/get", func(in url.Values)  string {
		args := []string{"get"}
		fmt.Println(in)
		if in.Get("relay") != "" {
                        args = append(args,"--relay")
			args = append(args,(fmt.Sprintf("%s", in.Get("relay"))))
		}
		fmt.Println(args)
		cmd := exec.Command("/opt/relay_control/relay_control.py", args...)
		out, err := cmd.Output()

		if err != nil {
			println(err.Error())
			return ""
		}

		return fmt.Sprintf(string(out))
	})

	rest.HandlePOST("/set", func(in url.Values)  string {
		if in.Get("state") == "" {
			println("State param is mandatory.")
			return ""
		}
		fmt.Println(in)
		args := []string{"set","--state", fmt.Sprintf("%s", in.Get("state"))}
		if in.Get("relay") != "" {
			args = append(args,"--relay")
			args = append(args, fmt.Sprintf("%s", in.Get("relay")))
		}
		fmt.Println(args)
		cmd := exec.Command("/opt/relay_control/relay_control.py", args...)
		out, err := cmd.Output()

		if err != nil {
			println(err.Error())
			return ""
		}

		return fmt.Sprintf(string(out))
	})

	rest.HandleGET("/close", func() string {
		stopServerChan <- struct{}{}
		return "Stopping REST server..."
	})

	rest.RunServer("0.0.0.0:8080", stopServerChan)
}
<|endoftext|>


<commit_before>package check_test

import (
	"io/ioutil"
	"os"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/concourse/s3-resource"
	"github.com/concourse/s3-resource/fakes"

	. "github.com/concourse/s3-resource/check"
)

var _ = Describe("Check Command", func() {
	Describe("running the command", func() {
		var (
			tmpPath string
			request CheckRequest

			s3client *fakes.FakeS3Client
			command  *CheckCommand
		)

		BeforeEach(func() {
			var err error
			tmpPath, err = ioutil.TempDir("", "check_command")
			Ω(err).ShouldNot(HaveOccurred())

			request = CheckRequest{
				Source: s3resource.Source{
					Bucket: "bucket-name",
				},
			}

			s3client = &fakes.FakeS3Client{}
			command = NewCheckCommand(s3client)

			s3client.BucketFilesReturns([]string{
				"files/abc-0.0.1.tgz",
				"files/abc-2.33.333.tgz",
				"files/abc-2.4.3.tgz",
				"files/abc-3.53.tgz",
			}, nil)
		})

		AfterEach(func() {
			err := os.RemoveAll(tmpPath)
			Ω(err).ShouldNot(HaveOccurred())
		})

		Context("when there is no previous version", func() {
			It("includes the latest version only", func() {
				request.Version.Path = ""
				request.Source.Regexp = "files/abc-(.*).tgz"

				response, err := command.Run(request)
				Ω(err).ShouldNot(HaveOccurred())

				Ω(response).Should(HaveLen(1))
				Ω(response).Should(ConsistOf(
					s3resource.Version{
						Path: "files/abc-3.53.tgz",
					},
				))
			})

			Context("when the regexp does not match anything", func() {
				It("does not explode", func() {
					request.Source.Regexp = "no-files/missing-(.*).tgz"
					response, err := command.Run(request)
					Ω(err).ShouldNot(HaveOccurred())

					Ω(response).Should(HaveLen(0))
				})
			})

			Context("when the regex does not match the previous version", func() {
				It("returns the latest version that matches the regex", func() {
					request.Source.Regexp = `files/abc-(2\.33.*).tgz`
					response, err := command.Run(request)
					Ω(err).ShouldNot(HaveOccurred())

					Ω(response).Should(HaveLen(1))
					Expect(response).To(ConsistOf(s3resource.Version{Path: "files/abc-2.33.333.tgz"}))
				})
			})
		})

		Context("when there is a previous version", func() {
			Context("when using regex that matches the provided version", func() {
				It("includes all versions from the previous one and the current one", func() {
					request.Version.Path = "files/abc-2.4.3.tgz"
					request.Source.Regexp = "files/abc-(.*).tgz"

					response, err := command.Run(request)
					Ω(err).ShouldNot(HaveOccurred())

					Ω(response).Should(HaveLen(3))
					Ω(response).Should(ConsistOf(
						s3resource.Version{
							Path: "files/abc-2.4.3.tgz",
						},
						s3resource.Version{
							Path: "files/abc-2.33.333.tgz",
						},
						s3resource.Version{
							Path: "files/abc-3.53.tgz",
						},
					))
				})
			})

			Context("when using versioned file", func() {
				BeforeEach(func() {
					s3client.BucketFileVersionsReturns([]string{
						"file-version-3",
						"file-version-2",
						"file-version-1",
					}, nil)
				})

				It("includes all versions from the previous one and the current one", func() {
					request.Version.VersionID = "file-version-2"
					request.Source.VersionedFile = "files/(.*).tgz"

					response, err := command.Run(request)
					Ω(err).ShouldNot(HaveOccurred())

					Ω(response).Should(HaveLen(2))
					Ω(response).Should(ConsistOf(
						s3resource.Version{
							VersionID: "file-version-2",
						},
						s3resource.Version{
							VersionID: "file-version-3",
						},
					))
				})
			})
		})
	})
})
<commit_msg>re-add very important line of test code<commit_after>package check_test

import (
	"io/ioutil"
	"os"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/concourse/s3-resource"
	"github.com/concourse/s3-resource/fakes"

	. "github.com/concourse/s3-resource/check"
)

var _ = Describe("Check Command", func() {
	Describe("running the command", func() {
		var (
			tmpPath string
			request CheckRequest

			s3client *fakes.FakeS3Client
			command  *CheckCommand
		)

		BeforeEach(func() {
			var err error
			tmpPath, err = ioutil.TempDir("", "check_command")
			Ω(err).ShouldNot(HaveOccurred())

			request = CheckRequest{
				Source: s3resource.Source{
					Bucket: "bucket-name",
				},
			}

			s3client = &fakes.FakeS3Client{}
			command = NewCheckCommand(s3client)

			s3client.BucketFilesReturns([]string{
				"files/abc-0.0.1.tgz",
				"files/abc-2.33.333.tgz",
				"files/abc-2.4.3.tgz",
				"files/abc-3.53.tgz",
			}, nil)
		})

		AfterEach(func() {
			err := os.RemoveAll(tmpPath)
			Ω(err).ShouldNot(HaveOccurred())
		})

		Context("when there is no previous version", func() {
			It("includes the latest version only", func() {
				request.Version.Path = ""
				request.Source.Regexp = "files/abc-(.*).tgz"

				response, err := command.Run(request)
				Ω(err).ShouldNot(HaveOccurred())

				Ω(response).Should(HaveLen(1))
				Ω(response).Should(ConsistOf(
					s3resource.Version{
						Path: "files/abc-3.53.tgz",
					},
				))
			})

			Context("when the regexp does not match anything", func() {
				It("does not explode", func() {
					request.Source.Regexp = "no-files/missing-(.*).tgz"
					response, err := command.Run(request)
					Ω(err).ShouldNot(HaveOccurred())

					Ω(response).Should(HaveLen(0))
				})
			})

			Context("when the regex does not match the previous version", func() {
				It("returns the latest version that matches the regex", func() {
					request.Version.Path = "files/abc-0.0.1.tgz"
					request.Source.Regexp = `files/abc-(2\.33.*).tgz`
					response, err := command.Run(request)
					Ω(err).ShouldNot(HaveOccurred())

					Ω(response).Should(HaveLen(1))
					Expect(response).To(ConsistOf(s3resource.Version{Path: "files/abc-2.33.333.tgz"}))
				})
			})
		})

		Context("when there is a previous version", func() {
			Context("when using regex that matches the provided version", func() {
				It("includes all versions from the previous one and the current one", func() {
					request.Version.Path = "files/abc-2.4.3.tgz"
					request.Source.Regexp = "files/abc-(.*).tgz"

					response, err := command.Run(request)
					Ω(err).ShouldNot(HaveOccurred())

					Ω(response).Should(HaveLen(3))
					Ω(response).Should(ConsistOf(
						s3resource.Version{
							Path: "files/abc-2.4.3.tgz",
						},
						s3resource.Version{
							Path: "files/abc-2.33.333.tgz",
						},
						s3resource.Version{
							Path: "files/abc-3.53.tgz",
						},
					))
				})
			})

			Context("when using versioned file", func() {
				BeforeEach(func() {
					s3client.BucketFileVersionsReturns([]string{
						"file-version-3",
						"file-version-2",
						"file-version-1",
					}, nil)
				})

				It("includes all versions from the previous one and the current one", func() {
					request.Version.VersionID = "file-version-2"
					request.Source.VersionedFile = "files/(.*).tgz"

					response, err := command.Run(request)
					Ω(err).ShouldNot(HaveOccurred())

					Ω(response).Should(HaveLen(2))
					Ω(response).Should(ConsistOf(
						s3resource.Version{
							VersionID: "file-version-2",
						},
						s3resource.Version{
							VersionID: "file-version-3",
						},
					))
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>package text

import (
	"unicode"
)

type Query interface {
	Match(string) int
}

// Word matches complete words only.
// The "complete words" of a string s is defined as the result of
// splitting the string on every single Unicode whitespace character.
type Word struct {
	W string
}

func (q Word) Match(s string) int {
	if s == q.W {
		return 0
	}

	sr := []rune(s)
	qr := []rune(q.W)

	if len(sr) < len(qr) {
		return -1
	}

	var nMatch int

	for i := 0; i < len(sr); i++ {
		if nMatch < len(qr) && sr[i] != qr[nMatch] {
			nMatch = 0
			continue
		}

		nMatch++

		if nMatch == len(qr) {
			// Check that any immediately preceding or following
			// characters are spaces.

			next := i + 1
			prev := i - nMatch
			hasNext := len(sr) > next
			hasPrev := i-nMatch >= 0

			if hasNext && !(unicode.IsSpace(sr[next]) || sr[next] == ',') ||
				hasPrev && !(unicode.IsSpace(sr[prev]) || sr[prev] == '\n') {
				nMatch = 0
				continue
			}

			return i - nMatch + 1
		}
	}

	return -1
}
<commit_msg>Shorten text.Word.Match logic<commit_after>package text

import (
	"strings"
	"unicode"
	"unicode/utf8"
)

type Query interface {
	Match(string) int
}

// Word matches complete words only.
// The "complete words" of a string s is defined as the result of
// splitting the string on every single Unicode whitespace character.
type Word struct {
	W string
}

func (q Word) Match(s string) int {
	if s == q.W || len(s) == 0 {
		return 0
	}

	sp := strings.FieldsFunc(s, func(r rune) bool {
		if unicode.IsSpace(r) {
			return true
		}

		switch r {
		case ',', '\n':
			return true
		default:
			return false
		}
	})

	var rCount int
	for i, w := range sp {
		if w == q.W {
			return rCount + i
		}
		rCount += utf8.RuneCountInString(w)
	}

	return -1
}
<|endoftext|>


<commit_before>package service

import (
	"bufio"
	"os"
	"os/exec"
	"strings"
)

const go_spfc_test_plist string = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> " +
	"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"> " +
	"<plist version=\"1.0\"> " +
	"<dict> " +
	"  <key>Label</key> " +
	"  <string>go-spfc-test</string> " +
	"  <key>ProgramArguments</key> " +
	"  <array> " +
	"    <string>bash</string> " +
	"    <string>-c</string> " +
	"    <string>sleep 60</string> " +
	"  </array> " +
	"  <key>UserName</key> " +
	"  <string>'$USER'</string> " +
	"</dict> " +
	"</plist> "

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func createService() {
	s := []string{os.Getenv("HOME"), "/Library/LaunchAgents/", servNameTest, ".plist"}
	servNameTestPList := strings.Join(s, "")
	f, err := os.Create(servNameTestPList)
	w := bufio.NewWriter(f)
	_, err = w.WriteString(go_spfc_test_plist)
	w.Flush()
	check(err)
	execLaunchctl("load", servNameTestPList)
}

func removeService() {
	execLaunchctl("remove", servNameTest)
}

func execLaunchctl(arg ...string) {
	cmd := exec.Command("launchctl", arg...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		panic(string(out))
	}
}
<commit_msg> go lint free<commit_after>package service

import (
	"bufio"
	"os"
	"os/exec"
	"strings"
)

const goSpfcTestPlist string = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> " +
	"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"> " +
	"<plist version=\"1.0\"> " +
	"<dict> " +
	"  <key>Label</key> " +
	"  <string>go-spfc-test</string> " +
	"  <key>ProgramArguments</key> " +
	"  <array> " +
	"    <string>bash</string> " +
	"    <string>-c</string> " +
	"    <string>sleep 60</string> " +
	"  </array> " +
	"  <key>UserName</key> " +
	"  <string>'$USER'</string> " +
	"</dict> " +
	"</plist> "

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func createService() {
	s := []string{os.Getenv("HOME"), "/Library/LaunchAgents/", servNameTest, ".plist"}
	servNameTestPList := strings.Join(s, "")
	f, err := os.Create(servNameTestPList)
	w := bufio.NewWriter(f)
	_, err = w.WriteString(go_spfc_test_plist)
	w.Flush()
	check(err)
	execLaunchctl("load", servNameTestPList)
}

func removeService() {
	execLaunchctl("remove", servNameTest)
}

func execLaunchctl(arg ...string) {
	cmd := exec.Command("launchctl", arg...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		panic(string(out))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"os"
	"path/filepath"
	"time"

	humanize "github.com/dustin/go-humanize"
	"github.com/itchio/butler/comm"
	"github.com/itchio/wharf/counter"
	"github.com/itchio/wharf/eos"
)

func cp(src string, dest string, resume bool) {
	must(doCp(src, dest, resume))
}

func doCp(srcPath string, destPath string, resume bool) error {
	src, err := eos.Open(srcPath)
	if err != nil {
		return err
	}

	defer src.Close()

	dir := filepath.Dir(destPath)
	err = os.MkdirAll(dir, 0755)
	if err != nil {
		return err
	}

	flags := os.O_CREATE | os.O_WRONLY

	dest, err := os.OpenFile(destPath, flags, 0644)
	if err != nil {
		return err
	}

	defer dest.Close()

	stats, err := src.Stat()
	if err != nil {
		return err
	}

	totalBytes := int64(stats.Size())
	startOffset := int64(0)

	if resume {
		startOffset, err = dest.Seek(0, os.SEEK_END)
		if err != nil {
			return err
		}

		if startOffset == 0 {
			comm.Logf("Downloading %s", humanize.IBytes(uint64(totalBytes)))
		} else if startOffset > totalBytes {
			comm.Logf("Existing data too big (%s > %s), starting over", humanize.IBytes(uint64(startOffset)), humanize.IBytes(uint64(totalBytes)))
		} else if startOffset == totalBytes {
			comm.Logf("All %s already there", humanize.IBytes(uint64(totalBytes)))
			return nil
		} else {
			comm.Logf("Resuming at %s / %s", humanize.IBytes(uint64(startOffset)), humanize.IBytes(uint64(totalBytes)))

			_, err = src.Seek(startOffset, os.SEEK_SET)
			if err != nil {
				return err
			}
		}
	} else {
		comm.Logf("Downloading %s", humanize.IBytes(uint64(totalBytes)))
	}

	start := time.Now()

	comm.StartProgress()
	comm.Progress(float64(startOffset) / float64(totalBytes))

	cw := counter.NewWriterCallback(func(count int64) {
		alpha := float64(startOffset+count) / float64(totalBytes)
		comm.Progress(alpha)
	}, dest)

	copiedBytes, err := io.Copy(cw, src)
	if err != nil {
		return err
	}
	comm.EndProgress()

	totalDuration := time.Since(start)
	prettyStartOffset := humanize.IBytes(uint64(startOffset))
	prettySize := humanize.IBytes(uint64(copiedBytes))
	perSecond := humanize.IBytes(uint64(float64(totalBytes-startOffset) / totalDuration.Seconds()))
	comm.Statf("%s + %s copied @ %s/s\n", prettyStartOffset, prettySize, perSecond)

	return nil
}
<commit_msg>Only startprogress after initial progress is done<commit_after>package main

import (
	"io"
	"os"
	"path/filepath"
	"time"

	humanize "github.com/dustin/go-humanize"
	"github.com/itchio/butler/comm"
	"github.com/itchio/wharf/counter"
	"github.com/itchio/wharf/eos"
)

func cp(src string, dest string, resume bool) {
	must(doCp(src, dest, resume))
}

func doCp(srcPath string, destPath string, resume bool) error {
	src, err := eos.Open(srcPath)
	if err != nil {
		return err
	}

	defer src.Close()

	dir := filepath.Dir(destPath)
	err = os.MkdirAll(dir, 0755)
	if err != nil {
		return err
	}

	flags := os.O_CREATE | os.O_WRONLY

	dest, err := os.OpenFile(destPath, flags, 0644)
	if err != nil {
		return err
	}

	defer dest.Close()

	stats, err := src.Stat()
	if err != nil {
		return err
	}

	totalBytes := int64(stats.Size())
	startOffset := int64(0)

	if resume {
		startOffset, err = dest.Seek(0, os.SEEK_END)
		if err != nil {
			return err
		}

		if startOffset == 0 {
			comm.Logf("Downloading %s", humanize.IBytes(uint64(totalBytes)))
		} else if startOffset > totalBytes {
			comm.Logf("Existing data too big (%s > %s), starting over", humanize.IBytes(uint64(startOffset)), humanize.IBytes(uint64(totalBytes)))
		} else if startOffset == totalBytes {
			comm.Logf("All %s already there", humanize.IBytes(uint64(totalBytes)))
			return nil
		} else {
			comm.Logf("Resuming at %s / %s", humanize.IBytes(uint64(startOffset)), humanize.IBytes(uint64(totalBytes)))

			_, err = src.Seek(startOffset, os.SEEK_SET)
			if err != nil {
				return err
			}
		}
	} else {
		comm.Logf("Downloading %s", humanize.IBytes(uint64(totalBytes)))
	}

	start := time.Now()

	comm.Progress(float64(startOffset) / float64(totalBytes))
	comm.StartProgress()

	cw := counter.NewWriterCallback(func(count int64) {
		alpha := float64(startOffset+count) / float64(totalBytes)
		comm.Progress(alpha)
	}, dest)

	copiedBytes, err := io.Copy(cw, src)
	if err != nil {
		return err
	}
	comm.EndProgress()

	totalDuration := time.Since(start)
	prettyStartOffset := humanize.IBytes(uint64(startOffset))
	prettySize := humanize.IBytes(uint64(copiedBytes))
	perSecond := humanize.IBytes(uint64(float64(totalBytes-startOffset) / totalDuration.Seconds()))
	comm.Statf("%s + %s copied @ %s/s\n", prettyStartOffset, prettySize, perSecond)

	return nil
}
<|endoftext|>


<commit_before>package db

import (
	"sync"
	"sync/atomic"
	"unsafe"

	"github.com/azmodb/db/pb"
	"github.com/azmodb/llrb"
)

var valuePool = sync.Pool{New: func() interface{} { return &Value{&pb.Value{}} }}

// Value represents a read-only key/value database query result. The
// caller must close the value when done with it.
type Value struct {
	*pb.Value
}

func newValue(data interface{}, revs []int64) *Value {
	v := valuePool.Get().(*Value)
	switch t := data.(type) {
	case []byte:
		if cap(v.Value.Data) < len(t) {
			v.Value.Data = make([]byte, len(t))
		}
		v.Value.Data = v.Value.Data[:len(t)]
		copy(v.Value.Data, t)
	case int64:
		v.Value.Num = t
	default:
		panic("invalid value type")
	}

	v.Value.Revs = revs
	return v
}

// Close closes the value, rendering it unusable.
func (v *Value) Close() {
	if cap(v.Value.Data) > 8192 { // TODO
		v.Value.Data = v.Value.Data[:8192]
	}
	v.Value.Num = 0
	v.Value.Revs = nil
	valuePool.Put(v)
}

// IsNum returns true if this value is a numeric value.
func (v *Value) IsNum() bool { return v.Value.Data == nil }

// Bytes returns the underlying value byte slice. If Value is a numeric
// value Bytes returns nil.
func (v *Value) Bytes() []byte { return v.Value.Data }

// Num returns the underlying integer. If Value is a byte slice value
// Num returns 0.
func (v *Value) Num() int64 { return v.Value.Num }

// Revs() returns a revision numbers of this value.
func (v *Value) Revs() []int64 { return v.Value.Revs }

type DB struct {
	writer sync.Mutex // exclusive writer transaction
	tree   unsafe.Pointer
}

type tree struct {
	root *llrb.Tree
	rev  int64
}

// New returns a consistent in-memory key/value database.
func New() *DB { return newDB(nil) }

func newDB(t *tree) *DB {
	if t == nil {
		t = &tree{root: &llrb.Tree{}}
	}
	return &DB{tree: unsafe.Pointer(t)}
}

func (db *DB) store(t *tree) {
	atomic.StorePointer(&db.tree, unsafe.Pointer(t))
}

func (db *DB) load() *tree {
	return (*tree)(atomic.LoadPointer(&db.tree))
}

func (db *DB) Next() *Batch {
	db.writer.Lock()
	tree := db.load()
	return &Batch{txn: tree.root.Txn(), rev: tree.rev, db: db}
}

// Rev returns the current revision of the database.
func (db *DB) Rev() int64 {
	tree := db.load()
	return tree.rev
}

// len returns the number of keys in the database. only usefull for unit
// testing.
func (db *DB) len() int {
	tree := db.load()
	return tree.root.Len()
}
<commit_msg>document Value<commit_after>// Package db implements an immutable, consistent, in-memory key/value database.
package db

import (
	"sync"
	"sync/atomic"
	"unsafe"

	"github.com/azmodb/db/pb"
	"github.com/azmodb/llrb"
)

var valuePool = sync.Pool{New: func() interface{} { return &Value{&pb.Value{}} }}

const maxPooledLen = 8192 // TODO: find better value

// Value represents a read-only key/value database query result. The
// caller must close the value when done with it.
type Value struct {
	*pb.Value
}

func newValue(data interface{}, revs []int64) *Value {
	v := valuePool.Get().(*Value)
	switch t := data.(type) {
	case []byte:
		if cap(v.Value.Data) < len(t) {
			v.Value.Data = make([]byte, len(t))
		}
		v.Value.Data = v.Value.Data[:len(t)]
		copy(v.Value.Data, t)
	case int64:
		v.Value.Num = t
	default:
		panic("invalid value type")
	}

	v.Value.Revs = revs
	return v
}

// Close closes the value, rendering it unusable. The caller must close
// the value when done with it.
func (v *Value) Close() {
	if cap(v.Value.Data) > maxPooledLen {
		v.Value.Data = v.Value.Data[0:0:maxPooledLen]
	}
	v.Value.Num = 0
	v.Value.Revs = nil
	valuePool.Put(v)
}

// IsNum returns true if this value is a numeric value.
func (v *Value) IsNum() bool { return v.Value.Data == nil }

// Bytes returns the underlying value byte slice. If Value is a numeric
// value Bytes returns nil.
func (v *Value) Bytes() []byte { return v.Value.Data }

// Num returns the underlying integer. If Value is a byte slice value
// Num returns 0.
func (v *Value) Num() int64 { return v.Value.Num }

// Revs returns the revision numbers of this value.
func (v *Value) Revs() []int64 { return v.Value.Revs }

type DB struct {
	writer sync.Mutex // exclusive writer transaction
	tree   unsafe.Pointer
}

type tree struct {
	root *llrb.Tree
	rev  int64
}

// New returns a consistent in-memory key/value database.
func New() *DB { return newDB(nil) }

func newDB(t *tree) *DB {
	if t == nil {
		t = &tree{root: &llrb.Tree{}}
	}
	return &DB{tree: unsafe.Pointer(t)}
}

func (db *DB) store(t *tree) {
	atomic.StorePointer(&db.tree, unsafe.Pointer(t))
}

func (db *DB) load() *tree {
	return (*tree)(atomic.LoadPointer(&db.tree))
}

func (db *DB) Next() *Batch {
	db.writer.Lock()
	tree := db.load()
	return &Batch{txn: tree.root.Txn(), rev: tree.rev, db: db}
}

// Rev returns the current revision of the database.
func (db *DB) Rev() int64 {
	tree := db.load()
	return tree.rev
}

// len returns the number of keys in the database. only usefull for unit
// testing.
func (db *DB) len() int {
	tree := db.load()
	return tree.root.Len()
}
<|endoftext|>


<commit_before>package mdq

import (
	"database/sql"
	"fmt"
)

type Executor interface {
	Query(query string, args ...interface{}) (*sql.Rows, error)
}

type Result struct {
	Database string
	Headers  []string
	Rows     []map[string]interface{}
}

type column struct {
	value interface{}
}

func (c *column) Scan(src interface{}) error {
	c.value = src
	if bs, ok := src.([]byte); ok {
		c.value = string(bs)
	}
	return nil
}

type DB interface {
	Query(query string) (Result, error)
}

func NewDB(name string, executor Executor) DB {
	return db{name, executor}
}

type db struct {
	name     string
	executor Executor
}

func (db db) Query(query string) (Result, error) {
	rows, err := db.executor.Query(query)
	if err != nil {
		return Result{}, db.err(err, "execution failed")
	}

	columns, err := rows.Columns()
	if err != nil {
		return Result{}, db.err(err, "failed to fetch column name")
	}
	columnSize := len(columns)

	result := Result{
		Database: db.name,
		Headers:  columns,
	}
	for rows.Next() {
		values := make([]AnyValue, columnSize)
		valuePtrs := make([]interface{}, columnSize)
		for i := range columns {
			valuePtrs[i] = &values[i]
		}
		err := rows.Scan(valuePtrs...)
		if err != nil {
			return Result{}, db.err(err, "failed to bind values")
		}

		row := make(map[string]interface{})
		for i, col := range columns {
			v := values[i].val
			row[col] = v
		}
		result.Rows = append(result.Rows, row)
	}
	return result, nil
}

func (db db) err(err error, message string) error {
	return fmt.Errorf("%s: %s: %v", db.name, message, err)
}

type AnyValue struct {
	val interface{}
}

func (v *AnyValue) Scan(src interface{}) error {
	v.val = src
	if bs, ok := src.([]byte); ok {
		v.val = string(bs)
	}
	return nil
}
<commit_msg>Rename Headers to Columns<commit_after>package mdq

import (
	"database/sql"
	"fmt"
)

type Executor interface {
	Query(query string, args ...interface{}) (*sql.Rows, error)
}

type Result struct {
	Database string
	Columns  []string
	Rows     []map[string]interface{}
}

type column struct {
	value interface{}
}

func (c *column) Scan(src interface{}) error {
	c.value = src
	if bs, ok := src.([]byte); ok {
		c.value = string(bs)
	}
	return nil
}

type DB interface {
	Query(query string) (Result, error)
}

func NewDB(name string, executor Executor) DB {
	return db{name, executor}
}

type db struct {
	name     string
	executor Executor
}

func (db db) Query(query string) (Result, error) {
	rows, err := db.executor.Query(query)
	if err != nil {
		return Result{}, db.err(err, "execution failed")
	}

	columns, err := rows.Columns()
	if err != nil {
		return Result{}, db.err(err, "failed to fetch column name")
	}
	columnSize := len(columns)

	result := Result{
		Database: db.name,
		Columns:  columns,
	}
	for rows.Next() {
		values := make([]AnyValue, columnSize)
		valuePtrs := make([]interface{}, columnSize)
		for i := range columns {
			valuePtrs[i] = &values[i]
		}
		err := rows.Scan(valuePtrs...)
		if err != nil {
			return Result{}, db.err(err, "failed to bind values")
		}

		row := make(map[string]interface{})
		for i, col := range columns {
			v := values[i].val
			row[col] = v
		}
		result.Rows = append(result.Rows, row)
	}
	return result, nil
}

func (db db) err(err error, message string) error {
	return fmt.Errorf("%s: %s: %v", db.name, message, err)
}

type AnyValue struct {
	val interface{}
}

func (v *AnyValue) Scan(src interface{}) error {
	v.val = src
	if bs, ok := src.([]byte); ok {
		v.val = string(bs)
	}
	return nil
}
<|endoftext|>


<commit_before>package simpledb

import (
	//"encoding/json"
	"errors"
	"fmt"
	"strconv"

	"github.com/garyburd/redigo/redis"
)

type DB struct {
	Name string
}

func Open(name string) *DB {
	return &DB{Name: name}
}

func (db *DB) Close() {
}

func (db *DB) GenMaxIdKey() (maxIdKey string) {
	return fmt.Sprintf("%v/maxid", db.Name)
}

func (db *DB) GetMaxId(c redis.Conn) (maxId uint64, err error) {
	k := db.GenMaxIdKey()
	exists, err := redis.Bool(c.Do("EXISTS", k))
	if err != nil {
		debugPrintf("GenMaxId() error: %v\n", err)
		return 0, err
	}

	if !exists {
		return 0, nil
	}

	maxId, err = redis.Uint64(c.Do("GET", k))
	if err != nil {
		debugPrintf("GenMaxId() error: %v\n", err)
		return 0, err
	}

	return maxId, nil
}

func (db *DB) GenMaxBucketIdKey() (maxBucketIdKey string) {
	return fmt.Sprintf("%v/maxbucketid", db.Name)
}

func (db *DB) GetMaxBucketId(c redis.Conn) (maxBucketId uint64, err error) {
	k := db.GenMaxBucketIdKey()
	exists, err := redis.Bool(c.Do("EXISTS", k))
	if err != nil {
		debugPrintf("GetMaxBucketId() error: %v\n", err)
		return 0, err
	}

	if !exists {
		_, err := redis.String(c.Do("SET", k, 1))
		if err != nil {
			debugPrintf("GetMaxBucketId() error: %v\n", err)
			return 0, err
		}

		return 1, nil
	}

	maxBucketId, err = redis.Uint64(c.Do("GET", k))
	if err != nil {
		debugPrintf("GetMaxBucketId() error: %v\n", err)
		return 0, err
	}

	return maxBucketId, nil
}

func (db *DB) GenHashKey(bucketId uint64) (recordHashKey, indexHashKey string) {
	bucketIdStr := strconv.FormatUint(bucketId, 10)
	recordHashKey = fmt.Sprintf("%v/bucket/%v", db.Name, bucketIdStr)
	indexHashKey = fmt.Sprintf("%v/idx/bucket/%v", db.Name, bucketIdStr)
	return recordHashKey, indexHashKey
}

func (db *DB) IndexExists(c redis.Conn, data string) (exists bool, err error) {
	maxBucketId, err := db.GetMaxBucketId(c)
	if err != nil {
		debugPrintf("GetMaxBucketId() error: %v\n", err)
		return false, err
	}

	indexHashKey := ""
	indexHashField := data

	for i := maxBucketId; i >= 1; i-- {
		_, indexHashKey = db.GenHashKey(i)
		exists, err := redis.Bool(c.Do("HEXISTS", indexHashKey, indexHashField))
		if err != nil {
			debugPrintf("IndexExists() error: %v\n", err)
			return false, err
		}

		if exists {
			return true, nil
		}
	}
	return false, nil
}

func (db *DB) Create(c redis.Conn, data string) (id string, err error) {
	// 1. Check json data.
	if len(data) == 0 {
		err = errors.New("Empty data.")
		debugPrintf("Create() error: %v\n", err)
		return "", err
	}

	// 2. Get max id and compute current id.
	maxId, err := db.GetMaxId(c)
	if err != nil {
		debugPrintf("Create() error: %v\n", err)
		return "", err
	}

	nId := maxId + 1

	// 3. Compute current bucket id.
	//    Increase max bucket id if current bucket id > max bucket id.
	bucketId := ComputeBucketId(nId)

	maxBucketId, err := db.GetMaxBucketId(c)
	if err != nil {
		debugPrintf("Create() error: %v\n", err)
		return "", err
	}

	if bucketId > maxBucketId {
		k := db.GenMaxBucketIdKey()
		_, err := c.Do("INCR", k)
		if err != nil {
			debugPrintf("Create() error: %v\n", err)
			return "", err
		}
	}

	// 4. Generate hash key for record and index.
	recordHashKey, indexHashKey := db.GenHashKey(bucketId)

	// 5. Check if data already exists.
	exists, err := db.IndexExists(c, data)
	if err != nil {
		debugPrintf("Create(): error: %v\n", err)
		return "", err
	}

	// Index already exists, it means the record also exists.
	if exists {
		err = errors.New("Data already exists.")
		debugPrintf("Create(): error: %v\n", err)
		return "", err
	}

	// 6. Create record and index
	recordHashField := nId
	indexHashField := data
	maxIdKey := db.GenMaxIdKey()

	c.Send("MULTI")
	c.Send("HSET", recordHashKey, recordHashField, data)
	c.Send("HSET", indexHashKey, indexHashField, nId)
	c.Send("INCR", maxIdKey)
	ret, err := c.Do("EXEC")
	if err != nil {
		debugPrintf("Create() error: %v\n", err)
		return "", err
	}

	debugPrintf("Create(): ok: %v\n", ret)

	id = strconv.FormatUint(nId, 10)
	return id, nil
}

func (db *DB) BatchCreate(c redis.Conn, dataArr []string) (ids []string, err error) {
	ids = []string{}
	id := ""

	for _, v := range dataArr {
		if id, err = db.Create(c, v); err != nil {
			debugPrintf("BatchCreate(): error: %v\n", err)
			return ids, err
		}
		ids = append(ids, id)
	}
	return ids, nil
}

func (db *DB) Exists(c redis.Conn, id string) (exists bool, recordHashKey, indexHashKey string, recordHashField uint64, err error) {
	nId, err := strconv.ParseUint(id, 10, 64)
	if err != nil {
		debugPrintf("Exists() strconv.ParseUint() error: %v\n", err)
		return false, "", "", 0, err
	}

	bucketId := ComputeBucketId(nId)
	recordHashKey, indexHashKey = db.GenHashKey(bucketId)
	recordHashField = nId

	exists, err = redis.Bool(c.Do("HEXISTS", recordHashKey, recordHashField))
	if err != nil {
		debugPrintf("Exists() error: %v\n", err)
		return false, "", "", 0, err
	}

	return exists, recordHashKey, indexHashKey, recordHashField, nil
}

func (db *DB) Get(c redis.Conn, id string) (data string, err error) {
	exists, recordHashKey, _, recordHashField, err := db.Exists(c, id)
	if err != nil {
		debugPrintf("Get(): db.Exists() error: %v\n", err)
		return "", err
	}

	if !exists {
		err = errors.New("Record filed does not exists in hash key.")
		debugPrintf("Get(): error: %v\n", err)
		return "", err
	}

	data, err = redis.String(c.Do("HGET", recordHashKey, recordHashField))
	if err != nil {
		debugPrintf("Get(): error: %v\n", err)
		return "", err
	}

	return data, nil
}

func (db *DB) BatchGet(c redis.Conn, ids []string) (dataMap map[string]string, err error) {
	dataMap = make(map[string]string)
	data := ""

	for _, id := range ids {
		data, err = db.Get(c, id)
		if err != nil {
			debugPrintf("BatchGet(): db.Get() error: %v\n", err)
			return dataMap, err
		}
		dataMap[id] = data
	}
	return dataMap, nil
}

func (db *DB) Update(c redis.Conn, id, data string) error {
	exists, recordHashKey, indexHashKey, recordHashField, err := db.Exists(c, id)
	if err != nil {
		debugPrintf("Update() db.Exists() error: %v\n", err)
		return err
	}

	if !exists {
		err = errors.New("Record does not exist.")
		debugPrintf("Update() error: %v\n", err)
		return err
	}

	oldData, err := db.Get(c, id)
	if err != nil {
		debugPrintf("Update() db.Get() error: %v\n", err)
		return err
	}

	oldIndexHashField := oldData
	newIndexHashField := data
	nId := recordHashField

	// Check if data already exists.
	exists, err = db.IndexExists(c, data)
	if err != nil {
		debugPrintf("Update() error: %v\n", err)
		return err
	}

	// Index already exists, it means the there's already a record has the same value / index.
	if exists {
		debugPrintf("Update(): same data / index already exists. No need to update.\n")
		return nil
	}

	c.Send("MULTI")
	c.Send("HSET", recordHashKey, recordHashField, data)
	c.Send("HSET", indexHashKey, newIndexHashField, nId)
	c.Send("HDEL", indexHashKey, oldIndexHashField)
	ret, err := c.Do("EXEC")
	if err != nil {
		debugPrintf("Update() error: %v\n", err)
		return err
	}

	debugPrintf("Update() ok. ret: %v\n", ret)

	return nil
}

func (db *DB) Search(c redis.Conn, pattern string) (ids []string, err error) {
	if len(pattern) == 0 {
		err = errors.New("Empty pattern.")
		debugPrintf("Search() error: %v\n", err)
		return []string{}, err
	}

	maxBucketId, err := db.GetMaxBucketId(c)
	if err != nil {
		debugPrintf("Search() error: %v\n", err)
		return []string{}, err
	}

	indexHashKey := ""
	ids = []string{}
	items := []string{}

	for i := maxBucketId; i >= 1; i-- {
		_, indexHashKey = db.GenHashKey(i)
		cursor := 0
		for {
			v, err := redis.Values(c.Do("HSCAN", indexHashKey, cursor, "match", pattern))
			if err != nil {
				debugPrintf("Search(): HSCAN error: %v\n", err)
				return []string{}, err
			}

			v, err = redis.Scan(v, &cursor, &items)
			if err != nil {
				fmt.Printf("err: %v\n", err)
				return []string{}, err
			}

			l := len(items)
			if l > 0 && l%2 == 0 {
				for m := 1; m < l; m += 2 {
					ids = append(ids, items[m])
				}
			}

			if cursor == 0 {
				break
			}
		}
	}
	return ids, nil
}
<commit_msg>DB.Update() will return error if new value already exists.<commit_after>package simpledb

import (
	//"encoding/json"
	"errors"
	"fmt"
	"strconv"

	"github.com/garyburd/redigo/redis"
)

type DB struct {
	Name string
}

func Open(name string) *DB {
	return &DB{Name: name}
}

func (db *DB) Close() {
}

func (db *DB) GenMaxIdKey() (maxIdKey string) {
	return fmt.Sprintf("%v/maxid", db.Name)
}

func (db *DB) GetMaxId(c redis.Conn) (maxId uint64, err error) {
	k := db.GenMaxIdKey()
	exists, err := redis.Bool(c.Do("EXISTS", k))
	if err != nil {
		debugPrintf("GenMaxId() error: %v\n", err)
		return 0, err
	}

	if !exists {
		return 0, nil
	}

	maxId, err = redis.Uint64(c.Do("GET", k))
	if err != nil {
		debugPrintf("GenMaxId() error: %v\n", err)
		return 0, err
	}

	return maxId, nil
}

func (db *DB) GenMaxBucketIdKey() (maxBucketIdKey string) {
	return fmt.Sprintf("%v/maxbucketid", db.Name)
}

func (db *DB) GetMaxBucketId(c redis.Conn) (maxBucketId uint64, err error) {
	k := db.GenMaxBucketIdKey()
	exists, err := redis.Bool(c.Do("EXISTS", k))
	if err != nil {
		debugPrintf("GetMaxBucketId() error: %v\n", err)
		return 0, err
	}

	if !exists {
		_, err := redis.String(c.Do("SET", k, 1))
		if err != nil {
			debugPrintf("GetMaxBucketId() error: %v\n", err)
			return 0, err
		}

		return 1, nil
	}

	maxBucketId, err = redis.Uint64(c.Do("GET", k))
	if err != nil {
		debugPrintf("GetMaxBucketId() error: %v\n", err)
		return 0, err
	}

	return maxBucketId, nil
}

func (db *DB) GenHashKey(bucketId uint64) (recordHashKey, indexHashKey string) {
	bucketIdStr := strconv.FormatUint(bucketId, 10)
	recordHashKey = fmt.Sprintf("%v/bucket/%v", db.Name, bucketIdStr)
	indexHashKey = fmt.Sprintf("%v/idx/bucket/%v", db.Name, bucketIdStr)
	return recordHashKey, indexHashKey
}

func (db *DB) IndexExists(c redis.Conn, data string) (exists bool, err error) {
	maxBucketId, err := db.GetMaxBucketId(c)
	if err != nil {
		debugPrintf("GetMaxBucketId() error: %v\n", err)
		return false, err
	}

	indexHashKey := ""
	indexHashField := data

	for i := maxBucketId; i >= 1; i-- {
		_, indexHashKey = db.GenHashKey(i)
		exists, err := redis.Bool(c.Do("HEXISTS", indexHashKey, indexHashField))
		if err != nil {
			debugPrintf("IndexExists() error: %v\n", err)
			return false, err
		}

		if exists {
			return true, nil
		}
	}
	return false, nil
}

func (db *DB) Create(c redis.Conn, data string) (id string, err error) {
	// 1. Check json data.
	if len(data) == 0 {
		err = errors.New("Empty data.")
		debugPrintf("Create() error: %v\n", err)
		return "", err
	}

	// 2. Get max id and compute current id.
	maxId, err := db.GetMaxId(c)
	if err != nil {
		debugPrintf("Create() error: %v\n", err)
		return "", err
	}

	nId := maxId + 1

	// 3. Compute current bucket id.
	//    Increase max bucket id if current bucket id > max bucket id.
	bucketId := ComputeBucketId(nId)

	maxBucketId, err := db.GetMaxBucketId(c)
	if err != nil {
		debugPrintf("Create() error: %v\n", err)
		return "", err
	}

	if bucketId > maxBucketId {
		k := db.GenMaxBucketIdKey()
		_, err := c.Do("INCR", k)
		if err != nil {
			debugPrintf("Create() error: %v\n", err)
			return "", err
		}
	}

	// 4. Generate hash key for record and index.
	recordHashKey, indexHashKey := db.GenHashKey(bucketId)

	// 5. Check if data already exists.
	exists, err := db.IndexExists(c, data)
	if err != nil {
		debugPrintf("Create(): error: %v\n", err)
		return "", err
	}

	// Index already exists, it means the record also exists.
	if exists {
		err = errors.New("Data already exists.")
		debugPrintf("Create(): error: %v\n", err)
		return "", err
	}

	// 6. Create record and index
	recordHashField := nId
	indexHashField := data
	maxIdKey := db.GenMaxIdKey()

	c.Send("MULTI")
	c.Send("HSET", recordHashKey, recordHashField, data)
	c.Send("HSET", indexHashKey, indexHashField, nId)
	c.Send("INCR", maxIdKey)
	ret, err := c.Do("EXEC")
	if err != nil {
		debugPrintf("Create() error: %v\n", err)
		return "", err
	}

	debugPrintf("Create(): ok: %v\n", ret)

	id = strconv.FormatUint(nId, 10)
	return id, nil
}

func (db *DB) BatchCreate(c redis.Conn, dataArr []string) (ids []string, err error) {
	ids = []string{}
	id := ""

	for _, v := range dataArr {
		if id, err = db.Create(c, v); err != nil {
			debugPrintf("BatchCreate(): error: %v\n", err)
			return ids, err
		}
		ids = append(ids, id)
	}
	return ids, nil
}

func (db *DB) Exists(c redis.Conn, id string) (exists bool, recordHashKey, indexHashKey string, recordHashField uint64, err error) {
	nId, err := strconv.ParseUint(id, 10, 64)
	if err != nil {
		debugPrintf("Exists() strconv.ParseUint() error: %v\n", err)
		return false, "", "", 0, err
	}

	bucketId := ComputeBucketId(nId)
	recordHashKey, indexHashKey = db.GenHashKey(bucketId)
	recordHashField = nId

	exists, err = redis.Bool(c.Do("HEXISTS", recordHashKey, recordHashField))
	if err != nil {
		debugPrintf("Exists() error: %v\n", err)
		return false, "", "", 0, err
	}

	return exists, recordHashKey, indexHashKey, recordHashField, nil
}

func (db *DB) Get(c redis.Conn, id string) (data string, err error) {
	exists, recordHashKey, _, recordHashField, err := db.Exists(c, id)
	if err != nil {
		debugPrintf("Get(): db.Exists() error: %v\n", err)
		return "", err
	}

	if !exists {
		err = errors.New("Record filed does not exists in hash key.")
		debugPrintf("Get(): error: %v\n", err)
		return "", err
	}

	data, err = redis.String(c.Do("HGET", recordHashKey, recordHashField))
	if err != nil {
		debugPrintf("Get(): error: %v\n", err)
		return "", err
	}

	return data, nil
}

func (db *DB) BatchGet(c redis.Conn, ids []string) (dataMap map[string]string, err error) {
	dataMap = make(map[string]string)
	data := ""

	for _, id := range ids {
		data, err = db.Get(c, id)
		if err != nil {
			debugPrintf("BatchGet(): db.Get() error: %v\n", err)
			return dataMap, err
		}
		dataMap[id] = data
	}
	return dataMap, nil
}

func (db *DB) Update(c redis.Conn, id, data string) error {
	exists, recordHashKey, indexHashKey, recordHashField, err := db.Exists(c, id)
	if err != nil {
		debugPrintf("Update() db.Exists() error: %v\n", err)
		return err
	}

	if !exists {
		err = errors.New("Record does not exist.")
		debugPrintf("Update() error: %v\n", err)
		return err
	}

	oldData, err := db.Get(c, id)
	if err != nil {
		debugPrintf("Update() db.Get() error: %v\n", err)
		return err
	}

	oldIndexHashField := oldData
	newIndexHashField := data
	nId := recordHashField

	// Check if data already exists.
	exists, err = db.IndexExists(c, data)
	if err != nil {
		debugPrintf("Update() error: %v\n", err)
		return err
	}

	// Index already exists, it means the there's already a record has the same value / index.
	if exists {
		err = errors.New("Same data / index already exists.")
		debugPrintf("Update() error: %v\n", err)
		return err
	}

	c.Send("MULTI")
	c.Send("HSET", recordHashKey, recordHashField, data)
	c.Send("HSET", indexHashKey, newIndexHashField, nId)
	c.Send("HDEL", indexHashKey, oldIndexHashField)
	ret, err := c.Do("EXEC")
	if err != nil {
		debugPrintf("Update() error: %v\n", err)
		return err
	}

	debugPrintf("Update() ok. ret: %v\n", ret)

	return nil
}

func (db *DB) Search(c redis.Conn, pattern string) (ids []string, err error) {
	if len(pattern) == 0 {
		err = errors.New("Empty pattern.")
		debugPrintf("Search() error: %v\n", err)
		return []string{}, err
	}

	maxBucketId, err := db.GetMaxBucketId(c)
	if err != nil {
		debugPrintf("Search() error: %v\n", err)
		return []string{}, err
	}

	indexHashKey := ""
	ids = []string{}
	items := []string{}

	for i := maxBucketId; i >= 1; i-- {
		_, indexHashKey = db.GenHashKey(i)
		cursor := 0
		for {
			v, err := redis.Values(c.Do("HSCAN", indexHashKey, cursor, "match", pattern))
			if err != nil {
				debugPrintf("Search(): HSCAN error: %v\n", err)
				return []string{}, err
			}

			v, err = redis.Scan(v, &cursor, &items)
			if err != nil {
				fmt.Printf("err: %v\n", err)
				return []string{}, err
			}

			l := len(items)
			if l > 0 && l%2 == 0 {
				for m := 1; m < l; m += 2 {
					ids = append(ids, items[m])
				}
			}

			if cursor == 0 {
				break
			}
		}
	}
	return ids, nil
}
<|endoftext|>


<commit_before>package main

import (
  "database/sql"
  "time"
  _ "github.com/go-sql-driver/mysql"
  "fmt"
)

type Issue struct {
  id int
  title string
  description string
  description_output string
  created time.Time
  modified time.Time
}

func main() {
  connection, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/hitch?parseTime=true")
  defer connection.Close()

  if err != nil {
    panic(err)
  }

  row := connection.QueryRow("SELECT * FROM issues WHERE id=?", 1)
  issue := new(Issue)
  scanerr := row.Scan(&issue.id, &issue.title, &issue.description, &issue.description_output, &issue.created, &issue.modified)

  if scanerr != nil {
    panic(scanerr)
  }

  fmt.Printf(issue.title + "\n")
}
<commit_msg>added json test<commit_after>package main

import (
  "database/sql"
  "time"
  _ "github.com/go-sql-driver/mysql"
  "fmt"
  "encoding/json"
)

type Issue struct {
  Id int
  Title string
  Description string
  Description_output string
  Created time.Time
  Modified time.Time
}

func main() {
  connection, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/hitch?parseTime=true")
  defer connection.Close()

  if err != nil {
    panic(err)
  }

  row := connection.QueryRow("SELECT * FROM issues WHERE id=?", 1)
  issue := new(Issue)
  scanerr := row.Scan(&issue.Id, &issue.Title, &issue.Description, &issue.Description_output, &issue.Created, &issue.Modified)

  if scanerr != nil {
    panic(scanerr)
  }

  response, err := json.Marshal(issue)

  fmt.Println(string(response))
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"log"
	"time"

	_ "github.com/mattn/go-sqlite3"
	qs "github.com/nboughton/go-stupidqlite"
)

var (
	sqlPragmas = "PRAGMA journal_mode=WAL;	PRAGMA busy_timeout=5000"
	sqlSchema  = `
	CREATE TABLE IF NOT EXISTS results
	(id INTEGER PRIMARY KEY AUTOINCREMENT,
	date DATETIME,
	ball_set INT,
	ball_machine TEXT,
	num_1 INT,
	num_2 INT,
	num_3 INT,
	num_4 INT,
	num_5 INT,
	num_6 INT,
	bonus INT)`
	formatSqlite   = "2006-01-02 15:04:05-07:00"
	formatYYYYMMDD = "2006-01-02"
)

// AppDB is a wrapper for *sql.DB so we can extend it by adding our own methods
type AppDB struct {
	*sql.DB
}

type dbRow struct {
	Date    time.Time `json:"date"`
	Machine string    `json:"machine"`
	Set     int       `json:"set"`
	Num     []int     `json:"num"`
}

type queryParams struct {
	Type, Machine string
	Set           int
	Start, End    string
}

func connectDB(path string) *AppDB {
	// Connect to the database
	db, err := sql.Open("sqlite3", path)
	if err != nil {
		log.Fatal(err.Error())
	}

	// Disable connection pooling
	db.SetMaxOpenConns(1)
	db.SetMaxIdleConns(0)

	// Create DB schema if it doesn't exist
	if _, err := db.Exec(sqlSchema); err != nil {
		log.Fatal(err.Error())
	}

	// Set PRAGMAs
	if _, err := db.Exec(sqlPragmas); err != nil {
		log.Fatal(err.Error())
	}

	aDB := &AppDB{db}

	// Check if we have any data yet, otherwise populate the db
	log.Println("Checking database")
	if rows, _ := aDB.getRowCount(); rows == 0 {
		log.Println("No data, scraping site")
		if err := aDB.populateDB(); err != nil {
			log.Fatal(err.Error())
		}
	}

	// run update
	log.Println("Updating database")
	if err := aDB.updateDB(); err != nil {
		log.Fatal(err.Error())
	}

	return aDB
}

// Apply filters for queries
func qFilters(q *qs.Query, p queryParams) []interface{} {
	var qp []interface{}

	f := qs.NewFilterSet().Add(qs.Between, "date:DATE")
	if p.Machine != "all" && p.Set != 0 {
		q.Where(f.Add(qs.Eq, "ball_machine").Add(qs.Eq, "ball_set"))
		qp = []interface{}{p.Start, p.End, p.Machine, p.Set}

	} else if p.Machine != "all" && p.Set == 0 {
		q.Where(f.Add(qs.Eq, "ball_machine"))
		qp = []interface{}{p.Start, p.End, p.Machine}

	} else if p.Machine == "all" && p.Set != 0 {
		q.Where(f.Add(qs.Eq, "ball_set"))
		qp = []interface{}{p.Start, p.End, p.Set}

	} else {
		q.Where(f)
		qp = []interface{}{p.Start, p.End}

	}

	return qp
}

func (db *AppDB) getResults(p queryParams) <-chan dbRow {
	var c = make(chan dbRow)

	go func() {
		q := qs.NewQuery().
			Select("date", "ball_machine", "ball_set", "num_1", "num_2", "num_3", "num_4", "num_5", "num_6", "bonus").
			From("results")

		rows, err := db.Query(q.Order("DATE(date)").SQL, qFilters(q, p)...)

		if err != nil {
			log.Println(err.Error())
		}

		for rows.Next() {
			var r dbRow
			r.Num = make([]int, 7)
			if err := rows.Scan(&r.Date, &r.Machine, &r.Set, &r.Num[0], &r.Num[1], &r.Num[2], &r.Num[3], &r.Num[4], &r.Num[5], &r.Num[6]); err != nil {
				log.Println(err.Error())
			}

			c <- r
		}

		close(c)
	}()

	return c
}

func (db *AppDB) getResultsAverage(p queryParams) ([]int, error) {
	var (
		r = make([]int, 7)
		q = qs.NewQuery().
			Select("SUM(num_1)/COUNT(num_1)",
				"SUM(num_2)/COUNT(num_2)",
				"SUM(num_3)/COUNT(num_3)",
				"SUM(num_4)/COUNT(num_4)",
				"SUM(num_5)/COUNT(num_5)",
				"SUM(num_6)/COUNT(num_6)",
				"SUM(bonus)/COUNT(bonus)").
			From("results")
	)

	if err := db.QueryRow(q.SQL, qFilters(q, p)...).Scan(&r[0], &r[1], &r[2], &r[3], &r[4], &r[5], &r[6]); err != nil {
		return r, err
	}

	return r, nil
}

func (db *AppDB) getMachineList(p queryParams) ([]string, error) {
	var (
		result []string
		q      = qs.NewQuery().
			Select("DISTINCT(ball_machine)").
			From("results")
	)

	rows, err := db.Query(q.Order("ball_machine").SQL, qFilters(q, p)...)
	if err != nil {
		return result, err
	}

	for rows.Next() {
		var m string
		rows.Scan(&m)
		result = append(result, m)
	}

	return result, nil
}

func (db *AppDB) getSetList(p queryParams) ([]int, error) {
	var (
		result []int
		q      = qs.NewQuery().
			Select("DISTINCT(ball_set)").
			From("results")
	)

	rows, err := db.Query(q.Order("ball_set").SQL, qFilters(q, p)...)
	if err != nil {
		return result, err
	}

	for rows.Next() {
		var s int
		rows.Scan(&s)
		result = append(result, s)
	}

	return result, nil
}

func (db *AppDB) getRowCount() (int, error) {
	var rows int
	if err := db.QueryRow(qs.NewQuery().Select("COUNT(*)").From("results").SQL).Scan(&rows); err != nil {
		return rows, err
	}

	return rows, nil
}

func (db *AppDB) getDataRange() (time.Time, time.Time, error) {
	var (
		first string
		last  string
		q     = qs.NewQuery().
			Select("MIN(date)", "MAX(date)").
			From("results")
	)

	if err := db.QueryRow(q.SQL).Scan(&first, &last); err != nil {
		return time.Now(), time.Now(), err
	}

	f, _ := time.Parse(formatSqlite, first)
	l, _ := time.Parse(formatSqlite, last)
	return f, l, nil
}

func (db *AppDB) updateDB() error {
	// Prepare insert
	qInsert, err := db.Prepare(qs.NewQuery().
		Insert("results", "date", "ball_set", "ball_machine", "num_1", "num_2", "num_3", "num_4", "num_5", "num_6", "bonus").SQL)
	if err != nil {
		return err
	}
	// Generate Select
	qSelect := qs.NewQuery().
		Select("COUNT(*)").
		From("results").
		Where(qs.NewFilterSet().Add(qs.Eq, "date")).SQL

	// Begin transaction
	tx, err := db.Begin()
	if err != nil {
		return err
	}

	// Iterate scrape data
	for d := range updateScraper() {
		// Check we don't already have this record
		var i int
		if err := tx.QueryRow(qSelect, d.Date.Format(formatSqlite)).Scan(&i); err != nil {
			tx.Rollback()
			return err
		}
		if i != 0 {
			log.Println(d.Date.String(), "already in DB, skipping")
			continue
		}
		// Insert new record
		if _, err := tx.Stmt(qInsert).Exec(d.Date, d.Set, d.Machine, d.Num[0], d.Num[1], d.Num[2], d.Num[3], d.Num[4], d.Num[5], d.Num[6]); err != nil {
			tx.Rollback()
			return err
		}
		log.Println(d, " inserted")
	}
	tx.Commit()

	return nil
}

func (db *AppDB) populateDB() error {
	// Prepare statement
	q, err := db.Prepare(qs.NewQuery().
		Insert("results", "date", "ball_set", "ball_machine", "num_1", "num_2", "num_3", "num_4", "num_5", "num_6", "bonus").SQL)
	if err != nil {
		return err
	}

	// Begin transaction
	tx, err := db.Begin()
	if err != nil {
		return err
	}

	// Iterate scrape data
	for d := range archiveScraper() {
		if _, err := tx.Stmt(q).Exec(d.Date, d.Set, d.Machine, d.Num[0], d.Num[1], d.Num[2], d.Num[3], d.Num[4], d.Num[5], d.Num[6]); err != nil {
			tx.Rollback()
			return err
		}
		log.Println(d)
	}
	tx.Commit()

	return nil
}
<commit_msg>remember that qFilters alters q in place<commit_after>package main

import (
	"database/sql"
	"log"
	"time"

	_ "github.com/mattn/go-sqlite3"
	qs "github.com/nboughton/go-stupidqlite"
)

var (
	sqlPragmas = "PRAGMA journal_mode=WAL;	PRAGMA busy_timeout=5000"
	sqlSchema  = `
	CREATE TABLE IF NOT EXISTS results
	(id INTEGER PRIMARY KEY AUTOINCREMENT,
	date DATETIME,
	ball_set INT,
	ball_machine TEXT,
	num_1 INT,
	num_2 INT,
	num_3 INT,
	num_4 INT,
	num_5 INT,
	num_6 INT,
	bonus INT)`
	formatSqlite   = "2006-01-02 15:04:05-07:00"
	formatYYYYMMDD = "2006-01-02"
)

// AppDB is a wrapper for *sql.DB so we can extend it by adding our own methods
type AppDB struct {
	*sql.DB
}

type dbRow struct {
	Date    time.Time `json:"date"`
	Machine string    `json:"machine"`
	Set     int       `json:"set"`
	Num     []int     `json:"num"`
}

type queryParams struct {
	Type, Machine string
	Set           int
	Start, End    string
}

func connectDB(path string) *AppDB {
	// Connect to the database
	db, err := sql.Open("sqlite3", path)
	if err != nil {
		log.Fatal(err.Error())
	}

	// Disable connection pooling
	db.SetMaxOpenConns(1)
	db.SetMaxIdleConns(0)

	// Create DB schema if it doesn't exist
	if _, err := db.Exec(sqlSchema); err != nil {
		log.Fatal(err.Error())
	}

	// Set PRAGMAs
	if _, err := db.Exec(sqlPragmas); err != nil {
		log.Fatal(err.Error())
	}

	aDB := &AppDB{db}

	// Check if we have any data yet, otherwise populate the db
	log.Println("Checking database")
	if rows, _ := aDB.getRowCount(); rows == 0 {
		log.Println("No data, scraping site")
		if err := aDB.populateDB(); err != nil {
			log.Fatal(err.Error())
		}
	}

	// run update
	log.Println("Updating database")
	if err := aDB.updateDB(); err != nil {
		log.Fatal(err.Error())
	}

	return aDB
}

// Apply filters for queries
func qFilters(q *qs.Query, p queryParams) []interface{} {
	var qp []interface{}

	f := qs.NewFilterSet().Add(qs.Between, "date:DATE")
	if p.Machine != "all" && p.Set != 0 {
		q.Where(f.Add(qs.Eq, "ball_machine").Add(qs.Eq, "ball_set"))
		qp = []interface{}{p.Start, p.End, p.Machine, p.Set}

	} else if p.Machine != "all" && p.Set == 0 {
		q.Where(f.Add(qs.Eq, "ball_machine"))
		qp = []interface{}{p.Start, p.End, p.Machine}

	} else if p.Machine == "all" && p.Set != 0 {
		q.Where(f.Add(qs.Eq, "ball_set"))
		qp = []interface{}{p.Start, p.End, p.Set}

	} else {
		q.Where(f)
		qp = []interface{}{p.Start, p.End}

	}

	return qp
}

func (db *AppDB) getResults(p queryParams) <-chan dbRow {
	var c = make(chan dbRow)

	go func() {
		q := qs.NewQuery().
			Select("date", "ball_machine", "ball_set", "num_1", "num_2", "num_3", "num_4", "num_5", "num_6", "bonus").
			From("results")

		qp := qFilters(q, p)
		rows, err := db.Query(q.Order("DATE(date)").SQL, qp...)

		if err != nil {
			log.Println(err.Error())
		}

		for rows.Next() {
			var r dbRow
			r.Num = make([]int, 7)
			if err := rows.Scan(&r.Date, &r.Machine, &r.Set, &r.Num[0], &r.Num[1], &r.Num[2], &r.Num[3], &r.Num[4], &r.Num[5], &r.Num[6]); err != nil {
				log.Println(err.Error())
			}

			c <- r
		}

		close(c)
	}()

	return c
}

func (db *AppDB) getResultsAverage(p queryParams) ([]int, error) {
	var (
		r = make([]int, 7)
		q = qs.NewQuery().
			Select("SUM(num_1)/COUNT(num_1)",
				"SUM(num_2)/COUNT(num_2)",
				"SUM(num_3)/COUNT(num_3)",
				"SUM(num_4)/COUNT(num_4)",
				"SUM(num_5)/COUNT(num_5)",
				"SUM(num_6)/COUNT(num_6)",
				"SUM(bonus)/COUNT(bonus)").
			From("results")
	)

	qp := qFilters(q, p)
	if err := db.QueryRow(q.SQL, qp...).Scan(&r[0], &r[1], &r[2], &r[3], &r[4], &r[5], &r[6]); err != nil {
		return r, err
	}

	return r, nil
}

func (db *AppDB) getMachineList(p queryParams) ([]string, error) {
	var (
		result []string
		q      = qs.NewQuery().
			Select("DISTINCT(ball_machine)").
			From("results")
	)

	qp := qFilters(q, p)
	rows, err := db.Query(q.Order("ball_machine").SQL, qp...)
	if err != nil {
		return result, err
	}

	for rows.Next() {
		var m string
		rows.Scan(&m)
		result = append(result, m)
	}

	return result, nil
}

func (db *AppDB) getSetList(p queryParams) ([]int, error) {
	var (
		result []int
		q      = qs.NewQuery().
			Select("DISTINCT(ball_set)").
			From("results")
	)

	qp := qFilters(q, p)
	rows, err := db.Query(q.Order("ball_set").SQL, qp...)
	if err != nil {
		return result, err
	}

	for rows.Next() {
		var s int
		rows.Scan(&s)
		result = append(result, s)
	}

	return result, nil
}

func (db *AppDB) getRowCount() (int, error) {
	var rows int
	if err := db.QueryRow(qs.NewQuery().Select("COUNT(*)").From("results").SQL).Scan(&rows); err != nil {
		return rows, err
	}

	return rows, nil
}

func (db *AppDB) getDataRange() (time.Time, time.Time, error) {
	var (
		first string
		last  string
		q     = qs.NewQuery().
			Select("MIN(date)", "MAX(date)").
			From("results")
	)

	if err := db.QueryRow(q.SQL).Scan(&first, &last); err != nil {
		return time.Now(), time.Now(), err
	}

	f, _ := time.Parse(formatSqlite, first)
	l, _ := time.Parse(formatSqlite, last)
	return f, l, nil
}

func (db *AppDB) updateDB() error {
	// Prepare insert
	qInsert, err := db.Prepare(qs.NewQuery().
		Insert("results", "date", "ball_set", "ball_machine", "num_1", "num_2", "num_3", "num_4", "num_5", "num_6", "bonus").SQL)
	if err != nil {
		return err
	}
	// Generate Select
	qSelect := qs.NewQuery().
		Select("COUNT(*)").
		From("results").
		Where(qs.NewFilterSet().Add(qs.Eq, "date")).SQL

	// Begin transaction
	tx, err := db.Begin()
	if err != nil {
		return err
	}

	// Iterate scrape data
	for d := range updateScraper() {
		// Check we don't already have this record
		var i int
		if err := tx.QueryRow(qSelect, d.Date.Format(formatSqlite)).Scan(&i); err != nil {
			tx.Rollback()
			return err
		}
		if i != 0 {
			log.Println(d.Date.String(), "already in DB, skipping")
			continue
		}
		// Insert new record
		if _, err := tx.Stmt(qInsert).Exec(d.Date, d.Set, d.Machine, d.Num[0], d.Num[1], d.Num[2], d.Num[3], d.Num[4], d.Num[5], d.Num[6]); err != nil {
			tx.Rollback()
			return err
		}
		log.Println(d, " inserted")
	}
	tx.Commit()

	return nil
}

func (db *AppDB) populateDB() error {
	// Prepare statement
	q, err := db.Prepare(qs.NewQuery().
		Insert("results", "date", "ball_set", "ball_machine", "num_1", "num_2", "num_3", "num_4", "num_5", "num_6", "bonus").SQL)
	if err != nil {
		return err
	}

	// Begin transaction
	tx, err := db.Begin()
	if err != nil {
		return err
	}

	// Iterate scrape data
	for d := range archiveScraper() {
		if _, err := tx.Stmt(q).Exec(d.Date, d.Set, d.Machine, d.Num[0], d.Num[1], d.Num[2], d.Num[3], d.Num[4], d.Num[5], d.Num[6]); err != nil {
			tx.Rollback()
			return err
		}
		log.Println(d)
	}
	tx.Commit()

	return nil
}
<|endoftext|>


<commit_before>package fs

import "strings"

var (
	// Local is the local file system
	Local = &LocalFileSystem{
		DefaultCreatePermissions:    UserAndGroupReadWrite,
		DefaultCreateDirPermissions: UserAndGroupReadWrite + AllExecute,
	}

	// Registry contains all registerred file systems
	Registry = []FileSystem{Local}
)

func DeregisterFileSystem(fs FileSystem) bool {
	prefix := fs.Prefix()
	for i, regfs := range Registry {
		if regfs.Prefix() == prefix {
			if i < len(Registry)-1 {
				Registry = append(Registry[:i], Registry[i+1:]...)
			} else {
				Registry = Registry[:i]
			}
			return true
		}
	}
	return false
}

func GetFileSystem(uriParts ...string) FileSystem {
	if len(uriParts) == 0 {
		return Local
	}
	return getFileSystem(uriParts[0])
}

func getFileSystem(uri string) FileSystem {
	if uri == "" {
		return Local
	}
	for _, fs := range Registry {
		if strings.HasPrefix(uri, fs.Prefix()) {
			return fs
		}
	}
	return Local
}

func GetFile(uriParts ...string) File {
	return GetFileSystem(uriParts[0]).File(uriParts...)
}

func Exists(uriParts ...string) bool {
	return GetFile(uriParts...).Exists()
}

func IsDir(uriParts ...string) bool {
	return GetFile(uriParts...).IsDir()
}

func ListDir(uri string, callback func(File) error, patterns ...string) error {
	return GetFile(uri).ListDir(callback, patterns...)
}

// ListDirMax: n == -1 lists all
func ListDirMax(uri string, n int, patterns ...string) ([]File, error) {
	return GetFile(uri).ListDirMax(n, patterns...)
}

func Touch(uri string, perm ...Permissions) (File, error) {
	file := GetFile(uri)
	err := file.Touch(perm...)
	if err != nil {
		return "", err
	}
	return file, nil
}

func MakeDir(uri string, perm ...Permissions) (File, error) {
	file := GetFile(uri)
	err := file.MakeDir(perm...)
	if err != nil {
		return "", err
	}
	return file, nil
}

func MakeAllDirs(uri string, perm ...Permissions) (File, error) {
	file := GetFile(uri)
	err := file.MakeAllDirs(perm...)
	if err != nil {
		return "", err
	}
	return file, nil
}

func Truncate(uri string, size int64) error {
	return GetFile(uri).Truncate(size)
}

func Remove(uri string) error {
	return GetFile(uri).Remove()
}

func ReadFile(uri string) ([]byte, error) {
	return GetFile(uri).ReadAll()
}

func WriteFile(uri string, data []byte, perm ...Permissions) error {
	return GetFile(uri).WriteAll(data, perm...)
}

func Append(uri string, data []byte, perm ...Permissions) error {
	return GetFile(uri).Append(data, perm...)
}
<commit_msg>added TempDir()<commit_after>package fs

import (
	"os"
	"strings"
)

var (
	// Local is the local file system
	Local = &LocalFileSystem{
		DefaultCreatePermissions:    UserAndGroupReadWrite,
		DefaultCreateDirPermissions: UserAndGroupReadWrite + AllExecute,
	}

	// Registry contains all registerred file systems
	Registry = []FileSystem{Local}
)

func DeregisterFileSystem(fs FileSystem) bool {
	prefix := fs.Prefix()
	for i, regfs := range Registry {
		if regfs.Prefix() == prefix {
			if i < len(Registry)-1 {
				Registry = append(Registry[:i], Registry[i+1:]...)
			} else {
				Registry = Registry[:i]
			}
			return true
		}
	}
	return false
}

func GetFileSystem(uriParts ...string) FileSystem {
	if len(uriParts) == 0 {
		return Local
	}
	return getFileSystem(uriParts[0])
}

func getFileSystem(uri string) FileSystem {
	if uri == "" {
		return Local
	}
	for _, fs := range Registry {
		if strings.HasPrefix(uri, fs.Prefix()) {
			return fs
		}
	}
	return Local
}

func GetFile(uriParts ...string) File {
	return GetFileSystem(uriParts[0]).File(uriParts...)
}

func Exists(uriParts ...string) bool {
	return GetFile(uriParts...).Exists()
}

func IsDir(uriParts ...string) bool {
	return GetFile(uriParts...).IsDir()
}

func ListDir(uri string, callback func(File) error, patterns ...string) error {
	return GetFile(uri).ListDir(callback, patterns...)
}

// ListDirMax: n == -1 lists all
func ListDirMax(uri string, n int, patterns ...string) ([]File, error) {
	return GetFile(uri).ListDirMax(n, patterns...)
}

func Touch(uri string, perm ...Permissions) (File, error) {
	file := GetFile(uri)
	err := file.Touch(perm...)
	if err != nil {
		return "", err
	}
	return file, nil
}

func MakeDir(uri string, perm ...Permissions) (File, error) {
	file := GetFile(uri)
	err := file.MakeDir(perm...)
	if err != nil {
		return "", err
	}
	return file, nil
}

func MakeAllDirs(uri string, perm ...Permissions) (File, error) {
	file := GetFile(uri)
	err := file.MakeAllDirs(perm...)
	if err != nil {
		return "", err
	}
	return file, nil
}

func Truncate(uri string, size int64) error {
	return GetFile(uri).Truncate(size)
}

func Remove(uri string) error {
	return GetFile(uri).Remove()
}

func ReadFile(uri string) ([]byte, error) {
	return GetFile(uri).ReadAll()
}

func WriteFile(uri string, data []byte, perm ...Permissions) error {
	return GetFile(uri).WriteAll(data, perm...)
}

func Append(uri string, data []byte, perm ...Permissions) error {
	return GetFile(uri).Append(data, perm...)
}

func TempDir() File {
	return File(os.TempDir())
}
<|endoftext|>


<commit_before>/*
	Dump a file in hex.
*/
package main

import (
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"
)

const (
	OffLen  = 6       // Default lenght of the output Address/Offset field
	Version = "1.0.0" // Version Number
)

// Flag variables
var (
	inFile   string
	offBegin int
	offEnd   int
	lineLen  int
	innerLen int
	goDump   bool
	quiet    bool
	h        bool
	hexUpper bool
	edgeMark string
	//
	argFname  string
	fileLen   = -1
	addrFlen  = -1
	minOffLen = -1
	version   bool
)

// Main initialization, set flags up
func init() {
	flag.StringVar(&edgeMark, "edgeMark", "|",
		"single character at edges of the right hand character block.")

	flag.BoolVar(&goDump, "goDump", false,
		"if true, use standard go encoding/hex/Dump.")

	flag.BoolVar(&h, "h", false, "print usage message.")

	flag.BoolVar(&hexUpper, "hexUpper", false,
		"if true, print upper case hex.")

	flag.StringVar(&inFile, "inFile", "",
		"input file name.  Argument 0 may also be used.")

	flag.IntVar(&innerLen, "innerLen", 4,
		"dump line inner area byte count.")

	flag.IntVar(&lineLen, "lineLen", 16,
		"dump line total byte count.")

	flag.IntVar(&minOffLen, "minOffLen", -1,
		"minimum lenght of the offset field.")

	flag.IntVar(&offBegin, "offBegin", 0,
		"begin dump at file offset.")

	flag.IntVar(&offEnd, "offEnd", -1,
		"end dump at file offset.")

	flag.BoolVar(&quiet, "quiet", false,
		"if true, suppress header/trailer/informational messages.")

	flag.BoolVar(&version, "version", false,
		"if true, display program version and exit.")
}

func checkError(e error, ds string) {
	if e != nil {
		fmt.Printf("\n%s %s\n\n", ds, e)
		if !quiet {
			fmt.Println("DumpFile Ends, RC:", 1)
		}
		os.Exit(1)
	}
}

func hexDigitCount(i int) {
	addrFlen = 1
	for {
		i = i / 16
		if i == 0 {
			return
		}
		addrFlen++
	}
}

func setFileLen(f *os.File) {
	fi, err := f.Stat()
	checkError(err, "Stat Error ==>")
	fileLen = int(fi.Size())
	hexDigitCount(fileLen)
	addrFlen++
	if addrFlen < OffLen {
		addrFlen = OffLen
	}
	if addrFlen < minOffLen {
		addrFlen = minOffLen
	}
	// fmt.Printf("Hex Digit Count: %d\n", addrFlen)
}

func fileInit(fn, ed string) io.Reader {
	f, err := os.OpenFile(fn, os.O_RDONLY, 0644)
	checkError(err, ed+" Open Error ==>")
	setFileLen(f)
	if offBegin > 0 {
		_, err := f.Seek(int64(offBegin), io.SeekStart)
		checkError(err, "Seek Error ==>")
	}
	return f
}

func getReader() io.Reader {
	fa := flag.Args()
	if len(fa) >= 1 {
		argFname = fa[0]
	}
	if inFile == "" && argFname == "" {
		addrFlen = OffLen  // Arbitrary, file size is unknown
		if minOffLen > 0 { // Let user specify length
			if addrFlen < minOffLen {
				addrFlen = minOffLen
			}
		}
		return os.Stdin
	}
	if inFile != "" {
		return fileInit(inFile, "inFile")
	}
	if argFname != "" {
		return fileInit(argFname, "argFname")
	}
	// Never get here ......
	return nil
}

func goFormatDump(r io.Reader) {
	// Dump
	buff, err := ioutil.ReadAll(r)
	checkError(err, "ReadAll error ==>")
	fmt.Printf("%s", hex.Dump(buff))
	//
	return
}

func printOffset(o int) {
	hexFmt := "%016x"
	if hexUpper {
		hexFmt = "%016X"
	}
	had := fmt.Sprintf(hexFmt, o)
	fmt.Printf("%s  ", had[16-addrFlen:])
}

func blankBuf(l int) []byte {
	s := strings.Repeat(" ", l)
	return []byte(s)
}

func printLeftBuffer(br int, ib []byte) {
	nol := (lineLen / innerLen) + 1
	os := ""
	noff := 0
	hexFmt := "%s%02x"
	if hexUpper {
		hexFmt = "%s%02X"
	}
leftFor:
	for no := 0; no < nol; no++ {
		for ni := 0; ni < innerLen; ni++ {
			if noff < br {
				nbi := int(ib[noff])
				os = fmt.Sprintf(hexFmt, os, nbi)
			} else {
				os = os + "  " // Add two blanks here
			}
			noff++
			if noff > br {
				os = os + "          " // Alignment need fixed.
				break leftFor
			}
		}
		os = os + " " // Blank at end of inner
	}
	fmt.Print(os)
	fmt.Print(edgeMark)
}

func printRightBuffer(br int, ib []byte) {
	bb := blankBuf(br)
	for i := 0; i < br; i++ {
		bb[i] = ib[i]
		if bb[i] < byte(0x20) {
			bb[i] = byte('.')
		}
	}
	fmt.Print(string(bb))
	fmt.Print(edgeMark)
}

func main() {
	flag.Parse() // Parse all flags

	if h {
		flag.PrintDefaults()
		return
	}
	if version {
		fmt.Printf("hd Version: %s\n", Version)
		return
	}

	if !quiet {
		fmt.Println("DumpFile Starts....")
	}
	// fmt.Println("Line Length:", lineLen)
	r := getReader()
	if goDump {
		goFormatDump(r)
		if !quiet {
			fmt.Println("DumpFile Ends....")
		}
		return
	}
	if offEnd > 0 && offEnd <= offBegin {
		fmt.Printf("Offset Error: offEnd(%d) must be > offBegin(%d)\n",
			offEnd, offBegin)
		if !quiet {
			fmt.Println("DumpFile Ends, RC:", 2)
		}
		os.Exit(2)
	}
	//
	roff := offBegin
	for {
		readLen := lineLen
		if offEnd > 0 && roff+readLen > offEnd {
			readLen = offEnd - roff + 1
		}
		ib := blankBuf(readLen)
		// fmt.Println("ReadLen is now:", readLen)
		br, _ := io.ReadAtLeast(r, ib, readLen)
		// fmt.Println("Actual Read Length:", br)
		if br == 0 {
			break
		}
		printOffset(roff)
		printLeftBuffer(br, ib)
		printRightBuffer(br, ib)
		roff += lineLen
		if offEnd > 0 && roff > offEnd {
			break
		}
		fmt.Println()
	}
	if !quiet {
		fmt.Println("DumpFile Ends....")
	}
}
<commit_msg>Format last output line correctly in all cases.<commit_after>/*
	Dump a file in hex.
*/
package main

import (
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"
)

const (
	OffLen  = 6       // Default lenght of the output Address/Offset field
	Version = "1.0.0" // Version Number
)

// Flag variables
var (
	inFile   string
	offBegin int
	offEnd   int
	lineLen  int
	innerLen int
	goDump   bool
	quiet    bool
	h        bool
	hexUpper bool
	edgeMark string
	//
	argFname  string
	fileLen   = -1
	addrFlen  = -1
	minOffLen = -1
	version   bool
	//
	normLineLen = -1
	lastLineLen = -1
	pad         = "                           "
)

// Main initialization, set flags up
func init() {
	flag.StringVar(&edgeMark, "edgeMark", "|",
		"single character at edges of the right hand character block.")

	flag.BoolVar(&goDump, "goDump", false,
		"if true, use standard go encoding/hex/Dump.")

	flag.BoolVar(&h, "h", false, "print usage message.")

	flag.BoolVar(&hexUpper, "hexUpper", false,
		"if true, print upper case hex.")

	flag.StringVar(&inFile, "inFile", "",
		"input file name.  Argument 0 may also be used.")

	flag.IntVar(&innerLen, "innerLen", 4,
		"dump line inner area byte count.")

	flag.IntVar(&lineLen, "lineLen", 16,
		"dump line total byte count.")

	flag.IntVar(&minOffLen, "minOffLen", -1,
		"minimum lenght of the offset field.")

	flag.IntVar(&offBegin, "offBegin", 0,
		"begin dump at file offset.")

	flag.IntVar(&offEnd, "offEnd", -1,
		"end dump at file offset.")

	flag.BoolVar(&quiet, "quiet", false,
		"if true, suppress header/trailer/informational messages.")

	flag.BoolVar(&version, "version", false,
		"if true, display program version and exit.")
}

func checkError(e error, ds string) {
	if e != nil {
		fmt.Printf("\n%s %s\n\n", ds, e)
		if !quiet {
			fmt.Println("DumpFile Ends, RC:", 1)
		}
		os.Exit(1)
	}
}

func hexDigitCount(i int) {
	addrFlen = 1
	for {
		i = i / 16
		if i == 0 {
			return
		}
		addrFlen++
	}
}

func setFileLen(f *os.File) {
	fi, err := f.Stat()
	checkError(err, "Stat Error ==>")
	fileLen = int(fi.Size())
	hexDigitCount(fileLen)
	addrFlen++
	if addrFlen < OffLen {
		addrFlen = OffLen
	}
	if addrFlen < minOffLen {
		addrFlen = minOffLen
	}
	// fmt.Printf("Hex Digit Count: %d\n", addrFlen)
}

func fileInit(fn, ed string) io.Reader {
	f, err := os.OpenFile(fn, os.O_RDONLY, 0644)
	checkError(err, ed+" Open Error ==>")
	setFileLen(f)
	if offBegin > 0 {
		_, err := f.Seek(int64(offBegin), io.SeekStart)
		checkError(err, "Seek Error ==>")
	}
	return f
}

func getReader() io.Reader {
	fa := flag.Args()
	if len(fa) >= 1 {
		argFname = fa[0]
	}
	if inFile == "" && argFname == "" {
		addrFlen = OffLen  // Arbitrary, file size is unknown
		if minOffLen > 0 { // Let user specify length
			if addrFlen < minOffLen {
				addrFlen = minOffLen
			}
		}
		return os.Stdin
	}
	if inFile != "" {
		return fileInit(inFile, "inFile")
	}
	if argFname != "" {
		return fileInit(argFname, "argFname")
	}
	// Never get here ......
	return nil
}

func goFormatDump(r io.Reader) {
	// Dump
	buff, err := ioutil.ReadAll(r)
	checkError(err, "ReadAll error ==>")
	fmt.Printf("%s", hex.Dump(buff))
	//
	return
}

func printOffset(o int) {
	hexFmt := "%016x"
	if hexUpper {
		hexFmt = "%016X"
	}
	had := fmt.Sprintf(hexFmt, o)
	fmt.Printf("%s  ", had[16-addrFlen:])
}

func blankBuf(l int) []byte {
	s := strings.Repeat(" ", l)
	return []byte(s)
}

func printLeftBuffer(br int, ib []byte) {
	nol := (lineLen / innerLen) + 1
	os := ""
	noff := 0
	hexFmt := "%s%02x"
	if hexUpper {
		hexFmt = "%s%02X"
	}
leftFor:
	for no := 0; no < nol; no++ {
		for ni := 0; ni < innerLen; ni++ {
			if noff < br {
				nbi := int(ib[noff])
				os = fmt.Sprintf(hexFmt, os, nbi)
			} else {
				os = os + "  " // Add two blanks here
			}
			if normLineLen < len(os) {
				normLineLen = len(os)
			}
			noff++
			if noff > br {
				// os = os + "          " // Alignment need fixed.
				os = os + " " // End of left (center) data
				lastLineLen = len(os) - 1
				break leftFor
			}
		}
		os = os + " " // Blank at end of inner
	}
	fmt.Print(os)
	if lastLineLen < normLineLen {
		fmt.Print(pad[:normLineLen-lastLineLen])
	}
	// fmt.Println("lll", lastLineLen, "nll", normLineLen)
	fmt.Print(edgeMark)
}

func printRightBuffer(br int, ib []byte) {
	bb := blankBuf(br)
	for i := 0; i < br; i++ {
		bb[i] = ib[i]
		if bb[i] < byte(0x20) {
			bb[i] = byte('.')
		}
	}
	fmt.Print(string(bb))
	fmt.Print(edgeMark)
}

func main() {
	flag.Parse() // Parse all flags

	if h {
		flag.PrintDefaults()
		return
	}
	if version {
		fmt.Printf("hd Version: %s\n", Version)
		return
	}

	if !quiet {
		fmt.Println("DumpFile Starts....")
	}
	// fmt.Println("Line Length:", lineLen)
	r := getReader()
	if goDump {
		goFormatDump(r)
		if !quiet {
			fmt.Println("DumpFile Ends....")
		}
		return
	}
	if offEnd > 0 && offEnd <= offBegin {
		fmt.Printf("Offset Error: offEnd(%d) must be > offBegin(%d)\n",
			offEnd, offBegin)
		if !quiet {
			fmt.Println("DumpFile Ends, RC:", 2)
		}
		os.Exit(2)
	}
	//
	roff := offBegin
	needcr := false
	for {
		readLen := lineLen
		if offEnd > 0 && roff+readLen > offEnd {
			readLen = offEnd - roff + 1
		}
		ib := blankBuf(readLen)
		// fmt.Println("ReadLen is now:", readLen)
		br, _ := io.ReadAtLeast(r, ib, readLen)
		// fmt.Println("Actual Read Length:", br)
		if br == 0 {
			break
		}
		printOffset(roff)
		printLeftBuffer(br, ib)
		printRightBuffer(br, ib)
		roff += lineLen
		if offEnd > 0 && roff > offEnd {
			needcr = true
			break
		}
		fmt.Println()
	}
	if needcr {
		fmt.Println()
	}
	if !quiet {
		fmt.Println("DumpFile Ends....")
	}
}
<|endoftext|>