<commit_before>package utils

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"errors"
	"fmt"
	"image"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"io"
	"math/rand"
	"mime/multipart"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/eirka/eirka-libs/amazon"
	"github.com/eirka/eirka-libs/config"
	"github.com/eirka/eirka-libs/db"
	e "github.com/eirka/eirka-libs/errors"

	local "github.com/eirka/eirka-post/config"
)

// valid file extensions
var validExt = map[string]bool{
	".jpg":  true,
	".jpeg": true,
	".png":  true,
	".gif":  true,
	".webm": true,
}

type ImageType struct {
	File        multipart.File
	Header      *multipart.FileHeader
	Ib          uint
	Filename    string
	Thumbnail   string
	Filepath    string
	Thumbpath   string
	Ext         string
	MD5         string
	OrigWidth   int
	OrigHeight  int
	ThumbWidth  int
	ThumbHeight int
	image       *bytes.Buffer
	mime        string
	video       bool
	duration    int
}

func (i *ImageType) IsValid() bool {

	if i.Filename == "" {
		return false
	}

	if i.Filepath == "" {
		return false
	}

	if i.Thumbnail == "" {
		return false
	}

	if i.Thumbpath == "" {
		return false
	}

	if i.Ib == 0 {
		return false
	}

	if i.Ext == "" {
		return false
	}

	if i.MD5 == "" {
		return false
	}

	if i.mime == "" {
		return false
	}

	return true
}

func (i *ImageType) IsValidPost() bool {
	if i.OrigWidth == 0 {
		return false
	}

	if i.OrigHeight == 0 {
		return false
	}

	if i.ThumbWidth == 0 {
		return false
	}

	if i.ThumbHeight == 0 {
		return false
	}

	return true
}

// save an image file
func (i *ImageType) SaveImage() (err error) {

	// check given file ext
	err = i.checkReqExt()
	if err != nil {
		return
	}

	// get file md5
	err = i.getMD5()
	if err != nil {
		return
	}

	// check to see if the file already exists
	err = i.checkDuplicate()
	if err != nil {
		return
	}

	// check file magic sig
	err = i.checkMagic()
	if err != nil {
		return
	}

	// check image stats
	err = i.getStats()
	if err != nil {
		return
	}

	// save the file to disk
	err = i.saveFile()
	if err != nil {
		return
	}

	// process a webm
	if i.video {
		// check the webm info
		err = i.checkWebM()
		if err != nil {
			return
		}

		// create thumbnail from webm
		err = i.createWebMThumbnail()
		if err != nil {
			return
		}

	}

	// create a thumbnail
	err = i.createThumbnail()
	if err != nil {
		return
	}

	// copy the file to s3
	err = i.copyToS3()
	if err != nil {
		return
	}

	// check final state
	if !i.IsValidPost() {
		return errors.New("ImageType is not valid")
	}

	return

}

// Get file extension from request header
func (i *ImageType) checkReqExt() (err error) {
	// Get ext from request header
	name := i.Header.Filename
	ext := filepath.Ext(name)

	if ext == "" {
		return errors.New("No file extension")
	}

	// Check to see if extension is allowed
	if !isAllowedExt(ext) {
		return errors.New("Format not supported")
	}

	return

}

// Check if file ext allowed
func isAllowedExt(ext string) bool {
	return validExt[strings.ToLower(ext)]
}

// Get image MD5 and write file into buffer
func (i *ImageType) getMD5() (err error) {

	defer i.File.Close()

	hasher := md5.New()

	i.image = new(bytes.Buffer)

	// Save file and also read into hasher for md5
	_, err = io.Copy(i.image, io.TeeReader(i.File, hasher))
	if err != nil {
		return errors.New("Problem copying file")
	}

	// Set md5sum from hasher
	i.MD5 = hex.EncodeToString(hasher.Sum(nil))

	return

}

// check if the md5 is already in the database
func (i *ImageType) checkDuplicate() (err error) {

	// Get Database handle
	dbase, err := db.GetDb()
	if err != nil {
		return
	}

	var check bool
	var thread, post uint

	err = dbase.QueryRow(`select count(1),posts.post_num,threads.thread_id from threads
	LEFT JOIN posts on threads.thread_id = posts.thread_id
	LEFT JOIN images on posts.post_id = images.post_id
	WHERE image_hash = ? AND ib_id = ?`, i.MD5, i.Ib).Scan(&check, &post, &thread)
	if err != nil {
		return e.ErrInternalError
	}

	// return error if it exists
	if check {
		return fmt.Errorf("Image has already been posted. Thread: %d Post: %d", thread, post)
	}

	return
}

func (i *ImageType) checkMagic() (err error) {

	// detect the mime type
	i.mime = http.DetectContentType(i.image.Bytes())

	switch i.mime {
	case "image/png":
		i.Ext = ".png"
	case "image/jpeg":
		i.Ext = ".jpg"
	case "image/gif":
		i.Ext = ".gif"
	case "video/webm":
		i.Ext = ".webm"
		i.video = true
	default:
		return errors.New("Unknown file type")
	}

	// Check to see if extension is allowed
	if !isAllowedExt(i.Ext) {
		return errors.New("Format not supported")
	}

	return

}

func (i *ImageType) getStats() (err error) {

	// decode image config
	img, _, err := image.DecodeConfig(bytes.NewReader(i.image.Bytes()))
	if err != nil {
		return errors.New("Problem decoding image")
	}

	// set original width
	i.OrigWidth = img.Width
	// set original height
	i.OrigHeight = img.Height

	// Check against maximum sizes
	switch {
	case i.OrigWidth > config.Settings.Limits.ImageMaxWidth:
		return errors.New("Image width too large")
	case img.Width < config.Settings.Limits.ImageMinWidth:
		return errors.New("Image width too small")
	case i.OrigHeight > config.Settings.Limits.ImageMaxHeight:
		return errors.New("Image height too large")
	case img.Height < config.Settings.Limits.ImageMinHeight:
		return errors.New("Image height too small")
	case i.image.Len() > config.Settings.Limits.ImageMaxSize:
		return errors.New("Image size too large")
	}

	return

}

func (i *ImageType) saveFile() (err error) {

	defer i.image.Reset()

	// generate filenames
	i.makeFilenames()

	if !i.IsValid() {
		return errors.New("ImageType is not valid")
	}

	imagefile := filepath.Join(local.Settings.Directories.ImageDir, i.Filename)

	image, err := os.Create(imagefile)
	if err != nil {
		return errors.New("Problem saving file")
	}
	defer image.Close()

	_, err = io.Copy(image, bytes.NewReader(i.image.Bytes()))
	if err != nil {
		return errors.New("Problem saving file")
	}

	return

}

// Make a random unix time filename
func (i *ImageType) makeFilenames() {

	// Create seed for random
	rand.Seed(time.Now().UnixNano())

	// Get random 3 digit int to append to unix time
	rand_t := rand.Intn(899) + 100

	// Get current unix time
	time_t := time.Now().Unix()

	// Append random int to unix time
	file_t := fmt.Sprintf("%d%d", time_t, rand_t)

	// Append ext to filename
	i.Filename = fmt.Sprintf("%s%s", file_t, i.Ext)

	// Append jpg to thumbnail name because it is always a jpg
	i.Thumbnail = fmt.Sprintf("%ss.jpg", file_t)

	// set the full file path
	i.Filepath = filepath.Join(local.Settings.Directories.ImageDir, i.Filename)

	// set the full thumbnail path
	i.Thumbpath = filepath.Join(local.Settings.Directories.ThumbnailDir, i.Thumbnail)

	return

}

func (i *ImageType) createThumbnail() (err error) {

	var imagef string

	if i.video {
		imagef = fmt.Sprintf("%s[0]", i.Thumbpath)
	} else {
		imagef = fmt.Sprintf("%s[0]", i.Filepath)
	}

	orig_dimensions := fmt.Sprintf("%dx%d", i.OrigWidth, i.OrigHeight)
	thumb_dimensions := fmt.Sprintf("%dx%d>", config.Settings.Limits.ThumbnailMaxWidth, config.Settings.Limits.ThumbnailMaxHeight)

	args := []string{
		"-background",
		"white",
		"-flatten",
		"-size",
		orig_dimensions,
		"-resize",
		thumb_dimensions,
		"-quality",
		"90",
		imagef,
		i.Thumbpath,
	}

	_, err = exec.Command("convert", args...).Output()
	if err != nil {
		return errors.New("Problem making thumbnail")
	}

	thumb, err := os.Open(i.Thumbpath)
	if err != nil {
		return errors.New("Problem making thumbnail")
	}
	defer thumb.Close()

	img, _, err := image.DecodeConfig(thumb)
	if err != nil {
		return errors.New("Problem decoding thumbnail")
	}

	i.ThumbWidth = img.Width
	i.ThumbHeight = img.Height

	return

}

func (i *ImageType) copyToS3() (err error) {

	s3 := amazon.New()

	err = s3.Save(i.Filepath, fmt.Sprintf("src/%s", i.Filename), i.mime)
	if err != nil {
		return
	}

	err = s3.Save(i.Thumbpath, fmt.Sprintf("thumb/%s", i.Thumbnail), "image/jpeg")
	if err != nil {
		return
	}

	return
}
<commit_msg>make image processing better<commit_after>package utils

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"errors"
	"fmt"
	"image"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"io"
	"math/rand"
	"mime/multipart"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/eirka/eirka-libs/amazon"
	"github.com/eirka/eirka-libs/config"
	"github.com/eirka/eirka-libs/db"
	e "github.com/eirka/eirka-libs/errors"

	local "github.com/eirka/eirka-post/config"
)

// valid file extensions
var validExt = map[string]bool{
	".jpg":  true,
	".jpeg": true,
	".png":  true,
	".gif":  true,
	".webm": true,
}

type ImageType struct {
	File        multipart.File
	Header      *multipart.FileHeader
	Ib          uint
	Filename    string
	Thumbnail   string
	Filepath    string
	Thumbpath   string
	Ext         string
	MD5         string
	OrigWidth   int
	OrigHeight  int
	ThumbWidth  int
	ThumbHeight int
	image       *bytes.Buffer
	mime        string
	video       bool
	duration    int
}

func (i *ImageType) IsValid() bool {

	if i.Filename == "" {
		return false
	}

	if i.Filepath == "" {
		return false
	}

	if i.Thumbnail == "" {
		return false
	}

	if i.Thumbpath == "" {
		return false
	}

	if i.Ib == 0 {
		return false
	}

	if i.Ext == "" {
		return false
	}

	if i.MD5 == "" {
		return false
	}

	if i.mime == "" {
		return false
	}

	return true
}

func (i *ImageType) IsValidPost() bool {
	if i.OrigWidth == 0 {
		return false
	}

	if i.OrigHeight == 0 {
		return false
	}

	if i.ThumbWidth == 0 {
		return false
	}

	if i.ThumbHeight == 0 {
		return false
	}

	return true
}

// save an image file
func (i *ImageType) SaveImage() (err error) {

	// check given file ext
	err = i.checkReqExt()
	if err != nil {
		return
	}

	// get file md5
	err = i.getMD5()
	if err != nil {
		return
	}

	// check to see if the file already exists
	err = i.checkDuplicate()
	if err != nil {
		return
	}

	// check file magic sig
	err = i.checkMagic()
	if err != nil {
		return
	}

	// check image stats
	err = i.getStats()
	if err != nil {
		return
	}

	// save the file to disk
	err = i.saveFile()
	if err != nil {
		return
	}

	// process a webm
	if i.video {
		// check the webm info
		err = i.checkWebM()
		if err != nil {
			return
		}

		// create thumbnail from webm
		err = i.createWebMThumbnail()
		if err != nil {
			return
		}

	}

	// create a thumbnail
	err = i.createThumbnail()
	if err != nil {
		return
	}

	// copy the file to s3
	err = i.copyToS3()
	if err != nil {
		return
	}

	// check final state
	if !i.IsValidPost() {
		return errors.New("ImageType is not valid")
	}

	return

}

// Get file extension from request header
func (i *ImageType) checkReqExt() (err error) {
	// Get ext from request header
	name := i.Header.Filename
	ext := filepath.Ext(name)

	if ext == "" {
		return errors.New("No file extension")
	}

	// Check to see if extension is allowed
	if !isAllowedExt(ext) {
		return errors.New("Format not supported")
	}

	return

}

// Check if file ext allowed
func isAllowedExt(ext string) bool {
	return validExt[strings.ToLower(ext)]
}

// Get image MD5 and write file into buffer
func (i *ImageType) getMD5() (err error) {

	defer i.File.Close()

	hasher := md5.New()

	i.image = new(bytes.Buffer)

	// Save file and also read into hasher for md5
	_, err = io.Copy(i.image, io.TeeReader(i.File, hasher))
	if err != nil {
		return errors.New("Problem copying file")
	}

	// Set md5sum from hasher
	i.MD5 = hex.EncodeToString(hasher.Sum(nil))

	return

}

