<commit_before>package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"
)

const (
	version = "0.8.6"
)

func init() {
	flag.Usage = func() {
		h := "Usage:\n"
		h += "  bozr [OPTIONS] (DIR|FILE)\n\n"

		h += "Options:\n"
		h += "  -d, --debug		Enable debug mode\n"
		h += "  -H, --host		Server to test\n"
		h += "  -h, --help		Print usage\n"
		h += "  -i, --info		Enable info mode. Print request and response details.\n"
		h += "      --junit		Enable junit xml reporter\n"
		h += "  -v, --version		Print version information and quit\n\n"

		h += "Examples:\n"
		h += "  bozr ./examples\n"
		h += "  bozr -H http://example.com ./examples \n"

		fmt.Fprintf(os.Stderr, h)
	}
}

var (
	suiteDir    string
	hostFlag    string
	infoFlag    bool
	debugFlag   bool
	helpFlag    bool
	versionFlag bool
	junitFlag   bool

	info  *log.Logger
	debug *log.Logger
)

func initLogger() {
	infoHandler := ioutil.Discard
	debugHandler := ioutil.Discard

	if infoFlag {
		infoHandler = os.Stdout
	}

	if debugFlag {
		debugHandler = os.Stdout
	}

	info = log.New(infoHandler, "", 0)
	debug = log.New(debugHandler, "DEBUG: ", log.Ltime|log.Lshortfile)
}

func main() {
	flag.BoolVar(&debugFlag, "d", false, "Enable debug mode.")
	flag.BoolVar(&debugFlag, "debug", false, "Enable debug mode")

	flag.BoolVar(&infoFlag, "i", false, "Enable info mode. Print request and response details.")
	flag.BoolVar(&infoFlag, "info", false, "Enable info mode. Print request and response details.")

	flag.StringVar(&hostFlag, "H", "", "Test server address. Example: http://example.com/api.")

	flag.BoolVar(&helpFlag, "h", false, "Print usage")
	flag.BoolVar(&helpFlag, "help", false, "Print usage")

	flag.BoolVar(&versionFlag, "v", false, "Print version information and quit")
	flag.BoolVar(&versionFlag, "version", false, "Print version information and quit")

	flag.BoolVar(&junitFlag, "junit", false, "Enable junit xml reporter")

	flag.Parse()

	initLogger()

	if versionFlag {
		fmt.Println("bozr version " + version)
		return
	}

	if helpFlag {
		flag.Usage()
		return
	}

	if len(hostFlag) > 0 {
		_, err := url.ParseRequestURI(hostFlag)
		if err != nil {
			fmt.Println("Invalid host is specified.")
			os.Exit(1)
			return
		}
	}

	src := flag.Arg(0)

	if src == "" {
		fmt.Print("You must specify a directory or file with tests.\n\n")
		flag.Usage()
		os.Exit(1)
		return
	}

	// check specified source dir/file exists
	_, err := os.Lstat(src)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
		return
	}

	var ch <-chan TestSuite
	if filepath.Ext(src) == "" {
		debug.Print("Loading from directory")
		suiteDir = src
		ch = NewDirLoader(suiteDir)
	} else {
		debug.Print("Loading from file")
		suiteDir = filepath.Dir(src)
		ch = NewFileLoader(src)
	}

	reporters := []Reporter{NewConsoleReporter()}
	if junitFlag {
		path, _ := filepath.Abs("./report")
		reporters = append(reporters, NewJUnitReporter(path))
	}
	reporter := NewMultiReporter(reporters...)

	// test case runner?
	for suite := range ch {
		for _, testCase := range suite.Cases {

			result := TestResult{
				Suite: suite,
				Case:  testCase,
			}

			if testCase.Ignore != nil {
				result.Skipped = true
				if msg, ok := testCase.Ignore.(string); ok {
					result.SkippedMsg = msg
				}
				reporter.Report(result)
				continue
			}

			rememberedMap := make(map[string]interface{})
			start := time.Now()
			for _, c := range testCase.Calls {
				addAll(c.Args, rememberedMap)
				terr := call(suite, testCase, c, rememberedMap)
				if terr != nil {
					result.Error = terr
					break
				}
			}

			result.Duration = time.Since(start)

			reporter.Report(result)
		}
	}

	reporter.Flush()
}

func addAll(src, target map[string]interface{}) {
	for key, val := range src {
		target[key] = val
	}
}

func call(testSuite TestSuite, testCase TestCase, call Call, rememberMap map[string]interface{}) *TError {
	debug.Printf("Starting call: %s - %s", testSuite.Name, testCase.Name)
	terr := &TError{}

	on := call.On

	dat := []byte(on.Body)
	if on.BodyFile != "" {
		uri, err := toAbsPath(testSuite.Dir, on.BodyFile)
		if err != nil {
			terr.Cause = err
			return terr
		}

		if d, err := ioutil.ReadFile(uri); err == nil {
			dat = d
		} else {
			terr.Cause = fmt.Errorf("Can't read body file: %s", err.Error())
			return terr
		}
	}

	req, err := populateRequest(on, string(dat), rememberMap)
	if err != nil {
		terr.Cause = err
		return terr
	}

	printRequestInfo(req, dat)

	client := &http.Client{}

	resp, err := client.Do(req)

	if err != nil {
		debug.Print("Error when sending request", err)
		terr.Cause = err
		return terr
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		debug.Print("Error reading response")
		terr.Cause = err
		return terr
	}

	testResp := Response{http: *resp, body: body}
	terr.Resp = testResp

	info.Println(strings.Repeat("-", 50))
	info.Println(testResp.ToString())
	info.Println("")

	exps, err := expectations(call, testSuite.Dir)
	if err != nil {
		terr.Cause = err
		return terr
	}

	for _, exp := range exps {
		checkErr := exp.check(testResp)
		if checkErr != nil {
			terr.Cause = checkErr
			return terr
		}
	}

	m, err := testResp.parseBody()
	if err != nil {
		debug.Print("Can't parse response body to Map for [remember]")
		terr.Cause = err
		return terr
	}

	err = rememberBody(m, call.Remember(RememberSourceBody), rememberMap)
	debug.Print("Remember: ", rememberMap)
	if err != nil {
		debug.Print("Error remember")
		terr.Cause = err
		return terr
	}

	rememberHeaders(testResp.http.Header, call.Remember(RememberSourceHeader), rememberMap)

	return nil
}

func populateRequest(on On, body string, rememberMap map[string]interface{}) (*http.Request, error) {

	urlStr, err := urlPrefix(populateRememberedVars(on.URL, rememberMap))
	if err != nil {
		return nil, errors.New("Cannot create request. Invalid url: " + on.URL)
	}

	body = populateRememberedVars(body, rememberMap)
	dat := []byte(body)

	req, err := http.NewRequest(on.Method, urlStr, bytes.NewBuffer(dat))
	if err != nil {
		return nil, err
	}

	for key, value := range on.Headers {
		req.Header.Add(key, populateRememberedVars(value, rememberMap))
	}

	q := req.URL.Query()
	for key, value := range on.Params {
		q.Add(key, populateRememberedVars(value, rememberMap))
	}
	req.URL.RawQuery = q.Encode()

	return req, nil
}

func urlPrefix(p string) (string, error) {
	if strings.HasPrefix(p, "http://") || strings.HasPrefix(p, "https://") {
		return p, nil
	}

	return concatURL(hostFlag, p)
}

func concatURL(base string, p string) (string, error) {
	baseURL, err := url.ParseRequestURI(base)
	if err != nil {
		return "", err
	}
	return baseURL.Scheme + "://" + baseURL.Host + path.Join(baseURL.Path, p), nil
}

func populateRememberedVars(str string, rememberMap map[string]interface{}) string {
	res := str
	for varName, val := range rememberMap {
		placeholder := "{" + varName + "}"
		res = strings.Replace(res, placeholder, toString(val), -1)
	}
	return res
}

// toString returns value suitable to insert as an argument
// if value if a float where decimal part is zero - convert to int
func toString(rw interface{}) string {
	var sv interface{} = rw
	if fv, ok := rw.(float64); ok {
		_, frac := math.Modf(fv)
		if frac == 0 {
			sv = int(fv)
		}
	}

	return fmt.Sprintf("%v", sv)
}

func expectations(call Call, srcDir string) ([]ResponseExpectation, error) {
	var exps []ResponseExpectation
	if call.Expect.StatusCode != 0 {
		exps = append(exps, StatusCodeExpectation{statusCode: call.Expect.StatusCode})
	}

	if call.Expect.hasSchema() {
		var (
			schemeURI string
			err       error
		)

		if call.Expect.BodySchemaFile != "" {
			schemeURI, err = toAbsPath(srcDir, call.Expect.BodySchemaFile)
			if err != nil {
				return nil, err
			}
			schemeURI = "file:///" + schemeURI
		}

		if call.Expect.BodySchemaURI != "" {
			isHTTP := strings.HasPrefix(call.Expect.BodySchemaURI, "http://")
			isHTTPS := strings.HasPrefix(call.Expect.BodySchemaURI, "https://")
			if !(isHTTP || isHTTPS) {
				schemeURI = hostFlag + call.Expect.BodySchemaURI
			} else {
				schemeURI = call.Expect.BodySchemaURI
			}
		}
		exps = append(exps, BodySchemaExpectation{schemaURI: schemeURI})
	}

	if len(call.Expect.Body) > 0 {
		exps = append(exps, BodyExpectation{pathExpectations: call.Expect.Body})
	}

	if len(call.Expect.Absent) > 0 {
		exps = append(exps, AbsentExpectation{paths: call.Expect.Absent})
	}

	if len(call.Expect.Headers) > 0 {
		for k, v := range call.Expect.Headers {
			exps = append(exps, HeaderExpectation{Name: k, Value: v})
		}
	}

	if call.Expect.ContentType != "" {
		exps = append(exps, ContentTypeExpectation{call.Expect.ContentType})
	}

	// and so on
	return exps, nil
}

func toAbsPath(srcDir string, assetPath string) (string, error) {
	if filepath.IsAbs(assetPath) {
		// ignore srcDir
		return assetPath, nil
	}

	uri, err := filepath.Abs(filepath.Join(suiteDir, srcDir, assetPath))
	if err != nil {
		return "", errors.New("Invalid file path: " + assetPath)
	}

	return filepath.ToSlash(uri), nil
}

func rememberBody(body interface{}, remember map[string]string, rememberedMap map[string]interface{}) (err error) {

	for varName, pathLine := range remember {

		if rememberVar, err := getByPath(body, pathLine); err == nil {
			rememberedMap[varName] = rememberVar
		} else {
			strErr := fmt.Sprintf("Remembered value not found, path: %v", pathLine)
			err = errors.New(strErr)
		}
		//fmt.Printf("v: %v\n", getByPath(bodyMap, b...))
	}

	return err
}

func rememberHeaders(header http.Header, remember map[string]string, rememberedMap map[string]interface{}) {
	for valueName, headerName := range remember {
		value := header.Get(headerName)
		if value == "" {
			continue
		}

		rememberedMap[valueName] = value
	}
}

func printRequestInfo(req *http.Request, body []byte) {
	info.Println()
	info.Printf("%s %s %s\n", req.Method, req.URL.String(), req.Proto)

	if len(req.Header) > 0 {
		info.Println()
	}

	for k, v := range req.Header {
		info.Printf("%s: %s", k, strings.Join(v, " "))
	}
	info.Println()

	if len(body) > 0 {
		info.Printf(string(body))
	}
}
<commit_msg>better console reporter summary. additionally closes #31<commit_after>package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"
)

const (
	version = "0.8.7"
)

