<commit_before>package main

import (
	"bufio"
	"encoding/csv"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/gorilla/mux"
	"github.com/raff/godet"

	"github.com/njasm/marionette_client"
	"github.com/spf13/viper"
)

type Program struct {
	URL      string
	Duration time.Duration
}

// InitializeConfig loads our configuration using Viper package.
func InitializeConfig() {

	viper.SetConfigType("yaml")
	viper.SetConfigName("config")

	viper.AddConfigPath("$HOME/.gotator")
	viper.AddConfigPath(".")

	err := viper.ReadInConfig()
	if err != nil {
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}

	viper.SetDefault("debug", false)

	viper.SetEnvPrefix("gorotator") // will be uppercased automatically
	viper.BindEnv("debug")
	viper.BindEnv("browser_ip")
	viper.BindEnv("browser_port")
	viper.BindEnv("gotator_port")

	if !viper.IsSet("browser_ip") || !viper.IsSet("browser_port") {
		fmt.Fprintln(os.Stderr, "Configuration error.  Both BROWSER_IP and BROWSER_PORT must be set via either config or environment.")
		os.Exit(1)
	}
	mode := viper.Get("BROWSER_CONTROL_MODE")
	ipStr := viper.Get("BROWSER_IP")
	if mode == 1 {
		log.Println("Using MODE1 (aka. FF Remote Control plugin) -- [DEPRECATED in newer versio of Firefox]")
	}
	if mode == 2 {
		log.Printf("Using MODE2:  Firefox Marionette protocol.  ")
		log.Printf("  IP is %s, but localhost will be used instead.\n", ipStr)
	}
	if mode == 3 {
		log.Printf("Using MODE3:  Chrome Debugging protocol.  ")
	}

	viper.WatchConfig()
	viper.OnConfigChange(func(e fsnotify.Event) {
		log.Println("\nConfig file changed:", e.Name)
		skip <- struct{}{}
		log.Printf("Content will change immediately.\n\n")

	})

}

// Loads a list of programs.
// A program consists of a list things to display on the rotator along
// with a number of seconds to display each one before moving on.
func loadProgramList(filename string) []Program {

	var list []Program

	bytes, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	webpages := string(bytes)

	r := csv.NewReader(strings.NewReader(webpages))
	r.LazyQuotes = true

	var c = 0
	for {
		var p Program
		record, err := r.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Printf("Error reading line from program file: %s.  Abandoning attempt to read programs.\n", filename)
			log.Fatal(err)
		}
		p.URL = record[0]
		p.Duration, err = time.ParseDuration(record[1])
		if err != nil {
			log.Println("Program rejected.  Invalid duration.")
		}

		list = append(list, p)
		c++
	}
	log.Printf("Loaded %d programs from %s", c, filename)
	return list
}

func runProgram(program Program) {

	timer_code := fmt.Sprintf(`

function addStyleString(str) {
    var node = document.createElement('style');
    node.innerHTML = str;
    document.body.appendChild(node);
}

var block_to_insert ;
var container_block ;
const duration = %v;
block_to_insert = document.createElement( 'div' );
block_to_insert.className = "gotator-overlay";
block_to_insert.innerHTML = '<progress value="0" max=%v id="progressBar"></progress>';

addStyleString('.gotator-overlay{ position: fixed; top: 0; left: 0; height: 0px; width: 100%%; z-index: 10000 ; background:white}');
addStyleString('#progressBar{-webkit-appearance: none; appearance: none; height: 5px; width: 100%%');

document.body.appendChild(block_to_insert);

var timeleft = duration;
var downloadTimer = setInterval(function(){
  document.getElementById("progressBar").value = duration - --timeleft;

  if(timeleft <= 0)
    clearInterval(downloadTimer);
},1000);
                `, program.Duration.Seconds(), program.Duration.Seconds())

	ip := viper.Get("BROWSER_IP")
	port := viper.GetInt("BROWSER_PORT")

	constr := fmt.Sprintf("%s:%d", ip, port)

	log.Printf("Running program for %s", program.Duration)
	log.Printf("  URL %s", program.URL)

	mode := viper.Get("BROWSER_CONTROL_MODE")
	if mode == 1 {
		// Connect to FF Remote Control
		conn, err := net.Dial("tcp", constr)
		if err != nil {
			log.Printf("  Error making network connection to: %s\n", constr)
			log.Println("  It is possible Firefox needs to be started or restarted.")
			log.Println("  It is possible FF Remote Control plugin is not installed.")
			log.Println("  Pausing for 30s")
			time.Sleep(30 * time.Second) // wait 30 seconds to slow retries
			return
		}

		// Actual control of browser starts here
		fmt.Fprintf(conn, "window.location='%s'\n", program.URL)
		status, err := bufio.NewReader(conn).ReadString('\n')
		if err != nil {
			log.Println("ERROR - URL didn't load as desired.")
		}

		var statusParsed interface{}
		err = json.Unmarshal([]byte(status), &statusParsed)

		m := statusParsed.(map[string]interface{})

		if m["result"] == program.URL {
			log.Println("RESULT: OK")
		} else {
			log.Println("RESULT: ERROR - URL didn't load as desired.")
		}
	}
	if mode == 2 {
		// Connect using Marionette
		client := marionette_client.NewClient()

		err := client.Connect("", 0) // this are the default marionette values for hostname, and port
		if err != nil {
			log.Println("Can't connect to firefox.  Sorry.")
			log.Println("It is possible Firefox needs to be started or restarted.")
			log.Println("Pausing for 30s")
			time.Sleep(30 * time.Second) // wait 30 seconds to slow retries
			return
		}
		client.NewSession("", nil) // let marionette generate the Session ID with it's default Capabilities
		client.Navigate(program.URL)

		if viper.IsSet("timeroverlay") && viper.Get("timeroverlay") == true {
			// Inject count down progress bar into page
			args := []interface{}{}
			client.ExecuteScript(timer_code, args, 1000, false)
		}
	}
	if mode == 3 {

		remote, err := godet.Connect("localhost:9222", false)
		if err != nil {
			log.Println("Can not connect to Chrome instance:")
			log.Println(err)
			log.Println("Sleeping for 30 seconds")
			time.Sleep(30 * time.Second) // wait 30 seconds to slow retries
			return
		}
		// disconnect when done
		defer remote.Close()

		remote.Navigate(program.URL)
		done := make(chan bool)
		remote.CallbackEvent("Page.frameStoppedLoading", func(params godet.Params) {
			log.Println("page loaded")
			done <- true
		})

		remote.PageEvents(true)

		_ = <-done

		if viper.IsSet("timeroverlay") && viper.Get("timeroverlay") == true {
			// Inject count down progress bar into page
			_, _ = remote.EvaluateWrap(timer_code)

		}

	}

	select {
	case <-time.After(program.Duration):
		return
	case <-skip:
		log.Println("Current program skipped")
		return
	}
}

func Pause() {
	mu.Lock()
	pause = true
	mu.Unlock()
	log.Println("Paused")
}

func Unpause() {
	mu.Lock()
	pause = false
	mu.Unlock()
	log.Println("Unpaused")
}

func IsPaused() bool {
	mu.Lock()
	defer mu.Unlock()

	return pause == true
}

func LoadAndRunLoop() {

	// Load and run the acctive program_file indefinately
	for {
		// We pull filename inside the loop because the
		// configuration can change while our program is running.
		filename := viper.GetString("program_file")

		for IsPaused() {
			fmt.Printf(".")
			time.Sleep(1 * time.Second)
		}

		pl := loadProgramList(filename)

		for _, p := range pl {
			for IsPaused() {
				fmt.Printf("X")
				time.Sleep(1 * time.Second)
			}
			runProgram(p)
		}

		log.Println("Looping back to play program list from beginning")
	}

}

func PlayHandler(w http.ResponseWriter, r *http.Request) {

	r.ParseForm()
	var p Program
	p.URL = r.Form.Get("url")
	log.Printf("URL: %s\n", p.URL)

	d := r.Form.Get("duration")
	log.Printf("Duration: %s\n", d)

	// CAREFUL: There may be bugs here...
	var err error
	p.Duration, err = time.ParseDuration(r.Form.Get("duration"))
	if err != nil {
		w.Write([]byte("Program rejected.  Invalid duration.\n"))
		return
	}

	// Stop normal rotation
	Pause()

	runProgram(p)
	w.Write([]byte("Program accepted\n"))
	Unpause()
}

func PauseHandler(w http.ResponseWriter, r *http.Request) {
	Pause()
	log.Println("Paused from web request")
	w.Write([]byte("Ok, paused.\n"))
}

func ResumeHandler(w http.ResponseWriter, r *http.Request) {
	Unpause()
	log.Println("Unpausing from web request")
	w.Write([]byte("Ok, unpaused.\n"))
}
func SkipHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Skipping from web request")
	Unpause()
	skip <- struct{}{}

	w.Write([]byte("Skipping current programming and resume program list runner from web request.\n"))
}

func readKeyboardLoop() {
	for {
		os.Stdin.Read(make([]byte, 1)) // read a single byte
		log.Printf(" >> Got keyboard input, that means you want to move to the next program.  Can do! << \n\n")
		Unpause()
		skip <- struct{}{}
	}
}

// Control channel to stop running programs immediately (yes, global)

var skip = make(chan struct{})
var exitprogram = make(chan struct{})
var pause bool
var mu = &sync.Mutex{}
var version = "0.1.1"

func main() {

	if len(os.Args) > 1 {
		if os.Args[1] == "version" {
			log.Println("Gotator version:", version)
			os.Exit(0)
		}
	}
	log.Println("Starting gotator: version", version)

	InitializeConfig()

	go LoadAndRunLoop()

	if viper.IsSet("interactive") && viper.Get("interactive") == true {
		go readKeyboardLoop()
	}

	if viper.IsSet("apienabled") && viper.Get("apienabled") == true {
		listen_port := ":8080"
		if viper.IsSet("gotator_port") {
			listen_port = ":" + viper.GetString("gotator_port")
		}

		log.Printf("Starting API server on port %s.  Notice:  This allows UNAUTHENTICATED remote control of Firefox. set 'apienabled: false' in config.yaml to disable.\n",
			listen_port)

		r := mux.NewRouter()
		r.HandleFunc("/play", PlayHandler)
		r.HandleFunc("/pause", PauseHandler)
		r.HandleFunc("/resume", ResumeHandler)
		r.HandleFunc("/skip", SkipHandler)

		if viper.IsSet("tlsenabled") && viper.Get("tlsenabled") == true {
			log.Printf("TLS is enabled.  Be sure to access API with https as protocol.")
			log.Fatal(http.ListenAndServeTLS(listen_port, "server.crt", "server.key", r))
		} else {
			log.Fatal(http.ListenAndServe(listen_port, r))
		}

	} else {
		log.Println("notice: rest API not enabled in configuration and will be unavailable.  set 'apienabled: true' in config.yaml if you want to use it.\n")
		// If we aren't doing http.ListenAndServe() we need to block here or else gotator would exit immediately
		<-exitprogram
	}

}
<commit_msg>Bump the version to release 0.2.0 (TLS support)<commit_after>package main

import (
	"bufio"
	"encoding/csv"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/gorilla/mux"
	"github.com/raff/godet"

	"github.com/njasm/marionette_client"
	"github.com/spf13/viper"
)

type Program struct {
	URL      string
	Duration time.Duration
}