// check if the md5 is already in the database
func (i *ImageType) checkDuplicate() (err error) {

	// Get Database handle
	dbase, err := db.GetDb()
	if err != nil {
		return
	}

	if i.Ib == 0 {
		return errors.New("No imageboard set on duplicate check")
	}

	var check bool
	var thread, post uint

	err = dbase.QueryRow(`select count(1),posts.post_num,threads.thread_id from threads
	LEFT JOIN posts on threads.thread_id = posts.thread_id
	LEFT JOIN images on posts.post_id = images.post_id
	WHERE image_hash = ? AND ib_id = ?`, i.MD5, i.Ib).Scan(&check, &post, &thread)
	if err != nil {
		return e.ErrInternalError
	}

	// return error if it exists
	if check {
		return fmt.Errorf("Image has already been posted. Thread: %d Post: %d", thread, post)
	}

	return
}

func (i *ImageType) checkMagic() (err error) {

	// detect the mime type
	i.mime = http.DetectContentType(i.image.Bytes())

	switch i.mime {
	case "image/png":
		i.Ext = ".png"
	case "image/jpeg":
		i.Ext = ".jpg"
	case "image/gif":
		i.Ext = ".gif"
	case "video/webm":
		i.Ext = ".webm"
		i.video = true
	default:
		return errors.New("Unknown file type")
	}

	// Check to see if extension is allowed
	if !isAllowedExt(i.Ext) {
		return errors.New("Format not supported")
	}

	return

}

func (i *ImageType) getStats() (err error) {

	// decode image config
	img, _, err := image.DecodeConfig(bytes.NewReader(i.image.Bytes()))
	if err != nil {
		return errors.New("Problem decoding image")
	}

	// set original width
	i.OrigWidth = img.Width
	// set original height
	i.OrigHeight = img.Height

	// Check against maximum sizes
	switch {
	case i.OrigWidth > config.Settings.Limits.ImageMaxWidth:
		return errors.New("Image width too large")
	case img.Width < config.Settings.Limits.ImageMinWidth:
		return errors.New("Image width too small")
	case i.OrigHeight > config.Settings.Limits.ImageMaxHeight:
		return errors.New("Image height too large")
	case img.Height < config.Settings.Limits.ImageMinHeight:
		return errors.New("Image height too small")
	case i.image.Len() > config.Settings.Limits.ImageMaxSize:
		return errors.New("Image size too large")
	}

	return

}

func (i *ImageType) saveFile() (err error) {

	defer i.image.Reset()

	// generate filenames
	i.makeFilenames()

	if !i.IsValid() {
		return errors.New("ImageType is not valid")
	}

	imagefile := filepath.Join(local.Settings.Directories.ImageDir, i.Filename)

	image, err := os.Create(imagefile)
	if err != nil {
		return errors.New("Problem saving file")
	}
	defer image.Close()

	_, err = io.Copy(image, bytes.NewReader(i.image.Bytes()))
	if err != nil {
		return errors.New("Problem saving file")
	}

	return

}

// Make a random unix time filename
func (i *ImageType) makeFilenames() {

	// Create seed for random
	rand.Seed(time.Now().UnixNano())

	// Get random 3 digit int to append to unix time
	rand_t := rand.Intn(899) + 100

	// Get current unix time
	time_t := time.Now().Unix()

	// Append random int to unix time
	file_t := fmt.Sprintf("%d%d", time_t, rand_t)

	// Append ext to filename
	i.Filename = fmt.Sprintf("%s%s", file_t, i.Ext)

	// Append jpg to thumbnail name because it is always a jpg
	i.Thumbnail = fmt.Sprintf("%ss.jpg", file_t)

	// set the full file path
	i.Filepath = filepath.Join(local.Settings.Directories.ImageDir, i.Filename)

	// set the full thumbnail path
	i.Thumbpath = filepath.Join(local.Settings.Directories.ThumbnailDir, i.Thumbnail)

	return

}

func (i *ImageType) createThumbnail() (err error) {

	var imagef string

	if i.video {
		imagef = fmt.Sprintf("%s[0]", i.Thumbpath)
	} else {
		imagef = fmt.Sprintf("%s[0]", i.Filepath)
	}

	orig_dimensions := fmt.Sprintf("%dx%d", i.OrigWidth, i.OrigHeight)
	thumb_dimensions := fmt.Sprintf("%dx%d>", config.Settings.Limits.ThumbnailMaxWidth, config.Settings.Limits.ThumbnailMaxHeight)

	args := []string{
		"-background",
		"white",
		"-flatten",
		"-size",
		orig_dimensions,
		"-resize",
		thumb_dimensions,
		"-quality",
		"90",
		imagef,
		i.Thumbpath,
	}

	_, err = exec.Command("convert", args...).Output()
	if err != nil {
		return errors.New("Problem making thumbnail")
	}

	thumb, err := os.Open(i.Thumbpath)
	if err != nil {
		return errors.New("Problem making thumbnail")
	}
	defer thumb.Close()

	img, _, err := image.DecodeConfig(thumb)
	if err != nil {
		return errors.New("Problem decoding thumbnail")
	}

	i.ThumbWidth = img.Width
	i.ThumbHeight = img.Height

	return

}

func (i *ImageType) copyToS3() (err error) {

	s3 := amazon.New()

	err = s3.Save(i.Filepath, fmt.Sprintf("src/%s", i.Filename), i.mime)
	if err != nil {
		return
	}

	err = s3.Save(i.Thumbpath, fmt.Sprintf("thumb/%s", i.Thumbnail), "image/jpeg")
	if err != nil {
		return
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
	"bitfx2/bitfinex"
	"bitfx2/exchange"
	"fmt"
	"log"
	"os"
	"os/exec"
)

var bf = bitfinex.New(os.Getenv("BITFINEX_KEY"), os.Getenv("BITFINEX_SECRET"), "ltc", "usd", 2, 0.001)

func main() {
	filename := "bfbook.log"
	logFile, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatal(err)
	}
	log.SetOutput(logFile)
	log.Println("Starting new run")

	doneChan := make(chan bool, 1)
	bookChan := make(chan exchange.Book)
	if err := bf.CommunicateBook(bookChan, doneChan); err != nil {
		log.Fatal(err)
	}
	inputChan := make(chan rune)
	go checkStdin(inputChan)

Loop:
	for {
		select {
		case book := <-bookChan:
			printBook(book)
		case <-inputChan:
			doneChan <- true
			break Loop
		}
	}

}

// Check for any user input
func checkStdin(inputChan chan<- rune) {
	var ch rune
	fmt.Scanf("%c", &ch)
	inputChan <- ch
}

// Print book data from each exchange
func printBook(book exchange.Book) {
	clearScreen()
	if book.Error != nil {
		log.Println(book.Error)
	} else {
		fmt.Println("----------------------------")
		fmt.Printf("%-10s%-10s%8s\n", " Bid", "  Ask", "Size ")
		fmt.Println("----------------------------")
		for i := range book.Asks {
			item := book.Asks[len(book.Asks)-1-i]
			fmt.Printf("%-10s%-10.4f%8.2f\n", "", item.Price, item.Amount)
		}
		for _, item := range book.Bids {
			fmt.Printf("%-10.4f%-10.2s%8.2f\n", item.Price, "", item.Amount)
		}
		fmt.Println("----------------------------")
	}
}

// Clear the terminal between prints
func clearScreen() {
	c := exec.Command("clear")
	c.Stdout = os.Stdout
	c.Run()
}
<commit_msg>fix to use changes in bitfinex methods<commit_after>package main

import (
	"bitfx2/bitfinex"
	"bitfx2/exchange"
	"fmt"
	"log"
	"os"
	"os/exec"
)

var bf = bitfinex.New(os.Getenv("BITFINEX_KEY"), os.Getenv("BITFINEX_SECRET"), "ltc", "usd", 2, 0.001, 1)

func main() {
	filename := "bfbook.log"
	logFile, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatal(err)
	}
	log.SetOutput(logFile)
	log.Println("Starting new run")

	doneChan := make(chan bool, 1)
	bookChan := make(chan exchange.Book)
	if book := bf.CommunicateBook(bookChan, doneChan); book.Error != nil {
		log.Fatal(book.Error)
	}
	inputChan := make(chan rune)
	go checkStdin(inputChan)

Loop:
	for {
		select {
		case book := <-bookChan:
			printBook(book)
		case <-inputChan:
			doneChan <- true
			break Loop
		}
	}

}

// Check for any user input
func checkStdin(inputChan chan<- rune) {
	var ch rune
	fmt.Scanf("%c", &ch)
	inputChan <- ch
}

// Print book data from each exchange
func printBook(book exchange.Book) {
	clearScreen()
	if book.Error != nil {
		log.Println(book.Error)
	} else {
		fmt.Println("----------------------------")
		fmt.Printf("%-10s%-10s%8s\n", " Bid", "  Ask", "Size ")
		fmt.Println("----------------------------")
		for i := range book.Asks {
			item := book.Asks[len(book.Asks)-1-i]
			fmt.Printf("%-10s%-10.4f%8.2f\n", "", item.Price, item.Amount)
		}
		for _, item := range book.Bids {
			fmt.Printf("%-10.4f%-10.2s%8.2f\n", item.Price, "", item.Amount)
		}
		fmt.Println("----------------------------")
	}
}

// Clear the terminal between prints
func clearScreen() {
	c := exec.Command("clear")
	c.Stdout = os.Stdout
	c.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/mholt/archiver"
)

func main() {
	log.SetFlags(0)
	log.SetPrefix("sevil: ")

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case "GET":
			handleIndex(w, r)
			return
		case "POST":
			handlePost(w, r)
			return
		default:
			http.NotFound(w, r)
			return
		}
	})
	log.Fatal(http.ListenAndServe(":1985", nil))
}

func handleIndex(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(indexHtml))
}

func handlePost(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, fmt.Sprintf("Error parsing form: %v", err), http.StatusInternalServerError)
		return
	}

	yt := r.FormValue("urlInput")
	if yt == "" {
		http.Error(w, "no address given", http.StatusBadRequest)
		return
	}

	p, err := download(yt)
	if err != nil {
		http.Error(w, fmt.Sprintf("Error downloading %q: %v", yt, err), http.StatusInternalServerError)
		return
	}
	defer os.Remove(p)

	_, fname := filepath.Split(p)
	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename='%v'", fname))
	http.ServeFile(w, r, p)
	return
}

func download(yt string) (string, error) {
	log.Printf("New download request for %q\n", yt)
	u, err := url.Parse(yt)
	if err != nil {
		return "", fmt.Errorf("Error parsing youtube url: %v", err)
	}

	playlist := u.Query().Get("list")
	var isPlaylist bool
	if playlist != "" {
		isPlaylist = true
	}

	tmpdir, err := ioutil.TempDir("/tmp/", "sevil-")
	if err != nil {
		return "", fmt.Errorf("Error creating temp directory: %v", err)
	}
	defer os.RemoveAll(tmpdir)

	var (
		bin  = "youtube-dl"
		args = []string{
			"--ignore-errors",
			"--extract-audio",
			"--audio-format",
			"mp3",
			"-o",
			tmpdir + "/" + "%(title)s.%(ext)s",
			u.String(),
		}
	)

	ctx := context.Background()
	cmd := exec.CommandContext(ctx, bin, args...)

	// cmd can fail since we passed the '--ignore-errors' parameter. even if
	// there is a download error, such as a private video in a playlist,
	// youtube-dl returns exit code 1. hence ignore the error.
	output, err := cmd.CombinedOutput()
	if err != nil {
		if strings.Contains(string(output), "is not a valid") {
			return "", fmt.Errorf("%q gecerli bir URL degil", yt)
		}
		log.Printf("youtube-dl failed for some reason: %v. Output: %v\n\nIgnoring the error...\n", err, string(output))
	}

	if isPlaylist {
		err = archiver.Zip.Make(tmpdir+".zip", []string{tmpdir})
		if err != nil {
			return "", fmt.Errorf("Error creating zip file: %v", err)
		}
		return tmpdir + ".zip", nil
	}

	files, err := ioutil.ReadDir(tmpdir)
	if err != nil {
		return "", fmt.Errorf("Error reading tmpdir: %v", err)
	}

	if len(files) == 0 {
		return "", fmt.Errorf("No file under %q", tmpdir)
	}

	fname := files[0].Name()
	fpath := filepath.Join(tmpdir, fname)

	os.Rename(fpath, "/tmp/"+fname)

	return filepath.Join("/tmp", fname), nil
}

const indexHtml = `
<!DOCTYPE html>

        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

        <form class="form-group" method="POST">

                        <input id="urlInput" type="text" class="form-control" name="urlInput">

                            <button id="submitBtn" class="btn btn-primary" type="submit">indir</button>

        </form>

`
<commit_msg>sevil: parameterize host and port<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/mholt/archiver"
)

func main() {
	log.SetFlags(0)
	log.SetPrefix("sevil: ")

	var (
		flagHost = flag.String("host", "0.0.0.0", "host")
		flagPort = flag.String("port", "1987", "port")
	)
	flag.Parse()

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case "GET":
			handleIndex(w, r)
			return
		case "POST":
			handlePost(w, r)
			return
		default:
			http.NotFound(w, r)
			return
		}
	})

	log.Fatal(http.ListenAndServe(net.JoinHostPort(*flagHost, *flagPort), nil))
}

func handleIndex(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(indexHtml))
}