func init() {
	flag.Usage = func() {
		h := "Usage:\n"
		h += "  bozr [OPTIONS] (DIR|FILE)\n\n"

		h += "Options:\n"
		h += "  -d, --debug		Enable debug mode\n"
		h += "  -H, --host		Server to test\n"
		h += "  -h, --help		Print usage\n"
		h += "  -i, --info		Enable info mode. Print request and response details.\n"
		h += "      --junit		Enable junit xml reporter\n"
		h += "  -v, --version		Print version information and quit\n\n"

		h += "Examples:\n"
		h += "  bozr ./examples\n"
		h += "  bozr -H http://example.com ./examples \n"

		fmt.Fprintf(os.Stderr, h)
	}
}

var (
	suiteDir    string
	hostFlag    string
	infoFlag    bool
	debugFlag   bool
	helpFlag    bool
	versionFlag bool
	junitFlag   bool

	info  *log.Logger
	debug *log.Logger
)

func initLogger() {
	infoHandler := ioutil.Discard
	debugHandler := ioutil.Discard

	if infoFlag {
		infoHandler = os.Stdout
	}

	if debugFlag {
		debugHandler = os.Stdout
	}

	info = log.New(infoHandler, "", 0)
	debug = log.New(debugHandler, "DEBUG: ", log.Ltime|log.Lshortfile)
}

func main() {
	flag.BoolVar(&debugFlag, "d", false, "Enable debug mode.")
	flag.BoolVar(&debugFlag, "debug", false, "Enable debug mode")

	flag.BoolVar(&infoFlag, "i", false, "Enable info mode. Print request and response details.")
	flag.BoolVar(&infoFlag, "info", false, "Enable info mode. Print request and response details.")

	flag.StringVar(&hostFlag, "H", "", "Test server address. Example: http://example.com/api.")

	flag.BoolVar(&helpFlag, "h", false, "Print usage")
	flag.BoolVar(&helpFlag, "help", false, "Print usage")

	flag.BoolVar(&versionFlag, "v", false, "Print version information and quit")
	flag.BoolVar(&versionFlag, "version", false, "Print version information and quit")

	flag.BoolVar(&junitFlag, "junit", false, "Enable junit xml reporter")

	flag.Parse()

	initLogger()

	if versionFlag {
		fmt.Println("bozr version " + version)
		return
	}

	if helpFlag {
		flag.Usage()
		return
	}

	if len(hostFlag) > 0 {
		_, err := url.ParseRequestURI(hostFlag)
		if err != nil {
			fmt.Println("Invalid host is specified.")
			os.Exit(1)
			return
		}
	}

	src := flag.Arg(0)

	if src == "" {
		fmt.Print("You must specify a directory or file with tests.\n\n")
		flag.Usage()
		os.Exit(1)
		return
	}

	// check specified source dir/file exists
	_, err := os.Lstat(src)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
		return
	}

	var ch <-chan TestSuite
	if filepath.Ext(src) == "" {
		debug.Print("Loading from directory")
		suiteDir = src
		ch = NewDirLoader(suiteDir)
	} else {
		debug.Print("Loading from file")
		suiteDir = filepath.Dir(src)
		ch = NewFileLoader(src)
	}

	reporters := []Reporter{NewConsoleReporter()}
	if junitFlag {
		path, _ := filepath.Abs("./report")
		reporters = append(reporters, NewJUnitReporter(path))
	}
	reporter := NewMultiReporter(reporters...)

	// test case runner?
	for suite := range ch {
		for _, testCase := range suite.Cases {

			result := TestResult{
				Suite: suite,
				Case:  testCase,
			}

			if testCase.Ignore != nil {
				result.Skipped = true
				if msg, ok := testCase.Ignore.(string); ok {
					result.SkippedMsg = msg
				}
				reporter.Report(result)
				continue
			}

			rememberedMap := make(map[string]interface{})
			start := time.Now()
			for _, c := range testCase.Calls {
				addAll(c.Args, rememberedMap)
				terr := call(suite, testCase, c, rememberedMap)
				if terr != nil {
					result.Error = terr
					break
				}
			}

			result.Duration = time.Since(start)

			reporter.Report(result)
		}
	}

	reporter.Flush()
}

func addAll(src, target map[string]interface{}) {
	for key, val := range src {
		target[key] = val
	}
}

func call(testSuite TestSuite, testCase TestCase, call Call, rememberMap map[string]interface{}) *TError {
	debug.Printf("Starting call: %s - %s", testSuite.Name, testCase.Name)
	terr := &TError{}

	on := call.On

	dat := []byte(on.Body)
	if on.BodyFile != "" {
		uri, err := toAbsPath(testSuite.Dir, on.BodyFile)
		if err != nil {
			terr.Cause = err
			return terr
		}

		if d, err := ioutil.ReadFile(uri); err == nil {
			dat = d
		} else {
			terr.Cause = fmt.Errorf("Can't read body file: %s", err.Error())
			return terr
		}
	}

	req, err := populateRequest(on, string(dat), rememberMap)
	if err != nil {
		terr.Cause = err
		return terr
	}

	printRequestInfo(req, dat)

	client := &http.Client{}

	resp, err := client.Do(req)

	if err != nil {
		debug.Print("Error when sending request", err)
		terr.Cause = err
		return terr
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		debug.Print("Error reading response")
		terr.Cause = err
		return terr
	}

	testResp := Response{http: *resp, body: body}
	terr.Resp = testResp

	info.Println(strings.Repeat("-", 50))
	info.Println(testResp.ToString())
	info.Println("")

	exps, err := expectations(call, testSuite.Dir)
	if err != nil {
		terr.Cause = err
		return terr
	}

	for _, exp := range exps {
		checkErr := exp.check(testResp)
		if checkErr != nil {
			terr.Cause = checkErr
			return terr
		}
	}

	m, err := testResp.parseBody()
	if err != nil {
		debug.Print("Can't parse response body to Map for [remember]")
		terr.Cause = err
		return terr
	}

	err = rememberBody(m, call.Remember(RememberSourceBody), rememberMap)
	debug.Print("Remember: ", rememberMap)
	if err != nil {
		debug.Print("Error remember")
		terr.Cause = err
		return terr
	}

	rememberHeaders(testResp.http.Header, call.Remember(RememberSourceHeader), rememberMap)

	return nil
}

func populateRequest(on On, body string, rememberMap map[string]interface{}) (*http.Request, error) {

	urlStr, err := urlPrefix(populateRememberedVars(on.URL, rememberMap))
	if err != nil {
		return nil, errors.New("Cannot create request. Invalid url: " + on.URL)
	}

	body = populateRememberedVars(body, rememberMap)
	dat := []byte(body)

	req, err := http.NewRequest(on.Method, urlStr, bytes.NewBuffer(dat))
	if err != nil {
		return nil, err
	}

	for key, value := range on.Headers {
		req.Header.Add(key, populateRememberedVars(value, rememberMap))
	}

	q := req.URL.Query()
	for key, value := range on.Params {
		q.Add(key, populateRememberedVars(value, rememberMap))
	}
	req.URL.RawQuery = q.Encode()

	return req, nil
}

func urlPrefix(p string) (string, error) {
	if strings.HasPrefix(p, "http://") || strings.HasPrefix(p, "https://") {
		return p, nil
	}

	return concatURL(hostFlag, p)
}

func concatURL(base string, p string) (string, error) {
	baseURL, err := url.ParseRequestURI(base)
	if err != nil {
		return "", err
	}
	return baseURL.Scheme + "://" + baseURL.Host + path.Join(baseURL.Path, p), nil
}

func populateRememberedVars(str string, rememberMap map[string]interface{}) string {
	res := str
	for varName, val := range rememberMap {
		placeholder := "{" + varName + "}"
		res = strings.Replace(res, placeholder, toString(val), -1)
	}
	return res
}

// toString returns value suitable to insert as an argument
// if value if a float where decimal part is zero - convert to int
func toString(rw interface{}) string {
	var sv interface{} = rw
	if fv, ok := rw.(float64); ok {
		_, frac := math.Modf(fv)
		if frac == 0 {
			sv = int(fv)
		}
	}

	return fmt.Sprintf("%v", sv)
}

func expectations(call Call, srcDir string) ([]ResponseExpectation, error) {
	var exps []ResponseExpectation
	if call.Expect.StatusCode != 0 {
		exps = append(exps, StatusCodeExpectation{statusCode: call.Expect.StatusCode})
	}

	if call.Expect.hasSchema() {
		var (
			schemeURI string
			err       error
		)

		if call.Expect.BodySchemaFile != "" {
			schemeURI, err = toAbsPath(srcDir, call.Expect.BodySchemaFile)
			if err != nil {
				return nil, err
			}
			schemeURI = "file:///" + schemeURI
		}

		if call.Expect.BodySchemaURI != "" {
			isHTTP := strings.HasPrefix(call.Expect.BodySchemaURI, "http://")
			isHTTPS := strings.HasPrefix(call.Expect.BodySchemaURI, "https://")
			if !(isHTTP || isHTTPS) {
				schemeURI = hostFlag + call.Expect.BodySchemaURI
			} else {
				schemeURI = call.Expect.BodySchemaURI
			}
		}
		exps = append(exps, BodySchemaExpectation{schemaURI: schemeURI})
	}

	if len(call.Expect.Body) > 0 {
		exps = append(exps, BodyExpectation{pathExpectations: call.Expect.Body})
	}

	if len(call.Expect.Absent) > 0 {
		exps = append(exps, AbsentExpectation{paths: call.Expect.Absent})
	}

	if len(call.Expect.Headers) > 0 {
		for k, v := range call.Expect.Headers {
			exps = append(exps, HeaderExpectation{Name: k, Value: v})
		}
	}

	if call.Expect.ContentType != "" {
		exps = append(exps, ContentTypeExpectation{call.Expect.ContentType})
	}

	// and so on
	return exps, nil
}

func toAbsPath(srcDir string, assetPath string) (string, error) {
	if filepath.IsAbs(assetPath) {
		// ignore srcDir
		return assetPath, nil
	}

	uri, err := filepath.Abs(filepath.Join(suiteDir, srcDir, assetPath))
	if err != nil {
		return "", errors.New("Invalid file path: " + assetPath)
	}

	return filepath.ToSlash(uri), nil
}

func rememberBody(body interface{}, remember map[string]string, rememberedMap map[string]interface{}) (err error) {

	for varName, pathLine := range remember {

		if rememberVar, err := getByPath(body, pathLine); err == nil {
			rememberedMap[varName] = rememberVar
		} else {
			strErr := fmt.Sprintf("Remembered value not found, path: %v", pathLine)
			err = errors.New(strErr)
		}
		//fmt.Printf("v: %v\n", getByPath(bodyMap, b...))
	}

	return err
}

func rememberHeaders(header http.Header, remember map[string]string, rememberedMap map[string]interface{}) {
	for valueName, headerName := range remember {
		value := header.Get(headerName)
		if value == "" {
			continue
		}

		rememberedMap[valueName] = value
	}
}

func printRequestInfo(req *http.Request, body []byte) {
	info.Println()
	info.Printf("%s %s %s\n", req.Method, req.URL.String(), req.Proto)

	if len(req.Header) > 0 {
		info.Println()
	}

	for k, v := range req.Header {
		info.Printf("%s: %s", k, strings.Join(v, " "))
	}
	info.Println()

	if len(body) > 0 {
		info.Printf(string(body))
	}
}
<|endoftext|>


<commit_before>package main

import "os"
import "fmt"
import "flag"
import "time"
import "runtime"
import "net/http"
import _ "net/http/pprof"

import "github.com/bnclabs/golog"
import "github.com/cloudfoundry/gosigar"

// TODO: add Validate for llrb and mvcc.

var options struct {
	db       string
	ref      string
	cpu      int
	load     int
	writes   int
	reads    int
	keylen   int
	vallen   int
	bogn     string
	capacity int
	memstore string
	period   int
	lsm      bool
	seed     int
}