// InitializeConfig loads our configuration using Viper package.
func InitializeConfig() {

	viper.SetConfigType("yaml")
	viper.SetConfigName("config")

	viper.AddConfigPath("$HOME/.gotator")
	viper.AddConfigPath(".")

	err := viper.ReadInConfig()
	if err != nil {
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}

	viper.SetDefault("debug", false)

	viper.SetEnvPrefix("gorotator") // will be uppercased automatically
	viper.BindEnv("debug")
	viper.BindEnv("browser_ip")
	viper.BindEnv("browser_port")
	viper.BindEnv("gotator_port")

	if !viper.IsSet("browser_ip") || !viper.IsSet("browser_port") {
		fmt.Fprintln(os.Stderr, "Configuration error.  Both BROWSER_IP and BROWSER_PORT must be set via either config or environment.")
		os.Exit(1)
	}
	mode := viper.Get("BROWSER_CONTROL_MODE")
	ipStr := viper.Get("BROWSER_IP")
	if mode == 1 {
		log.Println("Using MODE1 (aka. FF Remote Control plugin) -- [DEPRECATED in newer versio of Firefox]")
	}
	if mode == 2 {
		log.Printf("Using MODE2:  Firefox Marionette protocol.  ")
		log.Printf("  IP is %s, but localhost will be used instead.\n", ipStr)
	}
	if mode == 3 {
		log.Printf("Using MODE3:  Chrome Debugging protocol.  ")
	}

	viper.WatchConfig()
	viper.OnConfigChange(func(e fsnotify.Event) {
		log.Println("\nConfig file changed:", e.Name)
		skip <- struct{}{}
		log.Printf("Content will change immediately.\n\n")

	})

}

// Loads a list of programs.
// A program consists of a list things to display on the rotator along
// with a number of seconds to display each one before moving on.
func loadProgramList(filename string) []Program {

	var list []Program

	bytes, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	webpages := string(bytes)

	r := csv.NewReader(strings.NewReader(webpages))
	r.LazyQuotes = true

	var c = 0
	for {
		var p Program
		record, err := r.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Printf("Error reading line from program file: %s.  Abandoning attempt to read programs.\n", filename)
			log.Fatal(err)
		}
		p.URL = record[0]
		p.Duration, err = time.ParseDuration(record[1])
		if err != nil {
			log.Println("Program rejected.  Invalid duration.")
		}

		list = append(list, p)
		c++
	}
	log.Printf("Loaded %d programs from %s", c, filename)
	return list
}

func runProgram(program Program) {

	timer_code := fmt.Sprintf(`

function addStyleString(str) {
    var node = document.createElement('style');
    node.innerHTML = str;
    document.body.appendChild(node);
}

var block_to_insert ;
var container_block ;
const duration = %v;
block_to_insert = document.createElement( 'div' );
block_to_insert.className = "gotator-overlay";
block_to_insert.innerHTML = '<progress value="0" max=%v id="progressBar"></progress>';

addStyleString('.gotator-overlay{ position: fixed; top: 0; left: 0; height: 0px; width: 100%%; z-index: 10000 ; background:white}');
addStyleString('#progressBar{-webkit-appearance: none; appearance: none; height: 5px; width: 100%%');

document.body.appendChild(block_to_insert);

var timeleft = duration;
var downloadTimer = setInterval(function(){
  document.getElementById("progressBar").value = duration - --timeleft;

  if(timeleft <= 0)
    clearInterval(downloadTimer);
},1000);
                `, program.Duration.Seconds(), program.Duration.Seconds())

	ip := viper.Get("BROWSER_IP")
	port := viper.GetInt("BROWSER_PORT")

	constr := fmt.Sprintf("%s:%d", ip, port)

	log.Printf("Running program for %s", program.Duration)
	log.Printf("  URL %s", program.URL)

	mode := viper.Get("BROWSER_CONTROL_MODE")
	if mode == 1 {
		// Connect to FF Remote Control
		conn, err := net.Dial("tcp", constr)
		if err != nil {
			log.Printf("  Error making network connection to: %s\n", constr)
			log.Println("  It is possible Firefox needs to be started or restarted.")
			log.Println("  It is possible FF Remote Control plugin is not installed.")
			log.Println("  Pausing for 30s")
			time.Sleep(30 * time.Second) // wait 30 seconds to slow retries
			return
		}

		// Actual control of browser starts here
		fmt.Fprintf(conn, "window.location='%s'\n", program.URL)
		status, err := bufio.NewReader(conn).ReadString('\n')
		if err != nil {
			log.Println("ERROR - URL didn't load as desired.")
		}

		var statusParsed interface{}
		err = json.Unmarshal([]byte(status), &statusParsed)

		m := statusParsed.(map[string]interface{})

		if m["result"] == program.URL {
			log.Println("RESULT: OK")
		} else {
			log.Println("RESULT: ERROR - URL didn't load as desired.")
		}
	}
	if mode == 2 {
		// Connect using Marionette
		client := marionette_client.NewClient()

		err := client.Connect("", 0) // this are the default marionette values for hostname, and port
		if err != nil {
			log.Println("Can't connect to firefox.  Sorry.")
			log.Println("It is possible Firefox needs to be started or restarted.")
			log.Println("Pausing for 30s")
			time.Sleep(30 * time.Second) // wait 30 seconds to slow retries
			return
		}
		client.NewSession("", nil) // let marionette generate the Session ID with it's default Capabilities
		client.Navigate(program.URL)

		if viper.IsSet("timeroverlay") && viper.Get("timeroverlay") == true {
			// Inject count down progress bar into page
			args := []interface{}{}
			client.ExecuteScript(timer_code, args, 1000, false)
		}
	}
	if mode == 3 {

		remote, err := godet.Connect("localhost:9222", false)
		if err != nil {
			log.Println("Can not connect to Chrome instance:")
			log.Println(err)
			log.Println("Sleeping for 30 seconds")
			time.Sleep(30 * time.Second) // wait 30 seconds to slow retries
			return
		}
		// disconnect when done
		defer remote.Close()

		remote.Navigate(program.URL)
		done := make(chan bool)
		remote.CallbackEvent("Page.frameStoppedLoading", func(params godet.Params) {
			log.Println("page loaded")
			done <- true
		})

		remote.PageEvents(true)

		_ = <-done

		if viper.IsSet("timeroverlay") && viper.Get("timeroverlay") == true {
			// Inject count down progress bar into page
			_, _ = remote.EvaluateWrap(timer_code)

		}

	}

	select {
	case <-time.After(program.Duration):
		return
	case <-skip:
		log.Println("Current program skipped")
		return
	}
}

func Pause() {
	mu.Lock()
	pause = true
	mu.Unlock()
	log.Println("Paused")
}

func Unpause() {
	mu.Lock()
	pause = false
	mu.Unlock()
	log.Println("Unpaused")
}

func IsPaused() bool {
	mu.Lock()
	defer mu.Unlock()

	return pause == true
}

func LoadAndRunLoop() {

	// Load and run the acctive program_file indefinately
	for {
		// We pull filename inside the loop because the
		// configuration can change while our program is running.
		filename := viper.GetString("program_file")

		for IsPaused() {
			fmt.Printf(".")
			time.Sleep(1 * time.Second)
		}

		pl := loadProgramList(filename)

		for _, p := range pl {
			for IsPaused() {
				fmt.Printf("X")
				time.Sleep(1 * time.Second)
			}
			runProgram(p)
		}

		log.Println("Looping back to play program list from beginning")
	}

}

func PlayHandler(w http.ResponseWriter, r *http.Request) {

	r.ParseForm()
	var p Program
	p.URL = r.Form.Get("url")
	log.Printf("URL: %s\n", p.URL)

	d := r.Form.Get("duration")
	log.Printf("Duration: %s\n", d)

	// CAREFUL: There may be bugs here...
	var err error
	p.Duration, err = time.ParseDuration(r.Form.Get("duration"))
	if err != nil {
		w.Write([]byte("Program rejected.  Invalid duration.\n"))
		return
	}

	// Stop normal rotation
	Pause()

	runProgram(p)
	w.Write([]byte("Program accepted\n"))
	Unpause()
}

func PauseHandler(w http.ResponseWriter, r *http.Request) {
	Pause()
	log.Println("Paused from web request")
	w.Write([]byte("Ok, paused.\n"))
}

func ResumeHandler(w http.ResponseWriter, r *http.Request) {
	Unpause()
	log.Println("Unpausing from web request")
	w.Write([]byte("Ok, unpaused.\n"))
}
func SkipHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Skipping from web request")
	Unpause()
	skip <- struct{}{}

	w.Write([]byte("Skipping current programming and resume program list runner from web request.\n"))
}

func readKeyboardLoop() {
	for {
		os.Stdin.Read(make([]byte, 1)) // read a single byte
		log.Printf(" >> Got keyboard input, that means you want to move to the next program.  Can do! << \n\n")
		Unpause()
		skip <- struct{}{}
	}
}

// Control channel to stop running programs immediately (yes, global)

var skip = make(chan struct{})
var exitprogram = make(chan struct{})
var pause bool
var mu = &sync.Mutex{}
var version = "0.2.0"

func main() {

	if len(os.Args) > 1 {
		if os.Args[1] == "version" {
			log.Println("Gotator version:", version)
			os.Exit(0)
		}
	}
	log.Println("Starting gotator: version", version)

	InitializeConfig()

	go LoadAndRunLoop()

	if viper.IsSet("interactive") && viper.Get("interactive") == true {
		go readKeyboardLoop()
	}

	if viper.IsSet("apienabled") && viper.Get("apienabled") == true {
		listen_port := ":8080"
		if viper.IsSet("gotator_port") {
			listen_port = ":" + viper.GetString("gotator_port")
		}

		log.Printf("Starting API server on port %s.  Notice:  This allows UNAUTHENTICATED remote control of Firefox. set 'apienabled: false' in config.yaml to disable.\n",
			listen_port)

		r := mux.NewRouter()
		r.HandleFunc("/play", PlayHandler)
		r.HandleFunc("/pause", PauseHandler)
		r.HandleFunc("/resume", ResumeHandler)
		r.HandleFunc("/skip", SkipHandler)

		if viper.IsSet("tlsenabled") && viper.Get("tlsenabled") == true {
			log.Printf("TLS is enabled.  Be sure to access API with https as protocol.")
			log.Fatal(http.ListenAndServeTLS(listen_port, "server.crt", "server.key", r))
		} else {
			log.Fatal(http.ListenAndServe(listen_port, r))
		}

	} else {
		log.Println("notice: rest API not enabled in configuration and will be unavailable.  set 'apienabled: true' in config.yaml if you want to use it.\n")
		// If we aren't doing http.ListenAndServe() we need to block here or else gotator would exit immediately
		<-exitprogram
	}

}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/google/go-github/github"
	"github.com/r0y3/rmforks/config"
	"github.com/r0y3/rmforks/reposervice"
	"golang.org/x/oauth2"
)

func waitThenQuit(svc *reposervice.RepoService) {
	done := false
	for {
		select {
		case done = <-svc.Done():
			if done {
				break
			}
		default:
			continue
		}
		if done {
			break
		}
	}
}

var conf = flag.String("conf", "config.json", "Configuration file.")

func main() {
	flag.Parse()

	cfg, err := config.GetConfig(*conf)

	if err != nil {
		panic(err)
	}

	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: cfg.AccessToken},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)

	svc := reposervice.RepoService{
		User:    cfg.Username,
		Client:  github.NewClient(tc),
		Exclude: cfg.Exclude,
		Timeout: cfg.Timeout,
	}

	go svc.Fetch()
	go svc.Delete()

	waitThenQuit(&svc)
}
<commit_msg>Remove unnecessary codes.<commit_after>package main

import (
	"flag"
	"github.com/google/go-github/github"
	"github.com/r0y3/rmforks/config"
	"github.com/r0y3/rmforks/reposervice"
	"golang.org/x/oauth2"
)

func waitThenQuit(svc *reposervice.RepoService) {
	for {
		select {
		case done = <-svc.Done():
			if done {
				return
			}
		default:
			continue
		}
	}
}

var conf = flag.String("conf", "config.json", "Configuration file.")