func handlePost(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, fmt.Sprintf("Error parsing form: %v", err), http.StatusInternalServerError)
		return
	}

	yt := r.FormValue("urlInput")
	if yt == "" {
		http.Error(w, "no address given", http.StatusBadRequest)
		return
	}

	p, err := download(yt)
	if err != nil {
		http.Error(w, fmt.Sprintf("Error downloading %q: %v", yt, err), http.StatusInternalServerError)
		return
	}
	defer os.Remove(p)

	_, fname := filepath.Split(p)
	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename='%v'", fname))
	http.ServeFile(w, r, p)
	return
}

func download(yt string) (string, error) {
	log.Printf("New download request for %q\n", yt)
	u, err := url.Parse(yt)
	if err != nil {
		return "", fmt.Errorf("Error parsing youtube url: %v", err)
	}

	playlist := u.Query().Get("list")
	var isPlaylist bool
	if playlist != "" {
		isPlaylist = true
	}

	tmpdir, err := ioutil.TempDir("/tmp/", "sevil-")
	if err != nil {
		return "", fmt.Errorf("Error creating temp directory: %v", err)
	}
	defer os.RemoveAll(tmpdir)

	var (
		bin  = "youtube-dl"
		args = []string{
			"--ignore-errors",
			"--extract-audio",
			"--audio-format",
			"mp3",
			"-o",
			tmpdir + "/" + "%(title)s.%(ext)s",
			u.String(),
		}
	)

	ctx := context.Background()
	cmd := exec.CommandContext(ctx, bin, args...)

	// cmd can fail since we passed the '--ignore-errors' parameter. even if
	// there is a download error, such as a private video in a playlist,
	// youtube-dl returns exit code 1. hence ignore the error.
	output, err := cmd.CombinedOutput()
	if err != nil {
		if strings.Contains(string(output), "is not a valid") {
			return "", fmt.Errorf("%q gecerli bir URL degil", yt)
		}
		log.Printf("youtube-dl failed for some reason: %v. Output: %v\n\nIgnoring the error...\n", err, string(output))
	}

	if isPlaylist {
		err = archiver.Zip.Make(tmpdir+".zip", []string{tmpdir})
		if err != nil {
			return "", fmt.Errorf("Error creating zip file: %v", err)
		}
		return tmpdir + ".zip", nil
	}

	files, err := ioutil.ReadDir(tmpdir)
	if err != nil {
		return "", fmt.Errorf("Error reading tmpdir: %v", err)
	}

	if len(files) == 0 {
		return "", fmt.Errorf("No file under %q", tmpdir)
	}

	fname := files[0].Name()
	fpath := filepath.Join(tmpdir, fname)

	os.Rename(fpath, "/tmp/"+fname)

	return filepath.Join("/tmp", fname), nil
}

const indexHtml = `
<!DOCTYPE html>

        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

        <form class="form-group" method="POST">

                        <input id="urlInput" type="text" class="form-control" name="urlInput">

                            <button id="submitBtn" class="btn btn-primary" type="submit">indir</button>

        </form>

`
<|endoftext|>


<commit_before>package utils

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudformation"
)

type AwsRequest interface {
	Send() error
}

type RequestHandler interface {
	HandleRequest(req AwsRequest) error
}

type AwsRequestHandler struct {
}

func (r *AwsRequestHandler) HandleRequest(req AwsRequest) error {
	s := 1
	var err error
	for err = req.Send(); err != nil; err = req.Send() {
		if reqerr, ok := err.(awserr.RequestFailure); ok {
			if reqerr.Code() == "RequestLimitExceeded" {
				time.Sleep(time.Duration(s) * time.Second)
				s = s * 2
				continue
			}
		}
		return err
	}
	return nil
}

type Input struct {
	Source struct {
		Name               string `json:"name"`
		AwsAccessKeyId     string `json:"aws_access_key_id"`
		AwsSecretAccessKey string `json:"aws_secret_access_key"`
		Region             string `json:"region"`
	} `json:"source"`
	Version struct {
		LastUpdatedTime string `json:"LastUpdatedTime"`
	} `json:"version"`
	Params struct {
		Template     string   `json:"template"`
		Parameters   string   `json:"parameters"`
		Tags         string   `json:"tags"`
		Capabilities []string `json:"capabilities"`
		Delete       bool     `json:"delete"`
		Wait         bool     `json:"wait"`
	} `json:"params"`
}

func GetInput() Input {
	bytes, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		panic(err)
	}
	input := Input{}
	err = json.Unmarshal(bytes, &input)
	if err != nil {
		panic(err)
	}
	return input
}

type AwsCloudformationSvc interface {
	DescribeStacksRequest(input *cloudformation.DescribeStacksInput) (req *request.Request, output *cloudformation.DescribeStacksOutput)
	CreateStackRequest(input *cloudformation.CreateStackInput) (req *request.Request, output *cloudformation.CreateStackOutput)
	UpdateStackRequest(input *cloudformation.UpdateStackInput) (req *request.Request, output *cloudformation.UpdateStackOutput)
	DescribeStackEventsRequest(input *cloudformation.DescribeStackEventsInput) (req *request.Request, output *cloudformation.DescribeStackEventsOutput)
	DeleteStackRequest(input *cloudformation.DeleteStackInput) (req *request.Request, output *cloudformation.DeleteStackOutput)
}

func GetCloudformationService(input Input) AwsCloudformationSvc {
	creds := credentials.NewStaticCredentials(input.Source.AwsAccessKeyId, input.Source.AwsSecretAccessKey, "")
	awsConfig := aws.NewConfig().WithCredentials(creds).WithRegion(input.Source.Region)
	sess := session.Must(session.NewSession(awsConfig))
	svc := cloudformation.New(sess)
	return svc
}

func GoToBuildDirectory() {
	files, err := ioutil.ReadDir("/tmp/build")
	if err != nil {
		panic(err)
	}

	if len(files) != 1 {
		fmt.Printf("Expected only 1 file in /tmp/build but found %d: %v\n", len(files), files)
		os.Exit(1)
	}

	os.Chdir("/tmp/build/" + files[0].Name())
}

func Logln(message interface{}) {
	Logf("%v\n", message)
}

func Logf(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format, a)
}
<commit_msg>Print message as string<commit_after>package utils

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudformation"
)

type AwsRequest interface {
	Send() error
}

type RequestHandler interface {
	HandleRequest(req AwsRequest) error
}

type AwsRequestHandler struct {
}

func (r *AwsRequestHandler) HandleRequest(req AwsRequest) error {
	s := 1
	var err error
	for err = req.Send(); err != nil; err = req.Send() {
		if reqerr, ok := err.(awserr.RequestFailure); ok {
			if reqerr.Code() == "RequestLimitExceeded" {
				time.Sleep(time.Duration(s) * time.Second)
				s = s * 2
				continue
			}
		}
		return err
	}
	return nil
}

type Input struct {
	Source struct {
		Name               string `json:"name"`
		AwsAccessKeyId     string `json:"aws_access_key_id"`
		AwsSecretAccessKey string `json:"aws_secret_access_key"`
		Region             string `json:"region"`
	} `json:"source"`
	Version struct {
		LastUpdatedTime string `json:"LastUpdatedTime"`
	} `json:"version"`
	Params struct {
		Template     string   `json:"template"`
		Parameters   string   `json:"parameters"`
		Tags         string   `json:"tags"`
		Capabilities []string `json:"capabilities"`
		Delete       bool     `json:"delete"`
		Wait         bool     `json:"wait"`
	} `json:"params"`
}

func GetInput() Input {
	bytes, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		panic(err)
	}
	input := Input{}
	err = json.Unmarshal(bytes, &input)
	if err != nil {
		panic(err)
	}
	return input
}

type AwsCloudformationSvc interface {
	DescribeStacksRequest(input *cloudformation.DescribeStacksInput) (req *request.Request, output *cloudformation.DescribeStacksOutput)
	CreateStackRequest(input *cloudformation.CreateStackInput) (req *request.Request, output *cloudformation.CreateStackOutput)
	UpdateStackRequest(input *cloudformation.UpdateStackInput) (req *request.Request, output *cloudformation.UpdateStackOutput)
	DescribeStackEventsRequest(input *cloudformation.DescribeStackEventsInput) (req *request.Request, output *cloudformation.DescribeStackEventsOutput)
	DeleteStackRequest(input *cloudformation.DeleteStackInput) (req *request.Request, output *cloudformation.DeleteStackOutput)
}

func GetCloudformationService(input Input) AwsCloudformationSvc {
	creds := credentials.NewStaticCredentials(input.Source.AwsAccessKeyId, input.Source.AwsSecretAccessKey, "")
	awsConfig := aws.NewConfig().WithCredentials(creds).WithRegion(input.Source.Region)
	sess := session.Must(session.NewSession(awsConfig))
	svc := cloudformation.New(sess)
	return svc
}

func GoToBuildDirectory() {
	files, err := ioutil.ReadDir("/tmp/build")
	if err != nil {
		panic(err)
	}

	if len(files) != 1 {
		fmt.Printf("Expected only 1 file in /tmp/build but found %d: %v\n", len(files), files)
		os.Exit(1)
	}

	os.Chdir("/tmp/build/" + files[0].Name())
}

func Logln(message interface{}) {
	Logf("%s\n", message)
}

func Logf(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format, a)
}
<|endoftext|>


<commit_before>// Package utils - 套件所需的公用工具
//
package utils

import (
	"regexp"
	"strconv"
	"time"
)

// ExchangeMap is simple to check tse or otc.
var ExchangeMap = map[string]bool{"tse": true, "otc": true}

// TaipeiTimeZone is for time.Data() setting.
var TaipeiTimeZone = time.FixedZone("Asia/Taipei", 8*3600)

// TWSE base url.
const (
	TWSEURL     string = "http://mis.tse.com.tw"
	TWSEHOST    string = "http://www.twse.com.tw"
	OTCHOST     string = "http://www.tpex.org.tw"
	OTCCSV      string = "/ch/stock/aftertrading/daily_trading_info/st43_download.php?d=%d/%02d&stkno=%s&r=%d" // year, mon, stock, rand
	TWSECSV     string = "/ch/trading/exchange/STOCK_DAY/STOCK_DAY_print.php?genpage=genpage/Report%d%02d/%d%02d_F3_1_8_%s.php&type=csv&r=%d"
	TWSELISTCSV string = "/ch/trading/exchange/MI_INDEX/MI_INDEX.php" // year, mon, day, type
	TWSEREAL    string = "/stock/api/getStockInfo.jsp?ex_ch=%s_%s.tw_%s&json=1&delay=0&_=%d"
)

// RandInt return random int.
func RandInt() int {
	return time.Now().Nanosecond()
}

//func RandInt() int64 {
//	result, _ := rand.Int(rand.Reader, big.NewInt(102400))
//	return result.Int64()
//}

//func RandInt() int64 {
//	r := rand.New(rand.NewSource(time.Now().UnixNano()))
//	return r.Int63n(100000)
//}

var dateReg = regexp.MustCompile(`([\d]{2,})/([\d]{1,2})/([\d]{1,2})`)

// ParseDate is to parse "104/01/13" format.
func ParseDate(strDate string) time.Time {
	p := dateReg.FindStringSubmatch(strDate)
	year, _ := strconv.Atoi(p[1])
	mon, _ := strconv.Atoi(p[2])
	day, _ := strconv.Atoi(p[3])
	return time.Date(year+1911, time.Month(mon), day, 0, 0, 0, 0, TaipeiTimeZone)
}

// SumFloat64 計算總和（float64）
func SumFloat64(data []float64) float64 {
	var result float64
	for _, v := range data {
		result += v
	}
	return result
}

// AvgFloat64 計算平均（float64）
func AvgFloat64(data []float64) float64 {
	return float64(int(SumFloat64(data)*100)/len(data)) / 100
}

// SumUint64 計算總和（uint64）
func SumUint64(data []uint64) uint64 {
	var result uint64
	for _, v := range data {
		result += v
	}
	return result
}

// AvgUint64 計算平均（uint64）
func AvgUint64(data []uint64) uint64 {
	return SumUint64(data) / uint64(len(data))
}

// ThanPastUint64 計算最後一個數值是否為過去幾天最大或最小（uint64）
func ThanPastUint64(data []uint64, days int, max bool) bool {
	var dataFloat64 = make([]float64, days+1)
	for i, v := range data[len(data)-1-days:] {
		dataFloat64[i] = float64(v)
	}
	return thanPast(dataFloat64, max)
}

// ThanPastFloat64 計算最後一個數值是否為過去幾天最大或最小（float64）
func ThanPastFloat64(data []float64, days int, max bool) bool {
	return thanPast(data[len(data)-1-days:], max)
}

func thanPast(data []float64, max bool) bool {
	//var dataFloat64 []float64
	//dataFloat64 = make([]float64, days+1)
	//for i, v := range data[len(data)-1-days : len(data)-1] {
	//	switch v.(type) {
	//	case int64:
	//		dataFloat64[i] = float64(v.(int64))
	//	case float64:
	//		dataFloat64[i] = v.(float64)
	//	}
	//}

	var base = data[len(data)-1]
	var condition func(b float64) bool

	if max {
		condition = func(b float64) bool { return base > b }
	} else {
		condition = func(b float64) bool { return base < b }
	}

	for _, v := range data[:len(data)-1] {
		if condition(v) {
			continue
		} else {
			return false
		}
	}
	return true
}

