<commit_before>package pdfhandler

import (
	"archive/zip"
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/satori/go.uuid"
)

var (
	acceptedContentTypes = []string{
		"application/zip",
		"application/pdf",
	}
	logger StdLogger
)

func init() {
	logger = &noOpLogger{}
}

type StdLogger interface {
	Debugf(string, ...interface{})
	Errorf(string, ...interface{})
}

type noOpLogger struct{}

func (l *noOpLogger) Debugf(format string, args ...interface{}) {
}
func (l *noOpLogger) Errorf(format string, args ...interface{}) {
}

func Error(w http.ResponseWriter, error string, code int) {
	logger.Errorf("http error: %q", error)
	http.Error(w, error, code)
}

func SetLogger(l StdLogger) {
	logger = l
}

type PDFHandler struct {
	filePath string
}

func New(path string) (*PDFHandler, error) {
	_, err := os.Stat(path)
	if err != nil {
		return nil, err
	}
	return &PDFHandler{path}, nil
}

func (ph PDFHandler) multi(mimetype string, pdfs []PDF, w http.ResponseWriter) error {
	dir, err := ioutil.TempDir("", "workpath")
	if err != nil {
		return err
	}
	defer os.RemoveAll(dir)

	type job struct {
		Pdf  PDF
		File string
		Body []byte
	}
	ch := make(chan job)

	go func() {
		var wg sync.WaitGroup
		for idx, p := range pdfs {
			wg.Add(1)
			go func(idx int, p PDF) {
				defer wg.Done()
				tmpfn := filepath.Join(dir, fmt.Sprintf("%d.pdf", idx))
				logger.Debugf("Rendering %s/%s to %s", ph.filePath, p.FileName, tmpfn)
				b, err := p.render(ph.filePath)
				if err != nil {
					return
				}
				ioutil.WriteFile(tmpfn, b, 0777)
				ch <- job{p, tmpfn, b}
			}(idx, p)
		}
		wg.Wait()
		close(ch)
	}()

	switch mimetype {
	case "application/pdf":
		cmd := exec.Command("pdftk")
		files := []string{}
		for j := range ch {
			files = append(files, j.File)
		}
		sort.Strings(files)
		for _, f := range files {
			cmd.Args = append(cmd.Args, f)
		}
		cmd.Args = append(cmd.Args, "cat")
		cmd.Args = append(cmd.Args, "output")
		cmd.Args = append(cmd.Args, "-")
		logger.Debugf("Executing pdftk: %q", strings.Join(cmd.Args, " "))
		var out bytes.Buffer
		cmd.Stdout = &out
		var t bytes.Buffer
		cmd.Stderr = &t
		err = cmd.Run()
		if err != nil {
			return errors.New(t.String())
		}
		w.Write(out.Bytes())
		return nil
	case "application/zip":
		zw := zip.NewWriter(w)
		for j := range ch {
			header := &zip.FileHeader{
				Name:   j.Pdf.FileName,
				Method: zip.Deflate,
			}
			header.SetModTime(time.Now())
			f, err := zw.CreateHeader(header)
			if err != nil {
				return err
			}
			_, err = f.Write(j.Body)
			if err != nil {
				return err
			}
		}
		return zw.Close()
	}
	return nil
}