func main() {
	flag.Parse()

	cfg, err := config.GetConfig(*conf)

	if err != nil {
		panic(err)
	}

	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: cfg.AccessToken},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)

	svc := reposervice.RepoService{
		User:    cfg.Username,
		Client:  github.NewClient(tc),
		Exclude: cfg.Exclude,
		Timeout: cfg.Timeout,
	}

	go svc.Fetch()
	go svc.Delete()

	waitThenQuit(&svc)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/0xAX/notificator"
)

var laddr *net.UDPAddr
var raddr *net.UDPAddr

var con *net.UDPConn

var servers = make(map[string]Server)

var notify *notificator.Notificator

func main() {
	addresses, _ := net.InterfaceAddrs()

	currentAddr := addresses[0]

	notify = notificator.New(notificator.Options{
		DefaultIcon: "icon/cod.png",
		AppName:     "CoD4 Server Notifier",
	})

	laddr, _ = net.ResolveUDPAddr("udp", ":28961")
	raddr, _ = net.ResolveUDPAddr("udp", "255.255.255.255:28960")

	var errs error

	con, errs = net.DialUDP("udp", laddr, raddr)

	if errs != nil {
		fmt.Println("Error dialing UDP:", errs)
		return
	}

	udpAddress, err2 := net.ResolveUDPAddr("udp4", currentAddr.String()+":28961")

	if err2 != nil {
		fmt.Println("Error resolving UDP address:", err2)
		return
	}

	ln, err := net.ListenUDP("udp", udpAddress)
	if err != nil {
		fmt.Println("There was a problem listening:", err)
		return
	}

	fmt.Println("We're watching for Call of Duty 4 Servers on the local network")

	go func() {
		for {
			time.Sleep(1000 * time.Millisecond)

			askForServer()
		}
	}()

	for {
		time.Sleep(1000 * time.Millisecond)

		var buf []byte = make([]byte, 200)

		n, address, err := ln.ReadFromUDP(buf)

		if err != nil {
			fmt.Println("Error reading from connection:", err)
			return
		}

		if address != nil && n > 0 {
			parseMessage(strings.TrimSpace(string(buf)))
		}
	}
}

func askForServer() {
	_, err := con.Write([]byte{0xff, 0xff, 0xff, 0xff, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x20, 0x78, 0x78, 0x78})

	if err != nil {
		fmt.Println("Error writing broadcast for servers:", err)
		return
	}
}

func parseMessage(msg string) {
	// Ignore getInfo requests
	if strings.Contains(msg, "getinfo xxx") {
		return
	}

	if strings.Contains(msg, "infoResponse") {
		parseServer(msg)
	}
}

func parseServer(msg string) {
	split := strings.Split(msg, "\\")

	server := Server{
		Hostname: split[6],
		Map:      split[8],
		GameType: split[12],
	}

	server.MaxPlayers, _ = strconv.ParseInt(split[10], 10, 64)
	server.Pure = parseBool(split[14])
	server.FriendlyFire = parseBool(split[16])
	server.Hardcore = parseBool(split[18])
	server.Something, _ = strconv.ParseInt(split[20], 10, 64)
	server.Modded = parseBool(split[22])
	server.Voice = parseBool(split[24])
	server.Punkbuster = parseBool(split[26])

	if _, ok := servers[server.Hostname]; !ok {
		servers[server.Hostname] = server

		dir, _ := os.Getwd()

		notify.Push("New Server!", server.String(), dir+"/icon/cod.png")
	}
}

type Server struct {
	Hostname     string
	Map          string
	MaxPlayers   int64
	GameType     string
	Pure         bool
	FriendlyFire bool
	Hardcore     bool
	Something    int64
	Modded       bool
	Voice        bool
	Punkbuster   bool
}

func (s Server) String() (data string) {
	data += "Hostname: " + s.Hostname + "\n"
	data += "Map: " + s.Map + "\n"
	data += "MaxPlayers: " + strconv.FormatInt(s.MaxPlayers, 10) + "\n"
	data += "GameType: " + s.GameType + "\n"
	data += "Pure: " + strconv.FormatBool(s.Pure) + "\n"
	data += "FriendlyFire: " + strconv.FormatBool(s.FriendlyFire) + "\n"
	data += "Hardcore: " + strconv.FormatBool(s.Hardcore) + "\n"
	data += "Modded: " + strconv.FormatBool(s.Modded) + "\n"
	data += "Voice: " + strconv.FormatBool(s.Voice) + "\n"
	data += "Punkbuster: " + strconv.FormatBool(s.Punkbuster) + "\n"

	return data
}

func parseBool(str string) bool {
	return str == "1"
}
<commit_msg>moar bytes needed<commit_after>package main

import (
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/0xAX/notificator"
)

var laddr *net.UDPAddr
var raddr *net.UDPAddr

var con *net.UDPConn

var servers = make(map[string]Server)

var notify *notificator.Notificator

func main() {
	addresses, _ := net.InterfaceAddrs()

	currentAddr := addresses[0]

	notify = notificator.New(notificator.Options{
		DefaultIcon: "icon/cod.png",
		AppName:     "CoD4 Server Notifier",
	})

	laddr, _ = net.ResolveUDPAddr("udp", ":28961")
	raddr, _ = net.ResolveUDPAddr("udp", "255.255.255.255:28960")

	var errs error

	con, errs = net.DialUDP("udp", laddr, raddr)

	if errs != nil {
		fmt.Println("Error dialing UDP:", errs)
		return
	}

	udpAddress, err2 := net.ResolveUDPAddr("udp4", currentAddr.String()+":28961")

	if err2 != nil {
		fmt.Println("Error resolving UDP address:", err2)
		return
	}

	ln, err := net.ListenUDP("udp", udpAddress)
	if err != nil {
		fmt.Println("There was a problem listening:", err)
		return
	}

	fmt.Println("We're watching for Call of Duty 4 Servers on the local network")

	go func() {
		for {
			time.Sleep(1000 * time.Millisecond)

			askForServer()
		}
	}()

	for {
		time.Sleep(1000 * time.Millisecond)

		var buf []byte = make([]byte, 500)

		n, address, err := ln.ReadFromUDP(buf)

		if err != nil {
			fmt.Println("Error reading from connection:", err)
			return
		}

		if address != nil && n > 0 {
			parseMessage(strings.TrimSpace(string(buf)))
		}
	}
}

func askForServer() {
	_, err := con.Write([]byte{0xff, 0xff, 0xff, 0xff, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x20, 0x78, 0x78, 0x78})

	if err != nil {
		fmt.Println("Error writing broadcast for servers:", err)
		return
	}
}

func parseMessage(msg string) {
	// Ignore getInfo requests
	if strings.Contains(msg, "getinfo xxx") {
		return
	}

	if strings.Contains(msg, "infoResponse") {
		parseServer(msg)
	}
}

func parseServer(msg string) {
	split := strings.Split(msg, "\\")

	server := Server{
		Hostname: split[6],
		Map:      split[8],
		GameType: split[12],
	}

	server.MaxPlayers, _ = strconv.ParseInt(split[10], 10, 64)
	server.Pure = parseBool(split[14])
	server.FriendlyFire = parseBool(split[16])
	server.Hardcore = parseBool(split[18])
	server.Something, _ = strconv.ParseInt(split[20], 10, 64)
	server.Modded = parseBool(split[22])
	server.Voice = parseBool(split[24])
	server.Punkbuster = parseBool(split[26])

	if _, ok := servers[server.Hostname]; !ok {
		servers[server.Hostname] = server

		dir, _ := os.Getwd()

		notify.Push("New Server!", server.String(), dir+"/icon/cod.png")
	}
}

type Server struct {
	Hostname     string
	Map          string
	MaxPlayers   int64
	GameType     string
	Pure         bool
	FriendlyFire bool
	Hardcore     bool
	Something    int64
	Modded       bool
	Voice        bool
	Punkbuster   bool
}

func (s Server) String() (data string) {
	data += "Hostname: " + s.Hostname + "\n"
	data += "Map: " + s.Map + "\n"
	data += "MaxPlayers: " + strconv.FormatInt(s.MaxPlayers, 10) + "\n"
	data += "GameType: " + s.GameType + "\n"
	data += "Pure: " + strconv.FormatBool(s.Pure) + "\n"
	data += "FriendlyFire: " + strconv.FormatBool(s.FriendlyFire) + "\n"
	data += "Hardcore: " + strconv.FormatBool(s.Hardcore) + "\n"
	data += "Modded: " + strconv.FormatBool(s.Modded) + "\n"
	data += "Voice: " + strconv.FormatBool(s.Voice) + "\n"
	data += "Punkbuster: " + strconv.FormatBool(s.Punkbuster) + "\n"

	return data
}

func parseBool(str string) bool {
	return str == "1"
}
<|endoftext|>


<commit_before>package main

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

	log "github.com/Sirupsen/logrus"
	cobra "github.com/spf13/cobra"
)

type handler func(*cobra.Command, []string) error

func genCommand(use, short string, fn handler) *cobra.Command {
	run := func(cmd *cobra.Command, args []string) {
		if err := fn(cmd, args); err != nil {
			log.Fatal(err)
		} else {
			log.Debug("success")
		}
	}
	return &cobra.Command{
		Use:   use,
		Short: short,
		Run:   run,
	}

}