// ThanSumPastFloat64 計算最後一個數值是否為過去幾天的總和大或小（float64）
func ThanSumPastFloat64(data []float64, days int, max bool) bool {
	return thanSumPast(data[len(data)-1-days:], max)
}

// ThanSumPastUint64 計算最後一個數值是否為過去幾天的總和大或小（uint64）
func ThanSumPastUint64(data []uint64, days int, max bool) bool {
	var dataFloat64 = make([]float64, days+1)
	for i, v := range data[len(data)-1-days:] {
		dataFloat64[i] = float64(v)
	}
	return thanSumPast(dataFloat64, max)
}

func thanSumPast(data []float64, max bool) bool {
	var result = data[len(data)-1] > SumFloat64(data[:len(data)-1])
	if max {
		return result
	}
	return !result
}

func countCountineFloat64(data []float64) (int, bool) {
	var condition func(x float64) bool
	if data[len(data)-1] > 0 {
		condition = func(x float64) bool { return x > 0 }
	} else {
		condition = func(x float64) bool { return x < 0 }
	}
	counter := 1
	for i := 1; i <= len(data)-1; i++ {
		if condition(data[len(data)-1-i]) {
			counter++
		} else {
			break
		}
	}
	return counter, data[len(data)-1] > 0
}
<commit_msg>Add `CountCountineFloat64`.<commit_after>// Package utils - 套件所需的公用工具
//
package utils

import (
	"regexp"
	"strconv"
	"time"
)

// ExchangeMap is simple to check tse or otc.
var ExchangeMap = map[string]bool{"tse": true, "otc": true}

// TaipeiTimeZone is for time.Data() setting.
var TaipeiTimeZone = time.FixedZone("Asia/Taipei", 8*3600)

// TWSE base url.
const (
	TWSEURL     string = "http://mis.tse.com.tw"
	TWSEHOST    string = "http://www.twse.com.tw"
	OTCHOST     string = "http://www.tpex.org.tw"
	OTCCSV      string = "/ch/stock/aftertrading/daily_trading_info/st43_download.php?d=%d/%02d&stkno=%s&r=%d" // year, mon, stock, rand
	TWSECSV     string = "/ch/trading/exchange/STOCK_DAY/STOCK_DAY_print.php?genpage=genpage/Report%d%02d/%d%02d_F3_1_8_%s.php&type=csv&r=%d"
	TWSELISTCSV string = "/ch/trading/exchange/MI_INDEX/MI_INDEX.php" // year, mon, day, type
	TWSEREAL    string = "/stock/api/getStockInfo.jsp?ex_ch=%s_%s.tw_%s&json=1&delay=0&_=%d"
)

// RandInt return random int.
func RandInt() int {
	return time.Now().Nanosecond()
}

//func RandInt() int64 {
//	result, _ := rand.Int(rand.Reader, big.NewInt(102400))
//	return result.Int64()
//}

//func RandInt() int64 {
//	r := rand.New(rand.NewSource(time.Now().UnixNano()))
//	return r.Int63n(100000)
//}

var dateReg = regexp.MustCompile(`([\d]{2,})/([\d]{1,2})/([\d]{1,2})`)

// ParseDate is to parse "104/01/13" format.
func ParseDate(strDate string) time.Time {
	p := dateReg.FindStringSubmatch(strDate)
	year, _ := strconv.Atoi(p[1])
	mon, _ := strconv.Atoi(p[2])
	day, _ := strconv.Atoi(p[3])
	return time.Date(year+1911, time.Month(mon), day, 0, 0, 0, 0, TaipeiTimeZone)
}

// SumFloat64 計算總和（float64）
func SumFloat64(data []float64) float64 {
	var result float64
	for _, v := range data {
		result += v
	}
	return result
}

// AvgFloat64 計算平均（float64）
func AvgFloat64(data []float64) float64 {
	return float64(int(SumFloat64(data)*100)/len(data)) / 100
}

// SumUint64 計算總和（uint64）
func SumUint64(data []uint64) uint64 {
	var result uint64
	for _, v := range data {
		result += v
	}
	return result
}

// AvgUint64 計算平均（uint64）
func AvgUint64(data []uint64) uint64 {
	return SumUint64(data) / uint64(len(data))
}

// ThanPastUint64 計算最後一個數值是否為過去幾天最大或最小（uint64）
func ThanPastUint64(data []uint64, days int, max bool) bool {
	var dataFloat64 = make([]float64, days+1)
	for i, v := range data[len(data)-1-days:] {
		dataFloat64[i] = float64(v)
	}
	return thanPast(dataFloat64, max)
}

// ThanPastFloat64 計算最後一個數值是否為過去幾天最大或最小（float64）
func ThanPastFloat64(data []float64, days int, max bool) bool {
	return thanPast(data[len(data)-1-days:], max)
}

func thanPast(data []float64, max bool) bool {
	//var dataFloat64 []float64
	//dataFloat64 = make([]float64, days+1)
	//for i, v := range data[len(data)-1-days : len(data)-1] {
	//	switch v.(type) {
	//	case int64:
	//		dataFloat64[i] = float64(v.(int64))
	//	case float64:
	//		dataFloat64[i] = v.(float64)
	//	}
	//}

	var base = data[len(data)-1]
	var condition func(b float64) bool

	if max {
		condition = func(b float64) bool { return base > b }
	} else {
		condition = func(b float64) bool { return base < b }
	}

	for _, v := range data[:len(data)-1] {
		if condition(v) {
			continue
		} else {
			return false
		}
	}
	return true
}

// ThanSumPastFloat64 計算最後一個數值是否為過去幾天的總和大或小（float64）
func ThanSumPastFloat64(data []float64, days int, max bool) bool {
	return thanSumPast(data[len(data)-1-days:], max)
}

// ThanSumPastUint64 計算最後一個數值是否為過去幾天的總和大或小（uint64）
func ThanSumPastUint64(data []uint64, days int, max bool) bool {
	var dataFloat64 = make([]float64, days+1)
	for i, v := range data[len(data)-1-days:] {
		dataFloat64[i] = float64(v)
	}
	return thanSumPast(dataFloat64, max)
}

func thanSumPast(data []float64, max bool) bool {
	var result = data[len(data)-1] > SumFloat64(data[:len(data)-1])
	if max {
		return result
	}
	return !result
}

func CountCountineFloat64(data []float64) (int, bool) {
	return countCountineFloat64(data)
}

func countCountineFloat64(data []float64) (int, bool) {
	var condition func(x float64) bool
	if data[len(data)-1] > 0 {
		condition = func(x float64) bool { return x > 0 }
	} else {
		condition = func(x float64) bool { return x < 0 }
	}
	counter := 1
	for i := 1; i <= len(data)-1; i++ {
		if condition(data[len(data)-1-i]) {
			counter++
		} else {
			break
		}
	}
	return counter, data[len(data)-1] > 0
}
<|endoftext|>


<commit_before>package utils

import (
	"net"
	"os"
	"reflect"
	"sync"
)

const (
	Empty = ""
)

var (
	internalIpOnce sync.Once
)

//ips
func GetInternal() string {
	internalIp := ""

	internalIpOnce.Do(func() {
		addrs, err := net.InterfaceAddrs()
		if err != nil {
			os.Stderr.WriteString("Oops:" + err.Error())
			os.Exit(1)
		}
		for _, a := range addrs {
			if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
				if ipnet.IP.To4() != nil {
					internalIp = ipnet.IP.To4().String()
				}
			}
		}
	})
	return internalIp
}

func IsNotNil(object interface{}) bool {
	return !IsNilObject(object)
}

func IsNilObject(object interface{}) bool {
	if object == nil {
		return true
	}

	value := reflect.ValueOf(object)
	kind := value.Kind()
	if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
		return true
	}

	return false
}
<commit_msg>add test case for utils<commit_after>package utils

import (
	"net"
	"os"
	"reflect"
	"sync"
)

const (
	Empty = ""
)

var (
	internalIpOnce sync.Once
	internalIp     = ""
)

//ips
func GetInternal() string {
	internalIpOnce.Do(func() {
		addrs, err := net.InterfaceAddrs()
		if err != nil {
			os.Stderr.WriteString("Oops:" + err.Error())
			os.Exit(1)
		}
		for _, a := range addrs {
			if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
				if ipnet.IP.To4() != nil {
					internalIp = ipnet.IP.To4().String()
				}
			}
		}
	})
	return internalIp
}

func IsNotNil(object interface{}) bool {
	return !IsNilObject(object)
}

func IsNilObject(object interface{}) bool {
	if object == nil {
		return true
	}

	value := reflect.ValueOf(object)
	kind := value.Kind()
	if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
		return true
	}

	return false
}
<|endoftext|>


<commit_before>
package route

import (
	//"log"
	"geo"
	"graph"
	"kdtree"
	"math"
)

type RoutePlanner struct {
	// Underlying graph structure
	Graph           *graph.ClusterGraph
	// User input
	Waypoints       []geo.Coordinate
	// Graph setting
	Transport       graph.Transport
	Metric          graph.Metric
	AvoidFerries    bool
	// Planner options
	ConcurrentKd    bool
	ConcurrentLegs  bool
	ConcurrentPaths bool
	// KdTree Output
	Locations       []kdtree.Location
}

// Execute f(0), f(1), ..., f(n-1) and do so in parallel, based on the
// corresponding flag.
func Multiplex(n int, inParallel bool, f func (int)) {
	if inParallel && n > 0 {
		barrier := make(chan int, n)
		for i := 0; i < n; i++ {
			go func() {
				f(i)
				barrier <- 1
			}()
		}
		for i := 0; i < n; i++ {
			<-barrier
		}
	} else {
		for i := 0; i < n; i++ {
			f(i)
		}
	}
}

func (r *RoutePlanner) Run() *Result {
	// Compute the closest point in the graph for each user specified waypoint.
	count := len(r.Waypoints)
	r.Locations = make([]kdtree.Location, count)
	//log.Printf("Planning a route containing %v legs.", count-1)
	Multiplex(count, r.ConcurrentKd, func (i int) {
		//log.Printf("NearestNeighbor(%v, %v)", r.Waypoints[i], r.Transport)
		r.Locations[i] = kdtree.NearestNeighbor(r.Waypoints[i], r.Transport)
	})

	// Now compute a shortest path for each leg.
	// If ConcurrentLegs is set compute the legs concurrently.
	legs := make([]Leg, count-1)
	Multiplex(count-1, r.ConcurrentLegs, func (i int) {
		//log.Printf("ComputeLeg(%v)", i)
		legs[i] = r.ComputeLeg(i)
	})

	// Format the results.
	//log.Printf("Output Results.")
	distance := 0
	duration := 0
	for _, leg := range legs {
		distance += leg.Distance.Value
		duration += leg.Duration.Value
	}

	route := Route{
		Distance:      FormatDistance(float64(distance)),
		Duration:      FormatDuration(float64(duration)),
		StartLocation: legs[0].StartLocation,
		EndLocation:   legs[len(legs)-1].EndLocation,
		Legs:          legs,
	}

	return &Result{
		BoundingBox: ComputeBounds(route),
		Routes:      []Route{route},
	}
}

func (r *RoutePlanner) UnionGraph(src, dst kdtree.Location) (*graph.UnionGraph, int, int) {
	overlay := r.Graph.Overlay
	cluster := []*graph.GraphFile(nil)
	indices := []int(nil)
	srcCluster := -1
	dstCluster := -1

	//log.Printf("Create UnionGraph")

	if src.Cluster != -1 {
		cluster = append(cluster, r.Graph.Cluster[src.Cluster])
		indices = append(indices, src.Cluster)
		srcCluster = 0
		//log.Printf("Cluster 0: %v\n", src.Cluster)
	}

	if dst.Cluster != -1 && src.Cluster != dst.Cluster {
		cluster = append(cluster, r.Graph.Cluster[dst.Cluster])
		indices = append(indices, dst.Cluster)
		dstCluster = srcCluster + 1
		//log.Printf("Cluster %v: %v\n", dstCluster, dst.Cluster)
	} else if src.Cluster == dst.Cluster {
		dstCluster = srcCluster
	}

	g := graph.NewUnionGraph(overlay, cluster, indices)
 	return g, srcCluster, dstCluster
}

// Convenience function to find a forward edge (of minimum weight) from
// vertex u to vertex v. Returns -1 if no edge was found.
func (r *RoutePlanner) EdgeBetween(g graph.Graph, u, v graph.Vertex) graph.Edge {
	minEdge   := graph.Edge(-1)
	minWeight := math.Inf(1)
	for _, e := range g.VertexEdges(u, true, r.Transport, nil) {
		n := g.EdgeOpposite(e, u)
		if n != v {
			continue
		}
		weight := g.EdgeWeight(e, r.Transport, r.Metric)
		if weight < minWeight {
			minEdge = e
			minWeight = weight
		}
	}
	return minEdge
}

func (r *RoutePlanner) DartBetween(g graph.Graph, u, v graph.Vertex) float32 {
	minWeight := float32(math.Inf(1))
	for _, d := range g.VertexNeighbors(u, true, r.Transport, r.Metric, nil) {
		if d.Vertex != v {
			continue
		}
		weight := d.Weight
		if weight < minWeight {
			minWeight = weight
		}
	}
	return minWeight
}