func optparse(args []string) {
	f := flag.NewFlagSet("dbperf", flag.ExitOnError)
	cpu := (runtime.GOMAXPROCS(-1) / 2) - 1
	if cpu <= 0 {
		cpu = 2
	}
	_, _, freeram := getsysmem()

	f.StringVar(&options.db, "db", "llrb",
		"lmdb|badger|llrb|mvcc|bubt|bogn store type")
	f.StringVar(&options.ref, "ref", "lmdb", "lmdb|badger store type")
	f.IntVar(&options.cpu, "cpu", cpu, "lmdb|llrb|mvcc|bubt|bogn store type")
	f.IntVar(&options.load, "load", 1000000, "number of entries to load initially")
	f.IntVar(&options.writes, "writes", 10000000, "total number of writes")
	f.IntVar(&options.reads, "reads", 10000000, "total number of read operations")
	f.IntVar(&options.keylen, "key", 32, "key size")
	f.IntVar(&options.vallen, "value", 32, "value size")
	f.IntVar(&options.seed, "seed", 0, "seed value to generate randomness")
	f.StringVar(&options.bogn, "bogn", "memonly", "memonly|durable|dgm|workset")
	f.IntVar(&options.capacity, "capacity", int(freeram), "in dgm, memory capacity")
	f.StringVar(&options.memstore, "memstore", "mvcc", "llrb|mvcc for bogn")
	f.IntVar(&options.period, "period", 10, "bogn flush period, in seconds")
	f.BoolVar(&options.lsm, "lsm", false, "use LSM deletes")
	f.Parse(args)

	if options.seed == 0 {
		options.seed = int(time.Now().UnixNano())
	}
	if options.vallen > 0 && options.vallen < 16 {
		fmt.Println("value length should be atleast 16 bytes")
		os.Exit(1)
	}
}

func main() {
	optparse(os.Args[1:])

	go func() {
		log.Infof("%v", http.ListenAndServe("localhost:6060", nil))
	}()

	if options.db == "lmdb" {
		testlmdb()
	} else if options.db == "badger" {
		testbadger()
	} else if options.db == "llrb" && options.ref == "lmdb" {
		(&TestLLRB{}).llrbwithlmdb()
	} else if options.db == "llrb" && options.ref == "badger" {
		(&TestLLRB{}).llrbwithbadg()
	} else if options.db == "mvcc" && options.ref == "lmdb" {
		(&TestMVCC{}).mvccwithlmdb()
	} else if options.db == "mvcc" && options.ref == "badg" {
		(&TestMVCC{}).mvccwithbadg()
	} else if options.db == "bubt" {
		testbubt()
	} else if options.db == "bogn" {
		testbogn()
	}
}

func getsysmem() (total, used, free uint64) {
	mem := sigar.Mem{}
	mem.Get()
	return mem.Total, mem.Used, mem.Free
}
<commit_msg>`npaths` cmdline arg for bubt.<commit_after>package main

import "os"
import "fmt"
import "flag"
import "time"
import "runtime"
import "net/http"
import _ "net/http/pprof"

import "github.com/bnclabs/golog"
import "github.com/cloudfoundry/gosigar"

// TODO: add Validate for llrb and mvcc.

var options struct {
	db       string
	ref      string
	cpu      int
	load     int
	writes   int
	reads    int
	keylen   int
	vallen   int
	bogn     string
	capacity int
	memstore string
	period   int
	lsm      bool
	seed     int
	npaths   int
}

func optparse(args []string) {
	f := flag.NewFlagSet("dbperf", flag.ExitOnError)
	cpu := (runtime.GOMAXPROCS(-1) / 2) - 1
	if cpu <= 0 {
		cpu = 2
	}
	_, _, freeram := getsysmem()

	f.StringVar(&options.db, "db", "llrb",
		"lmdb|badger|llrb|mvcc|bubt|bogn store type")
	f.StringVar(&options.ref, "ref", "lmdb", "lmdb|badger store type")
	f.IntVar(&options.cpu, "cpu", cpu, "lmdb|llrb|mvcc|bubt|bogn store type")
	f.IntVar(&options.load, "load", 1000000, "number of entries to load initially")
	f.IntVar(&options.writes, "writes", 10000000, "total number of writes")
	f.IntVar(&options.reads, "reads", 10000000, "total number of read operations")
	f.IntVar(&options.keylen, "key", 32, "key size")
	f.IntVar(&options.vallen, "value", 32, "value size")
	f.IntVar(&options.seed, "seed", 0, "seed value to generate randomness")
	f.StringVar(&options.bogn, "bogn", "memonly", "memonly|durable|dgm|workset")
	f.IntVar(&options.capacity, "capacity", int(freeram), "in dgm, memory capacity")
	f.StringVar(&options.memstore, "memstore", "mvcc", "llrb|mvcc for bogn")
	f.IntVar(&options.period, "period", 10, "bogn flush period, in seconds")
	f.BoolVar(&options.lsm, "lsm", false, "use LSM deletes")
	f.IntVar(&options.npaths, "npaths", 1, "number of directory paths for bubt")
	f.Parse(args)

	if options.seed == 0 {
		options.seed = int(time.Now().UnixNano())
	}
	if options.vallen > 0 && options.vallen < 16 {
		fmt.Println("value length should be atleast 16 bytes")
		os.Exit(1)
	}
}

func main() {
	optparse(os.Args[1:])

	go func() {
		log.Infof("%v", http.ListenAndServe("localhost:6060", nil))
	}()

	if options.db == "lmdb" {
		testlmdb()
	} else if options.db == "badger" {
		testbadger()
	} else if options.db == "llrb" && options.ref == "lmdb" {
		(&TestLLRB{}).llrbwithlmdb()
	} else if options.db == "llrb" && options.ref == "badger" {
		(&TestLLRB{}).llrbwithbadg()
	} else if options.db == "mvcc" && options.ref == "lmdb" {
		(&TestMVCC{}).mvccwithlmdb()
	} else if options.db == "mvcc" && options.ref == "badg" {
		(&TestMVCC{}).mvccwithbadg()
	} else if options.db == "bubt" {
		testbubt()
	} else if options.db == "bogn" {
		testbogn()
	}
}

func getsysmem() (total, used, free uint64) {
	mem := sigar.Mem{}
	mem.Get()
	return mem.Total, mem.Used, mem.Free
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/koron/gomigemo/embedict"
	"github.com/koron/gomigemo/migemo"
)

const version = "0.1.0"

var flag_n = flag.Bool("n", false, "print line number with output lines")
var flag_H = flag.Bool("H", false, "print the filename for each match")

type grepOpt struct {
	optNumber   bool
	optFilename bool
	filename    string
}

func main() {
	st := _main()
	os.Exit(st)
}

func _main() int {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "multigrep v%s\n\nUsage: multigrep [options] pattern [files...]\n", version)
		flag.PrintDefaults()
	}
	var dictPath = flag.String("d", "", "Alternate location to dictionary")

	flag.Parse()

	if flag.NArg() == 0 {
		flag.Usage()
		return 2
	}

	var dict migemo.Dict
	var err error
	if *dictPath == "" {
		dict, err = embedict.Load()
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 2
		}
	} else {
		dict, err = migemo.Load(*dictPath)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 2
		}
	}

	re, err := migemo.Compile(dict, flag.Arg(0))
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return 2
	}

	opt := &grepOpt{
		optNumber:   *flag_n,
		optFilename: *flag_H || flag.NArg() > 2,
	}

	res := make( []PolarizedMultiMatcher, 1 )
	res[0] = PolarizedMultiMatcher{ matcher: re, polar: true }

	total := 0
	// If there's only one arg, then we need to match against the input
	if flag.NArg() == 1 {
		opt.filename = "stdin"

		if total, err = grep(os.Stdin, res, opt); err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 2
		}

	} else {
		// More than one arg. We must be searching against a file
		for _, arg := range flag.Args()[1:] {
			f, err := os.Open(arg)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				return 2
			}
			defer f.Close()

			opt.filename = arg
			var count int
			if count, err = grep(f, res, opt); err != nil {
				fmt.Fprintln(os.Stderr, err)
				return 2
			}
			total += count
		}
	}

	if total == 0 {
		return 1
	}

	return 0
}
<commit_msg>Introduce match pattern divider<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/koron/gomigemo/embedict"
	"github.com/koron/gomigemo/migemo"
)

const version = "0.1.0"

const separator = " "

var flag_n = flag.Bool("n", false, "print line number with output lines")
var flag_H = flag.Bool("H", false, "print the filename for each match")

type grepOpt struct {
	optNumber   bool
	optFilename bool
	filename    string
}

func main() {
	st := _main()
	os.Exit(st)
}

func _main() int {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "multigrep v%s\n\nUsage: multigrep [options] pattern [files...]\n", version)
		flag.PrintDefaults()
	}
	var dictPath = flag.String("d", "", "Alternate location to dictionary")

	flag.Parse()

	if flag.NArg() == 0 {
		flag.Usage()
		return 2
	}

	var dict migemo.Dict
	var err error
	if *dictPath == "" {
		dict, err = embedict.Load()
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 2
		}
	} else {
		dict, err = migemo.Load(*dictPath)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 2
		}
	}

	res := make( []PolarizedMultiMatcher, 0, 10 )
	patterns := strings.Split(flag.Arg(0), separator)
	for _, pat := range patterns {
		re, err := migemo.Compile(dict, pat)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 2
		}
		res = append( res, PolarizedMultiMatcher{ matcher: re, polar: true } )
	}

	opt := &grepOpt{
		optNumber:   *flag_n,
		optFilename: *flag_H || flag.NArg() > 2,
	}

	total := 0
	// If there's only one arg, then we need to match against the input
	if flag.NArg() == 1 {
		opt.filename = "stdin"

		if total, err = grep(os.Stdin, res, opt); err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 2
		}

	} else {
		// More than one arg. We must be searching against a file
		for _, arg := range flag.Args()[1:] {
			f, err := os.Open(arg)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				return 2
			}
			defer f.Close()

			opt.filename = arg
			var count int
			if count, err = grep(f, res, opt); err != nil {
				fmt.Fprintln(os.Stderr, err)
				return 2
			}
			total += count
		}
	}

	if total == 0 {
		return 1
	}

	return 0
}
<|endoftext|>


<commit_before>package main

// about using johnniedoe/contrib/gzip:
// johnniedoe's fork fixes a critical issue for which .String resulted in
// an ERR_DECODING_FAILED. This is an actual pull request on the contrib
// repo, but apparently, gin is dead.

import (
	"encoding/gob"
	"fmt"

	"git.zxq.co/ripple/rippleapi/app"
	"git.zxq.co/ripple/schiavolib"
	"git.zxq.co/x/rs"
	"github.com/fatih/structs"
	"github.com/getsentry/raven-go"
	"github.com/gin-gonic/contrib/sessions"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
	"github.com/johnniedoe/contrib/gzip"
	"github.com/thehowl/conf"
	"github.com/thehowl/qsql"
	"gopkg.in/mailgun/mailgun-go.v1"
)

// version is the version of hanayo
const version = "0.5.1b"

var (
	config struct {
		ListenTo string `description:"ip:port from which to take requests."`
		Unix     bool   `description:"Whether ListenTo is an unix socket."`

		DSN string `description:"MySQL server DSN"`

		CookieSecret string

		RedisEnable         bool
		RedisMaxConnections int
		RedisNetwork        string
		RedisAddress        string
		RedisPassword       string

		AvatarURL     string
		BaseURL       string
		DiscordServer string

		API       string
		BanchoAPI string
		APISecret string

		IP_API string

		Offline          bool   `description:"If this is true, files will be served from the local server instead of the CDN."`
		MainRippleFolder string `description:"Folder where all the non-go projects are contained, such as old-frontend, lets, ci-system."`
		AvatarsFolder    string `description:"location folder of avatars"`

		MailgunDomain        string
		MailgunPrivateAPIKey string
		MailgunPublicAPIKey  string
		MailgunFrom          string

		RecaptchaSite    string
		RecaptchaPrivate string

		DiscordOAuthID     string
		DiscordOAuthSecret string
		DonorBotURL        string
		DonorBotSecret     string

		SentryDSN string
	}
	configMap map[string]interface{}
	db        *sqlx.DB
	qb        *qsql.DB
	mg        mailgun.Mailgun
)