func main() {
	dummyPersistentPreRun := func(cmd *cobra.Command, args []string) {
		// Use PersistentPreRun here to replace the root one
	}
	root := &cobra.Command{
		Use:  "gozfs",
		Long: "gozfs is the cli for testing the go zfs ioctl api",
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if cmd.Use == "gozfs" {
				return
			}
			name, err := cmd.Flags().GetString("name")
			if err != nil {
				log.Fatal(err)
			}
			if name == "" {
				log.Fatal("missing name")
			}
		},
		Run: help,
	}
	root.PersistentFlags().StringP("name", "n", "", "dataset name")

	cmdExists := genCommand("exists", "Test for dataset existence.",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			return exists(name)
		})

	cmdDestroy := genCommand("destroy", "Destroys a dataset or volume.",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			recursive, _ := cmd.Flags().GetBool("recursive")
			recursiveClones, _ := cmd.Flags().GetBool("recursiveclones")
			forceUnmount, _ := cmd.Flags().GetBool("forceunmount")
			deferDestroy, _ := cmd.Flags().GetBool("defer")

			d, err := GetDataset(name)
			if err != nil {
				return err
			}

			opts := &DestroyOptions{
				Recursive:       recursive,
				RecursiveClones: recursiveClones,
				ForceUnmount:    forceUnmount,
				Defer:           deferDestroy,
			}
			return d.Destroy(opts)
		})
	cmdDestroy.Flags().BoolP("defer", "d", false, "defer destroy")
	cmdDestroy.Flags().BoolP("recursive", "r", false, "recursively destroy datasets")
	cmdDestroy.Flags().BoolP("recursiveclones", "c", false, "recursively destroy clones")
	cmdDestroy.Flags().BoolP("forceunmount", "f", false, "force unmount")

	cmdHolds := genCommand("holds", "Retrieve list of user holds on the specified snapshot.",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			holds, err := holds(name)
			if err == nil {
				fmt.Println(holds)
			}
			return err
		})

	cmdSnapshot := genCommand("snapshot <snap1> <snap2> ...", "Creates a snapshot of a dataset or volume",
		func(cmd *cobra.Command, snaps []string) error {
			zpool, _ := cmd.Flags().GetString("zpool")
			if zpool == "" {
				log.Fatal("zpool required")
			}
			if len(snaps) == 0 {
				log.Fatal("at least one snapshot name required")
			}

			var props map[string]string
			propJSON, _ := cmd.Flags().GetString("props")
			if err := json.Unmarshal([]byte(propJSON), &props); err != nil {
				log.Fatal("bad prop json")
			}

			errlist, err := snapshot(zpool, snaps, props)
			if errlist != nil {
				log.Error(errlist)
			}
			return err
		},
	)
	cmdSnapshot.PersistentPreRun = dummyPersistentPreRun
	cmdSnapshot.Flags().StringP("zpool", "z", "", "zpool")
	cmdSnapshot.Flags().StringP("props", "p", "{}", "snapshot properties")

	cmdRollback := genCommand("rollback", "Rollback this filesystem or volume to its most recent snapshot.",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			result, err := rollback(name)
			if err == nil {
				fmt.Println(result)
			}
			return err
		},
	)

	cmdCreate := genCommand("create", "Create a ZFS dataset or volume",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			createTypeName, _ := cmd.Flags().GetString("type")
			createType, err := getDMUType(createTypeName)
			if err != nil {
				log.Fatal("invalid type")
			}

			propJSON, _ := cmd.Flags().GetString("props")
			var props map[string]interface{}
			if err := json.Unmarshal([]byte(propJSON), &props); err != nil {
				log.Fatal("bad prop json")
			}

			if volsize, ok := props["volsize"]; ok {
				if volsizeFloat, ok := volsize.(float64); ok {
					props["volsize"] = uint64(volsizeFloat)
				}
			}

			if volblocksize, ok := props["volblocksize"]; ok {
				if volblocksizeFloat, ok := volblocksize.(float64); ok {
					props["volblocksize"] = uint64(volblocksizeFloat)
				}
			}

			return create(name, createType, props)
		},
	)
	cmdCreate.Flags().StringP("type", "t", "zfs", "zfs or zvol")
	cmdCreate.Flags().StringP("props", "p", "{}", "create properties")

	cmdSend := genCommand("send", "Generate a send stream from a snapshot",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			largeBlockOK, _ := cmd.Flags().GetBool("largeblock")
			embedOK, _ := cmd.Flags().GetBool("embed")
			fromSnap, _ := cmd.Flags().GetString("fromsnap")

			outputFD := os.Stdout.Fd()
			output, _ := cmd.Flags().GetString("output")
			if output != "/dev/stdout" {
				outputFile, err := os.Create(output)
				if err != nil {
					return err
				}
				defer outputFile.Close()

				outputFD = outputFile.Fd()
			} else {
				// If sending on stdout, don't log anything else unless there's
				// an error
				log.SetLevel(log.ErrorLevel)
			}

			return send(name, outputFD, fromSnap, largeBlockOK, embedOK)
		},
	)
	cmdSend.Flags().StringP("output", "o", "/dev/stdout", "output file")
	cmdSend.Flags().StringP("fromsnap", "f", "", "full snap name to send incremental from")
	cmdSend.Flags().BoolP("embed", "e", false, "embed data")
	cmdSend.Flags().BoolP("largeblock", "l", false, "large block")

	cmdClone := genCommand("clone", "Creates a clone from a snapshot",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			origin, _ := cmd.Flags().GetString("origin")
			if origin == "" {
				log.Fatal("missing origin snapshot")
			}

			var props map[string]interface{}
			propJSON, _ := cmd.Flags().GetString("props")
			if err := json.Unmarshal([]byte(propJSON), &props); err != nil {
				log.Fatal("bad prop json")
			}

			return clone(name, origin, props)
		},
	)
	cmdClone.Flags().StringP("origin", "o", "", "name of origin snapshot")
	cmdClone.Flags().StringP("props", "p", "{}", "snapshot properties")

	cmdRename := genCommand("rename", "Rename a dataset",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			newName, _ := cmd.Flags().GetString("newname")
			recursive, _ := cmd.Flags().GetBool("recursive")

			failedName, err := rename(name, newName, recursive)
			if failedName != "" {
				log.Error(failedName)
			}
			return err
		},
	)
	cmdRename.Flags().StringP("newname", "o", "", "new name of dataset")
	cmdRename.Flags().BoolP("recursive", "r", false, "recursively rename snapshots")

	cmdList := genCommand("list", "List filesystems, volumes, snapshots and bookmarks.",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			recurse, _ := cmd.Flags().GetBool("recurse")
			depth, _ := cmd.Flags().GetUint64("depth")
			typesList, _ := cmd.Flags().GetString("types")

			types := map[string]bool{}
			if typesList != "" {
				for _, t := range strings.Split(typesList, ",") {
					types[t] = true
				}
			}

			m, err := list(name, types, recurse, depth)
			if err != nil {
				return err
			}
			out, err := json.MarshalIndent(m, "", "  ")
			if err != nil {
				return err
			}
			fmt.Printf("%s\n", out)
			return nil
		})
	cmdList.PersistentPreRun = dummyPersistentPreRun
	cmdList.Flags().StringP("types", "t", "", "Comma seperated list of dataset types to list.")
	cmdList.Flags().BoolP("recurse", "r", false, "Recurse to all levels.")
	cmdList.Flags().Uint64P("depth", "d", 0, "Recursion depth limit, 0 for unlimited")

	cmdGet := genCommand("get", "Get dataset properties",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			recurse, _ := cmd.Flags().GetBool("recurse")
			depth, _ := cmd.Flags().GetUint64("depth")
			typesList, _ := cmd.Flags().GetString("types")

			types := map[string]bool{}
			if typesList != "" {
				for _, t := range strings.Split(typesList, ",") {
					types[t] = true
				}
			}

			m, err := properties(name, types, recurse, depth)
			if err != nil {
				return err
			}
			out, err := json.MarshalIndent(m, "", "  ")
			if err != nil {
				return err
			}
			fmt.Printf("%s\n", out)
			return nil
		})
	cmdGet.Flags().StringP("types", "t", "", "Comma seperated list of dataset types to list.")
	cmdGet.Flags().BoolP("recurse", "r", false, "Recurse to all levels.")
	cmdGet.Flags().Uint64P("depth", "d", 0, "Recursion depth limit, 0 for unlimited")

	root.AddCommand(
		cmdExists,
		cmdDestroy,
		cmdHolds,
		cmdSnapshot,
		cmdRollback,
		cmdCreate,
		cmdSend,
		cmdClone,
		cmdRename,
		cmdList,
		cmdGet,
	)
	if err := root.Execute(); err != nil {
		log.Fatal("root execute failed:", err)
	}
}

func help(cmd *cobra.Command, _ []string) {
	if err := cmd.Help(); err != nil {
		log.Fatal("help failed:", err)
	}
}
<commit_msg>Use public API Dataset.Snapshot in command instead of private<commit_after>package main

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

	log "github.com/Sirupsen/logrus"
	cobra "github.com/spf13/cobra"
)

type handler func(*cobra.Command, []string) error

func genCommand(use, short string, fn handler) *cobra.Command {
	run := func(cmd *cobra.Command, args []string) {
		if err := fn(cmd, args); err != nil {
			log.Fatal(err)
		} else {
			log.Debug("success")
		}
	}
	return &cobra.Command{
		Use:   use,
		Short: short,
		Run:   run,
	}

}

func main() {
	dummyPersistentPreRun := func(cmd *cobra.Command, args []string) {
		// Use PersistentPreRun here to replace the root one
	}
	root := &cobra.Command{
		Use:  "gozfs",
		Long: "gozfs is the cli for testing the go zfs ioctl api",
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if cmd.Use == "gozfs" {
				return
			}
			name, err := cmd.Flags().GetString("name")
			if err != nil {
				log.Fatal(err)
			}
			if name == "" {
				log.Fatal("missing name")
			}
		},
		Run: help,
	}
	root.PersistentFlags().StringP("name", "n", "", "dataset name")

	cmdExists := genCommand("exists", "Test for dataset existence.",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			return exists(name)
		})

	cmdDestroy := genCommand("destroy", "Destroys a dataset or volume.",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			recursive, _ := cmd.Flags().GetBool("recursive")
			recursiveClones, _ := cmd.Flags().GetBool("recursiveclones")
			forceUnmount, _ := cmd.Flags().GetBool("forceunmount")
			deferDestroy, _ := cmd.Flags().GetBool("defer")

			d, err := GetDataset(name)
			if err != nil {
				return err
			}

			opts := &DestroyOptions{
				Recursive:       recursive,
				RecursiveClones: recursiveClones,
				ForceUnmount:    forceUnmount,
				Defer:           deferDestroy,
			}
			return d.Destroy(opts)
		})
	cmdDestroy.Flags().BoolP("defer", "d", false, "defer destroy")
	cmdDestroy.Flags().BoolP("recursive", "r", false, "recursively destroy datasets")
	cmdDestroy.Flags().BoolP("recursiveclones", "c", false, "recursively destroy clones")
	cmdDestroy.Flags().BoolP("forceunmount", "f", false, "force unmount")

	cmdHolds := genCommand("holds", "Retrieve list of user holds on the specified snapshot.",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			holds, err := holds(name)
			if err == nil {
				fmt.Println(holds)
			}
			return err
		})

	cmdSnapshot := genCommand("snapshot", "Creates a snapshot of a dataset or volume",
		func(cmd *cobra.Command, snaps []string) error {
			recursive, _ := cmd.Flags().GetBool("recursive")
			name, _ := cmd.Flags().GetString("name")
			nameParts := strings.Split(name, "@")
			if len(nameParts) != 2 {
				log.Fatal("invalid snapshot name")
			}

			var props map[string]string
			propJSON, _ := cmd.Flags().GetString("props")
			if err := json.Unmarshal([]byte(propJSON), &props); err != nil {
				log.Fatal("bad prop json")
			}

			d, err := GetDataset(nameParts[0])
			if err != nil {
				return err
			}

			return d.Snapshot(nameParts[1], recursive)
		},
	)
	cmdSnapshot.Flags().StringP("props", "p", "{}", "snapshot properties")
	cmdSnapshot.Flags().BoolP("recursive", "r", false, "recurisvely create snapshots")

	cmdRollback := genCommand("rollback", "Rollback this filesystem or volume to its most recent snapshot.",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			result, err := rollback(name)
			if err == nil {
				fmt.Println(result)
			}
			return err
		},
	)

	cmdCreate := genCommand("create", "Create a ZFS dataset or volume",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			createTypeName, _ := cmd.Flags().GetString("type")
			createType, err := getDMUType(createTypeName)
			if err != nil {
				log.Fatal("invalid type")
			}

			propJSON, _ := cmd.Flags().GetString("props")
			var props map[string]interface{}
			if err := json.Unmarshal([]byte(propJSON), &props); err != nil {
				log.Fatal("bad prop json")
			}

			if volsize, ok := props["volsize"]; ok {
				if volsizeFloat, ok := volsize.(float64); ok {
					props["volsize"] = uint64(volsizeFloat)
				}
			}

			if volblocksize, ok := props["volblocksize"]; ok {
				if volblocksizeFloat, ok := volblocksize.(float64); ok {
					props["volblocksize"] = uint64(volblocksizeFloat)
				}
			}

			return create(name, createType, props)
		},
	)
	cmdCreate.Flags().StringP("type", "t", "zfs", "zfs or zvol")
	cmdCreate.Flags().StringP("props", "p", "{}", "create properties")

	cmdSend := genCommand("send", "Generate a send stream from a snapshot",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			largeBlockOK, _ := cmd.Flags().GetBool("largeblock")
			embedOK, _ := cmd.Flags().GetBool("embed")
			fromSnap, _ := cmd.Flags().GetString("fromsnap")

			outputFD := os.Stdout.Fd()
			output, _ := cmd.Flags().GetString("output")
			if output != "/dev/stdout" {
				outputFile, err := os.Create(output)
				if err != nil {
					return err
				}
				defer outputFile.Close()

				outputFD = outputFile.Fd()
			} else {
				// If sending on stdout, don't log anything else unless there's
				// an error
				log.SetLevel(log.ErrorLevel)
			}

			return send(name, outputFD, fromSnap, largeBlockOK, embedOK)
		},
	)
	cmdSend.Flags().StringP("output", "o", "/dev/stdout", "output file")
	cmdSend.Flags().StringP("fromsnap", "f", "", "full snap name to send incremental from")
	cmdSend.Flags().BoolP("embed", "e", false, "embed data")
	cmdSend.Flags().BoolP("largeblock", "l", false, "large block")

	cmdClone := genCommand("clone", "Creates a clone from a snapshot",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			origin, _ := cmd.Flags().GetString("origin")
			if origin == "" {
				log.Fatal("missing origin snapshot")
			}

			var props map[string]interface{}
			propJSON, _ := cmd.Flags().GetString("props")
			if err := json.Unmarshal([]byte(propJSON), &props); err != nil {
				log.Fatal("bad prop json")
			}

			return clone(name, origin, props)
		},
	)
	cmdClone.Flags().StringP("origin", "o", "", "name of origin snapshot")
	cmdClone.Flags().StringP("props", "p", "{}", "snapshot properties")

	cmdRename := genCommand("rename", "Rename a dataset",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			newName, _ := cmd.Flags().GetString("newname")
			recursive, _ := cmd.Flags().GetBool("recursive")

			failedName, err := rename(name, newName, recursive)
			if failedName != "" {
				log.Error(failedName)
			}
			return err
		},
	)
	cmdRename.Flags().StringP("newname", "o", "", "new name of dataset")
	cmdRename.Flags().BoolP("recursive", "r", false, "recursively rename snapshots")

	cmdList := genCommand("list", "List filesystems, volumes, snapshots and bookmarks.",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			recurse, _ := cmd.Flags().GetBool("recurse")
			depth, _ := cmd.Flags().GetUint64("depth")
			typesList, _ := cmd.Flags().GetString("types")

			types := map[string]bool{}
			if typesList != "" {
				for _, t := range strings.Split(typesList, ",") {
					types[t] = true
				}
			}

			m, err := list(name, types, recurse, depth)
			if err != nil {
				return err
			}
			out, err := json.MarshalIndent(m, "", "  ")
			if err != nil {
				return err
			}
			fmt.Printf("%s\n", out)
			return nil
		})
	cmdList.PersistentPreRun = dummyPersistentPreRun
	cmdList.Flags().StringP("types", "t", "", "Comma seperated list of dataset types to list.")
	cmdList.Flags().BoolP("recurse", "r", false, "Recurse to all levels.")
	cmdList.Flags().Uint64P("depth", "d", 0, "Recursion depth limit, 0 for unlimited")

	cmdGet := genCommand("get", "Get dataset properties",
		func(cmd *cobra.Command, args []string) error {
			name, _ := cmd.Flags().GetString("name")
			recurse, _ := cmd.Flags().GetBool("recurse")
			depth, _ := cmd.Flags().GetUint64("depth")
			typesList, _ := cmd.Flags().GetString("types")

			types := map[string]bool{}
			if typesList != "" {
				for _, t := range strings.Split(typesList, ",") {
					types[t] = true
				}
			}

			m, err := properties(name, types, recurse, depth)
			if err != nil {
				return err
			}
			out, err := json.MarshalIndent(m, "", "  ")
			if err != nil {
				return err
			}
			fmt.Printf("%s\n", out)
			return nil
		})
	cmdGet.Flags().StringP("types", "t", "", "Comma seperated list of dataset types to list.")
	cmdGet.Flags().BoolP("recurse", "r", false, "Recurse to all levels.")
	cmdGet.Flags().Uint64P("depth", "d", 0, "Recursion depth limit, 0 for unlimited")

	root.AddCommand(
		cmdExists,
		cmdDestroy,
		cmdHolds,
		cmdSnapshot,
		cmdRollback,
		cmdCreate,
		cmdSend,
		cmdClone,
		cmdRename,
		cmdList,
		cmdGet,
	)
	if err := root.Execute(); err != nil {
		log.Fatal("root execute failed:", err)
	}
}