func (r *RoutePlanner) ReverseDartBetween(g graph.Graph, u, v graph.Vertex) float32 {
	minWeight := float32(math.Inf(1))
	for _, d := range g.VertexNeighbors(v, false, r.Transport, r.Metric, nil) {
		if d.Vertex != u {
			continue
		}
		weight := d.Weight
		if weight < minWeight {
			minWeight = weight
		}
	}
	return minWeight
}

// Compute one path segment between location[waypointIndex] and location[waypointIndex+1]
func (r *RoutePlanner) ComputeLeg(waypointIndex int) Leg {
	src := r.Locations[waypointIndex]
	dst := r.Locations[waypointIndex+1]
	buf := []geo.Coordinate(nil)
	srcWays := src.Decode(true  /* forward */, r.Transport, &buf)
	dstWays := dst.Decode(false /* forward */, r.Transport, &buf)

	// Compute the union of the source and target clusters.
	g, srcCluster, dstCluster := r.UnionGraph(src, dst)

	// Run Dijkstra on the union graph
	//log.Printf("Dijkstra on the UnionGraph")
	router := &BidiRouter{
		Transport: r.Transport,
		Metric:    r.Metric,
	}
	router.Reset(g)
	for _, srcWay := range srcWays {
		v := g.ToUnionVertex(srcWay.Vertex, srcCluster)
		router.AddSource(v, float32(srcWay.Length))
	}
	for _, dstWay := range dstWays {
		v := g.ToUnionVertex(dstWay.Vertex, dstCluster)
		router.AddTarget(v, float32(dstWay.Length))
	}
	router.Run()

	// Gather the result path.
	//log.Printf("Construct the vertex path.")
	vpath    := router.VPath()
	segments := [][]Step(nil)
	sketches := []int(nil)
	indices  := []int(nil)
	i        := 0
	for i < len(vpath)-1 {
		u, v   := vpath[i], vpath[i+1]
		uindex := g.VertexToCluster(u)
		vindex := g.VertexToCluster(v)
		steps  := []Step(nil)

		if uindex == -1 && vindex == -1 {
			// This might be a shortcut edge, or it might just be an edge on
			// the overlay graph. For simplicity we always treat this as a single
			// step.
			overlay := r.Graph.Overlay
			e := r.EdgeBetween(overlay, u, v)
			if int(e) == -1 {
				// Shortcut edge, we will have to elaborate it later.
				//log.Printf("Shortcut edge %v", len(segments))
				//log.Printf("From vertex %v to %v", u, v)
				//log.Printf("Weight:    %v", r.DartBetween(overlay, u, v))
				//log.Printf("RevWeight: %v", r.ReverseDartBetween(overlay, u, v))
				sketches = append(sketches, len(segments))
				indices  = append(indices, i)
			} else {
				// Cut edge
				//log.Printf("Cut edge %v", len(segments))
				//log.Printf("From vertex %v to %v", u, v)
				//log.Printf("Weight: %v", overlay.EdgeWeight(e, r.Transport, r.Metric))
				steps = append(steps, r.EdgeToStep(overlay, e, u, v))
			}
			//clusterIndexU, idU := g.Overlay.VertexCluster(u)
			//log.Printf("ClusterIndex u: %v, in cluster id: %v", clusterIndexU, idU)
			//clusterIndexV, idV := g.Overlay.VertexCluster(v)
			//log.Printf("ClusterIndex v: %v, in cluster id: %v", clusterIndexV, idV)
			i++
		} else {
			// A path within a cluster. We collect all edges until we hit the
			// next boundary vertex.
			//clusterIndex := -1
			//if uindex == -1 {
			//	clusterIndex, _ = g.Overlay.VertexCluster(u)
			//} else {
			//	clusterIndex = g.Indices[uindex]
			//}
			//log.Printf("Cluster path in cluster %v", clusterIndex)
			//log.Printf(" * Vertex %v", u)
			u, cluster := g.ToClusterVertex(u, uindex)
			//log.Printf("  * Cluster Vertex %v", u)
			j, done := i + 1, false
			for !done && j < len(vpath) {
				v := vpath[j]

				// Project the vertex v onto the current cluster.
				vindex := g.VertexToCluster(v)
				//log.Printf(" * Vertex %v, Index: %v", v, vindex)
				if vindex == -1 {
					done = true
					//var clusterIndexV int
					/*clusterIndexV*/_, v = g.Overlay.VertexCluster(v)
					//log.Printf("  * Cluster Vertex: %v, Cluster: %v", v, clusterIndexV)
				} else {
					v, _ = g.ToClusterVertex(v, vindex)
					//clusterIndexV := g.Indices[vindex]
					//log.Printf("  * Cluster Vertex: %v, Cluster: %v", v, clusterIndexV)
					j++
				}

				// Find the matching u - v edge in the current cluster
				e := r.EdgeBetween(cluster, u, v)
				steps = append(steps, r.EdgeToStep(cluster, e, u, v))
				u = v
			}
			i = j
		}

		segments = append(segments, steps)
	}

	// Elaborate the result path
	//log.Printf("Elaborate the path.")
	Multiplex(len(sketches), r.ConcurrentPaths, func (i int) {
		// Find the boundary vertices and cluster corresponding to this shortcut.
		index := indices[i]
		clusterIndex, u := g.Overlay.VertexCluster(vpath[index])
		cluster := r.Graph.Cluster[clusterIndex]
		_, v := g.Overlay.VertexCluster(vpath[index+1])

		// Ensure that the vertices are accessible
		//log.Printf("Routing from vertex %v to %v", u, v)
		//log.Printf("Access[u]: %v", cluster.VertexAccessible(u, r.Transport))
		//log.Printf("Access[v]: %v", cluster.VertexAccessible(v, r.Transport))

		// Run Dijkstra to find a u -> v path.
		//log.Printf("Run Dijkstra within a cluster.")
		router := &BidiRouter{
			Transport: r.Transport,
			Metric:    r.Metric,
		}
		router.Reset(cluster)
		router.AddSource(u, 0)
		router.AddTarget(v, 0)
		router.Run()

		// Convert this path into a step array.
		//log.Printf("Convert to step array.")
		vertices, edges := router.Path()
		steps := make([]Step, len(edges))
		for j, edge := range edges {
			s := vertices[j]
			t := vertices[j+1]
			steps[j] = r.EdgeToStep(cluster, edge, s, t)
		}
		segments[sketches[i]] = steps
	})

	// Build Leg
	//log.Printf("Build leg.")
	indexstart, indexend := -1, -1
	var startc, stopc geo.Coordinate
	for i, srcWay := range srcWays {
		vertex := g.ToUnionVertex(srcWay.Vertex, srcCluster)
		if vpath[0] == vertex {
			indexstart = i
			startc = src.Graph.VertexCoordinate(srcWay.Vertex)
			break
		}
	}
	for i, dstWay := range dstWays {
		vertex := g.ToUnionVertex(dstWay.Vertex, dstCluster)
		if vpath[len(vpath)-1] == vertex {
			indexend = i
			stopc = dst.Graph.VertexCoordinate(dstWay.Vertex)
			break
		}
	}
	steps := []Step(nil)
	for _, segment := range segments {
		steps = append(steps, segment...)
	}
	return r.StepsToLeg(steps, srcWays[indexstart], dstWays[indexend], startc, stopc)
}
<commit_msg>Beware of non-functional closure semantics.<commit_after>
package route

import (
	"log"
	"geo"
	"graph"
	"kdtree"
	"math"
)

type RoutePlanner struct {
	// Underlying graph structure
	Graph           *graph.ClusterGraph
	// User input
	Waypoints       []geo.Coordinate
	// Graph setting
	Transport       graph.Transport
	Metric          graph.Metric
	AvoidFerries    bool
	// Planner options
	ConcurrentKd    bool
	ConcurrentLegs  bool
	ConcurrentPaths bool
	// KdTree Output
	Locations       []kdtree.Location
}

// Execute f(0), f(1), ..., f(n-1) and do so in parallel, based on the
// corresponding flag.
func Multiplex(n int, inParallel bool, f func (int)) {
	if inParallel && n > 0 {
		barrier := make(chan int, n)
		for i := 0; i < n; i++ {
			go func(i int) {
				f(i)
				barrier <- 1
			}(i)
		}
		for i := 0; i < n; i++ {
			<-barrier
		}
	} else {
		for i := 0; i < n; i++ {
			f(i)
		}
	}
}

func (r *RoutePlanner) Run() *Result {
	// Compute the closest point in the graph for each user specified waypoint.
	count := len(r.Waypoints)
	r.Locations = make([]kdtree.Location, count)
	//log.Printf("Planning a route containing %v legs.", count-1)
	Multiplex(count, r.ConcurrentKd, func (i int) {
		r.Locations[i] = kdtree.NearestNeighbor(r.Waypoints[i], r.Transport)
	})

	// Now compute a shortest path for each leg.
	// If ConcurrentLegs is set compute the legs concurrently.
	legs := make([]Leg, count-1)
	Multiplex(count-1, r.ConcurrentLegs, func (i int) {
		//log.Printf("ComputeLeg(%v)", i)
		legs[i] = r.ComputeLeg(i)
	})

	// Format the results.
	//log.Printf("Output Results.")
	distance := 0
	duration := 0
	for _, leg := range legs {
		distance += leg.Distance.Value
		duration += leg.Duration.Value
	}

	route := Route{
		Distance:      FormatDistance(float64(distance)),
		Duration:      FormatDuration(float64(duration)),
		StartLocation: legs[0].StartLocation,
		EndLocation:   legs[len(legs)-1].EndLocation,
		Legs:          legs,
	}

	return &Result{
		BoundingBox: ComputeBounds(route),
		Routes:      []Route{route},
	}
}

func (r *RoutePlanner) UnionGraph(src, dst kdtree.Location) (*graph.UnionGraph, int, int) {
	overlay := r.Graph.Overlay
	cluster := []*graph.GraphFile(nil)
	indices := []int(nil)
	srcCluster := -1
	dstCluster := -1

	//log.Printf("Create UnionGraph")

	if src.Cluster != -1 {
		cluster = append(cluster, r.Graph.Cluster[src.Cluster])
		indices = append(indices, src.Cluster)
		srcCluster = 0
		//log.Printf("Cluster 0: %v\n", src.Cluster)
	}

	if dst.Cluster != -1 && src.Cluster != dst.Cluster {
		cluster = append(cluster, r.Graph.Cluster[dst.Cluster])
		indices = append(indices, dst.Cluster)
		dstCluster = srcCluster + 1
		//log.Printf("Cluster %v: %v\n", dstCluster, dst.Cluster)
	} else if src.Cluster == dst.Cluster {
		dstCluster = srcCluster
	}

	g := graph.NewUnionGraph(overlay, cluster, indices)
 	return g, srcCluster, dstCluster
}

// Convenience function to find a forward edge (of minimum weight) from
// vertex u to vertex v. Returns -1 if no edge was found.
func (r *RoutePlanner) EdgeBetween(g graph.Graph, u, v graph.Vertex) graph.Edge {
	minEdge   := graph.Edge(-1)
	minWeight := math.Inf(1)
	for _, e := range g.VertexEdges(u, true, r.Transport, nil) {
		n := g.EdgeOpposite(e, u)
		if n != v {
			continue
		}
		weight := g.EdgeWeight(e, r.Transport, r.Metric)
		if weight < minWeight {
			minEdge = e
			minWeight = weight
		}
	}
	return minEdge
}

func (r *RoutePlanner) DartBetween(g graph.Graph, u, v graph.Vertex) float32 {
	minWeight := float32(math.Inf(1))
	for _, d := range g.VertexNeighbors(u, true, r.Transport, r.Metric, nil) {
		if d.Vertex != v {
			continue
		}
		weight := d.Weight
		if weight < minWeight {
			minWeight = weight
		}
	}
	return minWeight
}

func (r *RoutePlanner) ReverseDartBetween(g graph.Graph, u, v graph.Vertex) float32 {
	minWeight := float32(math.Inf(1))
	for _, d := range g.VertexNeighbors(v, false, r.Transport, r.Metric, nil) {
		if d.Vertex != u {
			continue
		}
		weight := d.Weight
		if weight < minWeight {
			minWeight = weight
		}
	}
	return minWeight
}