func main() {
	fmt.Println("hanayo v" + version)

	err := conf.Load(&config, "hanayo.conf")
	switch err {
	case nil:
		// carry on
	case conf.ErrNoFile:
		conf.Export(config, "hanayo.conf")
		fmt.Println("The configuration file was not found. We created one for you.")
		return
	default:
		panic(err)
	}

	var configDefaults = map[*string]string{
		&config.ListenTo:         ":45221",
		&config.CookieSecret:     rs.String(46),
		&config.AvatarURL:        "https://a.ripple.moe",
		&config.BaseURL:          "https://ripple.moe",
		&config.BanchoAPI:        "https://c.ripple.moe",
		&config.API:              "http://localhost:40001/api/v1/",
		&config.APISecret:        "Potato",
		&config.IP_API:           "https://ip.zxq.co",
		&config.DiscordServer:    "#",
		&config.MainRippleFolder: "/home/ripple/ripple",
		&config.MailgunFrom:      `"Ripple" <noreply@ripple.moe>`,
	}
	for key, value := range configDefaults {
		if *key == "" {
			*key = value
		}
	}

	configMap = structs.Map(config)

	// initialise db
	db, err = sqlx.Open("mysql", config.DSN)
	if err != nil {
		panic(err)
	}
	qb = qsql.New(db.DB)
	if err != nil {
		panic(err)
	}

	// initialise mailgun
	mg = mailgun.NewMailgun(
		config.MailgunDomain,
		config.MailgunPrivateAPIKey,
		config.MailgunPublicAPIKey,
	)

	if gin.Mode() == gin.DebugMode {
		fmt.Println("Development environment detected. Starting fsnotify on template folder...")
		err := reloader()
		if err != nil {
			fmt.Println(err)
		}
	}

	schiavo.Prefix = "hanayo"
	schiavo.Bunker.Send(fmt.Sprintf("STARTUATO, mode: %s", gin.Mode()))

	// even if it's not release, we say that it's release
	// so that gin doesn't spam
	gin.SetMode(gin.ReleaseMode)

	gobRegisters := []interface{}{
		[]message{},
		errorMessage{},
		infoMessage{},
		neutralMessage{},
		warningMessage{},
		successMessage{},
	}
	for _, el := range gobRegisters {
		gob.Register(el)
	}

	fmt.Println("Importing templates...")
	loadTemplates("")

	fmt.Println("Setting up rate limiter...")
	setUpLimiter()

	fmt.Println("Exporting configuration...")

	conf.Export(config, "hanayo.conf")

	httpLoop()
}

func httpLoop() {
	for {
		e := generateEngine()
		fmt.Println("Starting webserver...")
		if !startuato(e) {
			break
		}
	}
}

func generateEngine() *gin.Engine {
	fmt.Println("Starting session system...")
	var store sessions.Store
	if config.RedisMaxConnections != 0 {
		var err error
		store, err = sessions.NewRedisStore(
			config.RedisMaxConnections,
			config.RedisNetwork,
			config.RedisAddress,
			config.RedisPassword,
			[]byte(config.CookieSecret),
		)
		if err != nil {
			fmt.Println(err)
			store = sessions.NewCookieStore([]byte(config.CookieSecret))
		}
	} else {
		store = sessions.NewCookieStore([]byte(config.CookieSecret))
	}

	r := gin.Default()

	// sentry
	if config.SentryDSN != "" {
		ravenClient, err := raven.New(config.SentryDSN)
		if err != nil {
			fmt.Println(err)
		} else {
			r.Use(app.Recovery(ravenClient, false))
		}
	}

	r.Use(
		gzip.Gzip(gzip.DefaultCompression),
		checkRedirect,
		sessions.Sessions("session", store),
		sessionInitializer(),
		rateLimiter(false),
		twoFALock,
	)

	r.Static("/static", "static")
	r.StaticFile("/favicon.ico", "static/favicon.ico")

	r.POST("/login", loginSubmit)
	r.GET("/logout", logout)

	r.GET("/register", register)
	r.POST("/register", registerSubmit)
	r.GET("/register/verify", verifyAccount)
	r.GET("/register/welcome", welcome)

	r.GET("/u/:user", userProfile)

	r.POST("/pwreset", passwordReset)
	r.GET("/pwreset/continue", passwordResetContinue)
	r.POST("/pwreset/continue", passwordResetContinueSubmit)

	r.GET("/2fa_gateway", tfaGateway)
	r.GET("/2fa_gateway/clear", clear2fa)
	r.GET("/2fa_gateway/verify", verify2fa)

	r.GET("/irc/generate", ircGenToken)

	r.GET("/settings/password", changePassword)
	r.POST("/settings/password", changePasswordSubmit)
	r.POST("/settings/userpage/parse", parseBBCode)
	r.POST("/settings/avatar", avatarSubmit)
	r.POST("/settings/2fa/disable", disable2fa)
	r.GET("/settings/discord/finish", discordFinish)

	loadSimplePages(r)

	r.NoRoute(notFound)

	return r
}

const alwaysRespondText = `Ooops! Looks like something went really wrong while trying to process your request.
Perhaps report this to a Ripple developer?
Retrying doing again what you were trying to do might work, too.`
<commit_msg>⬆ 0.6b ⬆<commit_after>package main

// about using johnniedoe/contrib/gzip:
// johnniedoe's fork fixes a critical issue for which .String resulted in
// an ERR_DECODING_FAILED. This is an actual pull request on the contrib
// repo, but apparently, gin is dead.

import (
	"encoding/gob"
	"fmt"

	"git.zxq.co/ripple/rippleapi/app"
	"git.zxq.co/ripple/schiavolib"
	"git.zxq.co/x/rs"
	"github.com/fatih/structs"
	"github.com/getsentry/raven-go"
	"github.com/gin-gonic/contrib/sessions"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
	"github.com/johnniedoe/contrib/gzip"
	"github.com/thehowl/conf"
	"github.com/thehowl/qsql"
	"gopkg.in/mailgun/mailgun-go.v1"
)

// version is the version of hanayo
const version = "0.6b"

var (
	config struct {
		ListenTo string `description:"ip:port from which to take requests."`
		Unix     bool   `description:"Whether ListenTo is an unix socket."`

		DSN string `description:"MySQL server DSN"`

		CookieSecret string

		RedisEnable         bool
		RedisMaxConnections int
		RedisNetwork        string
		RedisAddress        string
		RedisPassword       string

		AvatarURL     string
		BaseURL       string
		DiscordServer string

		API       string
		BanchoAPI string
		APISecret string

		IP_API string

		Offline          bool   `description:"If this is true, files will be served from the local server instead of the CDN."`
		MainRippleFolder string `description:"Folder where all the non-go projects are contained, such as old-frontend, lets, ci-system."`
		AvatarsFolder    string `description:"location folder of avatars"`

		MailgunDomain        string
		MailgunPrivateAPIKey string
		MailgunPublicAPIKey  string
		MailgunFrom          string

		RecaptchaSite    string
		RecaptchaPrivate string

		DiscordOAuthID     string
		DiscordOAuthSecret string
		DonorBotURL        string
		DonorBotSecret     string

		SentryDSN string
	}
	configMap map[string]interface{}
	db        *sqlx.DB
	qb        *qsql.DB
	mg        mailgun.Mailgun
)

func main() {
	fmt.Println("hanayo v" + version)

	err := conf.Load(&config, "hanayo.conf")
	switch err {
	case nil:
		// carry on
	case conf.ErrNoFile:
		conf.Export(config, "hanayo.conf")
		fmt.Println("The configuration file was not found. We created one for you.")
		return
	default:
		panic(err)
	}

	var configDefaults = map[*string]string{
		&config.ListenTo:         ":45221",
		&config.CookieSecret:     rs.String(46),
		&config.AvatarURL:        "https://a.ripple.moe",
		&config.BaseURL:          "https://ripple.moe",
		&config.BanchoAPI:        "https://c.ripple.moe",
		&config.API:              "http://localhost:40001/api/v1/",
		&config.APISecret:        "Potato",
		&config.IP_API:           "https://ip.zxq.co",
		&config.DiscordServer:    "#",
		&config.MainRippleFolder: "/home/ripple/ripple",
		&config.MailgunFrom:      `"Ripple" <noreply@ripple.moe>`,
	}
	for key, value := range configDefaults {
		if *key == "" {
			*key = value
		}
	}

	configMap = structs.Map(config)

	// initialise db
	db, err = sqlx.Open("mysql", config.DSN)
	if err != nil {
		panic(err)
	}
	qb = qsql.New(db.DB)
	if err != nil {
		panic(err)
	}

	// initialise mailgun
	mg = mailgun.NewMailgun(
		config.MailgunDomain,
		config.MailgunPrivateAPIKey,
		config.MailgunPublicAPIKey,
	)

	if gin.Mode() == gin.DebugMode {
		fmt.Println("Development environment detected. Starting fsnotify on template folder...")
		err := reloader()
		if err != nil {
			fmt.Println(err)
		}
	}

	schiavo.Prefix = "hanayo"
	schiavo.Bunker.Send(fmt.Sprintf("STARTUATO, mode: %s", gin.Mode()))

	// even if it's not release, we say that it's release
	// so that gin doesn't spam
	gin.SetMode(gin.ReleaseMode)

	gobRegisters := []interface{}{
		[]message{},
		errorMessage{},
		infoMessage{},
		neutralMessage{},
		warningMessage{},
		successMessage{},
	}
	for _, el := range gobRegisters {
		gob.Register(el)
	}

	fmt.Println("Importing templates...")
	loadTemplates("")

	fmt.Println("Setting up rate limiter...")
	setUpLimiter()

	fmt.Println("Exporting configuration...")

	conf.Export(config, "hanayo.conf")

	httpLoop()
}

func httpLoop() {
	for {
		e := generateEngine()
		fmt.Println("Starting webserver...")
		if !startuato(e) {
			break
		}
	}
}

func generateEngine() *gin.Engine {
	fmt.Println("Starting session system...")
	var store sessions.Store
	if config.RedisMaxConnections != 0 {
		var err error
		store, err = sessions.NewRedisStore(
			config.RedisMaxConnections,
			config.RedisNetwork,
			config.RedisAddress,
			config.RedisPassword,
			[]byte(config.CookieSecret),
		)
		if err != nil {
			fmt.Println(err)
			store = sessions.NewCookieStore([]byte(config.CookieSecret))
		}
	} else {
		store = sessions.NewCookieStore([]byte(config.CookieSecret))
	}

	r := gin.Default()

	// sentry
	if config.SentryDSN != "" {
		ravenClient, err := raven.New(config.SentryDSN)
		if err != nil {
			fmt.Println(err)
		} else {
			r.Use(app.Recovery(ravenClient, false))
		}
	}

	r.Use(
		gzip.Gzip(gzip.DefaultCompression),
		checkRedirect,
		sessions.Sessions("session", store),
		sessionInitializer(),
		rateLimiter(false),
		twoFALock,
	)

	r.Static("/static", "static")
	r.StaticFile("/favicon.ico", "static/favicon.ico")

	r.POST("/login", loginSubmit)
	r.GET("/logout", logout)

	r.GET("/register", register)
	r.POST("/register", registerSubmit)
	r.GET("/register/verify", verifyAccount)
	r.GET("/register/welcome", welcome)

	r.GET("/u/:user", userProfile)

	r.POST("/pwreset", passwordReset)
	r.GET("/pwreset/continue", passwordResetContinue)
	r.POST("/pwreset/continue", passwordResetContinueSubmit)

	r.GET("/2fa_gateway", tfaGateway)
	r.GET("/2fa_gateway/clear", clear2fa)
	r.GET("/2fa_gateway/verify", verify2fa)

	r.GET("/irc/generate", ircGenToken)

	r.GET("/settings/password", changePassword)
	r.POST("/settings/password", changePasswordSubmit)
	r.POST("/settings/userpage/parse", parseBBCode)
	r.POST("/settings/avatar", avatarSubmit)
	r.POST("/settings/2fa/disable", disable2fa)
	r.GET("/settings/discord/finish", discordFinish)

	loadSimplePages(r)

	r.NoRoute(notFound)

	return r
}