func (p PDFHandler) get(w http.ResponseWriter, req *http.Request) {
	files, err := ioutil.ReadDir(p.filePath)
	if err != nil {
		Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	ch := make(chan PDF)

	go func() {
		var wg sync.WaitGroup
		for _, file := range files {
			if !strings.HasSuffix(file.Name(), ".pdf") {
				continue
			}
			wg.Add(1)
			go func(fp string) {
				p, err := readFields(p.filePath, fp)
				if err == nil {
					ch <- *p
				}
				defer wg.Done()
			}(file.Name())
		}
		wg.Wait()
		close(ch)
	}()

	pdfs := []PDF{}
	for p := range ch {
		pdfs = append(pdfs, p)
	}

	enc := json.NewEncoder(w)
	err = enc.Encode(pdfs)
	if err != nil {
		Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

}

func (p PDFHandler) post(w http.ResponseWriter, req *http.Request) {
	ct := req.Header.Get("Content-Type")
	if ct != "application/json" {
		Error(w, "Invalid Content-Type", http.StatusBadRequest)
		return
	}

	ac := req.Header.Get("Accept")
	if !stringInSlice(ac, acceptedContentTypes) {
		Error(w, "Invalid Accept header", http.StatusBadRequest)
		return
	}

	filename := req.Header.Get("X-Filename")
	if filename == "" {
		uid, err := uuid.NewV4()
		if err != nil {
			filename = "unknown"
		} else {
			filename = uid.String()
		}
	}
	if strings.HasSuffix(ac, "zip") {
		filename += ".zip"
	} else if strings.HasSuffix(ac, "pdf") {
		filename += ".pdf"
	}

	r := bufio.NewReader(req.Body)
	dec := json.NewDecoder(r)
	ch, _ := r.Peek(1)

	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
	w.Header().Set("Content-Type", ac)

	switch string(ch) {
	case "{":
		var x PDF
		err := dec.Decode(&x)
		if err != nil {
			Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		out, err := x.render(p.filePath)
		if err != nil {
			Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Write(out)
		break
	case "[":
		var pdfs []PDF
		err := dec.Decode(&pdfs)
		if err != nil {
			Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		err = p.multi(ac, pdfs, w)
		if err != nil {
			Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		break
	default:
		Error(w, "Invalid input", http.StatusBadRequest)
		return
	}
}

func (p PDFHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	switch req.Method {
	case "GET":
		p.get(w, req)
	case "POST":
		p.post(w, req)
	default:
		Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}
}
<commit_msg>Fix bug after updating library<commit_after>package pdfhandler

import (
	"archive/zip"
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/satori/go.uuid"
)

var (
	acceptedContentTypes = []string{
		"application/zip",
		"application/pdf",
	}
	logger StdLogger
)

func init() {
	logger = &noOpLogger{}
}

type StdLogger interface {
	Debugf(string, ...interface{})
	Errorf(string, ...interface{})
}

type noOpLogger struct{}

func (l *noOpLogger) Debugf(format string, args ...interface{}) {
}
func (l *noOpLogger) Errorf(format string, args ...interface{}) {
}

func Error(w http.ResponseWriter, error string, code int) {
	logger.Errorf("http error: %q", error)
	http.Error(w, error, code)
}

func SetLogger(l StdLogger) {
	logger = l
}

type PDFHandler struct {
	filePath string
}

func New(path string) (*PDFHandler, error) {
	_, err := os.Stat(path)
	if err != nil {
		return nil, err
	}
	return &PDFHandler{path}, nil
}

func (ph PDFHandler) multi(mimetype string, pdfs []PDF, w http.ResponseWriter) error {
	dir, err := ioutil.TempDir("", "workpath")
	if err != nil {
		return err
	}
	defer os.RemoveAll(dir)

	type job struct {
		Pdf  PDF
		File string
		Body []byte
	}
	ch := make(chan job)

	go func() {
		var wg sync.WaitGroup
		for idx, p := range pdfs {
			wg.Add(1)
			go func(idx int, p PDF) {
				defer wg.Done()
				tmpfn := filepath.Join(dir, fmt.Sprintf("%d.pdf", idx))
				logger.Debugf("Rendering %s/%s to %s", ph.filePath, p.FileName, tmpfn)
				b, err := p.render(ph.filePath)
				if err != nil {
					return
				}
				ioutil.WriteFile(tmpfn, b, 0777)
				ch <- job{p, tmpfn, b}
			}(idx, p)
		}
		wg.Wait()
		close(ch)
	}()

	switch mimetype {
	case "application/pdf":
		cmd := exec.Command("pdftk")
		files := []string{}
		for j := range ch {
			files = append(files, j.File)
		}
		sort.Strings(files)
		for _, f := range files {
			cmd.Args = append(cmd.Args, f)
		}
		cmd.Args = append(cmd.Args, "cat")
		cmd.Args = append(cmd.Args, "output")
		cmd.Args = append(cmd.Args, "-")
		logger.Debugf("Executing pdftk: %q", strings.Join(cmd.Args, " "))
		var out bytes.Buffer
		cmd.Stdout = &out
		var t bytes.Buffer
		cmd.Stderr = &t
		err = cmd.Run()
		if err != nil {
			return errors.New(t.String())
		}
		w.Write(out.Bytes())
		return nil
	case "application/zip":
		zw := zip.NewWriter(w)
		for j := range ch {
			header := &zip.FileHeader{
				Name:   j.Pdf.FileName,
				Method: zip.Deflate,
			}
			header.SetModTime(time.Now())
			f, err := zw.CreateHeader(header)
			if err != nil {
				return err
			}
			_, err = f.Write(j.Body)
			if err != nil {
				return err
			}
		}
		return zw.Close()
	}
	return nil
}

func (p PDFHandler) get(w http.ResponseWriter, req *http.Request) {
	files, err := ioutil.ReadDir(p.filePath)
	if err != nil {
		Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	ch := make(chan PDF)

	go func() {
		var wg sync.WaitGroup
		for _, file := range files {
			if !strings.HasSuffix(file.Name(), ".pdf") {
				continue
			}
			wg.Add(1)
			go func(fp string) {
				p, err := readFields(p.filePath, fp)
				if err == nil {
					ch <- *p
				}
				defer wg.Done()
			}(file.Name())
		}
		wg.Wait()
		close(ch)
	}()

	pdfs := []PDF{}
	for p := range ch {
		pdfs = append(pdfs, p)
	}

	enc := json.NewEncoder(w)
	err = enc.Encode(pdfs)
	if err != nil {
		Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

}

func (p PDFHandler) post(w http.ResponseWriter, req *http.Request) {
	ct := req.Header.Get("Content-Type")
	if ct != "application/json" {
		Error(w, "Invalid Content-Type", http.StatusBadRequest)
		return
	}

	ac := req.Header.Get("Accept")
	if !stringInSlice(ac, acceptedContentTypes) {
		Error(w, "Invalid Accept header", http.StatusBadRequest)
		return
	}

	filename := req.Header.Get("X-Filename")
	if filename == "" {
		uid := uuid.NewV4()
		filename = uid.String()
	}
	if strings.HasSuffix(ac, "zip") {
		filename += ".zip"
	} else if strings.HasSuffix(ac, "pdf") {
		filename += ".pdf"
	}

	r := bufio.NewReader(req.Body)
	dec := json.NewDecoder(r)
	ch, _ := r.Peek(1)

	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
	w.Header().Set("Content-Type", ac)

	switch string(ch) {
	case "{":
		var x PDF
		err := dec.Decode(&x)
		if err != nil {
			Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		out, err := x.render(p.filePath)
		if err != nil {
			Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Write(out)
		break
	case "[":
		var pdfs []PDF
		err := dec.Decode(&pdfs)
		if err != nil {
			Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		err = p.multi(ac, pdfs, w)
		if err != nil {
			Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		break
	default:
		Error(w, "Invalid input", http.StatusBadRequest)
		return
	}
}

func (p PDFHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	switch req.Method {
	case "GET":
		p.get(w, req)
	case "POST":
		p.post(w, req)
	default:
		Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}
}
<|endoftext|>


<commit_before>package qbit

func Session(engine Engine) *session {
	return &session{
		engine: engine,
	}
}

type session struct {
	engine Engine
}
<commit_msg>fix Session func into NewSession, add Engine() function<commit_after>package qbit

func NewSession(engine *Engine) *session {
	return &session{
		engine: engine,
	}
}

type session struct {
	engine *Engine
}

func (s *session) Engine() *Engine {
	return s.engine
}
<|endoftext|>


<commit_before>package hpdf

/*
#cgo LDFLAGS: -lhpdf -lpng -lz
#include "hpdf.h"
*/
import "C"
import (
	"runtime"
	"time"
	"unsafe"
)

type PDF struct {
	doc C.HPDF_Doc
}

func finalizePDF(pdf *PDF) {
	pdf.FreeDocAll()
	pdf.Free()
}

func New() (*PDF, error) {
	doc := C.HPDF_New(nil, nil)

	if err := C.HPDF_GetError(doc); err != C.HPDF_OK {
		return nil, NewError(err, 0)
	}

	pdf := &PDF{doc}
	runtime.SetFinalizer(pdf, finalizePDF)
	return pdf, nil
}

func (pdf *PDF) GetLastError() error {
	defer C.HPDF_ResetError(pdf.doc)
	if err := C.HPDF_GetError(pdf.doc); err != C.HPDF_OK {
		detail := C.HPDF_GetErrorDetail(pdf.doc)
		return NewError(err, detail)
	}
	return nil
}

func (pdf *PDF) Free() {
	C.HPDF_Free(pdf.doc)
}

func (pdf *PDF) NewDoc() error {
	C.HPDF_NewDoc(pdf.doc)
	return pdf.GetLastError()
}

func (pdf *PDF) FreeDoc() {
	C.HPDF_FreeDoc(pdf.doc)
}

func (pdf *PDF) FreeDocAll() {
	C.HPDF_FreeDocAll(pdf.doc)
}

func (pdf *PDF) SaveToFile(fn string) error {
	cfn := C.CString(fn)
	C.HPDF_SaveToFile(pdf.doc, cfn)
	C.free(unsafe.Pointer(cfn))

	return pdf.GetLastError()
}

func (pdf *PDF) HasDoc() bool {
	return C.HPDF_HasDoc(pdf.doc) == C.HPDF_TRUE
}

func (pdf *PDF) SetPagesConfiguration(page_per_pages uint) error {
	C.HPDF_SetPagesConfiguration(pdf.doc, C.HPDF_UINT(page_per_pages))
	return pdf.GetLastError()
}

func (pdf *PDF) SetPageLayout(layout PageLayout) error {
	C.HPDF_SetPageLayout(pdf.doc, C.HPDF_PageLayout(layout))
	return pdf.GetLastError()
}

func (pdf *PDF) GetPageLayout() PageLayout {
	return PageLayout(C.HPDF_GetPageLayout(pdf.doc))
}

func (pdf *PDF) SetPageMode(pageMode PageMode) error {
	C.HPDF_SetPageMode(pdf.doc, C.HPDF_PageMode(pageMode))
	return pdf.GetLastError()
}

func (pdf *PDF) GetPageMode() PageMode {
	return PageMode(C.HPDF_GetPageMode(pdf.doc))
}

func (pdf *PDF) SetOpenAction(destination *Destination) error {
	C.HPDF_SetOpenAction(pdf.doc, destination.destination)
	return pdf.GetLastError()
}

func (pdf *PDF) AddPageLabel(
	pageNum uint, pageNumStyle PageNumStyle, firstPage uint, prefix string,
) error {
	cprefix := C.CString(prefix)
	C.HPDF_AddPageLabel(
		pdf.doc,
		C.HPDF_UINT(pageNum),
		C.HPDF_PageNumStyle(pageNumStyle),
		C.HPDF_UINT(firstPage),
		cprefix,
	)
	C.free(unsafe.Pointer(cprefix))
	return nil
}

func (pdf *PDF) SetInfoAttr(infoType InfoType, value string) error {
	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))
	C.HPDF_SetInfoAttr(pdf.doc, C.HPDF_InfoType(infoType), cvalue)
	return pdf.GetLastError()
}

func (pdf *PDF) GetInfoAttr(infoType InfoType) (string, error) {
	cvalue := C.HPDF_GetInfoAttr(pdf.doc, C.HPDF_InfoType(infoType))

	if cvalue != nil {
		defer C.free(unsafe.Pointer(cvalue))
		return C.GoString(cvalue), nil
	} else {
		return "", pdf.GetLastError()
	}
}

func (pdf *PDF) SetInfoDateAttr(infoType InfoType, datetime time.Time) error {
	cdate := timeToHPDFDate(datetime)
	C.HPDF_SetInfoDateAttr(pdf.doc, C.HPDF_InfoType(infoType), cdate)
	return pdf.GetLastError()
}

func (pdf *PDF) SetPassword(ownerPassword, userPassword string) error {
	cownerPassword := C.CString(ownerPassword)
	cuserPassword := C.CString(userPassword)
	C.HPDF_SetPassword(pdf.doc, cownerPassword, cuserPassword)
	C.free(unsafe.Pointer(cownerPassword))
	C.free(unsafe.Pointer(cuserPassword))
	return pdf.GetLastError()
}

func (pdf *PDF) SetPermission(permission Permission) error {
	C.HPDF_SetPermission(pdf.doc, C.HPDF_UINT(permission))
	return pdf.GetLastError()
}

func (pdf *PDF) GetViewerPreference() ViewerPreference {
	return ViewerPreference(C.HPDF_GetViewerPreference(pdf.doc))
}

func (pdf *PDF) SetViewerPreference(preference ViewerPreference) error {
	C.HPDF_SetViewerPreference(pdf.doc, C.HPDF_UINT(preference))
	return pdf.GetLastError()
}

func (pdf *PDF) SetEncryptMode(encryptMode EncryptMode, keyLen uint32) error {
	C.HPDF_SetEncryptionMode(
		pdf.doc, C.HPDF_EncryptMode(encryptMode), C.HPDF_UINT(keyLen),
	)
	return pdf.GetLastError()
}

func (pdf *PDF) SetCompressionMode(compressionMode CompressionMode) error {
	C.HPDF_SetCompressionMode(pdf.doc, C.HPDF_UINT(compressionMode))
	return pdf.GetLastError()
}
<commit_msg>Add SaveToStream.<commit_after>package hpdf

/*
#cgo LDFLAGS: -lhpdf -lpng -lz
#include "hpdf.h"
*/
import "C"
import (
	"io"
	"runtime"
	"time"
	"unsafe"
)

type PDF struct {
	doc C.HPDF_Doc
}

func finalizePDF(pdf *PDF) {
	pdf.FreeDocAll()
	pdf.Free()
}

func New() (*PDF, error) {
	doc := C.HPDF_New(nil, nil)

	if err := C.HPDF_GetError(doc); err != C.HPDF_OK {
		return nil, NewError(err, 0)
	}

	pdf := &PDF{doc}
	runtime.SetFinalizer(pdf, finalizePDF)
	return pdf, nil
}

func (pdf *PDF) GetLastError() error {
	defer C.HPDF_ResetError(pdf.doc)
	if err := C.HPDF_GetError(pdf.doc); err != C.HPDF_OK {
		detail := C.HPDF_GetErrorDetail(pdf.doc)
		return NewError(err, detail)
	}
	return nil
}

func (pdf *PDF) Free() {
	C.HPDF_Free(pdf.doc)
}

func (pdf *PDF) NewDoc() error {
	C.HPDF_NewDoc(pdf.doc)
	return pdf.GetLastError()
}

func (pdf *PDF) FreeDoc() {
	C.HPDF_FreeDoc(pdf.doc)
}

func (pdf *PDF) FreeDocAll() {
	C.HPDF_FreeDocAll(pdf.doc)
}

func (pdf *PDF) SaveToFile(fn string) error {
	cfn := C.CString(fn)
	C.HPDF_SaveToFile(pdf.doc, cfn)
	C.free(unsafe.Pointer(cfn))

	return pdf.GetLastError()
}

func (pdf *PDF) SaveToStream(wr io.Writer) error {
	if status := C.HPDF_SaveToStream(pdf.doc); status != C.HPDF_OK {
		return pdf.GetLastError()
	}
	if status := C.HPDF_ResetStream(pdf.doc); status != C.HPDF_OK {
		return pdf.GetLastError()
	}

	const blocksize = 4096
	var buf [blocksize]byte
	ptr := (*C.HPDF_BYTE)((unsafe.Pointer(&buf[0])))
	for {
		size := C.HPDF_UINT32(blocksize)
		C.HPDF_ReadFromStream(pdf.doc, ptr, &size)
		if size == 0 {
			break
		}
		if _, err := wr.Write(buf[:]); err != nil {
			return err
		}
	}

	return pdf.GetLastError()
}

func (pdf *PDF) HasDoc() bool {
	return C.HPDF_HasDoc(pdf.doc) == C.HPDF_TRUE
}

func (pdf *PDF) SetPagesConfiguration(page_per_pages uint) error {
	C.HPDF_SetPagesConfiguration(pdf.doc, C.HPDF_UINT(page_per_pages))
	return pdf.GetLastError()
}

func (pdf *PDF) SetPageLayout(layout PageLayout) error {
	C.HPDF_SetPageLayout(pdf.doc, C.HPDF_PageLayout(layout))
	return pdf.GetLastError()
}

func (pdf *PDF) GetPageLayout() PageLayout {
	return PageLayout(C.HPDF_GetPageLayout(pdf.doc))
}

func (pdf *PDF) SetPageMode(pageMode PageMode) error {
	C.HPDF_SetPageMode(pdf.doc, C.HPDF_PageMode(pageMode))
	return pdf.GetLastError()
}

func (pdf *PDF) GetPageMode() PageMode {
	return PageMode(C.HPDF_GetPageMode(pdf.doc))
}

func (pdf *PDF) SetOpenAction(destination *Destination) error {
	C.HPDF_SetOpenAction(pdf.doc, destination.destination)
	return pdf.GetLastError()
}

func (pdf *PDF) AddPageLabel(
	pageNum uint, pageNumStyle PageNumStyle, firstPage uint, prefix string,
) error {
	cprefix := C.CString(prefix)
	C.HPDF_AddPageLabel(
		pdf.doc,
		C.HPDF_UINT(pageNum),
		C.HPDF_PageNumStyle(pageNumStyle),
		C.HPDF_UINT(firstPage),
		cprefix,
	)
	C.free(unsafe.Pointer(cprefix))
	return nil
}

func (pdf *PDF) SetInfoAttr(infoType InfoType, value string) error {
	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))
	C.HPDF_SetInfoAttr(pdf.doc, C.HPDF_InfoType(infoType), cvalue)
	return pdf.GetLastError()
}

func (pdf *PDF) GetInfoAttr(infoType InfoType) (string, error) {
	cvalue := C.HPDF_GetInfoAttr(pdf.doc, C.HPDF_InfoType(infoType))

	if cvalue != nil {
		defer C.free(unsafe.Pointer(cvalue))
		return C.GoString(cvalue), nil
	} else {
		return "", pdf.GetLastError()
	}
}

func (pdf *PDF) SetInfoDateAttr(infoType InfoType, datetime time.Time) error {
	cdate := timeToHPDFDate(datetime)
	C.HPDF_SetInfoDateAttr(pdf.doc, C.HPDF_InfoType(infoType), cdate)
	return pdf.GetLastError()
}

func (pdf *PDF) SetPassword(ownerPassword, userPassword string) error {
	cownerPassword := C.CString(ownerPassword)
	cuserPassword := C.CString(userPassword)
	C.HPDF_SetPassword(pdf.doc, cownerPassword, cuserPassword)
	C.free(unsafe.Pointer(cownerPassword))
	C.free(unsafe.Pointer(cuserPassword))
	return pdf.GetLastError()
}

func (pdf *PDF) SetPermission(permission Permission) error {
	C.HPDF_SetPermission(pdf.doc, C.HPDF_UINT(permission))
	return pdf.GetLastError()
}

func (pdf *PDF) GetViewerPreference() ViewerPreference {
	return ViewerPreference(C.HPDF_GetViewerPreference(pdf.doc))
}

func (pdf *PDF) SetViewerPreference(preference ViewerPreference) error {
	C.HPDF_SetViewerPreference(pdf.doc, C.HPDF_UINT(preference))
	return pdf.GetLastError()
}

func (pdf *PDF) SetEncryptMode(encryptMode EncryptMode, keyLen uint32) error {
	C.HPDF_SetEncryptionMode(
		pdf.doc, C.HPDF_EncryptMode(encryptMode), C.HPDF_UINT(keyLen),
	)
	return pdf.GetLastError()
}

func (pdf *PDF) SetCompressionMode(compressionMode CompressionMode) error {
	C.HPDF_SetCompressionMode(pdf.doc, C.HPDF_UINT(compressionMode))
	return pdf.GetLastError()
}
<|endoftext|>


<commit_before>/*

   Copyright 2016 Wenhui Shen <www.webx.top>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/
package echo

var DefaultNopSession = &NopSession{}

// Options stores configuration for a session or session store.
// Fields are a subset of http.Cookie fields.
type SessionOptions struct {
	Engine string //Store Engine
	Name   string //Session Name
	*CookieOptions
}

// Wraps thinly gorilla-session methods.
// Session stores the values and optional configuration for a session.
type Session interface {
	// Get returns the session value associated to the given key.
	Get(key string) interface{}
	// Set sets the session value associated to the given key.
	Set(key string, val interface{}) Session
	SetId(id string) Session
	Id() string
	// Delete removes the session value associated to the given key.
	Delete(key string) Session
	// Clear deletes all values in the session.
	Clear() Session
	// AddFlash adds a flash message to the session.
	// A single variadic argument is accepted, and it is optional: it defines the flash key.
	// If not defined "_flash" is used by default.
	AddFlash(value interface{}, vars ...string) Session
	// Flashes returns a slice of flash messages from the session.
	// A single variadic argument is accepted, and it is optional: it defines the flash key.
	// If not defined "_flash" is used by default.
	Flashes(vars ...string) []interface{}

	Options(SessionOptions) Session

	// Save saves all sessions used during the current request.
	Save() error
}

type NopSession struct {
}

func (n *NopSession) Get(_ string) interface{} {
	return nil
}

func (n *NopSession) Set(_ string, _ interface{}) Session {
	return n
}

func (n *NopSession) SetId(_ string) Session {
	return n
}

func (n *NopSession) Id() string {
	return ``
}

func (n *NopSession) Delete(_ string) Session {
	return n
}

func (n *NopSession) Clear() Session {
	return n
}

func (n *NopSession) AddFlash(_ interface{}, _ ...string) Session {
	return n
}

func (n *NopSession) Flashes(_ ...string) []interface{} {
	return []interface{}{}
}

func (n *NopSession) Options(_ SessionOptions) Session {
	return n
}

func (n *NopSession) Save() error {
	return nil
}
<commit_msg>update<commit_after>/*

   Copyright 2016 Wenhui Shen <www.webx.top>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/
package echo

var DefaultNopSession Session = &NopSession{}

// Options stores configuration for a session or session store.
// Fields are a subset of http.Cookie fields.
type SessionOptions struct {
	Engine string //Store Engine
	Name   string //Session Name
	*CookieOptions
}

// Wraps thinly gorilla-session methods.
// Session stores the values and optional configuration for a session.
type Session interface {
	// Get returns the session value associated to the given key.
	Get(key string) interface{}
	// Set sets the session value associated to the given key.
	Set(key string, val interface{}) Session
	SetId(id string) Session
	Id() string
	// Delete removes the session value associated to the given key.
	Delete(key string) Session
	// Clear deletes all values in the session.
	Clear() Session
	// AddFlash adds a flash message to the session.
	// A single variadic argument is accepted, and it is optional: it defines the flash key.
	// If not defined "_flash" is used by default.
	AddFlash(value interface{}, vars ...string) Session
	// Flashes returns a slice of flash messages from the session.
	// A single variadic argument is accepted, and it is optional: it defines the flash key.
	// If not defined "_flash" is used by default.
	Flashes(vars ...string) []interface{}

	Options(SessionOptions) Session

	// Save saves all sessions used during the current request.
	Save() error
}

type NopSession struct {
}

func (n *NopSession) Get(_ string) interface{} {
	return nil
}

func (n *NopSession) Set(_ string, _ interface{}) Session {
	return n
}

func (n *NopSession) SetId(_ string) Session {
	return n
}

func (n *NopSession) Id() string {
	return ``
}

func (n *NopSession) Delete(_ string) Session {
	return n
}

func (n *NopSession) Clear() Session {
	return n
}

func (n *NopSession) AddFlash(_ interface{}, _ ...string) Session {
	return n
}

func (n *NopSession) Flashes(_ ...string) []interface{} {
	return []interface{}{}
}

func (n *NopSession) Options(_ SessionOptions) Session {
	return n
}

func (n *NopSession) Save() error {
	return nil
}
<|endoftext|>


<commit_before>package httptreemux

import (
	"net/http"
)

func SimplePanicHandler(w http.ResponseWriter, r *http.Request, err interface{}) {
	w.WriteHeader(http.StatusInternalServerError)
}

// This was taken from github.com/gocraft/web, which adapted it from the Traffic project.
<commit_msg>Add panic handler from gocraft/web<commit_after>package httptreemux

import (
	"bufio"
	"html/template"
	"net/http"
	"os"
	"runtime"
	"strings"
)

func SimplePanicHandler(w http.ResponseWriter, r *http.Request, err interface{}) {
	w.WriteHeader(http.StatusInternalServerError)
}

// Everything below was taken from github.com/gocraft/web, which adapted it from the Traffic project.
func ShowErrorsPanicHandler(w http.ResponseWriter, r *http.Request, err interface{}) {
	const size = 4096
	stack := make([]byte, size)
	stack = stack[:runtime.Stack(stack, false)]
}

func renderPrettyError(rw http.ResponseWriter, req *http.Request, err interface{}, stack []byte) {
	_, filePath, line, _ := runtime.Caller(5)

	data := map[string]interface{}{
		"Error":    err,
		"Stack":    string(stack),
		"Params":   req.URL.Query(),
		"Method":   req.Method,
		"FilePath": filePath,
		"Line":     line,
		"Lines":    readErrorFileLines(filePath, line),
	}

	rw.Header().Set("Content-Type", "text/html")
	rw.WriteHeader(http.StatusInternalServerError)

	tpl := template.Must(template.New("ErrorPage").Parse(panicPageTpl))
	tpl.Execute(rw, data)
}

func readErrorFileLines(filePath string, errorLine int) map[int]string {
	lines := make(map[int]string)

	file, err := os.Open(filePath)
	if err != nil {
		return lines
	}

	defer file.Close()

	reader := bufio.NewReader(file)
	currentLine := 0
	for {
		line, err := reader.ReadString('\n')
		if err != nil || currentLine > errorLine+5 {
			break
		}

		currentLine++

		if currentLine >= errorLine-5 {
			lines[currentLine] = strings.Replace(line, "\n", "", -1)
		}
	}

	return lines
}

const panicPageTpl string = `

      <title>Panic</title>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

Error

{{ .Error }}

        In {{ .FilePath }}:{{ .Line }}

{{ range $lineNumber, $line :=  .Lines }}{{ $lineNumber }}{{ end }}

{{ range $lineNumber, $line :=  .Lines }}{{ $line }}
{{ end }}

Stack

{{ .Stack }}

Request

Method: {{ .Method }}

Paramenters:

        {{ range $key, $value := .Params }}

{{ $key }}: {{ $value }}

        {{ end }}

  `
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/xml"
	"flag"
	irc "github.com/fluffle/goirc/client"
	_ "github.com/go-sql-driver/mysql"
	"github.com/tv42/base58"
	"html"
	"io/ioutil"
	"log"
	"math/big"
	"math/rand"
	"net/http"
	"regexp"
	"strings"
	"time"
)

type Config struct {
	Channel      string
	DBConn       string
	Nick         string
	Ident        string
	FullName     string
	FlickrAPIKey string
	IRCPass      string
}

var (
	config             Config
	urlRegex, regexErr = regexp.Compile(`(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s` + "`" + `!()\[\]{};:'".,<>?«»“”‘’]))`)
	helpstring         = "Available commands are !hacking, !help, and !haata"
)

type Setresp struct {
	Sets []Set `xml:"collections>collection>set"`
}

type Set struct {
	Id          string `xml:"id,attr"`
	Title       string `xml:"title,attr"`
	Description string `xml:"description,attr"`
}

type Photoresp struct {
	Photos []Photo `xml:"photoset>photo"`
}

type Photo struct {
	Id        int64  `xml:"id,attr"`
	Secret    string `xml:"secret,attr"`
	Server    string `xml:"server,attr"`
	Farm      string `xml:"farm,attr"`
	Title     string `xml:"title,attr"`
	Isprimary string `xml:"isprimary,attr"`
}

func htmlfetch(url string) ([]byte, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	respbody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return respbody, nil
}

func random(limit int) int {
	rand.Seed(time.Now().Unix())
	return rand.Intn(limit)
}

func sendUrl(channel, url string, conn *irc.Conn) {
	if !strings.HasPrefix(url, "http://") {
		url = "http://" + url
	}
	resp, err := http.Get(url)
	if err != nil {
		return
	}
	respbody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	stringbody := string(respbody)
	titlestart := strings.Index(stringbody, "<title>")
	titleend := strings.Index(stringbody, "</title>")
	if titlestart != -1 && titlestart != -1 {
		title := string(respbody[titlestart+7 : titleend])
		title = strings.TrimSpace(title)
		if title != "" {
			title = "Title: " + html.UnescapeString(title) + " (" + url + ")"
			conn.Privmsg(channel, title)
		}
	}
}

func dance(channel string, conn *irc.Conn) {
	conn.Privmsg(channel, ":D-<")
	time.Sleep(500 * time.Millisecond)
	conn.Privmsg(channel, ":D|<")
	time.Sleep(500 * time.Millisecond)
	conn.Privmsg(channel, ":D/<")
}

func haata(channel string, conn *irc.Conn) {
	sets, err := htmlfetch(`http://api.flickr.com/services/rest/?method=flickr.collections.getTree&api_key=` + config.FlickrAPIKey + `&user_id=57321699@N06&collection_id=57276377-72157635417889224`)
	if err != nil {
		return
	}
	var setresp Setresp
	xml.Unmarshal(sets, &setresp)
	randsetindex := random(len(setresp.Sets))
	randset := setresp.Sets[randsetindex].Id

	pics, err := htmlfetch(`http://api.flickr.com/services/rest/?method=flickr.photosets.getPhotos&api_key=` + config.FlickrAPIKey + `&photoset_id=` + randset)
	if err != nil {
		return
	}
	var photoresp Photoresp
	xml.Unmarshal(pics, &photoresp)
	randpic := random(len(photoresp.Photos))
	returnbytes := []byte{}
	photostring := string(base58.EncodeBig(returnbytes, big.NewInt(photoresp.Photos[randpic].Id)))
	conn.Privmsg(channel, `http://flic.kr/p/`+photostring)
}

func handleMessage(conn *irc.Conn, line *irc.Line) {
	urllist := []string{}
	numlinks := 0

	if strings.HasPrefix(line.Args[1], "!dance") && line.Nick == "sadbox" {
		go dance(line.Args[0], conn)
	} else if strings.HasPrefix(line.Args[1], "!audio") && line.Nick == "sadbox" {
		conn.Privmsg(line.Args[0], "https://sadbox.org/static/audiophile.html")
	} else if strings.HasPrefix(line.Args[1], "!hacking") {
		conn.Privmsg(line.Args[0], "This channel is about keyboards (not hacking), please read the topic.")
	} else if strings.HasPrefix(line.Args[1], "!help") {
		conn.Privmsg(line.Args[0], helpstring)
	} else if strings.HasPrefix(line.Args[1], "!haata") {
		go haata(line.Args[0], conn)
	}

NextWord:
	for _, word := range strings.Split(line.Args[1], " ") {
		word = strings.TrimSpace(word)
		if urlRegex.MatchString(word) {
			for _, subUrl := range urllist {
				if subUrl == word {
					continue NextWord
				}
			}
			numlinks++
			if numlinks > 3 {
				break
			}
			urllist = append(urllist, word)
			go sendUrl(line.Args[0], word, conn)
		}

	}
	db, err := sql.Open("mysql", config.DBConn)
	if err != nil {
		log.Println(err)
	}
	defer db.Close()
	_, err = db.Exec("insert into messages (Nick, Ident, Host, Src, Cmd, Channel, Message, Time) values (?, ?, ?, ?, ?, ?, ?, ?)", line.Nick, line.Ident, line.Host, line.Src, line.Cmd, line.Args[0], line.Args[1], line.Time)
	if err != nil {
		log.Println(err)
	}
}

func init() {
	log.Println("Starting sadbot")

	flag.Parse()

	if regexErr != nil {
		log.Panic(regexErr)
	}

	xmlFile, err := ioutil.ReadFile("config.xml")
	if err != nil {
		log.Fatal(err)
	}
	xml.Unmarshal(xmlFile, &config)
}

func main() {
	log.Printf("Joining channel %s", config.Channel)
	log.Printf("Nick: %s Ident: %s FullName: %s", config.Nick, config.Ident, config.FullName)

	c := irc.SimpleClient(config.Nick, config.Ident, config.FullName)

	c.AddHandler(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			conn.Join(config.Channel)
			conn.Privmsg("nickserv", "identify "+config.Nick+" "+config.IRCPass)
			log.Println("Connected!")
		})

	quit := make(chan bool)

	c.AddHandler(irc.DISCONNECTED,
		func(conn *irc.Conn, line *irc.Line) { quit <- true })

	c.AddHandler("PRIVMSG", handleMessage)

	if err := c.Connect("irc.freenode.net"); err != nil {
		log.Fatalln("Connection error: %s\n", err)
	}

	<-quit
}
<commit_msg>Now gives title of flickr set<commit_after>package main

import (
	"database/sql"
	"encoding/xml"
	"flag"
	irc "github.com/fluffle/goirc/client"
	_ "github.com/go-sql-driver/mysql"
	"github.com/tv42/base58"
	"html"
	"io/ioutil"
	"log"
	"math/big"
	"math/rand"
	"net/http"
	"regexp"
	"strings"
	"time"
)

type Config struct {
	Channel      string
	DBConn       string
	Nick         string
	Ident        string
	FullName     string
	FlickrAPIKey string
	IRCPass      string
}

var (
	config             Config
	urlRegex, regexErr = regexp.Compile(`(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s` + "`" + `!()\[\]{};:'".,<>?«»“”‘’]))`)
	helpstring         = "Available commands are !hacking, !help, and !haata"
)

type Setresp struct {
	Sets []Set `xml:"collections>collection>set"`
}

type Set struct {
	Id          string `xml:"id,attr"`
	Title       string `xml:"title,attr"`
	Description string `xml:"description,attr"`
}

type Photoresp struct {
	Photos []Photo `xml:"photoset>photo"`
}

type Photo struct {
	Id        int64  `xml:"id,attr"`
	Secret    string `xml:"secret,attr"`
	Server    string `xml:"server,attr"`
	Farm      string `xml:"farm,attr"`
	Title     string `xml:"title,attr"`
	Isprimary string `xml:"isprimary,attr"`
}

func htmlfetch(url string) ([]byte, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	respbody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return respbody, nil
}

func random(limit int) int {
	rand.Seed(time.Now().Unix())
	return rand.Intn(limit)
}

func sendUrl(channel, url string, conn *irc.Conn) {
	if !strings.HasPrefix(url, "http://") {
		url = "http://" + url
	}
	resp, err := http.Get(url)
	if err != nil {
		return
	}
	respbody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	stringbody := string(respbody)
	titlestart := strings.Index(stringbody, "<title>")
	titleend := strings.Index(stringbody, "</title>")
	if titlestart != -1 && titlestart != -1 {
		title := string(respbody[titlestart+7 : titleend])
		title = strings.TrimSpace(title)
		if title != "" {
			title = "Title: " + html.UnescapeString(title) + " (" + url + ")"
			conn.Privmsg(channel, title)
		}
	}
}

func dance(channel string, conn *irc.Conn) {
	conn.Privmsg(channel, ":D-<")
	time.Sleep(500 * time.Millisecond)
	conn.Privmsg(channel, ":D|<")
	time.Sleep(500 * time.Millisecond)
	conn.Privmsg(channel, ":D/<")
}

func haata(channel string, conn *irc.Conn) {
	sets, err := htmlfetch(`http://api.flickr.com/services/rest/?method=flickr.collections.getTree&api_key=` + config.FlickrAPIKey + `&user_id=57321699@N06&collection_id=57276377-72157635417889224`)
	if err != nil {
		return
	}
	var setresp Setresp
	xml.Unmarshal(sets, &setresp)
	randsetindex := random(len(setresp.Sets))
	randset := setresp.Sets[randsetindex].Id

	pics, err := htmlfetch(`http://api.flickr.com/services/rest/?method=flickr.photosets.getPhotos&api_key=` + config.FlickrAPIKey + `&photoset_id=` + randset)
	if err != nil {
		return
	}
	var photoresp Photoresp
	xml.Unmarshal(pics, &photoresp)
	randpic := random(len(photoresp.Photos))
	returnbytes := []byte{}
	photostring := string(base58.EncodeBig(returnbytes, big.NewInt(photoresp.Photos[randpic].Id)))
	conn.Privmsg(channel, strings.TrimSpace(setresp.Sets[randsetindex].Title)+`: http://flic.kr/p/`+photostring)
}

func handleMessage(conn *irc.Conn, line *irc.Line) {
	urllist := []string{}
	numlinks := 0

	if strings.HasPrefix(line.Args[1], "!dance") && line.Nick == "sadbox" {
		go dance(line.Args[0], conn)
	} else if strings.HasPrefix(line.Args[1], "!audio") && line.Nick == "sadbox" {
		conn.Privmsg(line.Args[0], "https://sadbox.org/static/audiophile.html")
	} else if strings.HasPrefix(line.Args[1], "!hacking") {
		conn.Privmsg(line.Args[0], "This channel is about keyboards (not hacking), please read the topic.")
	} else if strings.HasPrefix(line.Args[1], "!help") {
		conn.Privmsg(line.Args[0], helpstring)
	} else if strings.HasPrefix(line.Args[1], "!haata") {
		go haata(line.Args[0], conn)
	}

NextWord:
	for _, word := range strings.Split(line.Args[1], " ") {
		word = strings.TrimSpace(word)
		if urlRegex.MatchString(word) {
			for _, subUrl := range urllist {
				if subUrl == word {
					continue NextWord
				}
			}
			numlinks++
			if numlinks > 3 {
				break
			}
			urllist = append(urllist, word)
			go sendUrl(line.Args[0], word, conn)
		}

	}
	db, err := sql.Open("mysql", config.DBConn)
	if err != nil {
		log.Println(err)
	}
	defer db.Close()
	_, err = db.Exec("insert into messages (Nick, Ident, Host, Src, Cmd, Channel, Message, Time) values (?, ?, ?, ?, ?, ?, ?, ?)", line.Nick, line.Ident, line.Host, line.Src, line.Cmd, line.Args[0], line.Args[1], line.Time)
	if err != nil {
		log.Println(err)
	}
}

func init() {
	log.Println("Starting sadbot")

	flag.Parse()

	if regexErr != nil {
		log.Panic(regexErr)
	}

	xmlFile, err := ioutil.ReadFile("config.xml")
	if err != nil {
		log.Fatal(err)
	}
	xml.Unmarshal(xmlFile, &config)
}

func main() {
	log.Printf("Joining channel %s", config.Channel)
	log.Printf("Nick: %s Ident: %s FullName: %s", config.Nick, config.Ident, config.FullName)

	c := irc.SimpleClient(config.Nick, config.Ident, config.FullName)

	c.AddHandler(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			conn.Join(config.Channel)
			conn.Privmsg("nickserv", "identify "+config.Nick+" "+config.IRCPass)
			log.Println("Connected!")
		})

	quit := make(chan bool)

	c.AddHandler(irc.DISCONNECTED,
		func(conn *irc.Conn, line *irc.Line) { quit <- true })

	c.AddHandler("PRIVMSG", handleMessage)

	if err := c.Connect("irc.freenode.net"); err != nil {
		log.Fatalln("Connection error: %s\n", err)
	}

	<-quit
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package sh

import (
	"os"
	"path/filepath"
	"testing"
)

func TestParse(t *testing.T) {
	paths, err := filepath.Glob("testdata/*.sh")
	if err != nil {
		t.Fatal(err)
	}
	for _, path := range paths {
		testParse(t, path)
	}
}

func testParse(t *testing.T, path string) {
	println(path)
	f, err := os.Open(path)
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()
	if err := parse(f, path); err != nil {
		t.Fatalf("Parse error: %v", err)
	}
}
<commit_msg>Add expected parse error tests<commit_after>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package sh

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func TestParse(t *testing.T) {
	paths, err := filepath.Glob("testdata/*.sh")
	if err != nil {
		t.Fatal(err)
	}
	for _, path := range paths {
		testParse(t, path)
	}
}

func testParse(t *testing.T, path string) {
	f, err := os.Open(path)
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()
	if err := parse(f, path); err != nil {
		t.Fatalf("Parse error: %v", err)
	}
}

func TestParseErr(t *testing.T) {
	errs := []string{
		"'",
		";",
		//"{",
		"=",
		"foo(",
		"foo()",
		"foo &&",
		"foo |",
		"foo ||",
		"foo >",
		"foo >>",
		"foo >&",
		"foo <",
	}
	for _, s := range errs {
		r := strings.NewReader(s)
		if err := parse(r, "stdin.go"); err == nil {
			t.Fatalf("Expected error in: %s", s)
		}
	}
}
<|endoftext|>


<commit_before>// Code generated 2019-03-28T16:23:31+0000 DO NOT EDIT.
package main

const shellfn = `
cat /dev/null <<EOF
------------------------------------------------------------------------
https://github.com/client9/shlib - portable posix shell functions
Public domain - http://unlicense.org
https://github.com/client9/shlib/blob/master/LICENSE.md
but credit (and pull requests) appreciated.
------------------------------------------------------------------------
EOF
is_command() {
  command -v "$1" >/dev/null
}
echoerr() {
  echo "$@" 1>&2
}
log_prefix() {
  echo "$0"
}
_logp=6
log_set_priority() {
  _logp="$1"
}
log_priority() {
  if test -z "$1"; then
    echo "$_logp"
    return
  fi
  [ "$1" -le "$_logp" ]
}
log_tag() {
  case $1 in
    0) echo "emerg" ;;
    1) echo "alert" ;;
    2) echo "crit" ;;
    3) echo "err" ;;
    4) echo "warning" ;;
    5) echo "notice" ;;
    6) echo "info" ;;
    7) echo "debug" ;;
    *) echo "$1" ;;
  esac
}
log_debug() {
  log_priority 7 || return 0
  echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
}
log_info() {
  log_priority 6 || return 0
  echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
}
log_err() {
  log_priority 3 || return 0
  echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
}
log_crit() {
  log_priority 2 || return 0
  echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
}
uname_os() {
  os=$(uname -s | tr '[:upper:]' '[:lower:]')
  case "$os" in
    msys_nt) os="windows" ;;
  esac
  echo "$os"
}
uname_arch() {
  arch=$(uname -m)
  case $arch in
    x86_64) arch="amd64" ;;
    x86) arch="386" ;;
    i686) arch="386" ;;
    i386) arch="386" ;;
    aarch64) arch="arm64" ;;
    armv5*) arch="armv5" ;;
    armv6*) arch="armv6" ;;
    armv7*) arch="armv7" ;;
  esac
  echo ${arch}
}
uname_os_check() {
  os=$(uname_os)
  case "$os" in
    darwin) return 0 ;;
    dragonfly) return 0 ;;
    freebsd) return 0 ;;
    linux) return 0 ;;
    android) return 0 ;;
    nacl) return 0 ;;
    netbsd) return 0 ;;
    openbsd) return 0 ;;
    plan9) return 0 ;;
    solaris) return 0 ;;
    windows) return 0 ;;
  esac
  log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
  return 1
}
uname_arch_check() {
  arch=$(uname_arch)
  case "$arch" in
    386) return 0 ;;
    amd64) return 0 ;;
    arm64) return 0 ;;
    armv5) return 0 ;;
    armv6) return 0 ;;
    armv7) return 0 ;;
    ppc64) return 0 ;;
    ppc64le) return 0 ;;
    mips) return 0 ;;
    mipsle) return 0 ;;
    mips64) return 0 ;;
    mips64le) return 0 ;;
    s390x) return 0 ;;
    amd64p32) return 0 ;;
  esac
  log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value.  Please file bug report at https://github.com/client9/shlib"
  return 1
}
untar() {
  tarball=$1
  case "${tarball}" in
    *.tar.gz | *.tgz) tar -xzf "${tarball}" ;;
    *.tar) tar -xf "${tarball}" ;;
    *.zip) unzip "${tarball}" ;;
    *)
      log_err "untar unknown archive format for ${tarball}"
      return 1
      ;;
  esac
}
http_download_curl() {
  local_file=$1
  source_url=$2
  header=$3
  if [ -z "$header" ]; then
    code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url")
  else
    code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url")
  fi
  if [ "$code" != "200" ]; then
    log_debug "http_download_curl received HTTP status $code"
    return 1
  fi
  return 0
}
http_download_wget() {
  local_file=$1
  source_url=$2
  header=$3
  if [ -z "$header" ]; then
    wget -q -O "$local_file" "$source_url"
  else
    wget -q --header "$header" -O "$local_file" "$source_url"
  fi
}
http_download() {
  log_debug "http_download $2"
  if is_command curl; then
    http_download_curl "$@"
    return
  elif is_command wget; then
    http_download_wget "$@"
    return
  fi
  log_crit "http_download unable to find wget or curl"
  return 1
}
http_copy() {
  tmp=$(mktemp)
  http_download "${tmp}" "$1" "$2" || return 1
  body=$(cat "$tmp")
  rm -f "${tmp}"
  echo "$body"
}
github_release() {
  owner_repo=$1
  version=$2
  test -z "$version" && version="latest"
  giturl="https://github.com/${owner_repo}/releases/${version}"
  json=$(http_copy "$giturl" "Accept:application/json")
  test -z "$json" && return 1
  version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
  test -z "$version" && return 1
  echo "$version"
}
hash_sha256() {
  TARGET=${1:-/dev/stdin}
  if is_command gsha256sum; then
    hash=$(gsha256sum "$TARGET") || return 1
    echo "$hash" | cut -d ' ' -f 1
  elif is_command sha256sum; then
    hash=$(sha256sum "$TARGET") || return 1
    echo "$hash" | cut -d ' ' -f 1
  elif is_command shasum; then
    hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
    echo "$hash" | cut -d ' ' -f 1
  elif is_command openssl; then
    hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1
    echo "$hash" | cut -d ' ' -f a
  else
    log_crit "hash_sha256 unable to find command to compute sha-256 hash"
    return 1
  fi
}
hash_sha256_verify() {
  TARGET=$1
  checksums=$2
  if [ -z "$checksums" ]; then
    log_err "hash_sha256_verify checksum file not specified in arg2"
    return 1
  fi
  BASENAME=${TARGET##*/}
  want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1)
  if [ -z "$want" ]; then
    log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'"
    return 1
  fi
  got=$(hash_sha256 "$TARGET")
  if [ "$want" != "$got" ]; then
    log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got"
    return 1
  fi
}
cat /dev/null <<EOF
------------------------------------------------------------------------
End of functions from https://github.com/client9/shlib
------------------------------------------------------------------------
EOF
`
<commit_msg>feat: improve Windows OS detection (#127)<commit_after>// Code generated 2019-03-28T16:23:31+0000 DO NOT EDIT.
package main

const shellfn = `
cat /dev/null <<EOF
------------------------------------------------------------------------
https://github.com/client9/shlib - portable posix shell functions
Public domain - http://unlicense.org
https://github.com/client9/shlib/blob/master/LICENSE.md
but credit (and pull requests) appreciated.
------------------------------------------------------------------------
EOF
is_command() {
  command -v "$1" >/dev/null
}
echoerr() {
  echo "$@" 1>&2
}
log_prefix() {
  echo "$0"
}
_logp=6
log_set_priority() {
  _logp="$1"
}
log_priority() {
  if test -z "$1"; then
    echo "$_logp"
    return
  fi
  [ "$1" -le "$_logp" ]
}
log_tag() {
  case $1 in
    0) echo "emerg" ;;
    1) echo "alert" ;;
    2) echo "crit" ;;
    3) echo "err" ;;
    4) echo "warning" ;;
    5) echo "notice" ;;
    6) echo "info" ;;
    7) echo "debug" ;;
    *) echo "$1" ;;
  esac
}
log_debug() {
  log_priority 7 || return 0
  echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
}
log_info() {
  log_priority 6 || return 0
  echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
}
log_err() {
  log_priority 3 || return 0
  echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
}
log_crit() {
  log_priority 2 || return 0
  echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
}
uname_os() {
  os=$(uname -s | tr '[:upper:]' '[:lower:]')
  case "$os" in
    cygwin_nt*) os="windows" ;;
    mingw*) os="windows" ;;
    msys_nt*) os="windows" ;;
  esac
  echo "$os"
}
uname_arch() {
  arch=$(uname -m)
  case $arch in
    x86_64) arch="amd64" ;;
    x86) arch="386" ;;
    i686) arch="386" ;;
    i386) arch="386" ;;
    aarch64) arch="arm64" ;;
    armv5*) arch="armv5" ;;
    armv6*) arch="armv6" ;;
    armv7*) arch="armv7" ;;
  esac
  echo ${arch}
}
uname_os_check() {
  os=$(uname_os)
  case "$os" in
    darwin) return 0 ;;
    dragonfly) return 0 ;;
    freebsd) return 0 ;;
    linux) return 0 ;;
    android) return 0 ;;
    nacl) return 0 ;;
    netbsd) return 0 ;;
    openbsd) return 0 ;;
    plan9) return 0 ;;
    solaris) return 0 ;;
    windows) return 0 ;;
  esac
  log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
  return 1
}
uname_arch_check() {
  arch=$(uname_arch)
  case "$arch" in
    386) return 0 ;;
    amd64) return 0 ;;
    arm64) return 0 ;;
    armv5) return 0 ;;
    armv6) return 0 ;;
    armv7) return 0 ;;
    ppc64) return 0 ;;
    ppc64le) return 0 ;;
    mips) return 0 ;;
    mipsle) return 0 ;;
    mips64) return 0 ;;
    mips64le) return 0 ;;
    s390x) return 0 ;;
    amd64p32) return 0 ;;
  esac
  log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value.  Please file bug report at https://github.com/client9/shlib"
  return 1
}
untar() {
  tarball=$1
  case "${tarball}" in
    *.tar.gz | *.tgz) tar -xzf "${tarball}" ;;
    *.tar) tar -xf "${tarball}" ;;
    *.zip) unzip "${tarball}" ;;
    *)
      log_err "untar unknown archive format for ${tarball}"
      return 1
      ;;
  esac
}
http_download_curl() {
  local_file=$1
  source_url=$2
  header=$3
  if [ -z "$header" ]; then
    code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url")
  else
    code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url")
  fi
  if [ "$code" != "200" ]; then
    log_debug "http_download_curl received HTTP status $code"
    return 1
  fi
  return 0
}
http_download_wget() {
  local_file=$1
  source_url=$2
  header=$3
  if [ -z "$header" ]; then
    wget -q -O "$local_file" "$source_url"
  else
    wget -q --header "$header" -O "$local_file" "$source_url"
  fi
}
http_download() {
  log_debug "http_download $2"
  if is_command curl; then
    http_download_curl "$@"
    return
  elif is_command wget; then
    http_download_wget "$@"
    return
  fi
  log_crit "http_download unable to find wget or curl"
  return 1
}
http_copy() {
  tmp=$(mktemp)
  http_download "${tmp}" "$1" "$2" || return 1
  body=$(cat "$tmp")
  rm -f "${tmp}"
  echo "$body"
}
github_release() {
  owner_repo=$1
  version=$2
  test -z "$version" && version="latest"
  giturl="https://github.com/${owner_repo}/releases/${version}"
  json=$(http_copy "$giturl" "Accept:application/json")
  test -z "$json" && return 1
  version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
  test -z "$version" && return 1
  echo "$version"
}
hash_sha256() {
  TARGET=${1:-/dev/stdin}
  if is_command gsha256sum; then
    hash=$(gsha256sum "$TARGET") || return 1
    echo "$hash" | cut -d ' ' -f 1
  elif is_command sha256sum; then
    hash=$(sha256sum "$TARGET") || return 1
    echo "$hash" | cut -d ' ' -f 1
  elif is_command shasum; then
    hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
    echo "$hash" | cut -d ' ' -f 1
  elif is_command openssl; then
    hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1
    echo "$hash" | cut -d ' ' -f a
  else
    log_crit "hash_sha256 unable to find command to compute sha-256 hash"
    return 1
  fi
}
hash_sha256_verify() {
  TARGET=$1
  checksums=$2
  if [ -z "$checksums" ]; then
    log_err "hash_sha256_verify checksum file not specified in arg2"
    return 1
  fi
  BASENAME=${TARGET##*/}
  want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1)
  if [ -z "$want" ]; then
    log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'"
    return 1
  fi
  got=$(hash_sha256 "$TARGET")
  if [ "$want" != "$got" ]; then
    log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got"
    return 1
  fi
}
cat /dev/null <<EOF
------------------------------------------------------------------------
End of functions from https://github.com/client9/shlib
------------------------------------------------------------------------
EOF
`
<|endoftext|>


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

package flatpack

import (
	"math"
	"testing"
)

// Types used by recEqual tests:

type Basic struct {
	x int
	y float32
}

type NotBasic Basic

type Loop *Loop
type Loopy interface{}

type cons struct{ car, cdr interface{} }

// self is a special type that signals that the other agument should
// be compared against itself.
type self struct{}

// Simple functions for DeepEqual tests.
var (
	fn1 func()             // nil.
	fn2 func()             // nil.
	fn3 = func() { fn1() } // Not nil.
)

var loop1, loop2, loop3, loop4, loop5, loop6 Loop
var loopy1, loopy2, loopy3, loopy4, loopy5, loopy6 Loopy
var cons1, cons2, cons3 cons

func init() {
	loop1 = &loop2
	loop2 = &loop1

	loop3 = &loop4
	loop4 = &loop3

	loop5 = &loop5
	loop6 = &loop5

	loopy1 = &loopy2
	loopy2 = &loopy1

	loopy3 = &loopy4
	loopy4 = &loopy3

	loopy5 = &loopy5
	loopy6 = &loopy5

	cons1 = cons{"foo", "bar"}
	cons2 = cons{"foo", "bar"}
	cons3 = cons{"foo", "bar"}
}

type recEqualTest struct {
	a, b   interface{}
	eq     bool
	disjEq bool
}

var recEqualTests = []recEqualTest{
	// Equalities:
	{nil, nil, true, true},
	{1, 1, true, true},
	{int32(1), int32(1), true, true},
	{0.5, 0.5, true, true},
	{float32(0.5), float32(0.5), true, true},
	{math.Inf(1), math.Inf(1), true, true},
	{"hello", "hello", true, true},
	{make([]int, 10), make([]int, 10), true, true},
	{&[3]int{1, 2, 3}, &[3]int{1, 2, 3}, true, true},
	{Basic{1, 0.5}, Basic{1, 0.5}, true, true},
	{error(nil), error(nil), true, true},
	{map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true, true},
	{fn1, fn2, true, true},
	{[][]int{{1}}, [][]int{{1}}, true, true},
	{&[1]float64{math.NaN()}, &[1]float64{math.NaN()}, true, true},
	{[]float64{math.NaN()}, []float64{math.NaN()}, true, true},
	{math.NaN(), math.NaN(), true, true},

	// Equal, but not disjoint:
	{make([]int, 10), self{}, true, false},
	{&[1]float64{math.NaN()}, self{}, true, false},
	{[]float64{math.NaN()}, self{}, true, false},
	{map[float64]float64{1: math.NaN()}, self{}, true, false},

	// Inequalities:
	{1, 2, false, false},
	{int32(1), int32(2), false, false},
	{0.5, 0.6, false, false},
	{float32(0.5), float32(0.6), false, false},
	{math.Inf(1), math.Inf(-1), false, false},
	{math.Copysign(0, -1), 0.0, false, false},
	{"hello", "hey", false, false},
	{make([]int, 10), make([]int, 11), false, false},
	{&[3]int{1, 2, 3}, &[3]int{1, 2, 4}, false, false},
	{Basic{1, 0.5}, Basic{1, 0.6}, false, false},
	{Basic{1, 0}, Basic{2, 0}, false, false},
	{map[int]string{1: "one", 3: "two"}, map[int]string{2: "two", 1: "one"}, false, false},
	{map[int]string{1: "one", 2: "txo"}, map[int]string{2: "two", 1: "one"}, false, false},
	{map[int]string{1: "one"}, map[int]string{2: "two", 1: "one"}, false, false},
	{map[int]string{2: "two", 1: "one"}, map[int]string{1: "one"}, false, false},
	{nil, 1, false, false},
	{1, nil, false, false},
	{fn1, fn3, false, false},
	{fn3, fn3, true, true},
	{[][]int{{1}}, [][]int{{2}}, false, false},
	{map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false, false},

	// Nil vs empty: not the same:
	{[]int{}, []int(nil), false, false},
	{[]int{}, []int{}, true, true},
	{[]int(nil), []int(nil), true, true},
	{map[int]int{}, map[int]int(nil), false, false},
	{map[int]int{}, map[int]int{}, true, true},
	{map[int]int(nil), map[int]int(nil), true, true},

	// Mismatched types:
	{1, 1.0, false, false},
	{int32(1), int64(1), false, false},
	{0.5, "hello", false, false},
	{[]int{1, 2, 3}, [3]int{1, 2, 3}, false, false},
	{&[3]interface{}{1, 2, 4}, &[3]interface{}{1, 2, "s"}, false, false},
	{Basic{1, 0.5}, NotBasic{1, 0.5}, false, false},
	{map[uint]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, false, false},

	// Loops:
	{&loop1, self{}, true, false},
	{&loop1, &loop2, true, false},
	{&loop1, &loop3, true, true},
	{&loop1, &loop5, false, false},

	{&loopy1, self{}, true, false},
	{&loopy1, &loopy2, true, false},
	{&loopy1, &loopy3, true, true},
	{&loopy1, &loopy5, false, false},

	// M(ism)atched structure:
	{cons{&cons1, &cons2}, self{}, true, false},
	{cons{&cons1, &cons2}, cons{&cons3, &cons3}, false, false},
	{&loop1, &loop6, false, false},
	{&loop5, &loop6, false, false},
	{&loopy1, &loopy6, false, false},
	{&loopy5, &loopy6, false, false},
}

func TestRecEqual(t *testing.T) {
	for _, test := range recEqualTests {
		if test.b == (self{}) {
			test.b = test.a
		}
		if r := recEqual(test.a, test.b, false); r != test.eq {
			t.Errorf("recEqual(%#v, %#v, false) = %v, want %v", test.a, test.b, r, test.eq)
		}
		if r := recEqual(test.a, test.b, true); r != test.disjEq {
			t.Errorf("recEqual(%#v, %#v, true) = %v, want %v", test.a, test.b, r, test.disjEq)
		}
	}
}
<commit_msg>Add map shared/cyclic substructure tests<commit_after>/* Copyright 2017 Google Inc.
 * https://github.com/NeilFraser/CodeCity
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package flatpack

import (
	"math"
	"testing"
)

// Types used by recEqual tests:

type Basic struct {
	x int
	y float32
}

type NotBasic Basic

type Loop *Loop
type Loopy interface{}
type Loopier map[int]interface{}

type cons struct{ car, cdr interface{} }

// self is a special type that signals that the other agument should
// be compared against itself.
type self struct{}

// Simple functions for DeepEqual tests.
var (
	fn1 func()             // nil.
	fn2 func()             // nil.
	fn3 = func() { fn1() } // Not nil.
)

var loop1, loop2, loop3, loop4, loop5, loop6 Loop
var loopy1, loopy2, loopy3, loopy4, loopy5, loopy6 Loopy
var loopier1 = Loopier{}
var loopier2 = Loopier{}
var loopier3 = Loopier{}
var loopier4 = Loopier{}
var loopier5 = Loopier{}
var loopier6 = Loopier{}
var cons1, cons2, cons3 cons
var map1 = map[int]int{1: 2}
var map2 = map[int]int{1: 2}
var map3 = map[int]int{1: 2}

func init() {
	loop1 = &loop2
	loop2 = &loop1

	loop3 = &loop4
	loop4 = &loop3

	loop5 = &loop5
	loop6 = &loop5

	loopy1 = &loopy2
	loopy2 = &loopy1

	loopy3 = &loopy4
	loopy4 = &loopy3

	loopy5 = &loopy5
	loopy6 = &loopy5

	loopier1[1] = loopier2
	loopier2[1] = loopier1

	loopier3[1] = loopier4
	loopier4[1] = loopier3

	loopier5[1] = loopier5
	loopier6[1] = loopier5

	cons1 = cons{"foo", "bar"}
	cons2 = cons{"foo", "bar"}
	cons3 = cons{"foo", "bar"}
}

type recEqualTest struct {
	a, b   interface{}
	eq     bool
	disjEq bool
}

var recEqualTests = []recEqualTest{
	// Equalities:
	{nil, nil, true, true},
	{1, 1, true, true},
	{int32(1), int32(1), true, true},
	{0.5, 0.5, true, true},
	{float32(0.5), float32(0.5), true, true},
	{math.Inf(1), math.Inf(1), true, true},
	{"hello", "hello", true, true},
	{make([]int, 10), make([]int, 10), true, true},
	{&[3]int{1, 2, 3}, &[3]int{1, 2, 3}, true, true},
	{Basic{1, 0.5}, Basic{1, 0.5}, true, true},
	{error(nil), error(nil), true, true},
	{map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true, true},
	{fn1, fn2, true, true},
	{[][]int{{1}}, [][]int{{1}}, true, true},
	{&[1]float64{math.NaN()}, &[1]float64{math.NaN()}, true, true},
	{[]float64{math.NaN()}, []float64{math.NaN()}, true, true},
	{math.NaN(), math.NaN(), true, true},
	{cons{map1, map1}, cons{map2, map2}, true, true},

	// Equal, but not disjoint:
	{make([]int, 10), self{}, true, false},
	{&[1]float64{math.NaN()}, self{}, true, false},
	{[]float64{math.NaN()}, self{}, true, false},
	{map[float64]float64{1: math.NaN()}, self{}, true, false},
	{cons{map1, map2}, cons{map2, map3}, true, false},

	// Inequalities:
	{1, 2, false, false},
	{int32(1), int32(2), false, false},
	{0.5, 0.6, false, false},
	{float32(0.5), float32(0.6), false, false},
	{math.Inf(1), math.Inf(-1), false, false},
	{math.Copysign(0, -1), 0.0, false, false},
	{"hello", "hey", false, false},
	{make([]int, 10), make([]int, 11), false, false},
	{&[3]int{1, 2, 3}, &[3]int{1, 2, 4}, false, false},
	{Basic{1, 0.5}, Basic{1, 0.6}, false, false},
	{Basic{1, 0}, Basic{2, 0}, false, false},
	{map[int]string{1: "one", 3: "two"}, map[int]string{2: "two", 1: "one"}, false, false},
	{map[int]string{1: "one", 2: "txo"}, map[int]string{2: "two", 1: "one"}, false, false},
	{map[int]string{1: "one"}, map[int]string{2: "two", 1: "one"}, false, false},
	{map[int]string{2: "two", 1: "one"}, map[int]string{1: "one"}, false, false},
	{nil, 1, false, false},
	{1, nil, false, false},
	{fn1, fn3, false, false},
	{fn3, fn3, true, true},
	{[][]int{{1}}, [][]int{{2}}, false, false},
	{map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false, false},

	// Nil vs empty: not the same:
	{[]int{}, []int(nil), false, false},
	{[]int{}, []int{}, true, true},
	{[]int(nil), []int(nil), true, true},
	{map[int]int{}, map[int]int(nil), false, false},
	{map[int]int{}, map[int]int{}, true, true},
	{map[int]int(nil), map[int]int(nil), true, true},

	// Mismatched types:
	{1, 1.0, false, false},
	{int32(1), int64(1), false, false},
	{0.5, "hello", false, false},
	{[]int{1, 2, 3}, [3]int{1, 2, 3}, false, false},
	{&[3]interface{}{1, 2, 4}, &[3]interface{}{1, 2, "s"}, false, false},
	{Basic{1, 0.5}, NotBasic{1, 0.5}, false, false},
	{map[uint]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, false, false},

	// Loops:
	{&loop1, self{}, true, false},
	{&loop1, &loop2, true, false},
	{&loop1, &loop3, true, true},
	{&loop1, &loop5, false, false},

	{&loopy1, self{}, true, false},
	{&loopy1, &loopy2, true, false},
	{&loopy1, &loopy3, true, true},
	{&loopy1, &loopy5, false, false},

	{&loopier1, self{}, true, false},
	{&loopier1, &loopier2, true, false},
	{&loopier1, &loopier3, true, true},
	{&loopier1, &loopier5, false, false},

	// M(ism)atched structure:
	{cons{&cons1, &cons2}, self{}, true, false},
	{cons{&cons1, &cons2}, cons{&cons3, &cons3}, false, false},
	{&loop1, &loop6, false, false},
	{&loop5, &loop6, false, false},
	{&loopy1, &loopy6, false, false},
	{&loopy5, &loopy6, false, false},
	{cons{map1, map1}, cons{map2, map3}, false, false},
}

func TestRecEqual(t *testing.T) {
	for _, test := range recEqualTests {
		if test.b == (self{}) {
			test.b = test.a
		}
		if r := recEqual(test.a, test.b, false); r != test.eq {
			t.Errorf("recEqual(%#v, %#v, false) = %v, want %v", test.a, test.b, r, test.eq)
		}
		if r := recEqual(test.a, test.b, true); r != test.disjEq {
			t.Errorf("recEqual(%#v, %#v, true) = %v, want %v", test.a, test.b, r, test.disjEq)
		}
	}
}
<|endoftext|>


<commit_before>package cloudflare

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
)

// Client provides a client to the CloudflAre API
type Client struct {
	// Access Token
	Token string

	// User Email
	Email string

	// URL to the DO API to use
	URL string

	// HttpClient is the client to use. Default will be
	// used if not provided.
	Http *http.Client
}

// NewClient returns a new cloudflare client,
// requires an authorization token. You can generate
// an OAuth token by visiting the Apps & API section
// of the CloudflAre control panel for your account.
func NewClient(email string, token string) (*Client, error) {
	// If it exists, grab teh token from the environment
	if token == "" {
		token = os.Getenv("CLOUDFLARE_TOKEN")
	}

	if email == "" {
		email = os.Getenv("CLOUDFLARE_EMAIL")
	}

	client := Client{
		Token: token,
		Email: email,
		URL:   "https://www.cloudflare.com/api_json.html",
		Http:  http.DefaultClient,
	}
	return &client, nil
}

// Creates a new request with the params
func (c *Client) NewRequest(params map[string]string, method string, action string) (*http.Request, error) {
	p := url.Values{}
	u, err := url.Parse(c.URL)

	if err != nil {
		return nil, fmt.Errorf("Error parsing base URL: %s", err)
	}

	// Build up our request parameters
	for k, v := range params {
		p.Add(k, v)
	}

	// Add authentication details
	p.Add("tkn", c.Token)
	p.Add("email", c.Email)

	// The "action" to take against the API
	p.Add("a", action)

	// Add the params to our URL
	u.RawQuery = p.Encode()

	// Build the request
	req, err := http.NewRequest(method, u.String(), nil)

	if err != nil {
		return nil, fmt.Errorf("Error creating request: %s", err)
	}

	return req, nil

}

// decodeBody is used to JSON decode a body
func decodeBody(resp *http.Response, out interface{}) error {
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return err
	}

	if err = json.Unmarshal(body, &out); err != nil {
		return err
	}

	return nil
}

// checkResp wraps http.Client.Do() and verifies that the
// request was successful. A non-200 request returns an error
// formatted to included any validation problems or otherwise
func checkResp(resp *http.Response, err error) (*http.Response, error) {
	// If the err is already there, there was an error higher
	// up the chain, so just return that
	if err != nil {
		return resp, err
	}

	switch i := resp.StatusCode; {
	case i == 200:
		return resp, nil
	default:
		return resp, fmt.Errorf("API Error: %s", resp.Status)
	}
}
<commit_msg>Aggressively closing body on decoding<commit_after>package cloudflare

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
)

// Client provides a client to the CloudflAre API
type Client struct {
	// Access Token
	Token string

	// User Email
	Email string

	// URL to the DO API to use
	URL string

	// HttpClient is the client to use. Default will be
	// used if not provided.
	Http *http.Client
}

// NewClient returns a new cloudflare client,
// requires an authorization token. You can generate
// an OAuth token by visiting the Apps & API section
// of the CloudflAre control panel for your account.
func NewClient(email string, token string) (*Client, error) {
	// If it exists, grab teh token from the environment
	if token == "" {
		token = os.Getenv("CLOUDFLARE_TOKEN")
	}

	if email == "" {
		email = os.Getenv("CLOUDFLARE_EMAIL")
	}

	client := Client{
		Token: token,
		Email: email,
		URL:   "https://www.cloudflare.com/api_json.html",
		Http:  http.DefaultClient,
	}
	return &client, nil
}

// Creates a new request with the params
func (c *Client) NewRequest(params map[string]string, method string, action string) (*http.Request, error) {
	p := url.Values{}
	u, err := url.Parse(c.URL)

	if err != nil {
		return nil, fmt.Errorf("Error parsing base URL: %s", err)
	}

	// Build up our request parameters
	for k, v := range params {
		p.Add(k, v)
	}

	// Add authentication details
	p.Add("tkn", c.Token)
	p.Add("email", c.Email)

	// The "action" to take against the API
	p.Add("a", action)

	// Add the params to our URL
	u.RawQuery = p.Encode()

	// Build the request
	req, err := http.NewRequest(method, u.String(), nil)

	if err != nil {
		return nil, fmt.Errorf("Error creating request: %s", err)
	}

	return req, nil

}

// decodeBody is used to JSON decode a body
func decodeBody(resp *http.Response, out interface{}) error {
	body, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()

	if err != nil {
		return err
	}

	if err = json.Unmarshal(body, &out); err != nil {
		return err
	}

	return nil
}

// checkResp wraps http.Client.Do() and verifies that the
// request was successful. A non-200 request returns an error
// formatted to included any validation problems or otherwise
func checkResp(resp *http.Response, err error) (*http.Response, error) {
	// If the err is already there, there was an error higher
	// up the chain, so just return that
	if err != nil {
		return resp, err
	}

	switch i := resp.StatusCode; {
	case i == 200:
		return resp, nil
	default:
		return resp, fmt.Errorf("API Error: %s", resp.Status)
	}
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 Nicolas Lamirault <nicolas.lamirault@gmail.com>

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

// Version represents the application version using SemVer
const Version string = "0.2.0"
<commit_msg>bump version<commit_after>// Copyright (C) 2015 Nicolas Lamirault <nicolas.lamirault@gmail.com>

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

// Version represents the application version using SemVer
const Version string = "0.3.0"
<|endoftext|>


<commit_before>// Reddit audiences crawler
// Rémy Mathieu © 2016
package app

import (
	"log"
	"time"
)

const (
	SESSION_EXPIRATION = "2m"
)

func StartCleanSessionsJob(a *App) {
	duration, err := time.ParseDuration(SESSION_EXPIRATION)
	if err != nil {
		log.Println("err: the SESSION_EXPIRATION const is malformed:", SESSION_EXPIRATION)
		log.Println("err: the sessions won't expire.")
		return
	}

	ticker := time.NewTicker(time.Minute)

	log.Println("info: clean sessions job started.")

	for range ticker.C {
		cleanSessions(a, duration)
	}
	ticker.Stop()
}

func cleanSessions(a *App, sessionExpiration time.Duration) {
	if result, err := a.DB().DeleteExpiredSessions(sessionExpiration); err == nil {
		if ra, err := result.RowsAffected(); err == nil {
			if ra > 0 {
				log.Printf("info: %d session expired.\n", ra)
			}
		}
	}
}
<commit_msg>app: sessions expire after 60m.<commit_after>// Reddit audiences crawler
// Rémy Mathieu © 2016
package app

import (
	"log"
	"time"
)

const (
	SESSION_EXPIRATION = "60m"
)

func StartCleanSessionsJob(a *App) {
	duration, err := time.ParseDuration(SESSION_EXPIRATION)
	if err != nil {
		log.Println("err: the SESSION_EXPIRATION const is malformed:", SESSION_EXPIRATION)
		log.Println("err: the sessions won't expire.")
		return
	}

	ticker := time.NewTicker(time.Minute)

	log.Println("info: clean sessions job started.")

	for range ticker.C {
		cleanSessions(a, duration)
	}
	ticker.Stop()
}

func cleanSessions(a *App, sessionExpiration time.Duration) {
	if result, err := a.DB().DeleteExpiredSessions(sessionExpiration); err == nil {
		if ra, err := result.RowsAffected(); err == nil {
			if ra > 0 {
				log.Printf("info: %d session expired.\n", ra)
			}
		}
	}
}
<|endoftext|>


<commit_before>/*package parse contains routines for parsing config files. To parse a config
file, the user creates a ConfigVars struct and registers every variable in the
config file with it. Registration requires 1) the variable name, 2) a
default value, and 3) a location to write the variable to.

Config files have two parse with this package have two parts, a title and a
body. The title specifies the type of config file an the body contains
Variable = Value pairs. The title ensures that when a project has multiple
config files the wrong one isn't read by mistake.

Here is an example configuration file that collects information about
my cat:

    # Title
    [cat_info]

    # Body:
	CatName = Bob
	FurColors = White, Black
	Age = 7.5 # Inline comments are okay, too.
	Paws = 4

Here is an example of using the parse package to parse this type of config
file.

	type CatInfo struct {
		CatName string
		FurColors []string
		Age float
		Paws, Tails int
	}

	info := new(CatInfo)

    vars := ConfigVars("cat_info")
    vars.String(&info.CatName, "CatName", "")
    vars.Strings(&info.FurColors, "FurColors", []string{})
    vars.Float(&info.Age, "Age", -1)
    vars.Int(&info.Paws, "Paws", 4)
    vars.Int(&info.Tail, "Tail", 1)

    // Then, once a file has been provided

    err := ReadConfig("my_cat.config", vars)
    if err != nil {
        // Handle error
    }

A careful read of the above example will show that the supplied config file
does not consider the config file missing one or more files an error. This will
be annoying in some cases, but is usually the desired behavior. You will need to
explicitly check for variables that have not been set.

For additional examples, see the usage in config_test.go
*/
package parse

import (
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"
)

/////////////////////
// Conversion Code //
/////////////////////

type varType int

const (
	intVar varType = iota
	intsVar
	floatVar
	floatsVar
	stringVar
	stringsVar
	boolVar
	boolsVar
)

func (v varType) String() string {
	switch v {
	case intVar:
		return "int"
	case intsVar:
		return "int list"
	case floatVar:
		return "float"
	case floatsVar:
		return "float list"
	case stringVar:
		return "string"
	case stringsVar:
		return "string list"
	case boolVar:
		return "bool"
	case boolsVar:
		return "bool list"
	}
	panic("Impossible")
}

type conversionFunc func(string) bool

type ConfigVars struct {
	name            string
	varNames        []string
	varTypes        []varType
	conversionFuncs []conversionFunc
}

func intConv(ptr *int64) conversionFunc {
	return func(s string) bool {
		i, err := strconv.Atoi(s)
		if err != nil {
			return false
		}
		*ptr = int64(i)
		return true
	}
}

func floatConv(ptr *float64) conversionFunc {
	return func(s string) bool {
		f, err := strconv.ParseFloat(s, 64)
		if err != nil {
			return false
		}
		*ptr = f
		return true
	}
}

func stringConv(ptr *string) conversionFunc {
	return func(s string) bool {
		*ptr = strings.Trim(s, " ")
		return true
	}
}

func boolConv(ptr *bool) conversionFunc {
	return func(s string) bool {
		b, err := strconv.ParseBool(s)
		if err != nil {
			return false
		}
		*ptr = b
		return true
	}
}

func strToList(a string) []string {
	strs := strings.Split(a, ",")
	for i := range strs {
		strs[i] = strings.Trim(strs[i], " ")
	}
	return strs
}

func intsConv(ptr *[]int64) conversionFunc {
	return func(s string) bool {
		toks := strToList(s)
		*ptr = []int64{}
		for j := range toks {
			i, err := strconv.Atoi(toks[j])
			if err != nil {
				return false
			}
			*ptr = append(*ptr, int64(i))
		}
		return true
	}
}

func floatsConv(ptr *[]float64) conversionFunc {
	return func(s string) bool {
		toks := strToList(s)
		*ptr = []float64{}
		for j := range toks {
			f, err := strconv.ParseFloat(toks[j], 64)
			if err != nil {
				return false
			}
			*ptr = append(*ptr, f)
		}
		return true
	}
}

func stringsConv(ptr *[]string) conversionFunc {
	return func(s string) bool {
		toks := strToList(s)
		*ptr = []string{}
		for j := range toks {
			*ptr = append(*ptr, toks[j])
		}
		return true
	}
}

func boolsConv(ptr *[]bool) conversionFunc {
	return func(s string) bool {
		toks := strToList(s)
		*ptr = []bool{}
		for j := range toks {
			b, err := strconv.ParseBool(toks[j])
			if err != nil {
				return false
			}
			*ptr = append(*ptr, b)
		}
		return true
	}
}

func NewConfigVars(name string) *ConfigVars {
	return &ConfigVars{name: name}
}

func (vars *ConfigVars) Int(ptr *int64, name string, value int64) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, intConv(ptr))
	vars.varTypes = append(vars.varTypes, intVar)
}

func (vars *ConfigVars) Float(ptr *float64, name string, value float64) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, floatConv(ptr))
	vars.varTypes = append(vars.varTypes, floatVar)
}

func (vars *ConfigVars) String(ptr *string, name string, value string) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, stringConv(ptr))
	vars.varTypes = append(vars.varTypes, stringVar)
}

func (vars *ConfigVars) Bool(ptr *bool, name string, value bool) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, boolConv(ptr))
	vars.varTypes = append(vars.varTypes, boolVar)
}

func (vars *ConfigVars) Ints(ptr *[]int64, name string, value []int64) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, intsConv(ptr))
	vars.varTypes = append(vars.varTypes, intsVar)
}

func (vars *ConfigVars) Floats(ptr *[]float64, name string, value []float64) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, floatsConv(ptr))
	vars.varTypes = append(vars.varTypes, floatsVar)
}

func (vars *ConfigVars) Strings(ptr *[]string, name string, value []string) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, stringsConv(ptr))
	vars.varTypes = append(vars.varTypes, stringsVar)
}

func (vars *ConfigVars) Bools(ptr *[]bool, name string, value []bool) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, boolsConv(ptr))
	vars.varTypes = append(vars.varTypes, boolsVar)
}

//////////////////
// Parsing Code //
//////////////////

// ReadConfig parses the config file specified by fname using the set of
// variables vars. If successful nil is returned, otherwise an error is
// returned.
func ReadConfig(fname string, vars *ConfigVars) error {
	for i := range vars.varNames {
		vars.varNames[i] = strings.ToLower(vars.varNames[i])
	}

	// I/O

	bs, err := ioutil.ReadFile(fname)
	if err != nil {
		return err
	}

	// Begin tokenization; remember line numbers for better errors.

	lines := strings.Split(string(bs), "\n")
	lines, lineNums := removeComments(lines)
	for i := range lineNums {
		lineNums[i]++
	}

	if len(lines) == 0 || lines[0] != fmt.Sprintf("[%s]", vars.name) {
		return fmt.Errorf(
			"I expected the config file %s to have the header "+
				"[%s] at the top, but didn't find it.", fname, vars.name,
		)
	}
	lines = lines[1:]

	// Create associate list and check for name-based errors

	names, vals, errLine := associationList(lines)
	if errLine != -1 {
		return fmt.Errorf(
			"I could not parse line %d of the config file %s because it "+
				"did not take the form of a variable assignment.",
			lineNums[errLine+1], fname,
		)
	}

	if errLine = checkValidNames(names, vars); errLine != -1 {
		return fmt.Errorf(
			"Line %d of the config file %s assigns a value to the "+
				"variable '%s', but config files of type %s don't have that "+
				"variable.", lineNums[errLine+1], fname, names[errLine], vars.name,
		)
	}

	if errLine1, errLine2 := checkDuplicateNames(names); errLine1 != -1 {
		return fmt.Errorf(
			"Lines %d and %d of the config file %s both assign a value to "+
				"the variable '%s'.", lineNums[errLine1+1], lineNums[errLine2+1],
			fname, names[errLine1],
		)
	}

	// Convert every variable in the associate list.

	if errLine = convertAssoc(names, vals, vars); errLine != -1 {
		j := 0
		for ; j < len(vars.varNames); j++ {
			if vars.varNames[j] == names[errLine] {
				break
			}
		}
		typeName := vars.varTypes[j].String()
		a := "a"
		if typeName[0] == 'i' {
			a = "an"
		}
		return fmt.Errorf(
			"I could not parse line %d of the config file %s because '%s' "+
				"expects values of type %s and '%s' cannnot be converted to "+
				"%s %s.", lineNums[errLine+1], fname, vars.varNames[j], typeName,
			vals[j], a, typeName,
		)
	}

	return nil
}

// These functions are self-explanatory.

func removeComments(lines []string) ([]string, []int) {
	tmp := make([]string, len(lines))
	copy(tmp, lines)
	lines = tmp

	for i := range lines {
		comment := strings.Index(lines[i], "#")
		if comment == -1 {
			continue
		}
		lines[i] = lines[i][:comment]
	}

	out, lineNums := []string{}, []int{}
	for i := range lines {
		line := strings.Trim(lines[i], " ")
		if len(line) == 0 {
			continue
		}
		out = append(out, line)
		lineNums = append(lineNums, i)
	}

	return out, lineNums
}

func associationList(lines []string) ([]string, []string, int) {
	names, vals := []string{}, []string{}
	for i := range lines {
		eq := strings.Index(lines[i], "=")
		if eq == -1 {
			return nil, nil, i
		}
		name := lines[i][:eq]
		val := ""
		if len(lines[i])-1 > eq {
			val = lines[i][eq+1:]
		}
		names = append(names, strings.ToLower(strings.Trim(name, " ")))
		if len(names[len(names)-1]) == 0 {
			return nil, nil, i
		}
		vals = append(vals, strings.Trim(val, " "))
	}
	return names, vals, -1
}

func checkValidNames(names []string, vars *ConfigVars) int {
	for i := range names {
		found := false
		for j := range vars.varNames {
			if vars.varNames[j] == names[i] {
				found = true
				break
			}
		}
		if !found {
			return i
		}
	}
	return -1
}

func checkDuplicateNames(names []string) (int, int) {
	for i := range names {
		for j := i + 1; j < len(names); j++ {
			if names[i] == names[j] {
				return i, j
			}
		}
	}
	return -1, -1
}

func convertAssoc(names, vals []string, vars *ConfigVars) int {
	for i := range names {
		j := 0
		for ; j < len(vars.varNames); j++ {
			if vars.varNames[j] == names[i] {
				break
			}
		}

		ok := vars.conversionFuncs[j](vals[i])
		if !ok {
			return i
		}
	}
	return -1
}
<commit_msg>Fixed case-based error messages.<commit_after>/*package parse contains routines for parsing config files. To parse a config
file, the user creates a ConfigVars struct and registers every variable in the
config file with it. Registration requires 1) the variable name, 2) a
default value, and 3) a location to write the variable to.

Config files have two parse with this package have two parts, a title and a
body. The title specifies the type of config file an the body contains
Variable = Value pairs. The title ensures that when a project has multiple
config files the wrong one isn't read by mistake.

Here is an example configuration file that collects information about
my cat:

    # Title
    [cat_info]

    # Body:
	CatName = Bob
	FurColors = White, Black
	Age = 7.5 # Inline comments are okay, too.
	Paws = 4

Here is an example of using the parse package to parse this type of config
file.

	type CatInfo struct {
		CatName string
		FurColors []string
		Age float
		Paws, Tails int
	}

	info := new(CatInfo)

    vars := ConfigVars("cat_info")
    vars.String(&info.CatName, "CatName", "")
    vars.Strings(&info.FurColors, "FurColors", []string{})
    vars.Float(&info.Age, "Age", -1)
    vars.Int(&info.Paws, "Paws", 4)
    vars.Int(&info.Tail, "Tail", 1)

    // Then, once a file has been provided

    err := ReadConfig("my_cat.config", vars)
    if err != nil {
        // Handle error
    }

A careful read of the above example will show that the supplied config file
does not consider the config file missing one or more variables an error. This
will be annoying in some cases, but is usually the desired behavior. You will
need to explicitly check for variables that have not been set.

For additional examples, see the usage in config_test.go
*/
package parse

import (
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"
)

/////////////////////
// Conversion Code //
/////////////////////

type varType int

const (
	intVar varType = iota
	intsVar
	floatVar
	floatsVar
	stringVar
	stringsVar
	boolVar
	boolsVar
)

func (v varType) String() string {
	switch v {
	case intVar:
		return "int"
	case intsVar:
		return "int list"
	case floatVar:
		return "float"
	case floatsVar:
		return "float list"
	case stringVar:
		return "string"
	case stringsVar:
		return "string list"
	case boolVar:
		return "bool"
	case boolsVar:
		return "bool list"
	}
	panic("Impossible")
}

type conversionFunc func(string) bool

type ConfigVars struct {
	name            string
	varNames        []string
	varTypes        []varType
	conversionFuncs []conversionFunc
}

func intConv(ptr *int64) conversionFunc {
	return func(s string) bool {
		i, err := strconv.Atoi(s)
		if err != nil {
			return false
		}
		*ptr = int64(i)
		return true
	}
}

func floatConv(ptr *float64) conversionFunc {
	return func(s string) bool {
		f, err := strconv.ParseFloat(s, 64)
		if err != nil {
			return false
		}
		*ptr = f
		return true
	}
}

func stringConv(ptr *string) conversionFunc {
	return func(s string) bool {
		*ptr = strings.Trim(s, " ")
		return true
	}
}

func boolConv(ptr *bool) conversionFunc {
	return func(s string) bool {
		b, err := strconv.ParseBool(s)
		if err != nil {
			return false
		}
		*ptr = b
		return true
	}
}

func strToList(a string) []string {
	strs := strings.Split(a, ",")
	for i := range strs {
		strs[i] = strings.Trim(strs[i], " ")
	}
	return strs
}

func intsConv(ptr *[]int64) conversionFunc {
	return func(s string) bool {
		toks := strToList(s)
		*ptr = []int64{}
		for j := range toks {
			i, err := strconv.Atoi(toks[j])
			if err != nil {
				return false
			}
			*ptr = append(*ptr, int64(i))
		}
		return true
	}
}

func floatsConv(ptr *[]float64) conversionFunc {
	return func(s string) bool {
		toks := strToList(s)
		*ptr = []float64{}
		for j := range toks {
			f, err := strconv.ParseFloat(toks[j], 64)
			if err != nil {
				return false
			}
			*ptr = append(*ptr, f)
		}
		return true
	}
}

func stringsConv(ptr *[]string) conversionFunc {
	return func(s string) bool {
		toks := strToList(s)
		*ptr = []string{}
		for j := range toks {
			*ptr = append(*ptr, toks[j])
		}
		return true
	}
}

func boolsConv(ptr *[]bool) conversionFunc {
	return func(s string) bool {
		toks := strToList(s)
		*ptr = []bool{}
		for j := range toks {
			b, err := strconv.ParseBool(toks[j])
			if err != nil {
				return false
			}
			*ptr = append(*ptr, b)
		}
		return true
	}
}

func NewConfigVars(name string) *ConfigVars {
	return &ConfigVars{name: name}
}

func (vars *ConfigVars) Int(ptr *int64, name string, value int64) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, intConv(ptr))
	vars.varTypes = append(vars.varTypes, intVar)
}

func (vars *ConfigVars) Float(ptr *float64, name string, value float64) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, floatConv(ptr))
	vars.varTypes = append(vars.varTypes, floatVar)
}

func (vars *ConfigVars) String(ptr *string, name string, value string) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, stringConv(ptr))
	vars.varTypes = append(vars.varTypes, stringVar)
}

func (vars *ConfigVars) Bool(ptr *bool, name string, value bool) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, boolConv(ptr))
	vars.varTypes = append(vars.varTypes, boolVar)
}

func (vars *ConfigVars) Ints(ptr *[]int64, name string, value []int64) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, intsConv(ptr))
	vars.varTypes = append(vars.varTypes, intsVar)
}

func (vars *ConfigVars) Floats(ptr *[]float64, name string, value []float64) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, floatsConv(ptr))
	vars.varTypes = append(vars.varTypes, floatsVar)
}

func (vars *ConfigVars) Strings(ptr *[]string, name string, value []string) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, stringsConv(ptr))
	vars.varTypes = append(vars.varTypes, stringsVar)
}

func (vars *ConfigVars) Bools(ptr *[]bool, name string, value []bool) {
	*ptr = value
	vars.varNames = append(vars.varNames, name)
	vars.conversionFuncs = append(vars.conversionFuncs, boolsConv(ptr))
	vars.varTypes = append(vars.varTypes, boolsVar)
}

//////////////////
// Parsing Code //
//////////////////

// ReadConfig parses the config file specified by fname using the set of
// variables vars. If successful nil is returned, otherwise an error is
// returned.
func ReadConfig(fname string, vars *ConfigVars) error {
	//for i := range vars.varNames {
	//	vars.varNames[i] = strings.ToLower(vars.varNames[i])
	//}

	// I/O

	bs, err := ioutil.ReadFile(fname)
	if err != nil {
		return err
	}

	// Begin tokenization; remember line numbers for better errors.

	lines := strings.Split(string(bs), "\n")
	lines, lineNums := removeComments(lines)
	for i := range lineNums {
		lineNums[i]++
	}

	if len(lines) == 0 || lines[0] != fmt.Sprintf("[%s]", vars.name) {
		return fmt.Errorf(
			"I expected the config file %s to have the header "+
				"[%s] at the top, but didn't find it.", fname, vars.name,
		)
	}
	lines = lines[1:]

	// Create associate list and check for name-based errors

	names, vals, errLine := associationList(lines)
	if errLine != -1 {
		return fmt.Errorf(
			"I could not parse line %d of the config file %s because it "+
				"did not take the form of a variable assignment.",
			lineNums[errLine+1], fname,
		)
	}

	if errLine = checkValidNames(names, vars); errLine != -1 {
		return fmt.Errorf(
			"Line %d of the config file %s assigns a value to the "+
				"variable '%s', but config files of type %s don't have that "+
				"variable.", lineNums[errLine+1], fname, names[errLine], vars.name,
		)
	}

	if errLine1, errLine2 := checkDuplicateNames(names); errLine1 != -1 {
		return fmt.Errorf(
			"Lines %d and %d of the config file %s both assign a value to "+
				"the variable '%s'.", lineNums[errLine1+1], lineNums[errLine2+1],
			fname, names[errLine1],
		)
	}

	// Convert every variable in the associate list.

	if errLine = convertAssoc(names, vals, vars); errLine != -1 {
		j := 0
		for ; j < len(vars.varNames); j++ {
			if vars.varNames[j] == names[errLine] {
				break
			}
		}
		typeName := vars.varTypes[j].String()
		a := "a"
		if typeName[0] == 'i' {
			a = "an"
		}
		return fmt.Errorf(
			"I could not parse line %d of the config file %s because '%s' "+
				"expects values of type %s and '%s' cannnot be converted to "+
				"%s %s.", lineNums[errLine+1], fname, vars.varNames[j], typeName,
			vals[j], a, typeName,
		)
	}

	return nil
}

// These functions are self-explanatory.

func removeComments(lines []string) ([]string, []int) {
	tmp := make([]string, len(lines))
	copy(tmp, lines)
	lines = tmp

	for i := range lines {
		comment := strings.Index(lines[i], "#")
		if comment == -1 {
			continue
		}
		lines[i] = lines[i][:comment]
	}

	out, lineNums := []string{}, []int{}
	for i := range lines {
		line := strings.Trim(lines[i], " ")
		if len(line) == 0 {
			continue
		}
		out = append(out, line)
		lineNums = append(lineNums, i)
	}

	return out, lineNums
}

func associationList(lines []string) ([]string, []string, int) {
	names, vals := []string{}, []string{}
	for i := range lines {
		eq := strings.Index(lines[i], "=")
		if eq == -1 {
			return nil, nil, i
		}
		name := lines[i][:eq]
		val := ""
		if len(lines[i])-1 > eq {
			val = lines[i][eq+1:]
		}
		//names = append(names, strings.ToLower(strings.Trim(name, " ")))
		names = append(names, strings.Trim(name, " "))
		if len(names[len(names)-1]) == 0 {
			return nil, nil, i
		}
		vals = append(vals, strings.Trim(val, " "))
	}
	return names, vals, -1
}

func checkValidNames(names []string, vars *ConfigVars) int {
	for i := range names {
		found := false
		for j := range vars.varNames {
			if strings.ToLower(vars.varNames[j]) == strings.ToLower(names[i]) {
				found = true
				break
			}
		}
		if !found {
			return i
		}
	}
	return -1
}

func checkDuplicateNames(names []string) (int, int) {
	for i := range names {
		for j := i + 1; j < len(names); j++ {
			if strings.ToLower(names[i]) == strings.ToLower(names[j]) {
				return i, j
			}
		}
	}
	return -1, -1
}

func convertAssoc(names, vals []string, vars *ConfigVars) int {
	for i := range names {
		j := 0
		for ; j < len(vars.varNames); j++ {
			if strings.ToLower(vars.varNames[j]) == strings.ToLower(names[i]) {
				break
			}
		}

		ok := vars.conversionFuncs[j](vals[i])
		if !ok {
			return i
		}
	}
	return -1
}
<|endoftext|>


<commit_before>package permutation

// Naive permutation implementation

import (
	"sort"
)

// Sequence is the interface you need to implement, to allow permutations of it.
// It must consist of independent elements.
type Sequence interface {
	sort.Interface
	// Return whether seqence q matches the receiver
	Equal(q Sequence) bool
	// Create a copy of the sequence. Only the indexes need to be independent.
	Copy() Sequence
}

func nextPermutation(seq Sequence) bool {
	for j := seq.Len() - 1; j > 0; j-- {
		if seq.Less(j-1, j) {
			m := seq.Len() - 1
			for seq.Less(m, j-1) {
				m--
			}
			seq.Swap(j-1, m)
			reverse(seq, j)
			return true
		}
	}
	return false
}

// reverse a sequence of in place.
func reverse(seq Sequence, start int) {
	for i, j := start, seq.Len()-1; i < j; i, j = i+1, j-1 {
		seq.Swap(i, j)
	}
}

// Generic permutation type
type Permutation struct {
	seq Sequence
}

// Initializes a permutation from seqence seq and returns it
// TODO: Always copy it?
func New(seq Sequence) *Permutation {
	if !sort.IsSorted(seq) {
		sort.Sort(seq)
	}
	return &Permutation{seq: seq}
}

// Returns the current sequence
func (p *Permutation) Current() Sequence {
	return p.seq
}

// Generates the next permutation in place and returns, whether it is different.
// Use Current() to get newly generated sequence
func (p *Permutation) Next() bool {
	next, valid := nextPerm(p.seq)
	p.seq = next
	return valid
}

// How many possible permutations of this sequence exist?
func (p *Permutation) Len() int {
	result := 1
	for i := 2; i <= p.seq.Len(); i++ {
		result *= i
	}
	return result
}

func nextPerm(seq Sequence) (next Sequence, valid bool) {
	current := seq.Copy()

	for i := 1; i < seq.Len(); i++ {
		if ok := nextPermutation(seq); !ok {
			return seq, false
		}
		if ok := !seq.Equal(current); ok {
			return seq, true
		}
	}

	return seq, false
}
<commit_msg>fix thinko in algoritmic translation<commit_after>package permutation

// Naive permutation implementation

import (
	"sort"
)

// Sequence is the interface you need to implement, to allow permutations of it.
// It must consist of independent elements.
type Sequence interface {
	sort.Interface
	// Return whether seqence q matches the receiver
	Equal(q Sequence) bool
	// Create a copy of the sequence. Only the indexes need to be independent.
	Copy() Sequence
}

func nextPermutation(seq Sequence) bool {
	for j := seq.Len() - 1; j > 0; j-- {
		if seq.Less(j-1, j) {
			m := seq.Len() - 1
			for !seq.Less(j-1, m) && m > j {
				m--
			}
			seq.Swap(j-1, m)
			reverse(seq, j)
			return true
		}
	}
	return false
}

// reverse a sequence of in place.
func reverse(seq Sequence, start int) {
	for i, j := start, seq.Len()-1; i < j; i, j = i+1, j-1 {
		seq.Swap(i, j)
	}
}

// Generic permutation type
type Permutation struct {
	seq Sequence
}

// Initializes a permutation from seqence seq and returns it
// TODO: Always copy it?
func New(seq Sequence) *Permutation {
	if !sort.IsSorted(seq) {
		sort.Sort(seq)
	}
	return &Permutation{seq: seq}
}

// Returns the current sequence
func (p *Permutation) Current() Sequence {
	return p.seq
}

// Generates the next permutation in place and returns, whether it is different.
// Use Current() to get newly generated sequence
func (p *Permutation) Next() bool {
	next, valid := nextPerm(p.seq)
	p.seq = next
	return valid
}

// How many possible permutations of this sequence exist?
func (p *Permutation) Len() int {
	result := 1
	for i := 2; i <= p.seq.Len(); i++ {
		result *= i
	}
	return result
}

func nextPerm(seq Sequence) (next Sequence, valid bool) {
	current := seq.Copy()

	for i := 1; i < seq.Len(); i++ {
		if ok := nextPermutation(seq); !ok {
			return seq, false
		}
		if ok := !seq.Equal(current); ok {
			return seq, true
		}
	}

	return seq, false
}
<|endoftext|>