func help(cmd *cobra.Command, _ []string) {
	if err := cmd.Help(); err != nil {
		log.Fatal("help failed:", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha1"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html"
	"html/template"
	"io"
	"log"
	"net/http"

	"github.com/dchest/uniuri"
	"github.com/ewhal/pygments"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
)

const (
	ADDRESS  = "http://localhost:9900"
	LENGTH   = 6
	PORT     = ":9900"
	USERNAME = ""
	PASS     = ""
	NAME     = ""
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

type Response struct {
	ID     string `json:"id"`
	HASH   string `json:"hash"`
	URL    string `json:"url"`
	SIZE   int    `json:"size"`
	DELKEY string `json:"delkey"`
}

type Page struct {
	Title string
	Body  []byte
}

func check(err error) {
	if err != nil {
		fmt.Println(err)
	}
}

func generateName() string {
	s := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	query, err := db.Query("select id from pastebin")
	for query.Next() {
		var id string
		err := query.Scan(&id)
		if err != nil {

		}
		if id == s {
			generateName()
		}
	}
	db.Close()

	return s

}
func hash(paste string) string {
	hasher := sha1.New()

	hasher.Write([]byte(paste))
	sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
	return sha
}

func save(raw string, lang string) []string {
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	sha := hash(raw)
	query, err := db.Query("select id, hash, data, delkey from pastebin")
	for query.Next() {
		var id, hash, paste, delkey string
		err := query.Scan(&id, &hash, &paste, &delkey)
		check(err)
		if hash == sha {
			url := ADDRESS + "/p/" + id
			return []string{id, hash, url, paste, delkey}
		}
	}
	id := generateName()
	var url string
	if lang == "" {
		url = ADDRESS + "/p/" + id
	} else {
		url = ADDRESS + "/p/" + id + "/" + lang
	}
	delKey := uniuri.NewLen(40)
	paste := html.EscapeString(raw)

	stmt, err := db.Prepare("INSERT INTO pastebin(id, hash, data, delkey) values(?,?,?,?)")
	check(err)
	_, err = stmt.Exec(id, sha, paste, delKey)
	check(err)
	db.Close()
	return []string{id, sha, url, paste, delKey}
}

func delHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	delkey := vars["delKey"]

	db, err := sql.Open("mysql", DATABASE)
	check(err)

	stmt, err := db.Prepare("delete from pastebin where delkey=?")
	check(err)

	res, err := stmt.Exec(html.EscapeString(delkey))
	check(err)

	_, err = res.RowsAffected()
	if err == sql.ErrNoRows {
		io.WriteString(w, "Error invalid paste")
	} else {
		io.WriteString(w, paste+" deleted")
	}
	db.Close()

}
func saveHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	output := vars["output"]
	switch r.Method {
	case "POST":
		paste := r.FormValue("p")
		lang := r.FormValue("lang")
		if paste == "" {
			http.Error(w, "Empty paste", 500)
			return
		}
		values := save(paste, lang)
		b := &Response{
			ID:     values[0],
			HASH:   values[1],
			URL:    values[2],
			SIZE:   len(values[3]),
			DELKEY: values[4],
		}

		switch output {
		case "json":

			w.Header().Set("Content-Type", "application/json")
			err := json.NewEncoder(w).Encode(b)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		case "xml":
			x, err := xml.MarshalIndent(b, "", "  ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/xml")
			w.Write(x)

		case "html":
			w.Header().Set("Content-Type", "text/html")
			io.WriteString(w, "
URL: "+b.URL+"
")
			io.WriteString(w, "
Delete Key: "+b.DELKEY+"
")

		default:
			w.Header().Set("Content-Type", "plain/text")
			io.WriteString(w, b.URL+"\n")
			io.WriteString(w, "delete key: "+b.DELKEY+"\n")
		}
	}

}

func langHandler(w http.ResponseWriter, r *http.Request) {

	vars := mux.Vars(r)
	paste := vars["pasteId"]
	lang := vars["lang"]
	s := getPaste(paste)
	highlight, err := pygments.Highlight(html.UnescapeString(s), html.EscapeString(lang), "html", "full, style=autumn,linenos=True, lineanchors=True,anchorlinenos=True,", "utf-8")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	io.WriteString(w, highlight)

}

func getPaste(paste string) string {
	param1 := html.EscapeString(paste)
	db, err := sql.Open("mysql", DATABASE)
	var s string
	err = db.QueryRow("select data from pastebin where id=?", param1).Scan(&s)
	db.Close()
	check(err)

	if err == sql.ErrNoRows {
		return "Error invalid paste"
	} else {
		return html.UnescapeString(s)
	}

}

var templates = template.Must(template.ParseFiles("assets/paste.html"))

func pasteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s := getPaste(paste)
	p := &Page{
		Title: paste,
		Body:  []byte(s),
	}
	err := templates.ExecuteTemplate(w, "assets/paste.html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func rawHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s := getPaste(paste)
	io.WriteString(w, s)

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/p/{pasteId}", pasteHandler)
	router.HandleFunc("/raw/{pasteId}", rawHandler)
	router.HandleFunc("/p/{pasteId}/{lang}", langHandler)
	router.HandleFunc("/save", saveHandler)
	router.HandleFunc("/save/{output}", saveHandler)
	router.HandleFunc("/del/{pasteId}/{delKey}", delHandler)
	router.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("assets/"))))
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<commit_msg>Template bug fix<commit_after>package main

import (
	"crypto/sha1"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html"
	"html/template"
	"io"
	"log"
	"net/http"

	"github.com/dchest/uniuri"
	"github.com/ewhal/pygments"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
)

const (
	ADDRESS  = "http://localhost:9900"
	LENGTH   = 6
	PORT     = ":9900"
	USERNAME = ""
	PASS     = ""
	NAME     = ""
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

type Response struct {
	ID     string `json:"id"`
	HASH   string `json:"hash"`
	URL    string `json:"url"`
	SIZE   int    `json:"size"`
	DELKEY string `json:"delkey"`
}

type Page struct {
	Title string
	Body  []byte
}

func check(err error) {
	if err != nil {
		fmt.Println(err)
	}
}

func generateName() string {
	s := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	query, err := db.Query("select id from pastebin")
	for query.Next() {
		var id string
		err := query.Scan(&id)
		if err != nil {

		}
		if id == s {
			generateName()
		}
	}
	db.Close()

	return s

}
func hash(paste string) string {
	hasher := sha1.New()

	hasher.Write([]byte(paste))
	sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
	return sha
}

func save(raw string, lang string) []string {
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	sha := hash(raw)
	query, err := db.Query("select id, hash, data, delkey from pastebin")
	for query.Next() {
		var id, hash, paste, delkey string
		err := query.Scan(&id, &hash, &paste, &delkey)
		check(err)
		if hash == sha {
			url := ADDRESS + "/p/" + id
			return []string{id, hash, url, paste, delkey}
		}
	}
	id := generateName()
	var url string
	if lang == "" {
		url = ADDRESS + "/p/" + id
	} else {
		url = ADDRESS + "/p/" + id + "/" + lang
	}
	delKey := uniuri.NewLen(40)
	paste := html.EscapeString(raw)

	stmt, err := db.Prepare("INSERT INTO pastebin(id, hash, data, delkey) values(?,?,?,?)")
	check(err)
	_, err = stmt.Exec(id, sha, paste, delKey)
	check(err)
	db.Close()
	return []string{id, sha, url, paste, delKey}
}

func delHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	delkey := vars["delKey"]

	db, err := sql.Open("mysql", DATABASE)
	check(err)

	stmt, err := db.Prepare("delete from pastebin where delkey=?")
	check(err)

	res, err := stmt.Exec(html.EscapeString(delkey))
	check(err)

	_, err = res.RowsAffected()
	if err == sql.ErrNoRows {
		io.WriteString(w, "Error invalid paste")
	} else {
		io.WriteString(w, paste+" deleted")
	}
	db.Close()

}
func saveHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	output := vars["output"]
	switch r.Method {
	case "POST":
		paste := r.FormValue("p")
		lang := r.FormValue("lang")
		if paste == "" {
			http.Error(w, "Empty paste", 500)
			return
		}
		values := save(paste, lang)
		b := &Response{
			ID:     values[0],
			HASH:   values[1],
			URL:    values[2],
			SIZE:   len(values[3]),
			DELKEY: values[4],
		}

		switch output {
		case "json":

			w.Header().Set("Content-Type", "application/json")
			err := json.NewEncoder(w).Encode(b)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		case "xml":
			x, err := xml.MarshalIndent(b, "", "  ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/xml")
			w.Write(x)

		case "html":
			w.Header().Set("Content-Type", "text/html")
			io.WriteString(w, "
URL: "+b.URL+"
")
			io.WriteString(w, "
Delete Key: "+b.DELKEY+"
")

		default:
			w.Header().Set("Content-Type", "plain/text")
			io.WriteString(w, b.URL+"\n")
			io.WriteString(w, "delete key: "+b.DELKEY+"\n")
		}
	}

}

func langHandler(w http.ResponseWriter, r *http.Request) {

	vars := mux.Vars(r)
	paste := vars["pasteId"]
	lang := vars["lang"]
	s := getPaste(paste)
	highlight, err := pygments.Highlight(html.UnescapeString(s), html.EscapeString(lang), "html", "full, style=autumn,linenos=True, lineanchors=True,anchorlinenos=True,", "utf-8")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	io.WriteString(w, highlight)

}

func getPaste(paste string) string {
	param1 := html.EscapeString(paste)
	db, err := sql.Open("mysql", DATABASE)
	var s string
	err = db.QueryRow("select data from pastebin where id=?", param1).Scan(&s)
	db.Close()
	check(err)

	if err == sql.ErrNoRows {
		return "Error invalid paste"
	} else {
		return html.UnescapeString(s)
	}

}

func pasteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s := getPaste(paste)
	p := &Page{
		Title: paste,
		Body:  []byte(s),
	}
	t, err := template.ParseFiles("assets/edit.html")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	t.Execute(w, p)
}

func rawHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s := getPaste(paste)
	io.WriteString(w, s)

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/p/{pasteId}", pasteHandler)
	router.HandleFunc("/raw/{pasteId}", rawHandler)
	router.HandleFunc("/p/{pasteId}/{lang}", langHandler)
	router.HandleFunc("/save", saveHandler)
	router.HandleFunc("/save/{output}", saveHandler)
	router.HandleFunc("/del/{pasteId}/{delKey}", delHandler)
	router.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("assets/"))))
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha1"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"sync"
)