const alwaysRespondText = `Ooops! Looks like something went really wrong while trying to process your request.
Perhaps report this to a Ripple developer?
Retrying doing again what you were trying to do might work, too.`
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/jlaffaye/ftp"
	"golang.org/x/crypto/bcrypt"
)

var (
	rpcListenPort = flag.Int("rpc-listen-port", 7800, "Specify a port number for JSON-RPC server to listen to. Possible values: 1024-65535")
	rpcSecret     = flag.String("rpc-secret", "", "Set RPC secret authorization token (required)")
	maxRetries    = flag.Int("max-retries", 5, "The maximum number of sequential tries of an operation without success. Possible values: 1-100")

	n = flag.Int("n", 4, "Number of connections to use when downloading single file. Possible values: 1-100")
	o = flag.String("o", "", "Output directory (optional, default value is the current working directory)")
	p = flag.Int("p", 1, "Number of files to download in parallel when mirroring directories. Possible values: 1-10")
	s = flag.String("s", "", "Script to run after successful download")

	connectTimeout = 5 * time.Second

	// Info is used for logging information.
	Info = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)

	// Error is used for logging errors.
	Error = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)

	errMissingURL           = errors.New("No URL specified in a request")
	errProtocolMismatch     = errors.New("Only HTTP/FTP downloads are supported")
	errInvalidRequestFormat = errors.New("Invalid request format")
	errTokenMismatch        = errors.New("Secret token does not match")
	errUnauthorized         = errors.New("Missing or invalid credentials")
)

// Request represents single request for mirroring one FTP directory or a file.
type Request struct {
	Path     string `json:"path"`
	Username string `json:"username"`
	Password string `json:"password"`
	Secret   string `json:"secret"`
}

// Response represents response to a client with ID for a created job or error message in case of error.
type Response struct {
	ID      string `json:"id"`
	Message string `json:"message"`
}

// Handler implements http.Handler interface and processes download requests sequentially.
type Handler struct {
	Jobs        chan *Job
	HashedToken []byte
}

// JobID is unique identifier of a job.
type JobID [32]byte

// Job is single download request with associated LFTP command and script that will run after download is completed.
type Job struct {
	ID        *JobID
	Command   *exec.Cmd
	ScriptCmd *exec.Cmd
}

func (request *Request) extractURL() (*url.URL, error) {
	if request.Path == "" {
		return nil, errMissingURL
	}

	url, err := url.Parse(request.Path)

	if err != nil || url.Host == "" {
		return nil, fmt.Errorf("Invalid URL: %s", request.Path)
	}

	return url, nil
}

func makeLftpCmd(url *url.URL) string {
	escaped := "/"

	if url.Path != "" {
		escaped = strings.Replace(url.Path, "\"", "\\\"", -1)
	}

	var cmd string

	if url.Scheme == "ftp" && strings.HasSuffix(url.Path, "/") {
		cmd = fmt.Sprintf("mirror --parallel=%d --use-pget-n=%d \"%s\" && exit", *p, *n, escaped)
	} else {
		cmd = fmt.Sprintf("pget -n %d \"%s\" && exit", *n, escaped)
	}

	commands := []string{"set cmd:trace true", fmt.Sprintf("set net:max-retries %d", *maxRetries), cmd, "exit"}
	return strings.Join(commands, "; ")
}

func makeCmd(url *url.URL, username, password string) *exec.Cmd {
	lftpCmd := makeLftpCmd(url)
	var args []string

	if username != "" && password != "" {
		args = []string{"--user", username, "--password", password, "-e", lftpCmd, url.Host}
	} else {
		args = []string{"-e", lftpCmd, url.Host}
	}

	cmd := exec.Command("lftp", args...)

	cmd.Dir = *o
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	return cmd
}

func makeScriptCmd(path string) (*exec.Cmd, error) {
	scriptPath, err := filepath.Abs(*s)

	if err != nil {
		return nil, err
	}

	outputPath := filepath.Join(*o, filepath.Base(path))
	cmd := exec.Command(scriptPath, outputPath)

	cmd.Dir = *o
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	return cmd, nil
}

func connect(url *url.URL, username, password string) error {
	switch url.Scheme {
	case "http", "https":
		return connectHTTP(url, username, password)
	case "ftp":
		return connectFTP(url, username, password)
	}

	return errProtocolMismatch
}

func connectHTTP(url *url.URL, username, password string) error {
	req, err := http.NewRequest(http.MethodGet, url.String(), nil)

	if err != nil {
		return fmt.Errorf("Unable to connect to %s", url.Host)
	}

	req.SetBasicAuth(username, password)

	client := &http.Client{Timeout: connectTimeout}
	resp, err := client.Do(req)

	if err != nil {
		return fmt.Errorf("Unable to connect to %s", url.Host)
	}

	defer resp.Body.Close()

	if resp.StatusCode == http.StatusUnauthorized {
		return errUnauthorized
	}

	return nil
}

func connectFTP(url *url.URL, username, password string) error {
	host, port, err := net.SplitHostPort(url.Host)

	if err != nil {
		host, port = url.Host, strconv.Itoa(21)
	}

	conn, err := ftp.DialTimeout(net.JoinHostPort(host, port), connectTimeout)

	if err != nil {
		return fmt.Errorf("Unable to connect to %s", url.Host)
	}

	if username != "" && password != "" {
		err = conn.Login(username, password)
	} else {
		err = conn.Login("anonymous", "anonymous")
	}

	if err != nil {
		return errUnauthorized
	}

	conn.Logout()
	return nil
}

func newID() *JobID {
	var id JobID

	if _, err := rand.Read(id[:]); err != nil {
		panic("Random number generator failed")
	}

	return &id
}

func (id *JobID) serialize() string {
	return hex.EncodeToString(id[:])
}

func (id *JobID) String() string {
	return hex.EncodeToString(id[:6])
}

func (handler *Handler) processRequest(r *http.Request) (*JobID, error) {
	id := newID()
	Info.Printf("Received download request %s from %s\n", id, r.RemoteAddr)

	var request Request
	decoder := json.NewDecoder(r.Body)

	if err := decoder.Decode(&request); err != nil {
		return nil, errInvalidRequestFormat
	}

	if err := bcrypt.CompareHashAndPassword(handler.HashedToken, []byte(request.Secret)); err != nil {
		return nil, errTokenMismatch
	}

	Info.Printf("Download request %s has URL %s\n", id, request.Path)
	url, err := request.extractURL()

	if err != nil {
		return nil, err
	}

	if err = connect(url, request.Username, request.Password); err != nil {
		return nil, err
	}

	cmd := makeCmd(url, request.Username, request.Password)
	scriptCmd, err := makeScriptCmd(url.Path)

	if err != nil {
		Error.Printf("Error creating script command for request %s: %s", id, err.Error())
	}

	job := Job{ID: id, Command: cmd, ScriptCmd: scriptCmd}

	go func() {
		handler.Jobs <- &job
	}()

	return id, nil
}

func (handler *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	id, err := handler.processRequest(r)

	if err == nil {
		json.NewEncoder(w).Encode(Response{ID: id.serialize()})
		return
	}

	Error.Printf("Invalid request received: %s\n", err)
	status := http.StatusBadRequest

	if err == errUnauthorized {
		status = http.StatusUnauthorized
	}

	w.WriteHeader(status)
	json.NewEncoder(w).Encode(Response{Message: err.Error()})
}

func (handler *Handler) worker() {
	for job := range handler.Jobs {
		Info.Printf("Begin LFTP output for request %s", job.ID)
		err := job.Command.Run()
		Info.Printf("End LFTP output for request %s", job.ID)

		if err != nil {
			Error.Printf("Failed to execute request %s with error: %v\n", job.ID, err)
		} else {
			Info.Printf("Request %s completed", job.ID)
		}

		if err == nil && job.ScriptCmd != nil {
			Info.Printf("Begin script output for request %s", job.ID)
			err = job.ScriptCmd.Run()
			Info.Printf("End script output for request %s", job.ID)

			if err != nil {
				Error.Printf("Failed to execute script for request %s with error: %v\n", job.ID, err)
			} else {
				Info.Printf("Script for request %s completed", job.ID)
			}
		}
	}
}

func getOutputDir(dir string) (string, error) {
	var err error

	if dir == "" {
		if dir, err = os.Getwd(); err != nil {
			return "", err
		}
	}

	abs, err := filepath.Abs(dir)

	if err != nil {
		return "", err
	}

	file, err := os.Stat(abs)

	if err != nil {
		return "", err
	}

	if !file.IsDir() {
		return "", fmt.Errorf("%s is not a directory", abs)
	}

	return abs, nil
}

func main() {
	flag.Parse()

	if (*rpcListenPort < 1024 || *rpcListenPort > 65535) || *rpcSecret == "" {
		flag.Usage()
		os.Exit(1)
	}

	if *maxRetries < 1 || *maxRetries > 100 || *n < 1 || *n > 100 || *p < 1 || *p > 10 {
		flag.Usage()
		os.Exit(1)
	}

	if dir, err := getOutputDir(*o); err != nil {
		log.Fatal(err)
	} else {
		*o = dir
	}

	hashedToken, err := bcrypt.GenerateFromPassword([]byte(*rpcSecret), bcrypt.DefaultCost)

	if err != nil {
		log.Fatal("bcrypt failed to generate hashed token")
	}

	if _, err := exec.LookPath("lftp"); err != nil {
		log.Fatal("LFTP not found")
	}

	handler := &Handler{
		Jobs:        make(chan *Job, 10),
		HashedToken: hashedToken,
	}

	http.Handle("/jsonrpc", handler)
	go handler.worker()

	Info.Printf("Starting LFTP server on port %d\n", *rpcListenPort)
	Info.Printf("Output directory is %s\n", *o)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *rpcListenPort), nil))
}
<commit_msg>Do not run script after download if -s options was not used<commit_after>package main

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/jlaffaye/ftp"
	"golang.org/x/crypto/bcrypt"
)

var (
	rpcListenPort = flag.Int("rpc-listen-port", 7800, "Specify a port number for JSON-RPC server to listen to. Possible values: 1024-65535")
	rpcSecret     = flag.String("rpc-secret", "", "Set RPC secret authorization token (required)")
	maxRetries    = flag.Int("max-retries", 5, "The maximum number of sequential tries of an operation without success. Possible values: 1-100")

	n = flag.Int("n", 4, "Number of connections to use when downloading single file. Possible values: 1-100")
	o = flag.String("o", "", "Output directory (optional, default value is the current working directory)")
	p = flag.Int("p", 1, "Number of files to download in parallel when mirroring directories. Possible values: 1-10")
	s = flag.String("s", "", "Script to run after successful download")

	connectTimeout = 5 * time.Second

	// Info is used for logging information.
	Info = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)

	// Error is used for logging errors.
	Error = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)

	errMissingURL           = errors.New("No URL specified in a request")
	errProtocolMismatch     = errors.New("Only HTTP/FTP downloads are supported")
	errInvalidRequestFormat = errors.New("Invalid request format")
	errTokenMismatch        = errors.New("Secret token does not match")
	errUnauthorized         = errors.New("Missing or invalid credentials")
)

// Request represents single request for mirroring one FTP directory or a file.
type Request struct {
	Path     string `json:"path"`
	Username string `json:"username"`
	Password string `json:"password"`
	Secret   string `json:"secret"`
}