// Compute one path segment between location[waypointIndex] and location[waypointIndex+1]
func (r *RoutePlanner) ComputeLeg(waypointIndex int) Leg {
	src := r.Locations[waypointIndex]
	dst := r.Locations[waypointIndex+1]
	buf := []geo.Coordinate(nil)
	srcWays := src.Decode(true  /* forward */, r.Transport, &buf)
	dstWays := dst.Decode(false /* forward */, r.Transport, &buf)

	// Compute the union of the source and target clusters.
	g, srcCluster, dstCluster := r.UnionGraph(src, dst)

	// Run Dijkstra on the union graph
	//log.Printf("Dijkstra on the UnionGraph")
	router := &BidiRouter{
		Transport: r.Transport,
		Metric:    r.Metric,
	}
	router.Reset(g)
	for _, srcWay := range srcWays {
		v := g.ToUnionVertex(srcWay.Vertex, srcCluster)
		router.AddSource(v, float32(srcWay.Length))
	}
	for _, dstWay := range dstWays {
		v := g.ToUnionVertex(dstWay.Vertex, dstCluster)
		router.AddTarget(v, float32(dstWay.Length))
	}
	router.Run()

	// Gather the result path.
	//log.Printf("Construct the vertex path.")
	vpath    := router.VPath()
	segments := [][]Step(nil)
	sketches := []int(nil)
	indices  := []int(nil)
	i        := 0
	for i < len(vpath)-1 {
		u, v   := vpath[i], vpath[i+1]
		uindex := g.VertexToCluster(u)
		vindex := g.VertexToCluster(v)
		steps  := []Step(nil)

		if uindex == -1 && vindex == -1 {
			// This might be a shortcut edge, or it might just be an edge on
			// the overlay graph. For simplicity we always treat this as a single
			// step.
			overlay := r.Graph.Overlay
			e := r.EdgeBetween(overlay, u, v)
			if int(e) == -1 {
				// Shortcut edge, we will have to elaborate it later.
				//log.Printf("Shortcut edge %v", len(segments))
				//log.Printf("From vertex %v to %v", u, v)
				//log.Printf("Weight:    %v", r.DartBetween(overlay, u, v))
				//log.Printf("RevWeight: %v", r.ReverseDartBetween(overlay, u, v))
				sketches = append(sketches, len(segments))
				indices  = append(indices, i)
			} else {
				// Cut edge
				//log.Printf("Cut edge %v", len(segments))
				//log.Printf("From vertex %v to %v", u, v)
				//log.Printf("Weight: %v", overlay.EdgeWeight(e, r.Transport, r.Metric))
				steps = append(steps, r.EdgeToStep(overlay, e, u, v))
			}
			//clusterIndexU, idU := g.Overlay.VertexCluster(u)
			//log.Printf("ClusterIndex u: %v, in cluster id: %v", clusterIndexU, idU)
			//clusterIndexV, idV := g.Overlay.VertexCluster(v)
			//log.Printf("ClusterIndex v: %v, in cluster id: %v", clusterIndexV, idV)
			i++
		} else {
			// A path within a cluster. We collect all edges until we hit the
			// next boundary vertex.
			//clusterIndex := -1
			//if uindex == -1 {
			//	clusterIndex, _ = g.Overlay.VertexCluster(u)
			//} else {
			//	clusterIndex = g.Indices[uindex]
			//}
			//log.Printf("Cluster path in cluster %v", clusterIndex)
			//log.Printf(" * Vertex %v", u)
			u, cluster := g.ToClusterVertex(u, uindex)
			//log.Printf("  * Cluster Vertex %v", u)
			j, done := i + 1, false
			for !done && j < len(vpath) {
				v := vpath[j]

				// Project the vertex v onto the current cluster.
				vindex := g.VertexToCluster(v)
				//log.Printf(" * Vertex %v, Index: %v", v, vindex)
				if vindex == -1 {
					done = true
					//var clusterIndexV int
					/*clusterIndexV*/_, v = g.Overlay.VertexCluster(v)
					//log.Printf("  * Cluster Vertex: %v, Cluster: %v", v, clusterIndexV)
				} else {
					v, _ = g.ToClusterVertex(v, vindex)
					//clusterIndexV := g.Indices[vindex]
					//log.Printf("  * Cluster Vertex: %v, Cluster: %v", v, clusterIndexV)
					j++
				}

				// Find the matching u - v edge in the current cluster
				e := r.EdgeBetween(cluster, u, v)
				steps = append(steps, r.EdgeToStep(cluster, e, u, v))
				u = v
			}
			i = j
		}

		segments = append(segments, steps)
	}

	// Elaborate the result path
	//log.Printf("Elaborate the path.")
	Multiplex(len(sketches), r.ConcurrentPaths, func (i int) {
		// Find the boundary vertices and cluster corresponding to this shortcut.
		index := indices[i]
		clusterIndex, u := g.Overlay.VertexCluster(vpath[index])
		cluster := r.Graph.Cluster[clusterIndex]
		_, v := g.Overlay.VertexCluster(vpath[index+1])

		// Ensure that the vertices are accessible
		//log.Printf("Routing from vertex %v to %v", u, v)
		//log.Printf("Access[u]: %v", cluster.VertexAccessible(u, r.Transport))
		//log.Printf("Access[v]: %v", cluster.VertexAccessible(v, r.Transport))

		// Run Dijkstra to find a u -> v path.
		//log.Printf("Run Dijkstra within a cluster.")
		router := &BidiRouter{
			Transport: r.Transport,
			Metric:    r.Metric,
		}
		router.Reset(cluster)
		router.AddSource(u, 0)
		router.AddTarget(v, 0)
		router.Run()

		// Convert this path into a step array.
		//log.Printf("Convert to step array.")
		vertices, edges := router.Path()
		steps := make([]Step, len(edges))
		for j, edge := range edges {
			s := vertices[j]
			t := vertices[j+1]
			steps[j] = r.EdgeToStep(cluster, edge, s, t)
		}
		segments[sketches[i]] = steps
	})

	// Build Leg
	//log.Printf("Build leg.")
	indexstart, indexend := -1, -1
	var startc, stopc geo.Coordinate
	for i, srcWay := range srcWays {
		vertex := g.ToUnionVertex(srcWay.Vertex, srcCluster)
		if vpath[0] == vertex {
			indexstart = i
			startc = src.Graph.VertexCoordinate(srcWay.Vertex)
			break
		}
	}
	for i, dstWay := range dstWays {
		vertex := g.ToUnionVertex(dstWay.Vertex, dstCluster)
		if vpath[len(vpath)-1] == vertex {
			indexend = i
			stopc = dst.Graph.VertexCoordinate(dstWay.Vertex)
			break
		}
	}
	steps := []Step(nil)
	for _, segment := range segments {
		steps = append(steps, segment...)
	}
	return r.StepsToLeg(steps, srcWays[indexstart], dstWays[indexend], startc, stopc)
}
<|endoftext|>


<commit_before>// Package wiki implements a simple GitHub wiki miner and output formatter.
package wiki

import (
	"fmt"
	"io"
	"strings"

	"github.com/PuerkitoBio/goquery"
	"github.com/mitchellh/go-wordwrap"
	"golang.org/x/net/html"
)

// TODO: handle invalid page
// TODO: WikiTopics could be interactive
// TODO: ~/.apex.json user config, use here for color mapping etc

// colors.
const (
	none   = 0
	red    = 31
	green  = 32
	yellow = 33
	blue   = 34
	gray   = 37
)

// Endpoint used to lookup wiki information.
var Endpoint = "https://github.com/apex/apex/wiki"

// Topics outputs topic categories from the wiki index page.
func Topics(w io.Writer) error {
	doc, err := goquery.NewDocument(Endpoint)
	if err != nil {
		return err
	}

	fmt.Fprintf(w, "\n")
	defer fmt.Fprintf(w, "\n")

	doc.Find(`#wiki-content .markdown-body ul li`).Each(func(i int, s *goquery.Selection) {
		strs := strings.Split(text(s), ": ")
		fmt.Fprintf(w, "  \033[%dm%s\033[0m: %s \n", blue, strs[0], strs[1])
	})

	fmt.Fprintf(w, "\n  Use `apex wiki <topic>` to view a topic.\n")

	return nil
}

// Topic outputs documentation for the given `topic`'s page.
func Topic(topic string, w io.Writer) error {
	doc, err := goquery.NewDocument(fmt.Sprintf("%s/%s", Endpoint, topic))
	if err != nil {
		return err
	}

	fmt.Fprintln(w)
	fmt.Fprint(w, nodes(doc.Find(`#wiki-content .markdown-body`)))
	fmt.Fprintln(w)

	return nil
}

// nodes returns a string representation of the selection's children.
func nodes(s *goquery.Selection) string {
	return strings.Join(s.Children().Map(node), "")
}

// contents returns a string representation of the selection's contents.
func contents(s *goquery.Selection) string {
	return strings.Join(s.Contents().Map(node), "")
}

// node returns a string representation of the selection.
func node(i int, s *goquery.Selection) string {
	switch node := s.Get(0); {
	case node.Data == "h1":
		return fmt.Sprintf(" \033[%dm# %s\033[0m\n\n", blue, text(s))
	case node.Data == "h2":
		return fmt.Sprintf(" \033[%dm## %s\033[0m\n\n", blue, text(s))
	case node.Data == "h3":
		return fmt.Sprintf(" \033[%dm### %s\033[0m\n\n", blue, text(s))
	case node.Data == "p":
		return fmt.Sprintf("\033[%dm%s\033[0m\n\n", none, indent(text(s), 1))
	case node.Data == "pre" || s.HasClass("highlight"):
		return fmt.Sprintf("\033[%dm%s\033[0m\n\n", gray, indent(text(s), 2))
	case node.Data == "a":
		return fmt.Sprintf("%s (%s) ", s.Text(), s.AttrOr("href", "missing link"))
	case node.Data == "li":
		return fmt.Sprintf("  • %s\n", contents(s))
	case node.Data == "ul":
		return fmt.Sprintf("%s\n", nodes(s))
	case node.Data == "code":
		return fmt.Sprintf("\033[1m%s\033[0m ", s.Text())
	case node.Type == html.TextNode:
		return strings.TrimSpace(node.Data)
	default:
		return ""
	}
}

// text of selection, trimmed and wrapped.
func text(s *goquery.Selection) string {
	return wordwrap.WrapString(strings.TrimSpace(s.Text()), 80)
}

// indent string N times.
func indent(s string, n int) string {
	i := strings.Repeat("  ", n)
	return i + strings.Replace(s, "\n", "\n"+i, -1)
}
<commit_msg>change wiki code to bold instead of gray<commit_after>// Package wiki implements a simple GitHub wiki miner and output formatter.
package wiki

import (
	"fmt"
	"io"
	"strings"

	"github.com/PuerkitoBio/goquery"
	"github.com/mitchellh/go-wordwrap"
	"golang.org/x/net/html"
)

// TODO: handle invalid page
// TODO: WikiTopics could be interactive
// TODO: ~/.apex.json user config, use here for color mapping etc

// colors.
const (
	none   = 0
	red    = 31
	green  = 32
	yellow = 33
	blue   = 34
	gray   = 37
)

// Endpoint used to lookup wiki information.
var Endpoint = "https://github.com/apex/apex/wiki"

// Topics outputs topic categories from the wiki index page.
func Topics(w io.Writer) error {
	doc, err := goquery.NewDocument(Endpoint)
	if err != nil {
		return err
	}

	fmt.Fprintf(w, "\n")
	defer fmt.Fprintf(w, "\n")

	doc.Find(`#wiki-content .markdown-body ul li`).Each(func(i int, s *goquery.Selection) {
		strs := strings.Split(text(s), ": ")
		fmt.Fprintf(w, "  \033[%dm%s\033[0m: %s \n", blue, strs[0], strs[1])
	})

	fmt.Fprintf(w, "\n  Use `apex wiki <topic>` to view a topic.\n")

	return nil
}

// Topic outputs documentation for the given `topic`'s page.
func Topic(topic string, w io.Writer) error {
	doc, err := goquery.NewDocument(fmt.Sprintf("%s/%s", Endpoint, topic))
	if err != nil {
		return err
	}

	fmt.Fprintln(w)
	fmt.Fprint(w, nodes(doc.Find(`#wiki-content .markdown-body`)))
	fmt.Fprintln(w)

	return nil
}

// nodes returns a string representation of the selection's children.
func nodes(s *goquery.Selection) string {
	return strings.Join(s.Children().Map(node), "")
}

// contents returns a string representation of the selection's contents.
func contents(s *goquery.Selection) string {
	return strings.Join(s.Contents().Map(node), "")
}

// node returns a string representation of the selection.
func node(i int, s *goquery.Selection) string {
	switch node := s.Get(0); {
	case node.Data == "h1":
		return fmt.Sprintf(" \033[%dm# %s\033[0m\n\n", blue, text(s))
	case node.Data == "h2":
		return fmt.Sprintf(" \033[%dm## %s\033[0m\n\n", blue, text(s))
	case node.Data == "h3":
		return fmt.Sprintf(" \033[%dm### %s\033[0m\n\n", blue, text(s))
	case node.Data == "p":
		return fmt.Sprintf("\033[%dm%s\033[0m\n\n", none, indent(text(s), 1))
	case node.Data == "pre" || s.HasClass("highlight"):
		return fmt.Sprintf("\033[1m%s\033[0m\n\n", indent(text(s), 2))
	case node.Data == "a":
		return fmt.Sprintf("%s (%s) ", s.Text(), s.AttrOr("href", "missing link"))
	case node.Data == "li":
		return fmt.Sprintf("  • %s\n", contents(s))
	case node.Data == "ul":
		return fmt.Sprintf("%s\n", nodes(s))
	case node.Data == "code":
		return fmt.Sprintf("\033[1m%s\033[0m ", s.Text())
	case node.Type == html.TextNode:
		return strings.TrimSpace(node.Data)
	default:
		return ""
	}
}

// text of selection, trimmed and wrapped.
func text(s *goquery.Selection) string {
	return wordwrap.WrapString(strings.TrimSpace(s.Text()), 80)
}