// Node type
type Node struct {
	Path string // File path
	Hash string // Crypto signature in string form
}

// Constructor function from
func MakeNode(path string, fi os.FileInfo) (Node, error) {
	node := Node{Path: path}

	// Open file
	file, err := os.Open(path)
	if err != nil {
		return node, err
	}
	defer file.Close()

	var n int64 // bytes read
	hash := sha1.New()

	// Filesystem and memory optimal read
	n, err = io.Copy(hash, file)

	// Check for normal errors
	if err != nil {
		return node, err
	}

	// Paranoid sanity check
	if n != fi.Size() {
		return node, errors.New("Partial read")
	}

	// Add hash value
	node.Hash = fmt.Sprintf("%0x", hash.Sum(nil))

	// Collect garbage
	hash = nil

	return node, nil
}

// Dup type
type Dup struct {
	Hash  string   // Crypto signature
	Paths []string // Paths with matching signatures
}

func (d Dup) String() string {
	return fmt.Sprintf("%s %+v", d.Hash, d.Paths)
}

func main() {
	// Process command line params
	paths := os.Args[1:]

	if len(paths) == 0 {
		// Default is current directory
		paths = []string{"."}
	}

	// Start map-reduce
	for dup := range Reduce(Map(paths)) {
		fmt.Println(dup)
	}
}

func Reduce(in <-chan Node) <-chan Dup {
	out := make(chan Dup)
	nodes := make(map[string][]string)

	// Synchronously populate thread-unsafe map
	for n := range in {
		if v, ok := nodes[n.Hash]; ok {
			// Found dups
			nodes[n.Hash] = append(v, n.Path)
			// log.Printf("DEBUG Found dups for %s - %+v\n", n.Hash, nodes[n.Hash])
		} else {
			// Add new node
			nodes[n.Hash] = []string{n.Path}
		}
	}

	// Asyncronously send output
	go func() {
		for hash, paths := range nodes {
			if len(paths) > 1 {
				// Construct Dup and send it out
				out <- Dup{
					Hash:  hash,
					Paths: paths,
				}
			}
		}
		// Don't foget to clean-up
		close(out)
	}()

	return out
}

func Map(paths []string) <-chan Node {
	out := make(chan Node)

	// Start filesystem tree walking
	go func() {
		var wg sync.WaitGroup

		// Process all command line paths
		for _, p := range paths {
			err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
				// Handle passthrough error
				if err != nil {
					log.Println("WARN", err)
					return nil
				}

				// Only process simple files
				if IsFile(info) {
					// Add to wait group
					wg.Add(1)

					// Calculate hash
					go func(path string, fi os.FileInfo) {
						defer wg.Done() // Signal done
						n, err := MakeNode(path, fi)
						if err != nil {
							log.Println("WARN", err)
						}
						out <- n
					}(path, info)
				}
				return nil
			})

			if err != nil {
				log.Fatal(err)
			}
		}

		// Wait until all results are in
		wg.Wait()

		// Close output channel when all nodes were processed
		close(out)
	}()

	return out
}

func IsFile(fi os.FileInfo) bool {
	return fi.Mode()&os.ModeType == 0
}
<commit_msg>- Use all available CPU<commit_after>package main

import (
	"crypto/sha1"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"sync"
)

// Node type
type Node struct {
	Path string // File path
	Hash string // Crypto signature in string form
}

// Constructor function from
func MakeNode(path string, fi os.FileInfo) (Node, error) {
	node := Node{Path: path}

	// Open file
	file, err := os.Open(path)
	if err != nil {
		return node, err
	}
	defer file.Close()

	var n int64 // bytes read
	hash := sha1.New()

	// Filesystem and memory optimal read
	n, err = io.Copy(hash, file)

	// Check for normal errors
	if err != nil {
		return node, err
	}

	// Paranoid sanity check
	if n != fi.Size() {
		return node, errors.New("Partial read")
	}

	// Add hash value
	node.Hash = fmt.Sprintf("%0x", hash.Sum(nil))

	// Collect garbage
	hash = nil

	return node, nil
}

// Dup type
type Dup struct {
	Hash  string   // Crypto signature
	Paths []string // Paths with matching signatures
}

func (d Dup) String() string {
	return fmt.Sprintf("%s %+v", d.Hash, d.Paths)
}

func main() {
	// Use all available CPU cores
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Process command line params
	paths := os.Args[1:]

	if len(paths) == 0 {
		// Default is current directory
		paths = []string{"."}
	}

	// Start map-reduce
	for dup := range Reduce(Map(paths)) {
		fmt.Println(dup)
	}
}

func Reduce(in <-chan Node) <-chan Dup {
	out := make(chan Dup)
	nodes := make(map[string][]string)

	// Synchronously populate thread-unsafe map
	for n := range in {
		if v, ok := nodes[n.Hash]; ok {
			// Found dups
			nodes[n.Hash] = append(v, n.Path)
			// log.Printf("DEBUG Found dups for %s - %+v\n", n.Hash, nodes[n.Hash])
		} else {
			// Add new node
			nodes[n.Hash] = []string{n.Path}
		}
	}

	// Asyncronously send output
	go func() {
		for hash, paths := range nodes {
			if len(paths) > 1 {
				// Construct Dup and send it out
				out <- Dup{
					Hash:  hash,
					Paths: paths,
				}
			}
		}
		// Don't foget to clean-up
		close(out)
	}()

	return out
}

func Map(paths []string) <-chan Node {
	out := make(chan Node)

	// Start filesystem tree walking
	go func() {
		var wg sync.WaitGroup

		// Process all command line paths
		for _, p := range paths {
			err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
				// Handle passthrough error
				if err != nil {
					log.Println("WARN", err)
					return nil
				}

				// Only process simple files
				if IsFile(info) {
					// Add to wait group
					wg.Add(1)

					// Calculate hash
					go func(path string, fi os.FileInfo) {
						defer wg.Done() // Signal done
						n, err := MakeNode(path, fi)
						if err != nil {
							log.Println("WARN", err)
						}
						out <- n
					}(path, info)
				}
				return nil
			})

			if err != nil {
				log.Fatal(err)
			}
		}

		// Wait until all results are in
		wg.Wait()

		// Close output channel when all nodes were processed
		close(out)
	}()

	return out
}

func IsFile(fi os.FileInfo) bool {
	return fi.Mode()&os.ModeType == 0
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math"
	"math/rand"
)

type Obstgarten struct {
	// cherries, apples, plums and pears
	fruits [4]int

	crow int
}

func main() {
	const N = 10000
	fmt.Println("starting...")
	rand.Seed(131)
	wons := 0
	for i := 0; i < N; i++ {
		game := NewGame()
		won := game.play()
		if won {
			wons++
		}
	}
	percent := math.Floor(0.5 + (100.0 / float64(N) * float64(wons)))
	fmt.Printf("games won: %v out of %v (%v%%)", wons, N, percent)

}

func NewGame() Obstgarten {
	o := Obstgarten{}
	for i := 0; i < len(o.fruits); i++ {
		o.fruits[i] = 10
	}
	return o
}

func (o *Obstgarten) play() bool {
	for !o.over() {
		o.step()

	}
	return o.won()
}

func (o Obstgarten) fruitsLeft() int {
	n := 0
	for i := 0; i < len(o.fruits); i++ {
		n += o.fruits[i]
	}
	return n
}

func (o Obstgarten) won() bool {
	return o.fruitsLeft() == 0
}

func (o Obstgarten) lost() bool {
	return o.crow == 9
}

func (o Obstgarten) over() bool {
	return o.lost() || o.won()
}

func (o *Obstgarten) step() {
	n := rollDice()
	switch n {
	case 0, 1, 2, 3:
		if o.fruits[n] > 0 {
			o.fruits[n]--
		}

	// basket
	case 4:
		o.pick()
		o.pick()

	// crow
	case 5:
		o.crow++
	}
}

// simple strategy: pick first available fruit

func (o *Obstgarten) pick() {
	for i := 0; i < len(o.fruits); i++ {
		if o.fruits[i] > 0 {
			o.fruits[i]--
			break
		}
	}
}

func rollDice() int {
	return rand.Intn(6)
}
<commit_msg>feat: elaborate pick() strategy<commit_after>package main

import (
	"fmt"
	"math"
	"math/rand"
	"time"
)

type Obstgarten struct {
	// cherries, apples, plums and pears
	fruits [4]int

	crow int
}

func main() {
	const N = 10000
	fmt.Println("starting...")
	rand.Seed(time.Now().Unix())
	wons := 0
	for i := 0; i < N; i++ {
		game := NewGame()
		won := game.play()
		if won {
			wons++
		}
	}
	percent := math.Floor(0.5 + (100.0 / float64(N) * float64(wons)))
	fmt.Printf("games won: %v out of %v (%v%%)", wons, N, percent)

}

func NewGame() Obstgarten {
	o := Obstgarten{}
	for i := 0; i < len(o.fruits); i++ {
		o.fruits[i] = 10
	}
	return o
}

func (o *Obstgarten) play() bool {
	for !o.over() {
		o.step()

	}
	return o.won()
}

func (o Obstgarten) fruitsLeft() int {
	n := 0
	for i := 0; i < len(o.fruits); i++ {
		n += o.fruits[i]
	}
	return n
}

func (o Obstgarten) won() bool {
	return o.fruitsLeft() == 0
}

func (o Obstgarten) lost() bool {
	return o.crow == 9
}