// Response represents response to a client with ID for a created job or error message in case of error.
type Response struct {
	ID      string `json:"id"`
	Message string `json:"message"`
}

// Handler implements http.Handler interface and processes download requests sequentially.
type Handler struct {
	Jobs        chan *Job
	HashedToken []byte
}

// JobID is unique identifier of a job.
type JobID [32]byte

// Job is single download request with associated LFTP command and script that will run after download is completed.
type Job struct {
	ID        *JobID
	Command   *exec.Cmd
	ScriptCmd *exec.Cmd
}

func (request *Request) extractURL() (*url.URL, error) {
	if request.Path == "" {
		return nil, errMissingURL
	}

	url, err := url.Parse(request.Path)

	if err != nil || url.Host == "" {
		return nil, fmt.Errorf("Invalid URL: %s", request.Path)
	}

	return url, nil
}

func makeLftpCmd(url *url.URL) string {
	escaped := "/"

	if url.Path != "" {
		escaped = strings.Replace(url.Path, "\"", "\\\"", -1)
	}

	var cmd string

	if url.Scheme == "ftp" && strings.HasSuffix(url.Path, "/") {
		cmd = fmt.Sprintf("mirror --parallel=%d --use-pget-n=%d \"%s\" && exit", *p, *n, escaped)
	} else {
		cmd = fmt.Sprintf("pget -n %d \"%s\" && exit", *n, escaped)
	}

	commands := []string{"set cmd:trace true", fmt.Sprintf("set net:max-retries %d", *maxRetries), cmd, "exit"}
	return strings.Join(commands, "; ")
}

func makeCmd(url *url.URL, username, password string) *exec.Cmd {
	lftpCmd := makeLftpCmd(url)
	var args []string

	if username != "" && password != "" {
		args = []string{"--user", username, "--password", password, "-e", lftpCmd, url.Host}
	} else {
		args = []string{"-e", lftpCmd, url.Host}
	}

	cmd := exec.Command("lftp", args...)

	cmd.Dir = *o
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	return cmd
}

func makeScriptCmd(path string) (*exec.Cmd, error) {
	if *s == "" {
		return nil, nil
	}

	scriptPath, err := filepath.Abs(*s)

	if err != nil {
		return nil, err
	}

	outputPath := filepath.Join(*o, filepath.Base(path))
	cmd := exec.Command(scriptPath, outputPath)

	cmd.Dir = *o
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	return cmd, nil
}

func connect(url *url.URL, username, password string) error {
	switch url.Scheme {
	case "http", "https":
		return connectHTTP(url, username, password)
	case "ftp":
		return connectFTP(url, username, password)
	}

	return errProtocolMismatch
}

func connectHTTP(url *url.URL, username, password string) error {
	req, err := http.NewRequest(http.MethodGet, url.String(), nil)

	if err != nil {
		return fmt.Errorf("Unable to connect to %s", url.Host)
	}

	req.SetBasicAuth(username, password)

	client := &http.Client{Timeout: connectTimeout}
	resp, err := client.Do(req)

	if err != nil {
		return fmt.Errorf("Unable to connect to %s", url.Host)
	}

	defer resp.Body.Close()

	if resp.StatusCode == http.StatusUnauthorized {
		return errUnauthorized
	}

	return nil
}

func connectFTP(url *url.URL, username, password string) error {
	host, port, err := net.SplitHostPort(url.Host)

	if err != nil {
		host, port = url.Host, strconv.Itoa(21)
	}

	conn, err := ftp.DialTimeout(net.JoinHostPort(host, port), connectTimeout)

	if err != nil {
		return fmt.Errorf("Unable to connect to %s", url.Host)
	}

	if username != "" && password != "" {
		err = conn.Login(username, password)
	} else {
		err = conn.Login("anonymous", "anonymous")
	}

	if err != nil {
		return errUnauthorized
	}

	conn.Logout()
	return nil
}

func newID() *JobID {
	var id JobID

	if _, err := rand.Read(id[:]); err != nil {
		panic("Random number generator failed")
	}

	return &id
}

func (id *JobID) serialize() string {
	return hex.EncodeToString(id[:])
}

func (id *JobID) String() string {
	return hex.EncodeToString(id[:6])
}

func (handler *Handler) processRequest(r *http.Request) (*JobID, error) {
	id := newID()
	Info.Printf("Received download request %s from %s\n", id, r.RemoteAddr)

	var request Request
	decoder := json.NewDecoder(r.Body)

	if err := decoder.Decode(&request); err != nil {
		return nil, errInvalidRequestFormat
	}

	if err := bcrypt.CompareHashAndPassword(handler.HashedToken, []byte(request.Secret)); err != nil {
		return nil, errTokenMismatch
	}

	Info.Printf("Download request %s has URL %s\n", id, request.Path)
	url, err := request.extractURL()

	if err != nil {
		return nil, err
	}

	if err = connect(url, request.Username, request.Password); err != nil {
		return nil, err
	}

	cmd := makeCmd(url, request.Username, request.Password)
	scriptCmd, err := makeScriptCmd(url.Path)

	if err != nil {
		Error.Printf("Error creating script command for request %s: %s", id, err.Error())
	}

	job := Job{ID: id, Command: cmd, ScriptCmd: scriptCmd}

	go func() {
		handler.Jobs <- &job
	}()

	return id, nil
}

func (handler *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	id, err := handler.processRequest(r)

	if err == nil {
		json.NewEncoder(w).Encode(Response{ID: id.serialize()})
		return
	}

	Error.Printf("Invalid request received: %s\n", err)
	status := http.StatusBadRequest

	if err == errUnauthorized {
		status = http.StatusUnauthorized
	}

	w.WriteHeader(status)
	json.NewEncoder(w).Encode(Response{Message: err.Error()})
}

func (handler *Handler) worker() {
	for job := range handler.Jobs {
		Info.Printf("Begin LFTP output for request %s", job.ID)
		err := job.Command.Run()
		Info.Printf("End LFTP output for request %s", job.ID)

		if err != nil {
			Error.Printf("Failed to execute request %s with error: %v\n", job.ID, err)
		} else {
			Info.Printf("Request %s completed", job.ID)
		}

		if err == nil && job.ScriptCmd != nil {
			Info.Printf("Begin script output for request %s", job.ID)
			err = job.ScriptCmd.Run()
			Info.Printf("End script output for request %s", job.ID)

			if err != nil {
				Error.Printf("Failed to execute script for request %s with error: %v\n", job.ID, err)
			} else {
				Info.Printf("Script for request %s completed", job.ID)
			}
		}
	}
}

func getOutputDir(dir string) (string, error) {
	var err error

	if dir == "" {
		if dir, err = os.Getwd(); err != nil {
			return "", err
		}
	}

	abs, err := filepath.Abs(dir)

	if err != nil {
		return "", err
	}

	file, err := os.Stat(abs)

	if err != nil {
		return "", err
	}

	if !file.IsDir() {
		return "", fmt.Errorf("%s is not a directory", abs)
	}

	return abs, nil
}

func main() {
	flag.Parse()

	if (*rpcListenPort < 1024 || *rpcListenPort > 65535) || *rpcSecret == "" {
		flag.Usage()
		os.Exit(1)
	}

	if *maxRetries < 1 || *maxRetries > 100 || *n < 1 || *n > 100 || *p < 1 || *p > 10 {
		flag.Usage()
		os.Exit(1)
	}

	if dir, err := getOutputDir(*o); err != nil {
		log.Fatal(err)
	} else {
		*o = dir
	}

	hashedToken, err := bcrypt.GenerateFromPassword([]byte(*rpcSecret), bcrypt.DefaultCost)

	if err != nil {
		log.Fatal("bcrypt failed to generate hashed token")
	}

	if _, err := exec.LookPath("lftp"); err != nil {
		log.Fatal("LFTP not found")
	}

	handler := &Handler{
		Jobs:        make(chan *Job, 10),
		HashedToken: hashedToken,
	}

	http.Handle("/jsonrpc", handler)
	go handler.worker()

	Info.Printf("Starting LFTP server on port %d\n", *rpcListenPort)
	Info.Printf("Output directory is %s\n", *o)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *rpcListenPort), nil))
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	"fmt"

	"github.com/codegangsta/cli"
)

func main() {
	checkGo15VendorActivated()

	app := cli.NewApp()
	app.Name = "gpm"
	app.Usage = "Package Manager for Go 1.5+"
	app.Authors = []cli.Author{
		{
			Name:  "HectorJ",
			Email: "hector.jusforgues@gmail.com",
		},
	}
	app.EnableBashCompletion = true
	app.Commands = []cli.Command{
		{
			Name:  "vendor",
			Usage: "Scans imports from Go files and vendor them in the current Git repository. Takes files/directories path(s) as arguments",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "r",
					Usage: "Scan dirs recursively",
				},
			},
			Action: vendor,
		},
		{
			Name:    "remove",
			Aliases: []string{"rm"},
			Usage:   "Unvendors an import path. Takes a single import path as argument",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "y",
					Usage: "Remove the submodule without asking any confirmation",
				},
			},
			Action: remove,
		},
	}
	app.RunAndExitOnError()
}

func errorf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
}

func fatalErrorf(format string, args ...interface{}) {
	errorf(format, args...)
	os.Exit(1)
}

func checkGo15VendorActivated() {
	if os.Getenv("GO15VENDOREXPERIMENT") != "1" {
		fmt.Fprint(os.Stderr, "Warning : GO15VENDOREXPERIMENT is not activated.\ngpm relies entirely on that vendoring feature\nTo activate it, run `export GO15VENDOREXPERIMENT=1`\n")
	}
}
<commit_msg>Better autocompletion<commit_after>package main

import (
	"os"

	"fmt"

	"io/ioutil"

	"path"
	"path/filepath"

	"regexp"

	"github.com/codegangsta/cli"
)

var submodulesRegexp = regexp.MustCompile(`\[submodule "vendor/([^"]+)"\]`)

func main() {
	checkGo15VendorActivated()

	app := cli.NewApp()
	app.Name = "gpm"
	app.Usage = "Package Manager for Go 1.5+"
	app.Authors = []cli.Author{
		{
			Name:  "HectorJ",
			Email: "hector.jusforgues@gmail.com",
		},
	}
	app.EnableBashCompletion = true
	app.Commands = []cli.Command{
		{
			Name:  "vendor",
			Usage: "Scans imports from Go files and vendor them in the current Git repository. Takes files/directories path(s) as arguments",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "r",
					Usage: "Scan dirs recursively",
				},
			},
			Action: vendor,
			BashComplete: func(c *cli.Context) {
				// This will complete if no args are passed
				fmt.Println(".")
				fileInfos, err := ioutil.ReadDir(".")
				if err != nil {
					return
				}
				for _, fileInfo := range fileInfos {
					fmt.Println(fileInfo.Name())
				}
			},
		},
		{
			Name:    "remove",
			Aliases: []string{"rm"},
			Usage:   "Unvendors an import path. Takes a single import path as argument",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "y",
					Usage: "Remove the submodule without asking any confirmation",
				},
			},
			Action: remove,
			BashComplete: func(c *cli.Context) {
				currentDir, err := filepath.Abs(path.Dir(os.Args[0]))
				if err != nil {
					return
				}
				gitRoot, err := gitGetRootDir(currentDir)
				if err != nil {
					return
				}
				content, err := ioutil.ReadFile(path.Join(gitRoot, ".gitmodules"))
				if err != nil {
					return
				}

				matches := submodulesRegexp.FindAllStringSubmatch(string(content), -1)
				for _, match := range matches {
					fmt.Println(match[1])
				}
			},
		},
	}
	app.RunAndExitOnError()
}

func errorf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
}

func fatalErrorf(format string, args ...interface{}) {
	errorf(format, args...)
	os.Exit(1)
}