// indent string N times.
func indent(s string, n int) string {
	i := strings.Repeat("  ", n)
	return i + strings.Replace(s, "\n", "\n"+i, -1)
}
<|endoftext|>


<commit_before>package glug

import (
	"net/http"
	"net/url"
	"strings"
)

// Conn is a struct to carry all request related data through plugs.
type Conn struct {
	uuid    string
	halted  bool
	Writer  http.ResponseWriter
	Request *http.Request
	Params  url.Values
}

// Router is a http.Handler.
type Router struct {
	node node
}

// Plug is a function type we should use to make a pipeline for request.
type Plug func(Conn) Conn

type node struct {
	path     string
	methods  map[string][]Plug
	children map[string]*node
}

// Halt will stop execution of plugs.
func (conn Conn) Halt() Conn {
	conn.halted = true
	return conn
}

// Init will initialize new router.
func Init() *Router {
	return &Router{node: node{children: make(map[string]*node)}}
}

// HandleFunc will add new endpoint to router.
func (router *Router) HandleFunc(method string, path string, plugs ...Plug) {
	var curr *node

	parts := strings.Split(path, "/")[1:]
	depth := len(parts)
	prev := &router.node

	for index, part := range parts {
		if child, ok := prev.children[part]; ok {
			curr = child
		} else {
			curr = &node{path: part, children: make(map[string]*node)}
		}

		if depth == index+1 {
			curr.methods = make(map[string][]Plug)
			curr.methods[method] = plugs
		}

		prev.children[part] = curr
		prev = curr
	}
}

func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/favicon.ico" {
		logRequest(r.Method, r.URL.Path)
	}

	r.ParseForm()

	parts := strings.Split(r.URL.Path, "/")[1:]
	depth := len(parts)
	curr := &router.node

	for index, part := range parts {
		curr = curr.children[part]

		if depth == index+1 {
			break
		}
	}

	conn := Conn{Writer: w, Request: r, Params: r.Form, halted: false}

	for _, plug := range curr.methods[r.Method] {
		result := plug(conn)

		if result.halted == false {
			logPlug(plug)
			conn = result
		} else {
			logHalt(plug)
			break
		}
	}
}
<commit_msg>Fix handler for root node<commit_after>package glug

import (
	// "github.com/davecgh/go-spew/spew"
	"net/http"
	"net/url"
	"strings"
)

// Conn is a struct to carry all request related data through plugs.
type Conn struct {
	uuid    string
	halted  bool
	Writer  http.ResponseWriter
	Request *http.Request
	Params  url.Values
}

// Router is a http.Handler.
type Router struct {
	node node
}

// Plug is a function type we should use to make a pipeline for request.
type Plug func(Conn) Conn

type node struct {
	path     string
	methods  map[string][]Plug
	children map[string]*node
}

// Halt will stop execution of plugs.
func (conn Conn) Halt() Conn {
	conn.halted = true
	return conn
}

// Init will initialize new router.
func Init() *Router {
	return &Router{node: node{children: make(map[string]*node)}}
}

// HandleFunc will add new endpoint to router.
func (router *Router) HandleFunc(method string, path string, plugs ...Plug) {
	if path == "/" {
		router.node.path = path
		router.node.methods = make(map[string][]Plug)
		router.node.methods[method] = plugs
	} else {
		var curr *node

		parts := strings.Split(path, "/")[1:]
		depth := len(parts)
		prev := &router.node

		for index, part := range parts {
			if child, ok := prev.children[part]; ok {
				curr = child
			} else {
				curr = &node{path: part, children: make(map[string]*node)}
			}

			if depth == index+1 {
				curr.methods = make(map[string][]Plug)
				curr.methods[method] = plugs
			}

			prev.children[part] = curr
			prev = curr
		}
	}
}

func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/favicon.ico" {
		logRequest(r.Method, r.URL.Path)
	}

	r.ParseForm()

	curr := &router.node
	conn := Conn{Writer: w, Request: r, Params: r.Form, halted: false}

	if r.URL.Path != "/" {
		parts := strings.Split(r.URL.Path, "/")[1:]
		depth := len(parts)

		for index, part := range parts {
			curr = curr.children[part]

			if depth == index+1 {
				break
			}
		}
	}

	for _, plug := range curr.methods[r.Method] {
		result := plug(conn)

		if result.halted == false {
			logPlug(plug)
			conn = result
		} else {
			logHalt(plug)
			break
		}
	}
}
<|endoftext|>


<commit_before>package wini

import (
	"fmt"
	"path/filepath"
	"runtime"
	"testing"

	"github.com/bmizerany/assert"
)

func Test1(t *testing.T) {

	filename := filepath.Join(getTestDataDir(), "ini_parser_testfile.ini")
	ini := New()
	err := ini.ParseFile(filename)
	assert.Equal(t, nil, err)

	v, ok := ini.Get("mid")
	assert.Equal(t, v, "ac9219aa5232c4e519ae5fcb4d77ae5b")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("product")
	assert.Equal(t, v, "ppp")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("combo")
	assert.Equal(t, v, "ccc")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("aa")
	assert.Equal(t, v, "bb")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("axxxa")
	assert.Equal(t, v, "")
	assert.Equal(t, ok, false)

	m, ok := ini.GetKvmap("")
	assert.Equal(t, len(m), 6)
	assert.Equal(t, ok, true)

	n, ok := ini.GetKvmap("n")
	assert.Equal(t, len(n), 0)
	assert.Equal(t, ok, false)

	sss, ok := ini.GetKvmap("sss")
	assert.Equal(t, len(sss), 2)
	assert.Equal(t, ok, true)
	v, ok = ini.SectionGet("sss", "aa")
	assert.Equal(t, v, "bb")
	assert.Equal(t, ok, true)
	v, ok = ini.SectionGet("sss", "appext")
	assert.Equal(t, v, "ab=cd")
	assert.Equal(t, ok, true)
}

func TestUft8(t *testing.T) {
	/*
		title=百度搜索_ipad2
		url=http://www.baidu.com/s?bs=ipad&f=8&rsv_bp=1&wd=ipad2&inputT=397
		url_md5=5844a75423cd3372e1997360bd110a25
		refer=http://www.google.com
		anchor_text= google
		ret_form = json
		 ret_start = 0
		 ret_limit =    50
		page_info   =  0,0,50,1,0,20
		local=0
		mid=c4ca4238a0b923820dcc509a6f75849b
		product=test
		combo=test
		version=1.0.0.1
		debug=1
		encoding=1

	*/

	filename := filepath.Join(getTestDataDir(), "utf8.ini")
	ini := New()
	err := ini.ParseFile(filename)
	assert.Equal(t, nil, err)

	v, ok := ini.Get("title")
	assert.Equal(t, v, "百度搜索_ipad2")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("url_md5")
	assert.Equal(t, v, "5844a75423cd3372e1997360bd110a25")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("ret_form")
	assert.Equal(t, v, "json")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("ret_start")
	assert.Equal(t, v, "0")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("ret_limit")
	assert.Equal(t, v, "50")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("axxxa")
	assert.Equal(t, v, "")
	assert.Equal(t, ok, false)

	m, ok := ini.GetKvmap("")
	assert.Equal(t, len(m), 16)
	assert.Equal(t, ok, true)

	n, ok := ini.GetKvmap("n")
	assert.Equal(t, len(n), 0)
	assert.Equal(t, ok, false)
}

func TestErrorFormat(t *testing.T) {
	filename := filepath.Join(getTestDataDir(), "error.ini")
	ini := New()
	err := ini.ParseFile(filename)
	assert.NotEqual(t, nil, err)
}

func TestMemoryData1(t *testing.T) {
	raw := []byte("a:av||b:bv||c:cv||||d:dv||||||")
	ini := New()
	err := ini.Parse(raw, "|", ":")
	assert.Equal(t, nil, err)

	v, ok := ini.Get("a")
	assert.Equal(t, v, "av")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("b")
	assert.Equal(t, v, "bv")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("c")
	assert.Equal(t, v, "cv")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("d")
	assert.Equal(t, v, "dv")
	assert.Equal(t, ok, true)

	m, ok := ini.GetKvmap("")
	assert.Equal(t, len(m), 4)
	assert.Equal(t, ok, true)

	n, ok := ini.GetKvmap("n")
	assert.Equal(t, len(n), 0)
	assert.Equal(t, ok, false)
}

func TestMemoryData2(t *testing.T) {
	raw := []byte("a:av||b:bv||c:cv||||d:dv||||||")
	ini := New()
	err := ini.Parse(raw, "||", ":") // DIFFERENT with TestMemoryData1. use "||" instead of "|"
	assert.Equal(t, nil, err)

	v, ok := ini.Get("a")
	assert.Equal(t, v, "av")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("b")
	assert.Equal(t, v, "bv")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("c")
	assert.Equal(t, v, "cv")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("d")
	assert.Equal(t, v, "dv")
	assert.Equal(t, ok, true)

	m, ok := ini.GetKvmap("")
	assert.Equal(t, len(m), 4)
	assert.Equal(t, ok, true)

	n, ok := ini.GetKvmap("n")
	assert.Equal(t, len(n), 0)
	assert.Equal(t, ok, false)
}

func getTestDataDir() string {
	var file string
	var ok bool
	if _, file, _, ok = runtime.Caller(0); ok {
		fmt.Printf("file=%v\n", file)
	}

	curdir := filepath.Dir(file)
	return filepath.Join(curdir, "test/data")
}
<commit_msg>Add more memory data parsing test case<commit_after>package wini

import (
	"path/filepath"
	"runtime"
	"testing"

	"github.com/bmizerany/assert"
)

func Test1(t *testing.T) {

	filename := filepath.Join(getTestDataDir(t), "ini_parser_testfile.ini")
	ini := New()
	err := ini.ParseFile(filename)
	assert.Equal(t, nil, err)

	v, ok := ini.Get("mid")
	assert.Equal(t, v, "ac9219aa5232c4e519ae5fcb4d77ae5b")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("product")
	assert.Equal(t, v, "ppp")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("combo")
	assert.Equal(t, v, "ccc")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("aa")
	assert.Equal(t, v, "bb")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("axxxa")
	assert.Equal(t, v, "")
	assert.Equal(t, ok, false)

	m, ok := ini.GetKvmap("")
	assert.Equal(t, len(m), 6)
	assert.Equal(t, ok, true)

	n, ok := ini.GetKvmap("n")
	assert.Equal(t, len(n), 0)
	assert.Equal(t, ok, false)

	sss, ok := ini.GetKvmap("sss")
	assert.Equal(t, len(sss), 2)
	assert.Equal(t, ok, true)
	v, ok = ini.SectionGet("sss", "aa")
	assert.Equal(t, v, "bb")
	assert.Equal(t, ok, true)
	v, ok = ini.SectionGet("sss", "appext")
	assert.Equal(t, v, "ab=cd")
	assert.Equal(t, ok, true)
}

func TestUft8(t *testing.T) {
	/*
		title=百度搜索_ipad2
		url=http://www.baidu.com/s?bs=ipad&f=8&rsv_bp=1&wd=ipad2&inputT=397
		url_md5=5844a75423cd3372e1997360bd110a25
		refer=http://www.google.com
		anchor_text= google
		ret_form = json
		 ret_start = 0
		 ret_limit =    50
		page_info   =  0,0,50,1,0,20
		local=0
		mid=c4ca4238a0b923820dcc509a6f75849b
		product=test
		combo=test
		version=1.0.0.1
		debug=1
		encoding=1

	*/

	filename := filepath.Join(getTestDataDir(t), "utf8.ini")
	ini := New()
	err := ini.ParseFile(filename)
	assert.Equal(t, nil, err)

	v, ok := ini.Get("title")
	assert.Equal(t, v, "百度搜索_ipad2")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("url_md5")
	assert.Equal(t, v, "5844a75423cd3372e1997360bd110a25")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("ret_form")
	assert.Equal(t, v, "json")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("ret_start")
	assert.Equal(t, v, "0")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("ret_limit")
	assert.Equal(t, v, "50")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("axxxa")
	assert.Equal(t, v, "")
	assert.Equal(t, ok, false)

	m, ok := ini.GetKvmap("")
	assert.Equal(t, len(m), 16)
	assert.Equal(t, ok, true)

	n, ok := ini.GetKvmap("n")
	assert.Equal(t, len(n), 0)
	assert.Equal(t, ok, false)
}

func TestErrorFormat(t *testing.T) {
	filename := filepath.Join(getTestDataDir(t), "error.ini")
	ini := New()
	err := ini.ParseFile(filename)
	assert.NotEqual(t, nil, err)
}

func TestMemoryData1(t *testing.T) {
	raw := []byte("a:av||b:bv||c:cv||||d:dv||||||")
	ini := New()
	err := ini.Parse(raw, "|", ":")
	assert.Equal(t, nil, err)

	v, ok := ini.Get("a")
	assert.Equal(t, v, "av")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("b")
	assert.Equal(t, v, "bv")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("c")
	assert.Equal(t, v, "cv")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("d")
	assert.Equal(t, v, "dv")
	assert.Equal(t, ok, true)

	m, ok := ini.GetKvmap("")
	assert.Equal(t, len(m), 4)
	assert.Equal(t, ok, true)

	n, ok := ini.GetKvmap("n")
	assert.Equal(t, len(n), 0)
	assert.Equal(t, ok, false)
}