func (o Obstgarten) over() bool {
	return o.lost() || o.won()
}

func (o *Obstgarten) step() {
	n := rollDice()
	switch n {
	case 0, 1, 2, 3:
		if o.fruits[n] > 0 {
			o.fruits[n]--
		}

	// basket
	case 4:
		o.pick()
		o.pick()

	// crow
	case 5:
		o.crow++
	}
}

// simple strategy: pick first available fruit
func (o *Obstgarten) simplePick() {
	for i := 0; i < len(o.fruits); i++ {
		if o.fruits[i] > 0 {
			o.fruits[i]--
			break
		}
	}
}

// Pick the fruit that has the most pieces left,
// so that regular fruit rolls are not in vain
func (o *Obstgarten) pick() {
	max := 0
	maxIndex := 0

	for i := 0; i < len(o.fruits); i++ {
		if o.fruits[i] > max {
			max = o.fruits[i]
			maxIndex = i
		}
	}
	if o.fruits[maxIndex] > 0 {
		o.fruits[maxIndex]--
	}
}

func rollDice() int {
	return rand.Intn(6)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"io"
	"sort"
	"path/filepath"
	"strings"
	"strconv"
)

var dir_default = "/Users/matt/Documents"
var dir_desc = "directory to store list"

var name_default = "to.txt"
var name_desc = "name of list"

var remove_desc = "number of item to remove"

var to_dir = flag.String("d", dir_default, dir_desc)
var to_name = flag.String("n", name_default, name_desc)
var should_remove = flag.String("r", "", name_desc)
// print path option

func main() {
	flag.Parse()

	to_path := filepath.Join(*to_dir, *to_name)

	var (
		lines []string
		err error
	)
	if lines, err = read_lines(to_path); err != nil {
		fmt.Println(err)
		return
	}

	if len(*should_remove) != 0 {
		// find item with prefix
		var to_remove int
		if to_remove, err = strconv.Atoi(*should_remove); err != nil {
			fmt.Println(err)
			return
		}

		// remove element at to_remove
		copy(lines[to_remove:], lines[(to_remove + 1):len(lines)])
		lines = lines[:len(lines) - 1]
		if err = write_lines(to_path, lines); err != nil {
			fmt.Println(err)
			return
		}
		return
	}

	// just print the lines
	if len(flag.Args()) == 0 {
		width := 1
		if len(lines) > 10 {
			width = 2
		}
		var format = fmt.Sprintf("%%%dd - %%s\n", width)
		for i, line := range lines {
			fmt.Printf(format, i, line)
		}
		return
	}

	// join rest of arguments as item to add
	to := strings.Join(flag.Args(), " ")
	lines = append(lines, to)
	sort.Strings(lines)
	if err = write_lines(to_path, lines); err != nil {
		fmt.Println(err)
		return
	}
	return
}

// returns all lines in file at path (even blank ones)
func read_lines(path string) (lines []string, err error) {
	var (
		file *os.File
		line string
	)
	// open file for reading
	if file, err = os.Open(path); err != nil {
		return
	}
	defer file.Close()
	// start reading from file
	reader := bufio.NewReader(file)
	for {
		if line, err = reader.ReadString('\n'); err != nil {
			break
		}
		// don't include newline
		line = strings.TrimRight(line, "\n")
		// skip blank lines
		if len(line) != 0 {
			lines = append(lines, strings.TrimRight(line, "\n"))
		}
	}
	// don't report an end of file error
	if err == io.EOF {
		err = nil
	}
	return
}

func write_lines(path string, lines []string) (err error) {
	var (
		file *os.File
	)
	// open file for writing
	if file, err = os.Create(path); err != nil {
		return
	}
	defer file.Close()

	// write each line to file
	for _, line := range lines {
		_, err = file.WriteString(line + "\n");
		if err != nil {
			return
		}
	}
	return
}

<commit_msg>update style and refactor slightly<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"bufio"
	"io"
	"sort"
	"path/filepath"
	"strings"
)

func main() {
	// parse command line flags

	var (
		listDir string
		listName string
		removeItem int
	)

	flag.StringVar(&listDir, "d", "~/Documents", "directory of list")
	flag.StringVar(&listName, "n", "to.txt", "filename of list")
	flag.IntVar(&removeItem, "r", -1, "item to be removed")
	flag.Parse()

	// sanitize path to list

	tildeAt := strings.Index(listDir, "~")
	if tildeAt != -1 {
		homeDir := os.Getenv("HOME")
		if len(homeDir) == 0 {
			panic("can't determine home directory")
		}
		listDir = strings.Replace(listDir, "~", homeDir, 1)
	}

	listDir, err := filepath.Abs(listDir)
	if err != nil {
		panic(err)
	}
	if _, err := os.Stat(listDir); os.IsNotExist(err) {
		panic(err)
	}

	listPath := filepath.Join(listDir, listName)

	// get items from list

	lines, err := ReadLines(listPath)
	if err != nil {
		fmt.Println(err)
		return
	}

	switch {
	case removeItem >= 0:
		// remove item at removeItem
		if removeItem >= len(lines) {
			panic("item to remove doesn't exist")
		}
		lines[removeItem] = lines[len(lines) - 1]
		lines = lines[:len(lines) - 1]
		sort.Strings(lines)
		err = WriteLines(listPath, lines)
		if err != nil {
			panic(err)
		}
	case flag.NArg() == 0:
		// display item list
		width := 1
		if len(lines) > 10 {
			width = 2
		}
		var format = fmt.Sprintf("%%%dd - %%s\n", width)
		for i, line := range lines{
			fmt.Printf(format, i, line)
		}
	default:
		// join rest of arguments as item to add
		to := strings.Join(flag.Args(), " ")
		lines = append(lines, to)
		sort.Strings(lines)
		if err := WriteLines(listPath, lines); err != nil {
			panic(err)
		}
	}

	return
}

func ReadLines(path string) (lines []string, err error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	// read lines from buffered reader
	reader := bufio.NewReader(f)
	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			break
		}
		line = strings.TrimRight(line, "\n")
		if len(line) != 0 {
			lines = append(lines, line)
		}
	}

	// suppress EOF error
	if err == io.EOF {
		err = nil
	}

	return
}

func WriteLines(path string, lines []string) (err error) {
	f, err := os.Create(path)
	if err != nil {
		return
	}
	defer f.Close()

	// write each line to file
	for _, line := range lines {
		_, err = f.WriteString(line + "\n");
		if err != nil {
			return
		}
	}
	return
}

<|endoftext|>


<commit_before>package main

import (
	"os"

	"github.com/containerum/chkit/cmd"
	"github.com/sirupsen/logrus"
)

func main() {
	defer angel()
	switch err := cmd.Run(os.Args).(type) {
	case nil:
		err.Error()
	default:
		logrus.Fatalf("Something bad happend: %v", err)
	}
}
<commit_msg>rm test panic<commit_after>package main

import (
	"os"

	"github.com/containerum/chkit/cmd"
	"github.com/sirupsen/logrus"
)

func main() {
	defer angel()
	switch err := cmd.Run(os.Args).(type) {
	case nil:
		// pass
	default:
		logrus.Fatalf("Something bad happend: %v", err)
	}
}
<|endoftext|>


<commit_before>package bla

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"text/template"
	"time"

	"golang.org/x/net/webdav"

	"github.com/fsnotify/fsnotify"
)

// Initialize
type Handler struct {
	cfgPath string
	Cfg     *Config
	public  http.Handler
	webfs   http.Handler
	tpl     *template.Template

	publicPath   string
	templatePath string
	docPath      string

	mu       sync.RWMutex
	docs     map[string]*Doc
	sortDocs []*Doc
	tags     map[string][]*Doc
}

func NewHandler(cfgPath string) *Handler {

	h := &Handler{
		cfgPath: cfgPath,
		mu:      sync.RWMutex{},
	}

	h.loadConfig()
	h.watch()
	h.loadWebDav()

	return h
}

func (s *Handler) loadWebDav() {

	fs := webdav.Dir(s.Cfg.RootPath)
	ls := webdav.NewMemLS()

	handler := &webdav.Handler{
		Prefix:     "/fs",
		FileSystem: fs,
		LockSystem: ls,
	}
	a := NewAuthRateByIPHandler(s.Cfg.HostName, handler, s.Cfg.UserName,
		s.Cfg.Password, 3)
	s.webfs = a
}

func (s *Handler) watch() {

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		// init all docs
		s.loadData()
		s.loadTemplate()
		err := s.saveAll()
		if err != nil {
			log.Fatal("can't save docs:", err)
		}
		// loadData minial interval is 1 second
		ticker := time.NewTicker(time.Second)
		docChange := false
		rootChange := false

		for {

			select {
			case event := <-watcher.Events:
				switch ext := filepath.Ext(event.Name); ext {
				case ".md", ".tmpl":
					log.Println("modified file:", event.Name)
					docChange = true
				case ".swp":
					continue
				}

				rootChange = true
			case err := <-watcher.Errors:
				log.Println("error:", err)
			case <-ticker.C:
				if docChange {
					docChange = false
					s.loadData()
					s.loadTemplate()
				}

				if rootChange {
					rootChange = false
					err := s.saveAll()
					if err != nil {
						log.Fatal("can't save docs:", err)
					}
				}
			}
		}
	}()

	watcher.Add(s.Cfg.RootPath)
	watcher.Add(s.docPath)
	watcher.Add(s.templatePath)

	if err != nil {
		log.Print(err)
	}
}

func (s *Handler) clearOldTmp(exclude string) (err error) {
	realExcluded, err := filepath.Abs(exclude)
	if err != nil {
		return err
	}
	old, err := filepath.Glob(filepath.Join(os.TempDir(), "bla*"))
	if err != nil {
		return err
	}

	for _, path := range old {
		realPath, err := filepath.Abs(path)
		if err != nil {
			return err
		}
		if realPath == realExcluded {
			continue
		}
		log.Println("removing old public", realPath)
		os.RemoveAll(realPath)
	}
	return nil
}

func (s *Handler) saveAll() (err error) {

	s.publicPath, err = ioutil.TempDir("", "bla")
	if err != nil {
		return err
	}

	log.Print("Saving all docs...")
	s.mu.Lock()
	defer s.mu.Unlock()

	log.Println("Deleting public...")
	err = os.RemoveAll(s.publicPath)
	if err != nil {
		return
	}

	var f *os.File
	err = os.MkdirAll(s.publicPath, 0700)
	if err != nil {
		return
	}
	log.Printf("saving all docs...")
	for _, doc := range s.docs {
		f, err = os.Create(filepath.Join(s.publicPath, doc.SlugTitle))
		if err != nil {
			return
		}
		if err = s.tpl.ExecuteTemplate(f, "single",
			&singleData{s, doc.Title, doc}); err != nil {
			return
		}
		f.Close()
	}

	var docs []*Doc
	if len(s.sortDocs) > s.Cfg.HomeDocCount {
		docs = s.sortDocs[:s.Cfg.HomeDocCount]
	} else {
		docs = s.sortDocs
	}

	f, err = os.Create(filepath.Join(s.publicPath, "index.html"))
	if err != nil {
		return
	}
	defer f.Close()

	if err = s.tpl.ExecuteTemplate(f, "index",
		&mulDocData{s, "", docs}); err != nil {
		return
	}

	log.Printf("saving all tags...")
	err = os.MkdirAll(filepath.Join(s.publicPath, "tags"), 0700)
	if err != nil {
		return
	}

	for tagName, docs := range s.tags {
		f, err = os.Create(filepath.Join(s.publicPath, "/tags/", tagName))
		if err != nil {
			return
		}
		sort.Sort(docsByTime(docs))
		if err = s.tpl.ExecuteTemplate(f, "tag_page",
			&tagData{s, tagName, docs, tagName}); err != nil {
			return
		}
		f.Close()
	}

	log.Printf("linking all dir in %s", s.Cfg.RootPath)
	filepath.Walk(s.Cfg.RootPath, s.linkToPublic)
	log.Println("save completed")
	s.public = http.FileServer(http.Dir(s.publicPath))
	s.clearOldTmp(s.publicPath)
	return nil
}