func checkGo15VendorActivated() {
	if os.Getenv("GO15VENDOREXPERIMENT") != "1" {
		fmt.Fprint(os.Stderr, "Warning : GO15VENDOREXPERIMENT is not activated.\ngpm relies entirely on that vendoring feature\nTo activate it, run `export GO15VENDOREXPERIMENT=1`\n")
	}
}
<|endoftext|>


<commit_before>// Package commitfmt provides a git hook that validates the formatting of a
// commit message.
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/gcurtis/commitfmt/rules"
	"io/ioutil"
	"os"
	"strings"
)

// snipLine is the special line recognized by git that tells it to strip the
// rest of a commit message.
const snipLine = "------------------------ >8 ------------------------"

// commentChar is the character git uses for commenting out lines in commit
// messages.
const commentChar = '#'

// confName is the name of the commitfmt configuration file.
const confName = ".commitfmt"

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintln(os.Stderr, "You must provide a path to a file containing"+
			" the commit message.")
		os.Exit(1)
	}

	path := os.Args[1]
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Couldn't open file \"%s\".\n", path)
		os.Exit(1)
	}
	msg := string(bytes)

	conf := readConf()
	report := runRules(msg, conf)
	fmt.Println(report.string())
	if len(report.violations) > 0 {
		os.Exit(1)
	}
}

// runRules parses a commit message and then checks every rule found in the
// rules package.
func runRules(msg string, conf map[string]interface{}) (rep *report) {
	msg = strings.TrimSpace(msg)
	rep = &report{msg: msg}
	subject, body := parseMsg(msg)

	for _, rule := range rules.All {
		if conf != nil {
			ruleConf, ok := conf[rule.Name()]
			if ok && ruleConf != nil {
				if ruleConf == false {
					continue
				} else {
					rule.Config(ruleConf.(map[string]interface{}))
				}
			}
		}

		violations := rule.Check(subject, body)
		rep.append(violations...)
	}

	return
}

// parseMsg parses a message by breaking it up into a subject and a body. It
// will also remove any commented-out or snipped content.
func parseMsg(msg string) (subject string, body string) {
	remComments := bytes.Buffer{}
	split := strings.SplitAfter(msg, "\n")
	for _, line := range split {
		trim := strings.TrimSpace(line)
		if strings.Contains(trim, snipLine) {
			break
		}
		if strings.HasPrefix(trim, string(commentChar)) {
			continue
		}

		remComments.WriteString(line)
	}

	split = strings.SplitN(strings.TrimSpace(remComments.String()), "\n\n", 2)
	subject = split[0]
	if len(split) > 1 {
		body = split[1]
	}
	return
}

func readConf() (conf map[string]interface{}) {
	r, err := os.Open(confName)
	if err != nil {
		return
	}

	err = json.NewDecoder(r).Decode(&conf)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Couldn't parse conf file, proceeding with"+
			" default rules.")
	}
	return
}
<commit_msg>Save bad messages and give help for fixing them<commit_after>// Package commitfmt provides a git hook that validates the formatting of a
// commit message.
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/gcurtis/commitfmt/rules"
	"io/ioutil"
	"os"
	"strings"
)

// snipLine is the special line recognized by git that tells it to strip the
// rest of a commit message.
const snipLine = "------------------------ >8 ------------------------"

// commentChar is the character git uses for commenting out lines in commit
// messages.
const commentChar = '#'

// confName is the name of the commitfmt configuration file.
const confName = ".commitfmt"

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintln(os.Stderr, "You must provide a path to a file containing"+
			" the commit message.")
		os.Exit(1)
	}

	path := os.Args[1]
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Couldn't open file \"%s\".\n", path)
		os.Exit(1)
	}
	msg := string(bytes)

	conf := readConf()
	cleaned := cleanMsg(msg)
	report := runRules(cleaned, conf)
	fmt.Println(report.string())
	if len(report.violations) > 0 {
		// Make a best-effort to save the commit message and provide the user
		// with some help before exiting.
		if f, err := ioutil.TempFile("", "commitfmt"); err == nil {
			_, err := f.WriteString(cleaned)
			if err == nil {
				fmt.Fprintf(os.Stderr, "\nYour commit message has been saved. "+
					"You can edit your previous commit message with:\n"+
					"\tgit commit -e -F %[1]s\n"+
					"or you can bypass this check with:\n"+
					"\tgit commit --no-verify -e -F %[1]s\n",
					f.Name())
			}
			f.Close()
		}
		os.Exit(1)
	}
}

// runRules parses a cleaned commit message and then checks every rule found in
// the rules package.
func runRules(cleanMsg string, conf map[string]interface{}) (rep *report) {
	rep = &report{msg: cleanMsg}
	subject, body := parseMsg(cleanMsg)

	for _, rule := range rules.All {
		if conf != nil {
			ruleConf, ok := conf[rule.Name()]
			if ok && ruleConf != nil {
				if ruleConf == false {
					continue
				} else {
					rule.Config(ruleConf.(map[string]interface{}))
				}
			}
		}

		violations := rule.Check(subject, body)
		rep.append(violations...)
	}

	return
}

// cleanMsg removes any commented-out or snipped content from a commit message.
func cleanMsg(msg string) string {
	remComments := bytes.Buffer{}
	split := strings.SplitAfter(msg, "\n")
	for _, line := range split {
		trim := strings.TrimSpace(line)
		if strings.HasPrefix(trim, string(commentChar)+" "+snipLine) {
			break
		}
		if strings.HasPrefix(trim, string(commentChar)) {
			continue
		}

		remComments.WriteString(line)
	}
	return strings.TrimSpace(remComments.String())
}

// parseMsg parses a cleaned message by breaking it up into a subject and a
// body.
func parseMsg(cleanMsg string) (subject string, body string) {
	split := strings.SplitN(strings.TrimSpace(cleanMsg), "\n\n", 2)
	subject = split[0]
	if len(split) > 1 {
		body = split[1]
	}
	return
}

func readConf() (conf map[string]interface{}) {
	r, err := os.Open(confName)
	if err != nil {
		return
	}

	err = json.NewDecoder(r).Decode(&conf)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Couldn't parse conf file, proceeding with"+
			" default rules.")
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
    "io/ioutil"
    "os"
    "os/exec"
    "github.com/fatih/color"
)

var (
    progPath string
    testDir string
    ioStyle int
)

func main() {
    initVars()

    setIOStyle()

    cmd := exec.Command(progPath)
    err := cmd.Run()
    if err != nil {
        color.Red("Error running program:\n" + err.Error())
        os.Exit(0)
    }
}

func initVars() {
    numArgs := len(os.Args)
    if numArgs >= 2 {
        progPath = os.Args[1]
    } else {
        color.Red("Please specify exectable as first argument")
        os.Exit(0)
    }

    if numArgs >= 3 {
        testDir = os.Args[2]
    } else {
        x, err := os.Getwd()
        if err != nil {
            color.Red("Error getting current working directory:\n" + err.Error())
            os.Exit(0)
        }
        testDir = x
    }
}

func copyTestData(fileName string) {
    b, err := ioutil.ReadFile(fileName)
    if err != nil {
        panic(err)
    }

    err = ioutil.WriteFile(progPath + ".in", b, 0644)
    if err != nil {
        panic(err)
    }
}

// if outputs are not same, return position of nonequivalence; otherwise return -1
func compareOutput(a, b []byte) int {
    fileLen := len(a)
    for i := 0; i < fileLen; i++ {
        if a[i] != b[i] {
            return i
        }
    }
    return -1
}

func readOutput(fileName string) []byte {
    b, err := ioutil.ReadFile(fileName)
    if err != nil {
        panic(err)
    }
    return b
}

// ioStyle 1 is of form "X.in"; ioStyle 2 is of form "I.X";
// TODO: Change function to check if list of files in "testDir" contains "X.in" or "I.X" files using regex
func setIOStyle() {
    if _, err := os.Stat("1.in"); err == nil {
        ioStyle = 1
        return
    } else if _, err = os.Stat("I.1"); err == nil {
        ioStyle = 2
        return
    }
    color.Red("Could not find test input/ouput files:\n" + err.Error())
    os.Exit(0)
}
<commit_msg>update program and commit without testing<commit_after>package main

import (
    "io/ioutil"
    "os"
    "os/exec"
    "strconv"
    "github.com/fatih/color"
)

var (
    progPath string
    testDir string
    ioStyle int
)

func main() {
    initVars()

    setIOStyle()

    i := 1

    if ioStyle == 1 {
        for {
            if _, err := os.Stat(strconv.Itoa(i) + ".in"); os.IsNotExist(err) {
                // TODO: Add number of total test cases passed
                color.White("Finished testing")
                os.Exit(0)
            }

            copyTestData(strconv.Itoa(i) + ".in")

            runProgram()

            a := readOutput(progPath + ".out")
            b := readOutput(strconv.Itoa(i) + ".out")

            if compareOutput(a, b) != -1 {
                color.Red("Wrong output at character " + strconv.Itoa(i) + ":\nExpected: " + string(a[i]) + "\tFound: " + string(b[i]))
            } else {
                color.Green("Test case " + strconv.Itoa(i) + " passed");
            }
            i++
        }
    }

}

func initVars() {
    numArgs := len(os.Args)
    if numArgs >= 2 {
        progPath = os.Args[1]
    } else {
        color.Red("Please specify exectable as first argument")
        os.Exit(0)
    }

    if numArgs >= 3 {
        testDir = os.Args[2]
    } else {
        x, err := os.Getwd()
        if err != nil {
            color.Red("Error getting current working directory:\n" + err.Error())
            os.Exit(0)
        }
        testDir = x
    }
}

func copyTestData(fileName string) {
    b, err := ioutil.ReadFile(fileName)
    if err != nil {
        panic(err)
    }

    err = ioutil.WriteFile(progPath + ".in", b, 0644)
    if err != nil {
        panic(err)
    }
}

// if outputs are not same, return position of nonequivalence; otherwise return -1
func compareOutput(a, b []byte) int {
    fileLen := len(a)
    for i := 0; i < fileLen; i++ {
        if a[i] != b[i] {
            return i
        }
    }
    return -1
}

func readOutput(fileName string) []byte {
    b, err := ioutil.ReadFile(fileName)
    if err != nil {
        panic(err)
    }
    return b
}

// ioStyle 1 is of form "X.in"; ioStyle 2 is of form "I.X";
// TODO: Change function to check if list of files in "testDir" contains "X.in" or "I.X" files using regex
func setIOStyle() {
    if _, err := os.Stat("1.in"); err == nil {
        ioStyle = 1
        return
    } else if _, err = os.Stat("I.1"); err == nil {
        ioStyle = 2
        return
    }
    color.Red("Could not find test input/ouput files")
    os.Exit(0)
}

func runProgram() {
    cmd := exec.Command(progPath)
    err := cmd.Run()
    if err != nil {
        color.Red("Error running program:\n" + err.Error())
        os.Exit(0)
    }
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"os"
	"regexp"
	)

func main() {
	file, err := os.Open("targetlist.txt")

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	defer file.Close()

	scanner := bufio.NewScanner(file)

	r, _ := regexp.Compile("https?://(www.)?[a-zA-Z0-9.]{2,512}.[a-z]{2,10}")

	for scanner.Scan() {
		line := scanner.Text()

		if r.MatchString(line) {
			fmt.Println("Valid: " + line)
		}
	}
}
<commit_msg>Make HTTP request and save to file<commit_after>package main

import (
	"bufio"
	"fmt"
	"os"
	"regexp"
	"net/http"
	"io/ioutil"
	"strings"
	)

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

// Request webcontent from url
func Webrequest(url string) string {
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println(err)
	}
	defer resp.Body.Close()

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

	return string(body)
}

// Write content to file
func SaveFile(File string, ctx string) {
	d1 := []byte(ctx)
	err := ioutil.WriteFile(File, d1, 0644)
	check(err)
}

// Substract name from URL
func Makefilename(URL string) string {
	usz := len(URL)

	if URL[usz-1] == '/' {
		URL = URL[0:usz-1]
	}

	protpos := strings.Index(URL, "//")
	URL = URL[protpos+2:len(URL)]

	return strings.Replace(URL, ".", "_", -1)
}