func TestMemoryData2(t *testing.T) {
	raw := []byte("a:av||b:bv||c:cv||||d:dv||||||")
	ini := New()
	err := ini.Parse(raw, "||", ":") // DIFFERENT with TestMemoryData1. use "||" instead of "|"
	assert.Equal(t, nil, err)

	v, ok := ini.Get("a")
	assert.Equal(t, v, "av")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("b")
	assert.Equal(t, v, "bv")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("c")
	assert.Equal(t, v, "cv")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("d")
	assert.Equal(t, v, "dv")
	assert.Equal(t, ok, true)

	m, ok := ini.GetKvmap("")
	assert.Equal(t, len(m), 4)
	assert.Equal(t, ok, true)

	n, ok := ini.GetKvmap("n")
	assert.Equal(t, len(n), 0)
	assert.Equal(t, ok, false)
}

func TestMemoryData3(t *testing.T) {
	raw := []byte("@|@|@|@|@|@|  a:av  @| b : bv @| c:cv  @|@|d:  dv@|@|@|@|@|@|@|")
	ini := New()
	err := ini.Parse(raw, "@|", ":")
	assert.Equal(t, nil, err)

	v, ok := ini.Get("a")
	assert.Equal(t, v, "av")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("b")
	assert.Equal(t, v, "bv")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("c")
	assert.Equal(t, v, "cv")
	assert.Equal(t, ok, true)

	v, ok = ini.Get("d")
	assert.Equal(t, v, "dv")
	assert.Equal(t, ok, true)

	m, ok := ini.GetKvmap("")
	assert.Equal(t, len(m), 4)
	assert.Equal(t, ok, true)

	n, ok := ini.GetKvmap("n")
	assert.Equal(t, len(n), 0)
	assert.Equal(t, ok, false)
}

func getTestDataDir(t *testing.T) string {
	var file string
	var ok bool
	_, file, _, ok = runtime.Caller(0)
	assert.Equal(t, ok, true)

	curdir := filepath.Dir(file)
	return filepath.Join(curdir, "test/data")
}
<|endoftext|>


<commit_before>package network

import (
	"common"
	"log"
)

func NewSimpleMultiplexer() common.NetworkMultiplexer {
	in := make(chan common.NetworkMessage)
	out := make(chan chan common.NetworkMessage)
	s := &SimpleMultiplexer{in, out, nil}
	go s.listen()
	return s
}

type SimpleMultiplexer struct {
	in    chan common.NetworkMessage
	out   chan chan common.NetworkMessage
	Hosts []chan common.NetworkMessage
}

func (s *SimpleMultiplexer) listen() {

	for {
		select {
		case c := <-s.out:
			log.Println("MULTI: Host added")
			s.Hosts = append(s.Hosts, c)
		case o := <-s.in:
			log.Println("MULTI: Transaction ", o, " to be sent to ", len(s.Hosts))
			for _, s := range s.Hosts {
				go func(s chan common.NetworkMessage) {
					s <- o
					log.Println("MULTI: Transaction sent to host")
				}(s)
			}
			log.Println("MULTI: Finished Processing")
		}
		log.Println("MULTI: CYCLING")
	}
}

func (s *SimpleMultiplexer) AddListener(SwarmId string, c chan common.NetworkMessage) {
	s.out <- c
}

func (s *SimpleMultiplexer) SendNetworkMessage(o common.NetworkMessage) {
	s.in <- o
}

func (s *SimpleMultiplexer) Listen(addr string) {
	panic("Not implemented")
}

func (s *SimpleMultiplexer) Connect(addr string) {
	panic("Not implemented")
}
<commit_msg>Remove redundant logging in simple network multiplexer<commit_after>package network

import (
	"common"
	"log"
)

func NewSimpleMultiplexer() common.NetworkMultiplexer {
	in := make(chan common.NetworkMessage)
	out := make(chan chan common.NetworkMessage)
	s := &SimpleMultiplexer{in, out, nil}
	go s.listen()
	return s
}

type SimpleMultiplexer struct {
	in    chan common.NetworkMessage
	out   chan chan common.NetworkMessage
	Hosts []chan common.NetworkMessage
}

func (s *SimpleMultiplexer) listen() {

	for {
		select {
		case c := <-s.out:
			log.Println("MULTI: Host added")
			s.Hosts = append(s.Hosts, c)
		case o := <-s.in:
			log.Println("MULTI: Transaction ", o, " to be sent to ", len(s.Hosts))
			for _, s := range s.Hosts {
				go func(s chan common.NetworkMessage) {
					s <- o
				}(s)
			}
		}
	}
}

func (s *SimpleMultiplexer) AddListener(SwarmId string, c chan common.NetworkMessage) {
	s.out <- c
}

func (s *SimpleMultiplexer) SendNetworkMessage(o common.NetworkMessage) {
	s.in <- o
}

func (s *SimpleMultiplexer) Listen(addr string) {
	panic("Not implemented")
}

func (s *SimpleMultiplexer) Connect(addr string) {
	panic("Not implemented")
}
<|endoftext|>


<commit_before>
package networking

import (
	"fmt"
	"net"
	"os"
	"github.com/howeyc/fsnotify"
	"encoding/json"
	"encoding/gob"
	"lds"
)

func BroadcastEvents(service string, fevents chan *fsnotify.FileEvent) {
	for {
		event := <-fevents
		var e lds.BEvent
		e.Fname = event.Name
		if event.IsCreate(){
			e.Etype = 1
			e.Etypestring = "CREATE"
		}
		if event.IsDelete(){
			e.Etype = 2
			e.Etypestring = "DELETE"
		}

		if event.IsModify(){
			e.Etype = 3
			e.Etypestring = "MODIFY"
		}
		if event.IsRename(){
			e.Etype = 4
			e.Etypestring = "RENAME"
		}

		b, err := json.Marshal(e)
		if err != nil {
			os.Exit(1)
		}
		fmt.Println(e.Etypestring);
		go  SendStatus(service, b)
	}
}

func SendStatus(service string, msg []byte) {

	udpAddr, err := net.ResolveUDPAddr("udp4", service)
	checkError(err)

	conn, err := net.DialUDP("udp", nil, udpAddr)
	checkError(err)
	fmt.Println("Sending status...");
	_, err = conn.Write([]byte(msg))
	checkError(err)

	var buf [512]byte
	_, err = conn.Read(buf[0:])

	checkError(err)

}

func FileFetcher(queue chan lds.NBEvent, files chan lds.BFile){
	for{
		event := <-queue
		// Fetch file
		fmt.Println("Fetching file...")
		var file lds.BFile;
		file.Name = event.Fname;
		files<-file;
	}
}

func CreateStatusServer(queue chan lds.NBEvent) {
	fmt.Println("creating...")
	service := ":42000"
	udpAddr, err := net.ResolveUDPAddr("udp4", service)
	checkError(err)
	fmt.Println(udpAddr)
	conn, err := net.ListenUDP("udp", udpAddr)
	checkError(err)
	for {
		handleUDPClient(conn, queue)
	}
}

func CreateFileServer() {
	service := ":42000"
	ln, err := net.Listen("tcp", service)
	if err != nil {
		return
	}

	for{
		c, err := ln.Accept()
		if err != nil{
			continue
		}
		go handleFileRequest(c)
	}
}

func handleFileRequest(c net.Conn) {
	var f lds.FileRequest
	err := gob.NewDecoder(c).Decode(&f)
	if err != nil {
		fmt.Println(err)
	}else{
		fmt.Println("received", f.Fname)
		file, err := os.Open(f.Fname)
		if err != nil {
			fmt.Println(err)
		}
		err = gob.NewEncoder(c).Encode(file);
		if err != nil {
			fmt.Println(err)
		}
	}
	c.Close()
}

func SendRequest(address string, filename string) {
	// connect to the server
    c, err := net.Dial("tcp", address)
    if err != nil {
        fmt.Println(err)
        return
    }

    // send the message
    var fr lds.FileRequest
    fr.Fname = filename
    fmt.Println("Sending", fr.Fname)
    err = gob.NewEncoder(c).Encode(fr)
    if err != nil {
        fmt.Println(err)
    }
    c.Close()
}

func handleUDPClient(conn *net.UDPConn, queue chan lds.NBEvent) {
	fmt.Println("Handling new client...")
	var buf []byte = make([]byte, 10)

	s, addr, err := conn.ReadFromUDP(buf)
	if err != nil {
		return
	}
	go handlePacket(buf, s, addr.IP.String(), queue);
}

func handlePacket(buf []byte, s int, ip string, queue chan lds.NBEvent){
	var msg = string( buf[0:s] );
	//addr.IP.String()
	fmt.Println(msg);
	var m lds.BEvent
	err := json.Unmarshal(buf, m)
	if err != nil {
        fmt.Println(err)
    }
	var e lds.NBEvent
	e.Fname = m.Fname
	e.Etype = m.Etype
	e.Etypestring = m.Etypestring
	e.Remote = ip
	queue<-e
}

func checkError(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Fatal error ", err.Error())
		os.Exit(1)
	}
}
<commit_msg>changed protocol to udp4<commit_after>
package networking

import (
	"fmt"
	"net"
	"os"
	"github.com/howeyc/fsnotify"
	"encoding/json"
	"encoding/gob"
	"lds"
)

func BroadcastEvents(service string, fevents chan *fsnotify.FileEvent) {
	for {
		event := <-fevents
		var e lds.BEvent
		e.Fname = event.Name
		if event.IsCreate(){
			e.Etype = 1
			e.Etypestring = "CREATE"
		}
		if event.IsDelete(){
			e.Etype = 2
			e.Etypestring = "DELETE"
		}

		if event.IsModify(){
			e.Etype = 3
			e.Etypestring = "MODIFY"
		}
		if event.IsRename(){
			e.Etype = 4
			e.Etypestring = "RENAME"
		}

		b, err := json.Marshal(e)
		if err != nil {
			os.Exit(1)
		}
		fmt.Println(e.Etypestring);
		go  SendStatus(service, b)
	}
}

func SendStatus(service string, msg []byte) {

	udpAddr, err := net.ResolveUDPAddr("udp4", service)
	checkError(err)

	conn, err := net.DialUDP("udp4", nil, udpAddr)
	checkError(err)
	fmt.Println("Sending status...");
	_, err = conn.Write([]byte(msg))
	checkError(err)

	var buf [512]byte
	_, err = conn.Read(buf[0:])

	checkError(err)

}

func FileFetcher(queue chan lds.NBEvent, files chan lds.BFile){
	for{
		event := <-queue
		// Fetch file
		fmt.Println("Fetching file...")
		var file lds.BFile;
		file.Name = event.Fname;
		files<-file;
	}
}

func CreateStatusServer(queue chan lds.NBEvent) {
	fmt.Println("creating...")
	service := ":41000"
	udpAddr, err := net.ResolveUDPAddr("udp4", service)
	checkError(err)
	fmt.Println(udpAddr)
	conn, err := net.ListenUDP("udp4", udpAddr)
	checkError(err)
	for {
		handleUDPClient(conn, queue)
	}
}

func CreateFileServer() {
	service := ":42000"
	ln, err := net.Listen("tcp", service)
	if err != nil {
		return
	}

	for{
		c, err := ln.Accept()
		if err != nil{
			continue
		}
		go handleFileRequest(c)
	}
}

func handleFileRequest(c net.Conn) {
	var f lds.FileRequest
	err := gob.NewDecoder(c).Decode(&f)
	if err != nil {
		fmt.Println(err)
	}else{
		fmt.Println("received", f.Fname)
		file, err := os.Open(f.Fname)
		if err != nil {
			fmt.Println(err)
		}
		err = gob.NewEncoder(c).Encode(file);
		if err != nil {
			fmt.Println(err)
		}
	}
	c.Close()
}

func SendRequest(address string, filename string) {
	// connect to the server
    c, err := net.Dial("tcp", address)
    if err != nil {
        fmt.Println(err)
        return
    }

    // send the message
    var fr lds.FileRequest
    fr.Fname = filename
    fmt.Println("Sending", fr.Fname)
    err = gob.NewEncoder(c).Encode(fr)
    if err != nil {
        fmt.Println(err)
    }
    c.Close()
}

func handleUDPClient(conn *net.UDPConn, queue chan lds.NBEvent) {
	fmt.Println("Handling new client...")
	var buf []byte = make([]byte, 10)

	s, addr, err := conn.ReadFromUDP(buf)
	if err != nil {
		return
	}
	go handlePacket(buf, s, addr.IP.String(), queue);
}

func handlePacket(buf []byte, s int, ip string, queue chan lds.NBEvent){
	var msg = string( buf[0:s] );
	//addr.IP.String()
	fmt.Println(msg);
	var m lds.BEvent
	err := json.Unmarshal(buf, m)
	if err != nil {
        fmt.Println(err)
    }
	var e lds.NBEvent
	e.Fname = m.Fname
	e.Etype = m.Etype
	e.Etypestring = m.Etypestring
	e.Remote = ip
	queue<-e
}

func checkError(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Fatal error ", err.Error())
		os.Exit(1)
	}
}
<|endoftext|>