func (s *Handler) linkToPublic(path string, info os.FileInfo, err error) error {

	if path == s.Cfg.RootPath {
		return nil
	}

	if strings.Count(path, "/")-strings.Count(s.Cfg.RootPath, "/") > 1 {
		return nil
	}

	switch base := filepath.Base(path); base {
	case "template", "docs", ".public", "":
		return nil
	default:
		realPath, err := filepath.Abs(path)
		if err != nil {
			return err
		}
		target := filepath.Join(s.publicPath, base)
		log.Printf("link %s -> %s", realPath, target)

		err = os.Symlink(realPath, target)
		if err != nil {
			if os.IsExist(err) {
				return nil
			}
			log.Fatal(err)
		}
	}
	return nil
}

func (s *Handler) loadData() {
	log.Print("Loading docs from:", s.docPath)

	s.mu.Lock()
	s.sortDocs = []*Doc{}
	s.docs = map[string]*Doc{}
	s.tags = map[string][]*Doc{}
	s.mu.Unlock()

	f, err := os.Open(s.docPath)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	err = filepath.Walk(s.docPath, s.docWalker)
	if err != nil {
		log.Print(err)
	}
	sort.Sort(docsByTime(s.sortDocs))
	log.Print("End Loading docs from:", s.docPath)
}

func (s *Handler) docWalker(p string, info os.FileInfo, err error) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	start := time.Now()
	if info.IsDir() || filepath.Ext(info.Name()) != ".md" {
		return nil
	}
	var f *os.File
	f, err = os.Open(p)
	if err != nil {
		return err
	}
	defer f.Close()

	var doc *Doc
	doc, err = newDoc(f)
	if err != nil {
		return err
	}
	if !doc.Public {
		log.Printf("doc:%s loaded but not public", p)
		return nil
	}

	doc.SlugTitle = path.Base(p)[0 : len(path.Base(p))-3]

	for _, t := range doc.Tags {
		s.tags[t] = append(s.tags[t], doc)
	}
	s.docs[doc.SlugTitle] = doc
	s.sortDocs = append(s.sortDocs, doc)
	log.Printf("loaded doc:%s in %s", doc.SlugTitle,
		time.Now().Sub(start).String())
	return nil
}

func (h *Handler) loadConfig() {

	f, err := os.Open(h.cfgPath)
	if err != nil && os.IsExist(err) {
		log.Panic(err)
	}
	defer f.Close()

	log.Print("loading config")
	cfg := DefaultConfig()
	dec := json.NewDecoder(f)
	err = dec.Decode(cfg)

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

	h.templatePath = filepath.Join(cfg.RootPath, "template")
	h.docPath = filepath.Join(cfg.RootPath, "docs")

	h.Cfg = cfg
	log.Printf("%#v", *cfg)

}

func (s *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	if strings.HasPrefix(r.URL.Path, "/fs") {
		s.webfs.ServeHTTP(w, r)
	} else {
		s.public.ServeHTTP(w, r)
	}
}

func (s *Handler) loadTemplate() {
	s.mu.Lock()
	defer s.mu.Unlock()

	log.Printf("loding template:%s", s.templatePath)
	tpl, err := template.ParseGlob(s.templatePath + "/*.tmpl")
	if err != nil {
		log.Print(err)
		return
	}
	s.tpl = tpl
}
<commit_msg>don't break server<commit_after>package bla

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"text/template"
	"time"

	"golang.org/x/net/webdav"

	"github.com/fsnotify/fsnotify"
)

// Initialize
type Handler struct {
	cfgPath string
	Cfg     *Config
	public  http.Handler
	webfs   http.Handler
	tpl     *template.Template

	publicPath   string
	templatePath string
	docPath      string

	mu       sync.RWMutex
	docs     map[string]*Doc
	sortDocs []*Doc
	tags     map[string][]*Doc
}

func NewHandler(cfgPath string) *Handler {

	h := &Handler{
		cfgPath: cfgPath,
		mu:      sync.RWMutex{},
	}

	h.loadConfig()
	h.watch()
	h.loadWebDav()

	return h
}

func (s *Handler) loadWebDav() {

	fs := webdav.Dir(s.Cfg.RootPath)
	ls := webdav.NewMemLS()

	handler := &webdav.Handler{
		Prefix:     "/fs",
		FileSystem: fs,
		LockSystem: ls,
	}
	a := NewAuthRateByIPHandler(s.Cfg.HostName, handler, s.Cfg.UserName,
		s.Cfg.Password, 3)
	s.webfs = a
}

func (s *Handler) watch() {

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		// init all docs
		s.loadData()
		s.loadTemplate()
		err := s.saveAll()
		if err != nil {
			log.Fatal("can't save docs:", err)
		}
		// loadData minial interval is 1 second
		ticker := time.NewTicker(time.Second)
		docChange := false
		rootChange := false

		for {

			select {
			case event := <-watcher.Events:
				switch ext := filepath.Ext(event.Name); ext {
				case ".md", ".tmpl":
					log.Println("modified file:", event.Name)
					docChange = true
				case ".swp":
					continue
				}

				rootChange = true
			case err := <-watcher.Errors:
				log.Println("error:", err)
			case <-ticker.C:
				if docChange {
					docChange = false
					s.loadData()
					s.loadTemplate()
				}

				if rootChange {
					rootChange = false
					err := s.saveAll()
					if err != nil {
						log.Print("can't save docs:", err)
						continue
					}
				}
			}
		}
	}()

	watcher.Add(s.Cfg.RootPath)
	watcher.Add(s.docPath)
	watcher.Add(s.templatePath)

	if err != nil {
		log.Print(err)
	}
}

func (s *Handler) clearOldTmp(exclude string) (err error) {
	realExcluded, err := filepath.Abs(exclude)
	if err != nil {
		return err
	}
	old, err := filepath.Glob(filepath.Join(os.TempDir(), "bla*"))
	if err != nil {
		return err
	}

	for _, path := range old {
		realPath, err := filepath.Abs(path)
		if err != nil {
			return err
		}
		if realPath == realExcluded {
			continue
		}
		log.Println("removing old public", realPath)
		os.RemoveAll(realPath)
	}
	return nil
}

func (s *Handler) saveAll() (err error) {

	s.publicPath, err = ioutil.TempDir("", "bla")
	if err != nil {
		return err
	}

	log.Print("Saving all docs...")
	s.mu.Lock()
	defer s.mu.Unlock()

	log.Println("Deleting public...")
	err = os.RemoveAll(s.publicPath)
	if err != nil {
		return
	}

	var f *os.File
	err = os.MkdirAll(s.publicPath, 0700)
	if err != nil {
		return
	}
	log.Printf("saving all docs...")
	for _, doc := range s.docs {
		f, err = os.Create(filepath.Join(s.publicPath, doc.SlugTitle))
		if err != nil {
			return
		}
		if err = s.tpl.ExecuteTemplate(f, "single",
			&singleData{s, doc.Title, doc}); err != nil {
			return
		}
		f.Close()
	}

	var docs []*Doc
	if len(s.sortDocs) > s.Cfg.HomeDocCount {
		docs = s.sortDocs[:s.Cfg.HomeDocCount]
	} else {
		docs = s.sortDocs
	}

	f, err = os.Create(filepath.Join(s.publicPath, "index.html"))
	if err != nil {
		return
	}
	defer f.Close()

	if err = s.tpl.ExecuteTemplate(f, "index",
		&mulDocData{s, "", docs}); err != nil {
		return
	}

	log.Printf("saving all tags...")
	err = os.MkdirAll(filepath.Join(s.publicPath, "tags"), 0700)
	if err != nil {
		return
	}

	for tagName, docs := range s.tags {
		f, err = os.Create(filepath.Join(s.publicPath, "/tags/", tagName))
		if err != nil {
			return
		}
		sort.Sort(docsByTime(docs))
		if err = s.tpl.ExecuteTemplate(f, "tag_page",
			&tagData{s, tagName, docs, tagName}); err != nil {
			return
		}
		f.Close()
	}

	log.Printf("linking all dir in %s", s.Cfg.RootPath)
	filepath.Walk(s.Cfg.RootPath, s.linkToPublic)
	log.Println("save completed")
	s.public = http.FileServer(http.Dir(s.publicPath))
	s.clearOldTmp(s.publicPath)
	return nil
}

func (s *Handler) linkToPublic(path string, info os.FileInfo, err error) error {

	if path == s.Cfg.RootPath {
		return nil
	}

	if strings.Count(path, "/")-strings.Count(s.Cfg.RootPath, "/") > 1 {
		return nil
	}

	switch base := filepath.Base(path); base {
	case "template", "docs", ".public", "":
		return nil
	default:
		realPath, err := filepath.Abs(path)
		if err != nil {
			return err
		}
		target := filepath.Join(s.publicPath, base)
		log.Printf("link %s -> %s", realPath, target)

		err = os.Symlink(realPath, target)
		if err != nil {
			if os.IsExist(err) {
				return nil
			}
			log.Fatal(err)
		}
	}
	return nil
}

func (s *Handler) loadData() {
	log.Print("Loading docs from:", s.docPath)

	s.mu.Lock()
	s.sortDocs = []*Doc{}
	s.docs = map[string]*Doc{}
	s.tags = map[string][]*Doc{}
	s.mu.Unlock()

	f, err := os.Open(s.docPath)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	err = filepath.Walk(s.docPath, s.docWalker)
	if err != nil {
		log.Print(err)
	}
	sort.Sort(docsByTime(s.sortDocs))
	log.Print("End Loading docs from:", s.docPath)
}

func (s *Handler) docWalker(p string, info os.FileInfo, err error) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	start := time.Now()
	if info.IsDir() || filepath.Ext(info.Name()) != ".md" {
		return nil
	}
	var f *os.File
	f, err = os.Open(p)
	if err != nil {
		return err
	}
	defer f.Close()

	var doc *Doc
	doc, err = newDoc(f)
	if err != nil {
		return err
	}
	if !doc.Public {
		log.Printf("doc:%s loaded but not public", p)
		return nil
	}

	doc.SlugTitle = path.Base(p)[0 : len(path.Base(p))-3]

	for _, t := range doc.Tags {
		s.tags[t] = append(s.tags[t], doc)
	}
	s.docs[doc.SlugTitle] = doc
	s.sortDocs = append(s.sortDocs, doc)
	log.Printf("loaded doc:%s in %s", doc.SlugTitle,
		time.Now().Sub(start).String())
	return nil
}

func (h *Handler) loadConfig() {

	f, err := os.Open(h.cfgPath)
	if err != nil && os.IsExist(err) {
		log.Panic(err)
	}
	defer f.Close()

	log.Print("loading config")
	cfg := DefaultConfig()
	dec := json.NewDecoder(f)
	err = dec.Decode(cfg)

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

	h.templatePath = filepath.Join(cfg.RootPath, "template")
	h.docPath = filepath.Join(cfg.RootPath, "docs")

	h.Cfg = cfg
	log.Printf("%#v", *cfg)

}

func (s *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	if strings.HasPrefix(r.URL.Path, "/fs") {
		s.webfs.ServeHTTP(w, r)
	} else {
		s.public.ServeHTTP(w, r)
	}
}

func (s *Handler) loadTemplate() {
	s.mu.Lock()
	defer s.mu.Unlock()

	log.Printf("loding template:%s", s.templatePath)
	tpl, err := template.ParseGlob(s.templatePath + "/*.tmpl")
	if err != nil {
		log.Print(err)
		return
	}
	s.tpl = tpl
}
<|endoftext|>