func main() {
	file, err := os.Open("targetlist.txt")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	r, _ := regexp.Compile("^https?://(www.)?[a-zA-Z0-9.]{2,512}.[a-z]{2,10}/?$")
	for scanner.Scan() {
		line := scanner.Text()

		if r.MatchString(line) {
			fmt.Println("Valid: " + line)
			html := Webrequest(line)
			OutName := Makefilename(line) + ".txt"
			SaveFile(OutName, html)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"
)

//
func main() {
	handle(ClusterStart())
	handle(StatusStart())
	handle(DecisionStart())
	handle(ActionStart())

	// signal Handle
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, os.Kill, syscall.SIGQUIT, syscall.SIGHUP)

	// Block until a signal is received.
	for {
		s := <-c
		switch s {
		case syscall.SIGINT, os.Kill, syscall.SIGQUIT:
			// kill the database then quit
			log.Info("Signal Recieved: %s", s.String())
			if conf.Role == "monitor" {
				log.Info("shutting down")
			} else {
				log.Info("Killing Database")
				actions <- "kill"
				// called twice because the first call returns when the job is picked up
				// the second call returns when the first job is complete
				actions <- "kill"
			}
			log.Close()
			os.Exit(0)
		case syscall.SIGHUP:
			// demote
			log.Info("Signal Recieved: %s", s.String())
			log.Info("advising a demotion")
			advice <- "demote"
		}
	}

}

//
func handle(err error) {
	if err != nil {
		fmt.Println("error: " + err.Error())
		os.Exit(1)
	}
}
<commit_msg>that is somany better<commit_after>package main

import (
	"fmt"
	"os"
	"os/signal"
	"os/exec"
	"syscall"
)

//
func main() {
	// kill postgres server thats running
	log.Info("killing old postgres if there is one")
	killOldPostgres()

	handle(ClusterStart())
	handle(StatusStart())
	handle(DecisionStart())
	handle(ActionStart())

	// signal Handle
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, os.Kill, syscall.SIGQUIT, syscall.SIGHUP)

	// Block until a signal is received.
	for {
		s := <-c
		switch s {
		case syscall.SIGINT, os.Kill, syscall.SIGQUIT:
			// kill the database then quit
			log.Info("Signal Recieved: %s", s.String())
			if conf.Role == "monitor" {
				log.Info("shutting down")
			} else {
				log.Info("Killing Database")
				actions <- "kill"
				// called twice because the first call returns when the job is picked up
				// the second call returns when the first job is complete
				actions <- "kill"
			}
			log.Close()
			os.Exit(0)
		case syscall.SIGHUP:
			// demote
			log.Info("Signal Recieved: %s", s.String())
			log.Info("advising a demotion")
			advice <- "demote"
		}
	}

}

//
func handle(err error) {
	if err != nil {
		fmt.Println("error: " + err.Error())
		os.Exit(1)
	}
}

func killOldPostgres() {
	killOld := exec.Command("pg_ctl", "stop", "-D", conf.DataDir, "-m", "fast")
	killOld.Stdout = Piper{"[KillOLD.stdout]"}
	killOld.Stderr = Piper{"[KillOLD.stderr]"}
	if err := killOld.Run(); err != nil {
		log.Error("[action] KillOLD failed.")
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"github.com/globocom/tsuru/cmd"
	"launchpad.net/gnuflag"
	"os"
)

const (
	version = "0.2.1"
	header  = "Supported-Tsuru"
)

var appName = gnuflag.String("app", "", "App name for running app related commands.")
var logLines = gnuflag.Int("logLines", 10, "The number of log lines to display")

func buildManager(name string) *cmd.Manager {
	m := cmd.BuildBaseManager(name, version, header)
	m.Register(&AppRun{})
	m.Register(&AppInfo{})
	m.Register(&AppCreate{})
	m.Register(&AppRemove{})
	m.Register(&AppList{})
	m.Register(&AppLog{})
	m.Register(&AppGrant{})
	m.Register(&AppRevoke{})
	m.Register(&AppRestart{})
	m.Register(&EnvGet{})
	m.Register(&EnvSet{})
	m.Register(&EnvUnset{})
	m.Register(&KeyAdd{})
	m.Register(&KeyRemove{})
	m.Register(&ServiceList{})
	m.Register(&ServiceAdd{})
	m.Register(&ServiceRemove{})
	m.Register(&ServiceBind{})
	m.Register(&ServiceUnbind{})
	m.Register(&ServiceDoc{})
	m.Register(&ServiceInfo{})
	m.Register(&ServiceInstanceStatus{})
	return m
}

func main() {
	gnuflag.Parse(true)
	name := cmd.ExtractProgramName(os.Args[0])
	manager := buildManager(name)
	args := gnuflag.Args()
	manager.Run(args)
}
<commit_msg>Renamed logLines parameter to lines.<commit_after>// Copyright 2012 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"github.com/globocom/tsuru/cmd"
	"launchpad.net/gnuflag"
	"os"
)

const (
	version = "0.2.1"
	header  = "Supported-Tsuru"
)

var appName = gnuflag.String("app", "", "App name for running app related commands.")
var logLines = gnuflag.Int("lines", 10, "The number of log lines to display")

func buildManager(name string) *cmd.Manager {
	m := cmd.BuildBaseManager(name, version, header)
	m.Register(&AppRun{})
	m.Register(&AppInfo{})
	m.Register(&AppCreate{})
	m.Register(&AppRemove{})
	m.Register(&AppList{})
	m.Register(&AppLog{})
	m.Register(&AppGrant{})
	m.Register(&AppRevoke{})
	m.Register(&AppRestart{})
	m.Register(&EnvGet{})
	m.Register(&EnvSet{})
	m.Register(&EnvUnset{})
	m.Register(&KeyAdd{})
	m.Register(&KeyRemove{})
	m.Register(&ServiceList{})
	m.Register(&ServiceAdd{})
	m.Register(&ServiceRemove{})
	m.Register(&ServiceBind{})
	m.Register(&ServiceUnbind{})
	m.Register(&ServiceDoc{})
	m.Register(&ServiceInfo{})
	m.Register(&ServiceInstanceStatus{})
	return m
}

func main() {
	gnuflag.Parse(true)
	name := cmd.ExtractProgramName(os.Args[0])
	manager := buildManager(name)
	args := gnuflag.Args()
	manager.Run(args)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"flag"
	"os"
	"path"
	"strings"
	"image"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"github.com/Knorkebrot/ansirgb"
	"github.com/olekukonko/ts"
)

type Block struct {
	top	*ansirgb.Color
	bottom	*ansirgb.Color
}

func (b *Block) String() string {
	ret := fmt.Sprintf("\033[48;5;%dm", b.bottom.Code)
	if b.top != nil {
		ret += fmt.Sprintf("\033[38;5;%dm", b.top.Code)
		// If it's not a UTF-8 terminal, fall back to '#'
		if strings.Contains(os.Getenv("LANG"), "UTF-8") ||
		   strings.Contains(os.Getenv("LC_ALL"), "UTF-8") {
			ret += "\u2580"
		} else {
			ret += "#"
		}
	} else {
		ret += " "
	}
	return ret
}

func reset() {
	// add a space to prevent artifacts after resizing
	fmt.Printf("\033[0m ")
}

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s file [file...]\n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	var width int
	flag.IntVar(&width, "w", 0, "Output width, use 0 for terminal width")
	flag.Usage = usage
	flag.Parse()

	if len(flag.Args()) == 0 {
		flag.Usage()
		os.Exit(1)
	}

	if width == 0 {
		size, err := ts.GetSize()
		if err != nil {
			fmt.Fprintln(os.Stderr, err, "\nYou may need to "+
				"set width manually using -w num")
			os.Exit(2)
		}
		width = size.Col() - 1	// -1 for the reset column
	}

	for _, fpath := range flag.Args() {
		fh, err := os.Open(fpath)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(10)
		}

		img, _, err := image.Decode(fh)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			fh.Close()
			os.Exit(20)
		}

		imgWidth := img.Bounds().Dx()
		imgHeight := img.Bounds().Dy()

		if imgWidth < width {
			width = imgWidth
		}

		ratio := float64(imgWidth) / float64(width)
		rows := int(float64(imgHeight) / ratio)

		for i := 1; i < rows; i += 2 {
			for j := 0; j < width; j++ {
				x := int(ratio * float64(j))
				yTop := int(ratio * float64(i - 1))
				yBottom := int(ratio * float64(i))

				top := ansirgb.Convert(img.At(x, yTop))
				bottom := ansirgb.Convert(img.At(x, yBottom))

				b := &Block{}
				b.bottom = bottom

				// Foreground colors are lighter in some terminals.
				// Ignore top (FG) if it's the same color anyway
				if top.Code != bottom.Code {
					b.top = top
				}

				fmt.Printf("%s", b)
			}
			reset()
			fmt.Printf("\n")
		}
		fh.Close()

		fmt.Println("File:", path.Base(fpath), "size:", imgWidth, "x", imgHeight)
	}
}
<commit_msg>check LC_ALL before LANG<commit_after>package main

import (
	"fmt"
	"flag"
	"os"
	"path"
	"strings"
	"image"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"github.com/Knorkebrot/ansirgb"
	"github.com/olekukonko/ts"
)

type Block struct {
	top	*ansirgb.Color
	bottom	*ansirgb.Color
}

func (b *Block) String() string {
	ret := fmt.Sprintf("\033[48;5;%dm", b.bottom.Code)
	if b.top != nil {
		ret += fmt.Sprintf("\033[38;5;%dm", b.top.Code)
		// If it's not a UTF-8 terminal, fall back to '#'
		if strings.Contains(os.Getenv("LC_ALL"), "UTF-8") ||
		   strings.Contains(os.Getenv("LANG"), "UTF-8") {
			ret += "\u2580"
		} else {
			ret += "#"
		}
	} else {
		ret += " "
	}
	return ret
}

func reset() {
	// add a space to prevent artifacts after resizing
	fmt.Printf("\033[0m ")
}

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s file [file...]\n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	var width int
	flag.IntVar(&width, "w", 0, "Output width, use 0 for terminal width")
	flag.Usage = usage
	flag.Parse()

	if len(flag.Args()) == 0 {
		flag.Usage()
		os.Exit(1)
	}

	if width == 0 {
		size, err := ts.GetSize()
		if err != nil {
			fmt.Fprintln(os.Stderr, err, "\nYou may need to "+
				"set width manually using -w num")
			os.Exit(2)
		}
		width = size.Col() - 1	// -1 for the reset column
	}

	for _, fpath := range flag.Args() {
		fh, err := os.Open(fpath)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(10)
		}

		img, _, err := image.Decode(fh)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			fh.Close()
			os.Exit(20)
		}

		imgWidth := img.Bounds().Dx()
		imgHeight := img.Bounds().Dy()

		if imgWidth < width {
			width = imgWidth
		}

		ratio := float64(imgWidth) / float64(width)
		rows := int(float64(imgHeight) / ratio)

		for i := 1; i < rows; i += 2 {
			for j := 0; j < width; j++ {
				x := int(ratio * float64(j))
				yTop := int(ratio * float64(i - 1))
				yBottom := int(ratio * float64(i))

				top := ansirgb.Convert(img.At(x, yTop))
				bottom := ansirgb.Convert(img.At(x, yBottom))

				b := &Block{}
				b.bottom = bottom

				// Foreground colors are lighter in some terminals.
				// Ignore top (FG) if it's the same color anyway
				if top.Code != bottom.Code {
					b.top = top
				}

				fmt.Printf("%s", b)
			}
			reset()
			fmt.Printf("\n")
		}
		fh.Close()

		fmt.Println("File:", path.Base(fpath), "size:", imgWidth, "x", imgHeight)
	}
}
<|endoftext|>