<commit_before>/*
Execute shell commands via http server

Install:
	go get github.com/msoap/shell2http
	ln -s $GOPATH/bin/shell2http ~/bin/shell2http

Usage:
	shell2http [options] /path "shell command" /path2 "shell command2" ...
	options:
		-host="host" : host for http server, default - all interfaces
		-port=NNNN   : port for http server, default - 8080
		-form        : parse query into environment vars
		-cgi         : set some CGI variables in environment
		-no-index    : dont generate index page
		-add-exit    : add /exit command
		-log=filename: log filename, default - STDOUT
		-version
		-help

Examples:
	shell2http /top "top -l 1 | head -10"
	shell2http /date date /ps "ps aux"
	shell2http /env 'printenv | sort' /env/path 'echo $PATH' /env/gopath 'echo $GOPATH'
	shell2http /shell_vars_json 'perl -MJSON -E "say to_json(\%ENV)"'
	shell2http /cal_html 'echo "
Calendar
Date: $(date)

$(cal $(date +%Y))
"'
	shell2http -form /form 'echo $v_from, $v_to'
	shell2http -cgi /user_agent 'echo $HTTP_USER_AGENT'

Update:
	go get -u github.com/msoap/shell2http

*/
package main

import (
	"flag"
	"fmt"
	"html"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
)

// version
const VERSION = 1.1

// default port for http-server
const PORT = 8080

// ------------------------------------------------------------------
const INDEX_HTML = `
<!DOCTYPE html>

	<title>shell2http</title>

shell2http

		%s

	Get from: github.com/msoap/shell2http

`

// ------------------------------------------------------------------
// one command type
type command struct {
	path string
	cmd  string
}

// ------------------------------------------------------------------
// config struct
type config struct {
	host     string // server host
	port     int    // server port
	set_cgi  bool   // set CGI variables
	set_form bool   // parse form from URL
	no_index bool   // dont generate index page
	add_exit bool   // add /exit command
}

// ------------------------------------------------------------------
// parse arguments
func get_config() (cmd_handlers []command, app_config config, err error) {
	var log_filename string
	flag.StringVar(&log_filename, "log", "", "log filename, default - STDOUT")
	flag.IntVar(&app_config.port, "port", PORT, "port for http server")
	flag.StringVar(&app_config.host, "host", "", "host for http server")
	flag.BoolVar(&app_config.set_cgi, "cgi", false, "set some CGI variables in environment")
	flag.BoolVar(&app_config.set_form, "form", false, "parse query into environment vars")
	flag.BoolVar(&app_config.no_index, "no-index", false, "dont generate index page")
	flag.BoolVar(&app_config.add_exit, "add-exit", false, "add /exit command")
	flag.Usage = func() {
		fmt.Printf("usage: %s [options] /path \"shell command\" /path2 \"shell command2\"\n", os.Args[0])
		flag.PrintDefaults()
		os.Exit(0)
	}
	version := flag.Bool("version", false, "get version")
	flag.Parse()
	if *version {
		fmt.Println(VERSION)
		os.Exit(0)
	}

	// setup log file
	if len(log_filename) > 0 {
		fh_log, err := os.OpenFile(log_filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
		if err != nil {
			log.Fatalf("error opening log file: %v", err)
		}
		log.SetOutput(fh_log)
	}

	// need >= 2 arguments and count of it must be even
	args := flag.Args()
	if len(args) < 2 || len(args)%2 == 1 {
		return nil, config{}, fmt.Errorf("error: need pairs of path and shell command")
	}

	for i := 0; i < len(args); i += 2 {
		path, cmd := args[i], args[i+1]
		if path[0] != '/' {
			return nil, config{}, fmt.Errorf("error: path %s dont starts with /", path)
		}
		cmd_handlers = append(cmd_handlers, command{path: path, cmd: cmd})
	}

	return cmd_handlers, app_config, nil
}

// ------------------------------------------------------------------
// setup http handlers
func setup_handlers(cmd_handlers []command, app_config config) {
	index_li_html := ""
	for _, row := range cmd_handlers {
		path, cmd := row.path, row.cmd
		shell_handler := func(rw http.ResponseWriter, req *http.Request) {
			log.Println("GET", path)

			os_exec_command := exec.Command("sh", "-c", cmd)

			proxy_system_env(os_exec_command)
			if app_config.set_form {
				get_form(os_exec_command, req)
			}
			if app_config.set_cgi {
				set_cgi_env(os_exec_command, req, app_config)
			}

			os_exec_command.Stderr = os.Stderr
			shell_out, err := os_exec_command.Output()

			if err != nil {
				log.Println("exec error: ", err)
				fmt.Fprint(rw, "exec error: ", err)
			} else {
				fmt.Fprint(rw, string(shell_out))
			}

			return
		}

		http.HandleFunc(path, shell_handler)

		log.Printf("register: %s (%s)\n", path, cmd)
		index_li_html += fmt.Sprintf(`<li><a href="%s">%s</a> <span style="color: #888">- %s<span></li>`, path, path, html.EscapeString(cmd))
	}

	// --------------
	if app_config.add_exit {
		http.HandleFunc("/exit", func(rw http.ResponseWriter, req *http.Request) {
			log.Println("GET /exit")
			fmt.Fprint(rw, "Bye...")
			go os.Exit(0)

			return
		})

		log.Printf("register: %s (%s)\n", "/exit", "/exit")
		index_li_html += fmt.Sprintf(`<li><a href="%s">%s</a></li>`, "/exit", "/exit")
	}

	// --------------
	if !app_config.no_index {
		index_html := fmt.Sprintf(INDEX_HTML, index_li_html)
		http.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) {
			if req.URL.Path != "/" {
				log.Println("404")
				http.NotFound(rw, req)
				return
			}
			log.Println("GET /")
			fmt.Fprint(rw, index_html)

			return
		})
	}
}

// ------------------------------------------------------------------
// set some CGI variables
func set_cgi_env(cmd *exec.Cmd, req *http.Request, app_config config) {
	req_headers := [...]struct {
		cgi_name, req_field_name string
	}{
		{"HTTP_ACCEPT", "Accept"},
		{"HTTP_ACCEPT_ENCODING", "Accept-Encoding"},
		{"HTTP_ACCEPT_LANGUAGE", "Accept-Language"},
		{"HTTP_USER_AGENT", "User-Agent"},
	}

	for _, row := range req_headers {
		if header, exists := req.Header[row.req_field_name]; exists && len(header) > 0 {
			cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", row.cgi_name, header[0]))
		}
	}

	remote_addr := strings.Split(req.RemoteAddr, ":")
	CGI_vars := [...]struct {
		cgi_name, value string
	}{
		{"PATH_INFO", req.URL.Path},
		{"QUERY_STRING", req.URL.RawQuery},
		{"REMOTE_ADDR", remote_addr[0]},
		{"REMOTE_PORT", remote_addr[1]},
		{"REQUEST_METHOD", req.Method},
		{"REQUEST_URI", req.RequestURI},
		{"SCRIPT_NAME", req.URL.Path},
		{"SERVER_NAME", app_config.host},
		{"SERVER_PORT", fmt.Sprintf("%d", app_config.port)},
		{"SERVER_PROTOCOL", req.Proto},
		{"SERVER_SOFTWARE", "shell2http"},
	}

	for _, row := range CGI_vars {
		cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", row.cgi_name, row.value))
	}
}

// ------------------------------------------------------------------
// parse form into environment vars
func get_form(cmd *exec.Cmd, req *http.Request) {
	err := req.ParseForm()
	if err != nil {
		log.Println(err)
		return
	}

	for key, value := range req.Form {
		cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", "v_"+key, strings.Join(value, ",")))
	}
}

// ------------------------------------------------------------------
// proxy some system vars
func proxy_system_env(cmd *exec.Cmd) {
	for _, env_raw := range os.Environ() {
		env := strings.SplitN(env_raw, "=", 2)
		for _, env_var_name := range [...]string{"PATH", "HOME", "LANG", "USER", "TMPDIR"} {
			if env[0] == env_var_name {
				cmd.Env = append(cmd.Env, env_raw)
			}
		}
	}
}

// ------------------------------------------------------------------
func main() {
	cmd_handlers, app_config, err := get_config()
	if err != nil {
		log.Fatal(err)
	}
	setup_handlers(cmd_handlers, app_config)

	adress := fmt.Sprintf("%s:%d", app_config.host, app_config.port)
	log.Printf("listen http://%s/\n", adress)
	err = http.ListenAndServe(adress, nil)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>New release<commit_after>/*
Execute shell commands via http server

Install:
	go get github.com/msoap/shell2http
	ln -s $GOPATH/bin/shell2http ~/bin/shell2http

Usage:
	shell2http [options] /path "shell command" /path2 "shell command2" ...
	options:
		-host="host" : host for http server, default - all interfaces
		-port=NNNN   : port for http server, default - 8080
		-form        : parse query into environment vars
		-cgi         : set some CGI variables in environment
		-no-index    : dont generate index page
		-add-exit    : add /exit command
		-log=filename: log filename, default - STDOUT
		-version
		-help

Examples:
	shell2http /top "top -l 1 | head -10"
	shell2http /date date /ps "ps aux"
	shell2http /env 'printenv | sort' /env/path 'echo $PATH' /env/gopath 'echo $GOPATH'
	shell2http /shell_vars_json 'perl -MJSON -E "say to_json(\%ENV)"'
	shell2http /cal_html 'echo "
Calendar
Date: $(date)

$(cal $(date +%Y))
"'
	shell2http -form /form 'echo $v_from, $v_to'
	shell2http -cgi /user_agent 'echo $HTTP_USER_AGENT'

Update:
	go get -u github.com/msoap/shell2http

*/
package main

import (
	"flag"
	"fmt"
	"html"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
)

// version
const VERSION = 1.2

// default port for http-server
const PORT = 8080

// ------------------------------------------------------------------
const INDEX_HTML = `
<!DOCTYPE html>

	<title>shell2http</title>

shell2http

		%s

	Get from: github.com/msoap/shell2http

`

// ------------------------------------------------------------------
// one command type
type command struct {
	path string
	cmd  string
}

// ------------------------------------------------------------------
// config struct
type config struct {
	host     string // server host
	port     int    // server port
	set_cgi  bool   // set CGI variables
	set_form bool   // parse form from URL
	no_index bool   // dont generate index page
	add_exit bool   // add /exit command
}

// ------------------------------------------------------------------
// parse arguments
func get_config() (cmd_handlers []command, app_config config, err error) {
	var log_filename string
	flag.StringVar(&log_filename, "log", "", "log filename, default - STDOUT")
	flag.IntVar(&app_config.port, "port", PORT, "port for http server")
	flag.StringVar(&app_config.host, "host", "", "host for http server")
	flag.BoolVar(&app_config.set_cgi, "cgi", false, "set some CGI variables in environment")
	flag.BoolVar(&app_config.set_form, "form", false, "parse query into environment vars")
	flag.BoolVar(&app_config.no_index, "no-index", false, "dont generate index page")
	flag.BoolVar(&app_config.add_exit, "add-exit", false, "add /exit command")
	flag.Usage = func() {
		fmt.Printf("usage: %s [options] /path \"shell command\" /path2 \"shell command2\"\n", os.Args[0])
		flag.PrintDefaults()
		os.Exit(0)
	}
	version := flag.Bool("version", false, "get version")
	flag.Parse()
	if *version {
		fmt.Println(VERSION)
		os.Exit(0)
	}

	// setup log file
	if len(log_filename) > 0 {
		fh_log, err := os.OpenFile(log_filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
		if err != nil {
			log.Fatalf("error opening log file: %v", err)
		}
		log.SetOutput(fh_log)
	}

	// need >= 2 arguments and count of it must be even
	args := flag.Args()
	if len(args) < 2 || len(args)%2 == 1 {
		return nil, config{}, fmt.Errorf("error: need pairs of path and shell command")
	}

	for i := 0; i < len(args); i += 2 {
		path, cmd := args[i], args[i+1]
		if path[0] != '/' {
			return nil, config{}, fmt.Errorf("error: path %s dont starts with /", path)
		}
		cmd_handlers = append(cmd_handlers, command{path: path, cmd: cmd})
	}

	return cmd_handlers, app_config, nil
}

// ------------------------------------------------------------------
// setup http handlers
func setup_handlers(cmd_handlers []command, app_config config) {
	index_li_html := ""
	for _, row := range cmd_handlers {
		path, cmd := row.path, row.cmd
		shell_handler := func(rw http.ResponseWriter, req *http.Request) {
			log.Println("GET", path)

			os_exec_command := exec.Command("sh", "-c", cmd)

			proxy_system_env(os_exec_command)
			if app_config.set_form {
				get_form(os_exec_command, req)
			}
			if app_config.set_cgi {
				set_cgi_env(os_exec_command, req, app_config)
			}

			os_exec_command.Stderr = os.Stderr
			shell_out, err := os_exec_command.Output()

			if err != nil {
				log.Println("exec error: ", err)
				fmt.Fprint(rw, "exec error: ", err)
			} else {
				fmt.Fprint(rw, string(shell_out))
			}

			return
		}

		http.HandleFunc(path, shell_handler)

		log.Printf("register: %s (%s)\n", path, cmd)
		index_li_html += fmt.Sprintf(`<li><a href="%s">%s</a> <span style="color: #888">- %s<span></li>`, path, path, html.EscapeString(cmd))
	}

	// --------------
	if app_config.add_exit {
		http.HandleFunc("/exit", func(rw http.ResponseWriter, req *http.Request) {
			log.Println("GET /exit")
			fmt.Fprint(rw, "Bye...")
			go os.Exit(0)

			return
		})

		log.Printf("register: %s (%s)\n", "/exit", "/exit")
		index_li_html += fmt.Sprintf(`<li><a href="%s">%s</a></li>`, "/exit", "/exit")
	}

	// --------------
	if !app_config.no_index {
		index_html := fmt.Sprintf(INDEX_HTML, index_li_html)
		http.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) {
			if req.URL.Path != "/" {
				log.Println("404")
				http.NotFound(rw, req)
				return
			}
			log.Println("GET /")
			fmt.Fprint(rw, index_html)

			return
		})
	}
}

// ------------------------------------------------------------------
// set some CGI variables
func set_cgi_env(cmd *exec.Cmd, req *http.Request, app_config config) {
	req_headers := [...]struct {
		cgi_name, req_field_name string
	}{
		{"HTTP_ACCEPT", "Accept"},
		{"HTTP_ACCEPT_ENCODING", "Accept-Encoding"},
		{"HTTP_ACCEPT_LANGUAGE", "Accept-Language"},
		{"HTTP_USER_AGENT", "User-Agent"},
	}

	for _, row := range req_headers {
		if header, exists := req.Header[row.req_field_name]; exists && len(header) > 0 {
			cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", row.cgi_name, header[0]))
		}
	}

	remote_addr := strings.Split(req.RemoteAddr, ":")
	CGI_vars := [...]struct {
		cgi_name, value string
	}{
		{"PATH_INFO", req.URL.Path},
		{"QUERY_STRING", req.URL.RawQuery},
		{"REMOTE_ADDR", remote_addr[0]},
		{"REMOTE_PORT", remote_addr[1]},
		{"REQUEST_METHOD", req.Method},
		{"REQUEST_URI", req.RequestURI},
		{"SCRIPT_NAME", req.URL.Path},
		{"SERVER_NAME", app_config.host},
		{"SERVER_PORT", fmt.Sprintf("%d", app_config.port)},
		{"SERVER_PROTOCOL", req.Proto},
		{"SERVER_SOFTWARE", "shell2http"},
	}

	for _, row := range CGI_vars {
		cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", row.cgi_name, row.value))
	}
}

// ------------------------------------------------------------------
// parse form into environment vars
func get_form(cmd *exec.Cmd, req *http.Request) {
	err := req.ParseForm()
	if err != nil {
		log.Println(err)
		return
	}

	for key, value := range req.Form {
		cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", "v_"+key, strings.Join(value, ",")))
	}
}

// ------------------------------------------------------------------
// proxy some system vars
func proxy_system_env(cmd *exec.Cmd) {
	for _, env_raw := range os.Environ() {
		env := strings.SplitN(env_raw, "=", 2)
		for _, env_var_name := range [...]string{"PATH", "HOME", "LANG", "USER", "TMPDIR"} {
			if env[0] == env_var_name {
				cmd.Env = append(cmd.Env, env_raw)
			}
		}
	}
}

// ------------------------------------------------------------------
func main() {
	cmd_handlers, app_config, err := get_config()
	if err != nil {
		log.Fatal(err)
	}
	setup_handlers(cmd_handlers, app_config)

	adress := fmt.Sprintf("%s:%d", app_config.host, app_config.port)
	log.Printf("listen http://%s/\n", adress)
	err = http.ListenAndServe(adress, nil)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// MIT License
//
// Copyright (c) 2017 Stefan Wichmann
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package main

import "time"
import "github.com/bt51/ntpclient"

const TimeServer = "0.pool.ntp.org"
const MaxTimeDifferenceInSeconds = 60

// IsLocalTimeValid validates the clock on the local machine via NTP.
// If the network time differs more than one minute from the local
// time it is considered invalid. It is up to the caller to continue
// anyway.
func IsLocalTimeValid() (bool, error) {
	networkTime, err := ntpclient.GetNetworkTime(TimeServer, 123)
	if err != nil {
		return false, err
	}
	timeDifference := networkTime.Sub(time.Now()).Seconds()
	if timeDifference < 0 {
		timeDifference = timeDifference * -1
	}

	if timeDifference < MaxTimeDifferenceInSeconds {
		return true, nil
	}
	return false, nil
}
<commit_msg>golint<commit_after>// MIT License
//
// Copyright (c) 2017 Stefan Wichmann
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package main

import "time"
import "github.com/bt51/ntpclient"

const timeServer = "0.pool.ntp.org"
const maxTimeDifferenceInSeconds = 60

// IsLocalTimeValid validates the clock on the local machine via NTP.
// If the network time differs more than one minute from the local
// time it is considered invalid. It is up to the caller to continue
// anyway.
func IsLocalTimeValid() (bool, error) {
	networkTime, err := ntpclient.GetNetworkTime(timeServer, 123)
	if err != nil {
		return false, err
	}
	timeDifference := networkTime.Sub(time.Now()).Seconds()
	if timeDifference < 0 {
		timeDifference = timeDifference * -1
	}

	if timeDifference < maxTimeDifferenceInSeconds {
		return true, nil
	}
	return false, nil
}
<|endoftext|>


<commit_before>package tlsh

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"math"
	"os"
)

const (
	log1_5       = 0.4054651
	log1_3       = 0.26236426
	log1_1       = 0.095310180
	codeSize     = 32
	windowLength = 5
	effBuckets   = 128
	numBuckets   = 256
)

// LSH holds the hash components
type LSH struct {
	Checksum byte
	Length   byte
}

func getTriplets(slice [windowLength]byte) (triplets [6][3]byte) {
	triplets = [6][3]byte{
		{slice[0], slice[1], slice[2]},
		{slice[0], slice[1], slice[3]},
		{slice[0], slice[2], slice[3]},
		{slice[0], slice[2], slice[4]},
		{slice[0], slice[1], slice[4]},
		{slice[0], slice[3], slice[4]},
	}
	return triplets
}

func quartilePoints(buckets [numBuckets]uint) (q1, q2, q3 uint) {
	var spl, spr uint
	p1 := uint(effBuckets/4 - 1)
	p2 := uint(effBuckets/2 - 1)
	p3 := uint(effBuckets - effBuckets/4 - 1)
	end := uint(effBuckets - 1)

	bucketCopy := make([]uint, effBuckets)
	copy(bucketCopy, buckets[:effBuckets])

	shortCutLeft := make([]uint, effBuckets)
	shortCutRight := make([]uint, effBuckets)

	for l, r := uint(0), end; ; {
		ret := partition(&bucketCopy, l, r)
		if ret > p2 {
			r = ret - 1
			shortCutRight[spr] = ret
			spr++
		} else if ret < p2 {
			l = ret + 1
			shortCutLeft[spl] = ret
			spl++
		} else {
			q2 = bucketCopy[p2]
			break
		}
	}

	shortCutLeft[spl] = p2 - 1
	shortCutRight[spr] = p2 + 1

	for i, l := uint(0), uint(0); i <= spl; i++ {
		r := shortCutLeft[i]
		if r > p1 {
			for {
				ret := partition(&bucketCopy, l, r)
				if ret > p1 {
					r = ret - 1
				} else if ret < p1 {
					l = ret + 1
				} else {
					q1 = bucketCopy[p1]
					break
				}
			}
			break
		} else if r < p1 {
			l = r
		} else {
			q1 = bucketCopy[p1]
			break
		}
	}

	for i, r := uint(0), end; i <= spr; i++ {
		l := shortCutRight[i]
		if l < p3 {
			for {
				ret := partition(&bucketCopy, l, r)
				if ret > p3 {
					r = ret - 1
				} else if ret < p3 {
					l = ret + 1
				} else {
					q3 = bucketCopy[p3]
					break
				}
			}
			break
		} else if l > p3 {
			r = l
		} else {
			q3 = bucketCopy[p3]
			break
		}
	}

	return q1, q2, q3
}

func partition(buf *[]uint, left, right uint) uint {

	if left == right {
		return left
	}

	if left+1 == right {
		if (*buf)[left] > (*buf)[right] {
			(*buf)[right], (*buf)[left] = (*buf)[left], (*buf)[right]
		}
		return left
	}

	ret := left
	pivot := (left + right) >> 1
	val := (*buf)[pivot]

	(*buf)[pivot] = (*buf)[right]
	(*buf)[right] = val

	for i := left; i < right; i++ {
		if (*buf)[i] < val {
			(*buf)[i], (*buf)[ret] = (*buf)[ret], (*buf)[i]
			ret++
		}
	}

	(*buf)[right] = (*buf)[ret]
	(*buf)[ret] = val

	return ret
}

func lValue(length int) byte {
	var l byte

	if length <= 656 {
		l = byte(math.Floor(math.Log(float64(length)) / log1_5))
	} else if length <= 3199 {
		l = byte(math.Floor(math.Log(float64(length))/log1_3 - 8.72777))
	} else {
		l = byte(math.Floor(math.Log(float64(length))/log1_1 - 62.5472))
	}

	return l % 255
}

func swapByte(in byte) byte {
	var out byte

	out = ((in & 0xF0) >> 4) & 0x0F
	out |= ((in & 0x0F) << 4) & 0xF0

	return out
}

func bucketsBinaryRepresentation(buckets [numBuckets]uint, q1, q2, q3 uint) [codeSize]byte {
	var biHash [codeSize]byte

	for i := 0; i < codeSize; i++ {
		var h byte
		for j := 0; j < 4; j++ {
			k := buckets[4*i+j]
			if q3 < k {
				h += 3 << (byte(j) * 2)
			} else if q2 < k {
				h += 2 << (byte(j) * 2)
			} else if q1 < k {
				h += 1 << (byte(j) * 2)
			}
		}
		// Prepend the new h to the hash
		biHash[(codeSize-1)-i] = h
	}
	return biHash
}

func hashTLSH(length int, buckets [numBuckets]uint, checksum byte, q1, q2, q3 uint) []byte {

	// binary representation of buckets
	biHash := bucketsBinaryRepresentation(buckets, q1, q2, q3)

	q1Ratio := byte(float32(q1)*100/float32(q3)) % 16
	q2Ratio := byte(float32(q2)*100/float32(q3)) % 16

	qRatio := ((q1Ratio & 0xF) << 4) | (q2Ratio & 0xF)

	// prepend header
	return append([]byte{swapByte(checksum), swapByte(lValue(length)), qRatio}, biHash[:]...)
}

func makeStringTLSH(biHash []byte) (hash string) {

	for i := 0; i < len(biHash); i++ {
		hash += fmt.Sprintf("%02X", biHash[i])
	}

	return
}

func reverse(s [5]byte) [5]byte {
	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
		s[i], s[j] = s[j], s[i]
	}
	return s
}

func fillBuckets(r fuzzyReader) ([numBuckets]uint, byte, int, error) {
	buckets := [numBuckets]uint{}
	chunkSlice := make([]byte, windowLength)
	chunk := [windowLength]byte{}
	salt := [6]byte{2, 3, 5, 7, 11, 13}
	fileSize := 0
	checksum := byte(0)

	n, err := r.Read(chunkSlice)
	if err != nil {
		return [numBuckets]uint{}, 0, 0, err
	}
	copy(chunk[:], chunkSlice[0:5])
	chunk = reverse(chunk)
	fileSize += n
	for {
		checksum = pearsonHash(0, &[3]byte{chunk[0], chunk[1], checksum})

		buckets[pearsonHash(salt[0], &[3]byte{chunk[0], chunk[1], chunk[2]})]++
		buckets[pearsonHash(salt[1], &[3]byte{chunk[0], chunk[1], chunk[3]})]++
		buckets[pearsonHash(salt[2], &[3]byte{chunk[0], chunk[2], chunk[3]})]++
		buckets[pearsonHash(salt[3], &[3]byte{chunk[0], chunk[2], chunk[4]})]++
		buckets[pearsonHash(salt[4], &[3]byte{chunk[0], chunk[1], chunk[4]})]++
		buckets[pearsonHash(salt[5], &[3]byte{chunk[0], chunk[3], chunk[4]})]++

		copy(chunk[1:], chunk[0:4])
		chunk[0], err = r.ReadByte()
		if err != nil {
			if err != io.EOF {
				return [numBuckets]uint{}, 0, 0, err
			}
			break
		}
		fileSize++
	}
	return buckets, checksum, fileSize, nil
}

type fuzzyReader interface {
	Read([]byte) (int, error)
	ReadByte() (byte, error)
}

//HashReader calculates the TLSH for the input reader
func HashReader(r fuzzyReader) (hash string, err error) {
	buckets, checksum, fileSize, err := fillBuckets(r)
	if err != nil {
		return
	}
	q1, q2, q3 := quartilePoints(buckets)
	hash = makeStringTLSH(hashTLSH(fileSize, buckets, checksum, q1, q2, q3))

	return hash, nil
}

//HashBytes calculates the TLSH for the input byte slice
func HashBytes(blob []byte) (hash string, err error) {
	r := bytes.NewReader(blob)
	return HashReader(r)
}

//Hash calculates the TLSH for the input file
func Hash(filename string) (hash string, err error) {
	f, err := os.Open(filename)
	defer f.Close()
	if err != nil {
		return
	}
	r := bufio.NewReader(f)
	return HashReader(r)
}
<commit_msg>Cleanup<commit_after>package tlsh

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"math"
	"os"
)

const (
	log1_5       = 0.4054651
	log1_3       = 0.26236426
	log1_1       = 0.095310180
	codeSize     = 32
	windowLength = 5
	effBuckets   = 128
	numBuckets   = 256
)

// LSH holds the hash components
type LSH struct {
	Checksum byte
	Length   byte
}

func quartilePoints(buckets [numBuckets]uint) (q1, q2, q3 uint) {
	var spl, spr uint
	p1 := uint(effBuckets/4 - 1)
	p2 := uint(effBuckets/2 - 1)
	p3 := uint(effBuckets - effBuckets/4 - 1)
	end := uint(effBuckets - 1)

	bucketCopy := make([]uint, effBuckets)
	copy(bucketCopy, buckets[:effBuckets])

	shortCutLeft := make([]uint, effBuckets)
	shortCutRight := make([]uint, effBuckets)

	for l, r := uint(0), end; ; {
		ret := partition(&bucketCopy, l, r)
		if ret > p2 {
			r = ret - 1
			shortCutRight[spr] = ret
			spr++
		} else if ret < p2 {
			l = ret + 1
			shortCutLeft[spl] = ret
			spl++
		} else {
			q2 = bucketCopy[p2]
			break
		}
	}

	shortCutLeft[spl] = p2 - 1
	shortCutRight[spr] = p2 + 1

	for i, l := uint(0), uint(0); i <= spl; i++ {
		r := shortCutLeft[i]
		if r > p1 {
			for {
				ret := partition(&bucketCopy, l, r)
				if ret > p1 {
					r = ret - 1
				} else if ret < p1 {
					l = ret + 1
				} else {
					q1 = bucketCopy[p1]
					break
				}
			}
			break
		} else if r < p1 {
			l = r
		} else {
			q1 = bucketCopy[p1]
			break
		}
	}

	for i, r := uint(0), end; i <= spr; i++ {
		l := shortCutRight[i]
		if l < p3 {
			for {
				ret := partition(&bucketCopy, l, r)
				if ret > p3 {
					r = ret - 1
				} else if ret < p3 {
					l = ret + 1
				} else {
					q3 = bucketCopy[p3]
					break
				}
			}
			break
		} else if l > p3 {
			r = l
		} else {
			q3 = bucketCopy[p3]
			break
		}
	}

	return q1, q2, q3
}

func partition(buf *[]uint, left, right uint) uint {

	if left == right {
		return left
	}

	if left+1 == right {
		if (*buf)[left] > (*buf)[right] {
			(*buf)[right], (*buf)[left] = (*buf)[left], (*buf)[right]
		}
		return left
	}

	ret := left
	pivot := (left + right) >> 1
	val := (*buf)[pivot]

	(*buf)[pivot] = (*buf)[right]
	(*buf)[right] = val

	for i := left; i < right; i++ {
		if (*buf)[i] < val {
			(*buf)[i], (*buf)[ret] = (*buf)[ret], (*buf)[i]
			ret++
		}
	}

	(*buf)[right] = (*buf)[ret]
	(*buf)[ret] = val

	return ret
}

func lValue(length int) byte {
	var l byte

	if length <= 656 {
		l = byte(math.Floor(math.Log(float64(length)) / log1_5))
	} else if length <= 3199 {
		l = byte(math.Floor(math.Log(float64(length))/log1_3 - 8.72777))
	} else {
		l = byte(math.Floor(math.Log(float64(length))/log1_1 - 62.5472))
	}

	return l % 255
}

func swapByte(in byte) byte {
	var out byte

	out = ((in & 0xF0) >> 4) & 0x0F
	out |= ((in & 0x0F) << 4) & 0xF0

	return out
}

func bucketsBinaryRepresentation(buckets [numBuckets]uint, q1, q2, q3 uint) [codeSize]byte {
	var biHash [codeSize]byte

	for i := 0; i < codeSize; i++ {
		var h byte
		for j := 0; j < 4; j++ {
			k := buckets[4*i+j]
			if q3 < k {
				h += 3 << (byte(j) * 2)
			} else if q2 < k {
				h += 2 << (byte(j) * 2)
			} else if q1 < k {
				h += 1 << (byte(j) * 2)
			}
		}
		// Prepend the new h to the hash
		biHash[(codeSize-1)-i] = h
	}
	return biHash
}

func hashTLSH(length int, buckets [numBuckets]uint, checksum byte, q1, q2, q3 uint) []byte {

	// binary representation of buckets
	biHash := bucketsBinaryRepresentation(buckets, q1, q2, q3)

	q1Ratio := byte(float32(q1)*100/float32(q3)) % 16
	q2Ratio := byte(float32(q2)*100/float32(q3)) % 16

	qRatio := ((q1Ratio & 0xF) << 4) | (q2Ratio & 0xF)

	// prepend header
	return append([]byte{swapByte(checksum), swapByte(lValue(length)), qRatio}, biHash[:]...)
}

func makeStringTLSH(biHash []byte) (hash string) {

	for i := 0; i < len(biHash); i++ {
		hash += fmt.Sprintf("%02X", biHash[i])
	}

	return
}

func reverse(s [5]byte) [5]byte {
	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
		s[i], s[j] = s[j], s[i]
	}
	return s
}

func fillBuckets(r fuzzyReader) ([numBuckets]uint, byte, int, error) {
	buckets := [numBuckets]uint{}
	chunkSlice := make([]byte, windowLength)
	chunk := [windowLength]byte{}
	salt := [6]byte{2, 3, 5, 7, 11, 13}
	fileSize := 0
	checksum := byte(0)

	n, err := r.Read(chunkSlice)
	if err != nil {
		return [numBuckets]uint{}, 0, 0, err
	}
	copy(chunk[:], chunkSlice[0:5])
	chunk = reverse(chunk)
	fileSize += n
	for {
		checksum = pearsonHash(0, &[3]byte{chunk[0], chunk[1], checksum})

		buckets[pearsonHash(salt[0], &[3]byte{chunk[0], chunk[1], chunk[2]})]++
		buckets[pearsonHash(salt[1], &[3]byte{chunk[0], chunk[1], chunk[3]})]++
		buckets[pearsonHash(salt[2], &[3]byte{chunk[0], chunk[2], chunk[3]})]++
		buckets[pearsonHash(salt[3], &[3]byte{chunk[0], chunk[2], chunk[4]})]++
		buckets[pearsonHash(salt[4], &[3]byte{chunk[0], chunk[1], chunk[4]})]++
		buckets[pearsonHash(salt[5], &[3]byte{chunk[0], chunk[3], chunk[4]})]++

		copy(chunk[1:], chunk[0:4])
		chunk[0], err = r.ReadByte()
		if err != nil {
			if err != io.EOF {
				return [numBuckets]uint{}, 0, 0, err
			}
			break
		}
		fileSize++
	}
	return buckets, checksum, fileSize, nil
}

type fuzzyReader interface {
	Read([]byte) (int, error)
	ReadByte() (byte, error)
}

//HashReader calculates the TLSH for the input reader
func HashReader(r fuzzyReader) (hash string, err error) {
	buckets, checksum, fileSize, err := fillBuckets(r)
	if err != nil {
		return
	}
	q1, q2, q3 := quartilePoints(buckets)
	hash = makeStringTLSH(hashTLSH(fileSize, buckets, checksum, q1, q2, q3))

	return hash, nil
}

//HashBytes calculates the TLSH for the input byte slice
func HashBytes(blob []byte) (hash string, err error) {
	r := bytes.NewReader(blob)
	return HashReader(r)
}

//Hash calculates the TLSH for the input file
func Hash(filename string) (hash string, err error) {
	f, err := os.Open(filename)
	defer f.Close()
	if err != nil {
		return
	}
	r := bufio.NewReader(f)
	return HashReader(r)
}
<|endoftext|>


<commit_before>package gtd

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"

	mapset "github.com/deckarep/golang-set"
)

const (
	todoMark     = "\u2610 "
	todoDoneMark = "\u2713 "
	memoMark     = " \u270F"
)

type Todos struct {
	Todos []Todo `json:"todos"`
}

type Todo struct {
	Title    string `json:"title"`
	Done     bool   `json:"done"`
	Tag      string `json:"tag"`
	Date     string `json:"date"`
	Memo     string `json:"memo"`
	Children []Todo `json:"children"`
}

func (todos *Todos) SetTodos(todolist []Todo) {
	todos.Todos = todolist
}

func (todos *Todos) marshallJson(path string) error {
	indent := strings.Repeat(" ", 4)
	json, err := json.MarshalIndent(todos, "", indent)
	if err != nil {
		return fmt.Errorf("Failed to encode json: %v", err)
	}
	ioutil.WriteFile(path, json, 0644)
	return nil
}

func (todos *Todos) UnmarshallJson(path string) error {
	file, err := os.Stat(path)
	if err != nil || file.Size() == 0 {
		f, err := os.Create(path)
		if err != nil {
			return fmt.Errorf("Failed to create file: %v", err)
		}
		defer f.Close()
		_, err = f.Write([]byte("{}"))
		if err != nil {
			return fmt.Errorf("Failed to write file: %v", err)
		}
	}
	if data, err := ioutil.ReadFile(path); err != nil {
		return fmt.Errorf("Failed to read jsonfile %v", err)
	} else {
		if err := json.Unmarshal(data, todos); err != nil {
			return fmt.Errorf("Failed to decode data %v", err)
		}
		return nil
	}
}

// parse "1.1.0" to [1,1,0]
func parseTodoNum(todonum string) ([]int, error) {
	var todonums []int
	for _, v := range strings.Split(todonum, ".") {
		if id, err := strconv.Atoi(v); err != nil {
			return nil, err
		} else {
			todonums = append(todonums, id)
		}
	}
	return todonums, nil
}

func appendTodo(todos []Todo, todo Todo, todonumlist []int) ([]Todo, error) {
	if len(todonumlist) == 0 {
		return append(todos, todo), nil
	}
	if len(todos) == 0 || len(todos)-1 < todonumlist[0] {
		return todos, fmt.Errorf("Faild to access index")
	}
	if len(todonumlist) == 1 {
		todos[todonumlist[0]].Children = append(todos[todonumlist[0]].Children, todo)
		return todos, nil
	} else {
		todochildren, err := appendTodo(todos[todonumlist[0]].Children, todo, todonumlist[1:])
		if err != nil {
			return todos, err
		}
		todos[todonumlist[0]].Children = todochildren
		return todos, nil
	}
}

func displayTodo(todos []Todo, tab string, todoid string) {
	for id, todo := range todos {
		if todo.Done {
			continue
		}
		fmt.Print(tab, todoMark)
		if todo.Tag != "" {
			fmt.Printf("%v: %v (%v)", todoid+strconv.Itoa(id), todo.Title, todo.Tag)
		} else {
			fmt.Printf("%v: %v", todoid+strconv.Itoa(id), todo.Title)
		}
		if todo.Memo != "" {
			fmt.Print(memoMark)
		}
		fmt.Println()
		if todo.Children != nil {
			parentid := todoid + strconv.Itoa(id) + "."
			displayTodo(todo.Children, tab+" ", parentid)
		}
	}
}

func displayAllTodo(todos []Todo, tab string, todoid string) {
	for id, todo := range todos {
		if todo.Done {
			fmt.Print(tab, todoDoneMark)
		} else {
			fmt.Print(tab, todoMark)
		}
		if todo.Tag != "" {
			fmt.Printf("%v: %v (%v)", todoid+strconv.Itoa(id), todo.Title, todo.Tag)
		} else {
			fmt.Printf("%v: %v", todoid+strconv.Itoa(id), todo.Title)
		}
		if todo.Memo != "" {
			fmt.Print(memoMark)
		}
		fmt.Println()
		if todo.Children != nil {
			parentid := todoid + strconv.Itoa(id) + "."
			displayAllTodo(todo.Children, tab+" ", parentid)
		}
	}
}

func doneTodo(todos []Todo, todonumlist []int) ([]Todo, error) {
	if len(todos) == 0 || len(todos)-1 < todonumlist[0] {
		return todos, fmt.Errorf("Faild to access index")
	}
	if len(todonumlist) == 1 {
		todos[todonumlist[0]].Done = !todos[todonumlist[0]].Done
		return todos, nil
	} else {
		todochildren, err := doneTodo(todos[todonumlist[0]].Children, todonumlist[1:])
		if err != nil {
			return todos, err
		}
		todos[todonumlist[0]].Children = todochildren
		return todos, nil
	}
}

func displayTagTodo(todos []Todo, tag string, todoid string) {
	for id, todo := range todos {
		if todo.Tag == tag {
			fmt.Printf("%v: %v: %v (%v)", todoid+strconv.Itoa(id), todo.Title, todo.Date, todo.Tag)
			if todo.Memo != "" {
				fmt.Print(memoMark)
			}
			fmt.Println()
		}
		if todo.Children != nil {
			parentid := todoid + strconv.Itoa(id) + "."
			displayTagTodo(todo.Children, tag, parentid)
		}
	}
}

func displayAllTags(todos []Todo, tags mapset.Set) {
	for _, todo := range todos {
		add := tags.Add(todo.Tag)
		if todo.Tag != "" && add {
			fmt.Println(todo.Tag)
		}
		if todo.Children != nil {
			displayAllTags(todo.Children, tags)
		}
	}
}

func cleanAllTodos(todos []Todo) ([]Todo, error) {
	for id, todo := range todos {
		var todos_n []Todo
		if todo.Done == true {
			todos_n = removeTodo(todos, id)
			return todos_n, nil
		}
		if todo.Children != nil {
			todos[id].Children, _ = cleanAllTodos(todos[id].Children)
		}
	}
	return todos, nil
}

func deleteTodos(todos []Todo, todonumlist []int) ([]Todo, error) {
	if len(todos) == 0 || len(todos)-1 < todonumlist[0] {
		return todos, fmt.Errorf("Faild to access index")
	}
	if len(todonumlist) == 1 {
		todos = removeTodo(todos, todonumlist[0])
		return todos, nil
	} else {
		todochildren, err := doneTodo(todos[todonumlist[0]].Children, todonumlist[1:])
		if err != nil {
			return todos, err
		}
		todos[todonumlist[0]].Children = todochildren
		return todos, nil
	}
}

func searchTodo(todos []Todo, todonumlist []int) (*Todo, error) {
	if len(todos) == 0 || len(todos)-1 < todonumlist[0] {
		return nil, fmt.Errorf("Faild to access index")
	}
	if len(todonumlist) == 1 {
		return &todos[todonumlist[0]], nil
	} else {
		return searchTodo(todos[todonumlist[0]].Children, todonumlist[1:])
	}
}

func removeTodo(todos []Todo, todonum int) []Todo {
	todos = append(todos[:todonum], todos[todonum+1:]...)
	n := make([]Todo, len(todos))
	copy(n, todos)
	return n
}
<commit_msg>fix deletetodo func<commit_after>package gtd

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"

	mapset "github.com/deckarep/golang-set"
)

const (
	todoMark     = "\u2610 "
	todoDoneMark = "\u2713 "
	memoMark     = " \u270F"
)

type Todos struct {
	Todos []Todo `json:"todos"`
}

type Todo struct {
	Title    string `json:"title"`
	Done     bool   `json:"done"`
	Tag      string `json:"tag"`
	Date     string `json:"date"`
	Memo     string `json:"memo"`
	Children []Todo `json:"children"`
}

func (todos *Todos) SetTodos(todolist []Todo) {
	todos.Todos = todolist
}

func (todos *Todos) marshallJson(path string) error {
	indent := strings.Repeat(" ", 4)
	json, err := json.MarshalIndent(todos, "", indent)
	if err != nil {
		return fmt.Errorf("Failed to encode json: %v", err)
	}
	ioutil.WriteFile(path, json, 0644)
	return nil
}

func (todos *Todos) UnmarshallJson(path string) error {
	file, err := os.Stat(path)
	if err != nil || file.Size() == 0 {
		f, err := os.Create(path)
		if err != nil {
			return fmt.Errorf("Failed to create file: %v", err)
		}
		defer f.Close()
		_, err = f.Write([]byte("{}"))
		if err != nil {
			return fmt.Errorf("Failed to write file: %v", err)
		}
	}
	if data, err := ioutil.ReadFile(path); err != nil {
		return fmt.Errorf("Failed to read jsonfile %v", err)
	} else {
		if err := json.Unmarshal(data, todos); err != nil {
			return fmt.Errorf("Failed to decode data %v", err)
		}
		return nil
	}
}

// parse "1.1.0" to [1,1,0]
func parseTodoNum(todonum string) ([]int, error) {
	var todonums []int
	for _, v := range strings.Split(todonum, ".") {
		if id, err := strconv.Atoi(v); err != nil {
			return nil, err
		} else {
			todonums = append(todonums, id)
		}
	}
	return todonums, nil
}

func appendTodo(todos []Todo, todo Todo, todonumlist []int) ([]Todo, error) {
	if len(todonumlist) == 0 {
		return append(todos, todo), nil
	}
	if len(todos) == 0 || len(todos)-1 < todonumlist[0] {
		return todos, fmt.Errorf("Faild to access index")
	}
	if len(todonumlist) == 1 {
		todos[todonumlist[0]].Children = append(todos[todonumlist[0]].Children, todo)
		return todos, nil
	} else {
		todochildren, err := appendTodo(todos[todonumlist[0]].Children, todo, todonumlist[1:])
		if err != nil {
			return todos, err
		}
		todos[todonumlist[0]].Children = todochildren
		return todos, nil
	}
}

func displayTodo(todos []Todo, tab string, todoid string) {
	for id, todo := range todos {
		if todo.Done {
			continue
		}
		fmt.Print(tab, todoMark)
		if todo.Tag != "" {
			fmt.Printf("%v: %v (%v)", todoid+strconv.Itoa(id), todo.Title, todo.Tag)
		} else {
			fmt.Printf("%v: %v", todoid+strconv.Itoa(id), todo.Title)
		}
		if todo.Memo != "" {
			fmt.Print(memoMark)
		}
		fmt.Println()
		if todo.Children != nil {
			parentid := todoid + strconv.Itoa(id) + "."
			displayTodo(todo.Children, tab+" ", parentid)
		}
	}
}

func displayAllTodo(todos []Todo, tab string, todoid string) {
	for id, todo := range todos {
		if todo.Done {
			fmt.Print(tab, todoDoneMark)
		} else {
			fmt.Print(tab, todoMark)
		}
		if todo.Tag != "" {
			fmt.Printf("%v: %v (%v)", todoid+strconv.Itoa(id), todo.Title, todo.Tag)
		} else {
			fmt.Printf("%v: %v", todoid+strconv.Itoa(id), todo.Title)
		}
		if todo.Memo != "" {
			fmt.Print(memoMark)
		}
		fmt.Println()
		if todo.Children != nil {
			parentid := todoid + strconv.Itoa(id) + "."
			displayAllTodo(todo.Children, tab+" ", parentid)
		}
	}
}

func doneTodo(todos []Todo, todonumlist []int) ([]Todo, error) {
	if len(todos) == 0 || len(todos)-1 < todonumlist[0] {
		return todos, fmt.Errorf("Faild to access index")
	}
	if len(todonumlist) == 1 {
		todos[todonumlist[0]].Done = !todos[todonumlist[0]].Done
		return todos, nil
	} else {
		todochildren, err := doneTodo(todos[todonumlist[0]].Children, todonumlist[1:])
		if err != nil {
			return todos, err
		}
		todos[todonumlist[0]].Children = todochildren
		return todos, nil
	}
}

func displayTagTodo(todos []Todo, tag string, todoid string) {
	for id, todo := range todos {
		if todo.Tag == tag {
			fmt.Printf("%v: %v: %v (%v)", todoid+strconv.Itoa(id), todo.Title, todo.Date, todo.Tag)
			if todo.Memo != "" {
				fmt.Print(memoMark)
			}
			fmt.Println()
		}
		if todo.Children != nil {
			parentid := todoid + strconv.Itoa(id) + "."
			displayTagTodo(todo.Children, tag, parentid)
		}
	}
}

func displayAllTags(todos []Todo, tags mapset.Set) {
	for _, todo := range todos {
		add := tags.Add(todo.Tag)
		if todo.Tag != "" && add {
			fmt.Println(todo.Tag)
		}
		if todo.Children != nil {
			displayAllTags(todo.Children, tags)
		}
	}
}

func cleanAllTodos(todos []Todo) ([]Todo, error) {
	for id, todo := range todos {
		var todos_n []Todo
		if todo.Done == true {
			todos_n = removeTodo(todos, id)
			return todos_n, nil
		}
		if todo.Children != nil {
			todos[id].Children, _ = cleanAllTodos(todos[id].Children)
		}
	}
	return todos, nil
}

func deleteTodos(todos []Todo, todonumlist []int) ([]Todo, error) {
	if len(todos) == 0 || len(todos)-1 < todonumlist[0] {
		return todos, fmt.Errorf("Faild to access index")
	}
	if len(todonumlist) == 1 {
		todos = removeTodo(todos, todonumlist[0])
		return todos, nil
	} else {
		todochildren, err := deleteTodos(todos[todonumlist[0]].Children, todonumlist[1:])
		if err != nil {
			return todos, err
		}
		todos[todonumlist[0]].Children = todochildren
		return todos, nil
	}
}

func searchTodo(todos []Todo, todonumlist []int) (*Todo, error) {
	if len(todos) == 0 || len(todos)-1 < todonumlist[0] {
		return nil, fmt.Errorf("Faild to access index")
	}
	if len(todonumlist) == 1 {
		return &todos[todonumlist[0]], nil
	} else {
		return searchTodo(todos[todonumlist[0]].Children, todonumlist[1:])
	}
}

func removeTodo(todos []Todo, todonum int) []Todo {
	todos = append(todos[:todonum], todos[todonum+1:]...)
	n := make([]Todo, len(todos))
	copy(n, todos)
	return n
}
<|endoftext|>


<commit_before>package main

import (
  "bytes"
  "config"
  "fmt"
  "io"
  _ "io/ioutil"
  "log"
  "net"
  "net/http"
  "net/url"
  "strings"
  "time"
)

/**
http Header Copay
*/
func header_copy(s http.Header, d *http.Header) {
  for hk, _ := range s {
    d.Set(hk, s.Get(hk))
  }
}

func show_error(w http.ResponseWriter, status int, msg []byte) {
  w.WriteHeader(status)
  w.Header().Set("Content-Type", "text/plain; charset=utf-8")
  w.Write(msg)
}

func access_log(w http.ResponseWriter, r *http.Request, query_url string, startTime time.Time) {

  remoteAddr := strings.Split(r.RemoteAddr, ":")[0] //客户端地址
  if remoteAddr == "[" || len(remoteAddr) == 0 {
    remoteAddr = "127.0.0.1"
  }
  r.ParseForm()
  var postValues []string
  for k, _ := range r.PostForm {
    postValues = append(postValues, fmt.Sprintf("%s=%s", k, r.FormValue(k)))
  }
  if len(postValues) == 0 {
    postValues = append(postValues, "-")
  }
  logLine := fmt.Sprintf(`%s [%s] S:"%s %s F:{%s} %s %s" D:"%s" %.05fs`,
    remoteAddr,
    time.Now().Format("2006-01-02 15:04:05.999999999 -0700 MST"),
    r.Method,
    r.RequestURI,
    strings.Join(postValues, "&"),
    r.Proto,
    w.Header().Get("Content-Length"),
    fmt.Sprintf("%s F:{%s}", query_url, strings.Join(postValues, "&")),
    time.Now().Sub(startTime).Seconds(),
  )
  g_env.AccessLog.Println(logLine)
}

func parse_querys(r *http.Request) (raw_query []string) {
  r.ParseForm()
  for k, _ := range r.Form {
    raw_query = append(raw_query, fmt.Sprintf("%s=%s", k, r.Form.Get(k)))
  }
  if len(r.Referer()) > 0 {
    if uri, err := url.Parse(r.Referer()); err == nil {
      for k, _ := range uri.Query() {
        raw_query = append(raw_query, fmt.Sprintf("%s=%s", k, uri.Query().Get(k)))
      }
    }
  }
  return
}

/**
获取目标地址
*/
func target_path(r *http.Request, cfg *config.Config) (t string) {
  if len(cfg.TargetPath) > 0 {
    t = cfg.TargetPath
  } else {
    t = r.URL.Path
  }
  return
}

/**
获取目标服务服务器
*/
func target_server(cfg *config.Config) (s string) {
  if len(cfg.TargetServer) > 0 {
    s = cfg.TargetServer
  } else {
    s = g_config.Default.TargetServer
  }
  return
}

/**
获取查询参数并做替换
*/
func swap_raw_query(r *http.Request, cfg *config.Config) (q string) {
  if len(cfg.TargetParamNameSwap) == 0 {
    q = r.URL.RawQuery
    return
  }
  var tmpSlice []string
  for k, _ := range r.URL.Query() {
    if v, ok := cfg.TargetParamNameSwap[k]; ok {
      tmpSlice = append(tmpSlice, fmt.Sprintf("%s=%s", v, r.URL.Query().Get(k)))
    } else {
      tmpSlice = append(tmpSlice, fmt.Sprintf("%s=%s", k, r.URL.Query().Get(k)))
    }
  }
  q = strings.Join(tmpSlice, "&")
  return
}

func timeout_dialer(conn_timeout int, rw_timeout int) func(net, addr string) (c net.Conn, err error) {
  return func(netw, addr string) (net.Conn, error) {
    conn, err := net.DialTimeout(netw, addr, time.Duration(conn_timeout)*time.Second)
    if err != nil {
      log.Printf("Failed to connect to [%s]. Timed out after %d seconds\n", addr, rw_timeout)
      return nil, err
    }
    conn.SetDeadline(time.Now().Add(time.Duration(rw_timeout) * time.Second))
    return conn, nil
  }
}

func handler(w http.ResponseWriter, r *http.Request) {
  var (
    cfg                                  *config.Config
    cfg_err                              *config.ConfigErr
    conntction_timeout, response_timeout int
    req                                  *http.Request
    err                                  error
    //raw_body                             []byte
    raw_query []string //get ,post params
  )
  defer func() {
    if re := recover(); re != nil {
      g_env.ErrorLog.Println("Recovered in backendServer:", re)
    }
  }()

  defer r.Body.Close()

  start_at := time.Now()
  raw_query = parse_querys(r)

  if err != nil {
    g_env.ErrorLog.Println(req, err)
    show_error(w, http.StatusInternalServerError, []byte("Read Body Error."))
    return
  }

  //获取配置文件
  if cfg, cfg_err = g_config.FindBySourcePathAndParams(raw_query, r.URL.Path); cfg_err != nil {
    cfg = g_config.FindByParamsOrSourcePath(raw_query, r.URL.Path)
  }

  if conntction_timeout = cfg.ConnectionTimeout; conntction_timeout <= 0 {
    conntction_timeout = 15
  }

  if response_timeout = cfg.ResponseTimeout; response_timeout <= 0 {
    response_timeout = 120
  }

  transport := http.Transport{
    Dial: timeout_dialer(conntction_timeout, response_timeout),
    ResponseHeaderTimeout: time.Duration(response_timeout) * time.Second,
    DisableCompression:    false,
    DisableKeepAlives:     true,
    MaxIdleConnsPerHost:   2,
  }

  client := &http.Client{
    Transport: &transport,
  }

  query_url, _ := url.Parse(target_server(cfg) + target_path(r, cfg) + "?" + swap_raw_query(r, cfg))

  switch r.Method {
  case "GET", "HEAD":
    req, err = http.NewRequest(r.Method, query_url.String(), nil)
  case "POST":
    req, err = http.NewRequest(r.Method, query_url.String(), bytes.NewBufferString(strings.Join(raw_query, "&")))
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  default:
    show_error(w, http.StatusMethodNotAllowed, []byte("MethodNotAllowed"))
    return
  }

  req.Close = true

  header_copy(r.Header, &req.Header)

  if err != nil {
    g_env.ErrorLog.Println(err)
    show_error(w, http.StatusInternalServerError, []byte(err.Error()))
    return
  }

  resp, err := client.Do(req)
  if err != nil {
    g_env.ErrorLog.Println(req, err)
    show_error(w, http.StatusInternalServerError, []byte(err.Error()))
    return
  }

  defer resp.Body.Close()

  for hk, _ := range resp.Header {
    w.Header().Set(hk, resp.Header.Get(hk))
  }
	w.Header().Set("Connection","close")
	w.Header().Set("X-Transit","0.0.1")

  w.WriteHeader(resp.StatusCode)
  io.Copy(w, resp.Body)
  access_log(w, r, query_url.String(), start_at)
}

func Run() {
  g_env.ErrorLog.Printf("start@ %s:%d %v \n", g_config.Listen.Host, g_config.Listen.Port, time.Now())
  fmt.Printf("start@ %s:%d %v \n", g_config.Listen.Host, g_config.Listen.Port, time.Now())
  http.HandleFunc("/", handler)
  if err := http.ListenAndServe(fmt.Sprintf("%s:%d", g_config.Listen.Host, g_config.Listen.Port), nil); err != nil {
    log.Fatal(err)
  }
}
<commit_msg>add: defer transport.CloseIdleConnections()<commit_after>package main

import (
  "bytes"
  "config"
  "fmt"
  "io"
  _ "io/ioutil"
  "log"
  "net"
  "net/http"
  "net/url"
  "strings"
  "time"
)

/**
http Header Copay
*/
func header_copy(s http.Header, d *http.Header) {
  for hk, _ := range s {
    d.Set(hk, s.Get(hk))
  }
}

func show_error(w http.ResponseWriter, status int, msg []byte) {
  w.WriteHeader(status)
  w.Header().Set("Content-Type", "text/plain; charset=utf-8")
  w.Write(msg)
}

func access_log(w http.ResponseWriter, r *http.Request, query_url string, startTime time.Time) {

  remoteAddr := strings.Split(r.RemoteAddr, ":")[0] //客户端地址
  if remoteAddr == "[" || len(remoteAddr) == 0 {
    remoteAddr = "127.0.0.1"
  }
  r.ParseForm()
  var postValues []string
  for k, _ := range r.PostForm {
    postValues = append(postValues, fmt.Sprintf("%s=%s", k, r.FormValue(k)))
  }
  if len(postValues) == 0 {
    postValues = append(postValues, "-")
  }
  logLine := fmt.Sprintf(`%s [%s] S:"%s %s F:{%s} %s %s" D:"%s" %.05fs`,
    remoteAddr,
    time.Now().Format("2006-01-02 15:04:05.999999999 -0700 MST"),
    r.Method,
    r.RequestURI,
    strings.Join(postValues, "&"),
    r.Proto,
    w.Header().Get("Content-Length"),
    fmt.Sprintf("%s F:{%s}", query_url, strings.Join(postValues, "&")),
    time.Now().Sub(startTime).Seconds(),
  )
  g_env.AccessLog.Println(logLine)
}

func parse_querys(r *http.Request) (raw_query []string) {
  r.ParseForm()
  for k, _ := range r.Form {
    raw_query = append(raw_query, fmt.Sprintf("%s=%s", k, r.Form.Get(k)))
  }
  if len(r.Referer()) > 0 {
    if uri, err := url.Parse(r.Referer()); err == nil {
      for k, _ := range uri.Query() {
        raw_query = append(raw_query, fmt.Sprintf("%s=%s", k, uri.Query().Get(k)))
      }
    }
  }
  return
}

/**
获取目标地址
*/
func target_path(r *http.Request, cfg *config.Config) (t string) {
  if len(cfg.TargetPath) > 0 {
    t = cfg.TargetPath
  } else {
    t = r.URL.Path
  }
  return
}

/**
获取目标服务服务器
*/
func target_server(cfg *config.Config) (s string) {
  if len(cfg.TargetServer) > 0 {
    s = cfg.TargetServer
  } else {
    s = g_config.Default.TargetServer
  }
  return
}

/**
获取查询参数并做替换
*/
func swap_raw_query(r *http.Request, cfg *config.Config) (q string) {
  if len(cfg.TargetParamNameSwap) == 0 {
    q = r.URL.RawQuery
    return
  }
  var tmpSlice []string
  for k, _ := range r.URL.Query() {
    if v, ok := cfg.TargetParamNameSwap[k]; ok {
      tmpSlice = append(tmpSlice, fmt.Sprintf("%s=%s", v, r.URL.Query().Get(k)))
    } else {
      tmpSlice = append(tmpSlice, fmt.Sprintf("%s=%s", k, r.URL.Query().Get(k)))
    }
  }
  q = strings.Join(tmpSlice, "&")
  return
}

func timeout_dialer(conn_timeout int, rw_timeout int) func(net, addr string) (c net.Conn, err error) {
  return func(netw, addr string) (net.Conn, error) {
    conn, err := net.DialTimeout(netw, addr, time.Duration(conn_timeout)*time.Second)
    if err != nil {
      log.Printf("Failed to connect to [%s]. Timed out after %d seconds\n", addr, rw_timeout)
      return nil, err
    }
    conn.SetDeadline(time.Now().Add(time.Duration(rw_timeout) * time.Second))
    return conn, nil
  }
}

func handler(w http.ResponseWriter, r *http.Request) {
  var (
    cfg                                  *config.Config
    cfg_err                              *config.ConfigErr
    conntction_timeout, response_timeout int
    req                                  *http.Request
    err                                  error
    //raw_body                             []byte
    raw_query []string //get ,post params
  )
  defer func() {
    if re := recover(); re != nil {
      g_env.ErrorLog.Println("Recovered in backendServer:", re)
    }
  }()

  defer r.Body.Close()

  start_at := time.Now()
  raw_query = parse_querys(r)

  if err != nil {
    g_env.ErrorLog.Println(req, err)
    show_error(w, http.StatusInternalServerError, []byte("Read Body Error."))
    return
  }

  //获取配置文件
  if cfg, cfg_err = g_config.FindBySourcePathAndParams(raw_query, r.URL.Path); cfg_err != nil {
    cfg = g_config.FindByParamsOrSourcePath(raw_query, r.URL.Path)
  }

  if conntction_timeout = cfg.ConnectionTimeout; conntction_timeout <= 0 {
    conntction_timeout = 15
  }

  if response_timeout = cfg.ResponseTimeout; response_timeout <= 0 {
    response_timeout = 120
  }

  transport := http.Transport{
    Dial: timeout_dialer(conntction_timeout, response_timeout),
    ResponseHeaderTimeout: time.Duration(response_timeout) * time.Second,
    DisableCompression:    false,
    DisableKeepAlives:     true,
    MaxIdleConnsPerHost:   0,
  }
  defer transport.CloseIdleConnections()

  client := &http.Client{
    Transport: &transport,
  }

  query_url, _ := url.Parse(target_server(cfg) + target_path(r, cfg) + "?" + swap_raw_query(r, cfg))

  switch r.Method {
  case "GET", "HEAD":
    req, err = http.NewRequest(r.Method, query_url.String(), nil)
  case "POST":
    req, err = http.NewRequest(r.Method, query_url.String(), bytes.NewBufferString(strings.Join(raw_query, "&")))
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  default:
    show_error(w, http.StatusMethodNotAllowed, []byte("MethodNotAllowed"))
    return
  }
  defer func() { req.Close = true }()

  header_copy(r.Header, &req.Header)

  if err != nil {
    g_env.ErrorLog.Println(err)
    show_error(w, http.StatusInternalServerError, []byte(err.Error()))
    return
  }

  resp, err := client.Do(req)
  if err != nil {
    g_env.ErrorLog.Println(req, err)
    show_error(w, http.StatusInternalServerError, []byte(err.Error()))
    return
  }

  defer resp.Body.Close()
  for hk, _ := range resp.Header {
    w.Header().Set(hk, resp.Header.Get(hk))
  }

  w.WriteHeader(resp.StatusCode)
  io.Copy(w, resp.Body)
  access_log(w, r, query_url.String(), start_at)
}

func Run() {
  g_env.ErrorLog.Printf("start@ %s:%d %v \n", g_config.Listen.Host, g_config.Listen.Port, time.Now())
  fmt.Printf("start@ %s:%d %v \n", g_config.Listen.Host, g_config.Listen.Port, time.Now())
  http.HandleFunc("/", handler)
  if err := http.ListenAndServe(fmt.Sprintf("%s:%d", g_config.Listen.Host, g_config.Listen.Port), nil); err != nil {
    log.Fatal(err)
  }
}
<|endoftext|>


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

package main

import (
	"html/template"
	"io/ioutil"
	"net/http"
)

type Page struct {
	Title string
	Body  []byte
}

func (p *Page) save() error {
	filename := p.Title + ".txt"
	return ioutil.WriteFile(filename, p.Body, 0600)
}

func loadPage(title string) (*Page, error) {
	filename := title + ".txt"
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return &Page{Title: title, Body: body}, nil
}

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
	t, _ := template.ParseFiles(tmpl + ".html")
	t.Execute(w, p)
}

func viewHandler(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len("/view/"):]
	p, _ := loadPage(title)
	renderTemplate(w, "view", p)
}

func editHandler(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len("/edit/"):]
	p, err := loadPage(title)
	if err != nil {
		p = &Page{Title: title}
	}
	renderTemplate(w, "edit", p)
}

func main() {
	http.HandleFunc("/view/", viewHandler)
	http.HandleFunc("/edit/", editHandler)
//	http.HandleFunc("/save/", saveHandler)
	http.ListenAndServe(":8080", nil)
}
<commit_msg>Redirect for non-existing page<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"html/template"
	"io/ioutil"
	"net/http"
)

type Page struct {
	Title string
	Body  []byte
}

func (p *Page) save() error {
	filename := p.Title + ".txt"
	return ioutil.WriteFile(filename, p.Body, 0600)
}

func loadPage(title string) (*Page, error) {
	filename := title + ".txt"
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return &Page{Title: title, Body: body}, nil
}

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
	t, _ := template.ParseFiles(tmpl + ".html")
	t.Execute(w, p)
}

func viewHandler(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len("/view/"):]
	p, err := loadPage(title)
	if err != nil {
		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
		return
	}
	renderTemplate(w, "view", p)
}

func editHandler(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len("/edit/"):]
	p, err := loadPage(title)
	if err != nil {
		p = &Page{Title: title}
	}
	renderTemplate(w, "edit", p)
}

func main() {
	http.HandleFunc("/view/", viewHandler)
	http.HandleFunc("/edit/", editHandler)
//	http.HandleFunc("/save/", saveHandler)
	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


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

package main

import (
	"html/template"
	"io/ioutil"
	"net/http"
	"regexp"
)

type Page struct {
	Title string
	Body  []byte
}

func (p *Page) save() error {
	filename := p.Title + ".txt"
	return ioutil.WriteFile(filename, p.Body, 0600)
}

func loadPage(title string) (*Page, error) {
	filename := title + ".txt"
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return &Page{Title: title, Body: body}, nil
}

func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
	p, err := loadPage(title)
	if err != nil {
		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
		return
	}
	renderTemplate(w, "view", p)
}

func editHandler(w http.ResponseWriter, r *http.Request, title string) {
	p, err := loadPage(title)
	if err != nil {
		p = &Page{Title: title}
	}
	renderTemplate(w, "edit", p)
}

func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
	body := r.FormValue("body")
	p := &Page{Title: title, Body: []byte(body)}
	err := p.save()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/view/"+title, http.StatusFound)
}

var templates = template.Must(template.ParseFiles("edit.html", "view.html"))

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
	err := templates.ExecuteTemplate(w, tmpl+".html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$")

func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		m := validPath.FindStringSubmatch(r.URL.Path)
		if m == nil {
			http.NotFound(w, r)
			return
		}
		fn(w, r, m[2])
	}
}

func main() {
	http.HandleFunc("/view/", makeHandler(viewHandler))
	http.HandleFunc("/edit/", makeHandler(editHandler))
	http.HandleFunc("/save/", makeHandler(saveHandler))
	http.ListenAndServe(":8080", nil)
}
<commit_msg>Added blackfriday for processing markdown.<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"os/exec"
	"regexp"

	"github.com/russross/blackfriday"
)

type Page struct {
	Title string
	Body  []byte
}

func (p *Page) save() error {
	filename := p.Title + ".txt"
	return ioutil.WriteFile(filename, p.Body, 0600)
}

func loadPage(title string) (*Page, error) {
	filename := title + ".txt"
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	html := blackfriday.MarkdownCommon(body)
	fmt.Println(string(html)) // TODO remove
	return &Page{Title: title, Body: html}, nil
}

func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
	p, err := loadPage(title)
	if err != nil {
		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
		return
	}
	renderTemplate(w, "view", p)
}

func editHandler(w http.ResponseWriter, r *http.Request, title string) {
	p, err := loadPage(title)
	if err != nil {
		p = &Page{Title: title}
	}
	renderTemplate(w, "edit", p)
}

func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
	body := r.FormValue("body")
	p := &Page{Title: title, Body: []byte(body)}
	err := p.save()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/view/"+title, http.StatusFound)
}

var templates = template.Must(template.ParseFiles("edit.html", "view.html"))

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
	// TODO http://stackoverflow.com/questions/18589973/html-template-how-to-get-javascript-json-escaping-without-script-tag
	err := templates.ExecuteTemplate(w, tmpl+".html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$")

func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		m := validPath.FindStringSubmatch(r.URL.Path)
		if m == nil {
			http.NotFound(w, r)
			return
		}
		fn(w, r, m[2])
	}
}

func main() {
	var server = "localhost:8080"

	browser := exec.Command(`C:\Windows\System32\rundll32.exe`, "url.dll,FileProtocolHandler", "http://"+server+"/view/Home")
	if err := browser.Start(); err != nil {
		panic(err)
	}

	http.HandleFunc("/view/", makeHandler(viewHandler))
	http.HandleFunc("/edit/", makeHandler(editHandler))
	http.HandleFunc("/save/", makeHandler(saveHandler))
	http.ListenAndServe(server, nil)
}
<|endoftext|>


<commit_before>// Package work eases concurrency patterns by providing often used helpers.
package work

import (
	"runtime"
	"sync"
	"sync/atomic"
)

// NumRoutines defines the maximum number of goroutines to be used per helper.
// It defaults to GOMAXPROCS.
var NumRoutines int

func init() {
	NumRoutines = runtime.GOMAXPROCS(0)
}

// do spawns workers with index 0 to n, limiting their numbers by NumRoutines.
func do(n int, worker func(int)) {
	var wg sync.WaitGroup
	if n <= NumRoutines {
		// spawn as many goroutines as number of workers
		wg.Add(n)
		for i := 0; i < n; i++ {
			go func(idx int) {
				worker(idx)
				wg.Done()
			}(i)
		}
		wg.Wait()
		return
	}

	// spawn the maximum number of goroutines
	wg.Add(NumRoutines)
	for i := 0; i < NumRoutines; i++ {
		go func(idx int) {
			for ; idx < n; idx += NumRoutines {
				worker(idx)
			}
			wg.Done()
		}(i)
	}
	wg.Wait()
}

// doWithError spawns workers with index 0 to n, limiting their numbers by NumRoutines.
// Similar to do but with error handling.
// The first error encountered aborts all processing and is then returned.
func doWithError(n int, worker func(int) error) error {
	var (
		errv atomic.Value // worker error
		wg   sync.WaitGroup
	)

	if n <= NumRoutines {
		// spawn as many goroutines as number of workers
		wg.Add(n)
		for i := 0; i < n; i++ {
			go func(idx int) {
				if errv.Load() == nil {
					if err := worker(idx); err != nil {
						errv.Store(err)
					}
				}
				wg.Done()
			}(i)
		}
		wg.Wait()

		if err := errv.Load(); err != nil {
			return err.(error)
		}
		return nil
	}

	// spawn the maximum number of goroutines
	wg.Add(NumRoutines)
	for i := 0; i < NumRoutines; i++ {
		go func(idx int) {
			for ; idx < n && errv.Load() == nil; idx += NumRoutines {
				if err := worker(idx); err != nil {
					errv.Store(err)
					break
				}
			}
			wg.Done()
		}(i)
	}
	wg.Wait()

	if err := errv.Load(); err != nil {
		return err.(error)
	}
	return nil
}

// Do spawns workers with index 0 to n, limiting their numbers by NumRoutines.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func Do(n int, worker, finalizer func(idx int)) {
	switch n {
	case 0:
		return
	case 1:
		worker(0)
		if finalizer != nil {
			finalizer(0)
		}
		return
	}

	if finalizer == nil {
		do(n, worker)
		return
	}

	var (
		donec = make(chan struct{}, NumRoutines) // worker throttling
		workc = make(chan int)                   // results from workers
		wg    sync.WaitGroup
	)

	// initialize the go routine managing the results and
	// dispatching to the finalizer in order
	wg.Add(1)
	go func() {
		defer wg.Done()
		// buffer holds results that cannot be finalized yet.
		buffer := make(map[int]struct{})
		pos := 0
		for idx := range workc {
			if pos < idx {
				// out of order result
				buffer[idx] = struct{}{}
				continue
			}
			finalizer(idx)
			// process the results that were already received
			// ensuring they are processed in order
			for {
				pos++
				if pos == n {
					return
				}
				if _, ok := buffer[pos]; !ok {
					// no more result for the current position
					break
				}
				delete(buffer, pos)
				finalizer(pos)
			}
		}
	}()

	// process all items in the list, with a concurrency of NumRoutines
	for i := 0; i < n; i++ {
		wg.Add(1)
		go func(idx int) {
			worker(idx)
			workc <- idx
			<-donec
			wg.Done()
		}(i)
		// throttling
		donec <- struct{}{}
	}

	// done when all go routines are
	wg.Wait()

	return
}

// DoWithError spawns workers with index 0 to n, limiting their numbers by NumRoutines.
// Similar to Do but with error handling.
// The first error encountered aborts all processing and is then returned.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func DoWithError(n int, worker, finalizer func(idx int) error) error {
	switch n {
	case 0:
		return nil
	case 1:
		if err := worker(0); err != nil {
			return err
		}
		if finalizer != nil {
			return finalizer(0)
		}
		return nil
	}

	if finalizer == nil {
		return doWithError(n, worker)
	}

	var (
		errv    atomic.Value                       // worker/finalizer error
		donec   = make(chan struct{}, NumRoutines) // worker done channel
		workc   = make(chan int)                   // results from workers
		wg, wgf sync.WaitGroup
	)

	// initialize the go routine managing the results and
	// dispatching to the finalizer in order
	wgf.Add(1)
	go func() {
		// buffer holds results that cannot be finalized yet.
		buffer := make(map[int]struct{})
		// current index to be processed
		pos := 0
		// the finalizer routine exits when the channel is closed
		// or when it has completed all work
		for idx := range workc {
			if errv.Load() != nil {
				// upon error, wait for the workers to complete
				// which will close the channel
				continue
			}
			if pos < idx {
				// out of order result
				buffer[idx] = struct{}{}
				continue
			}
			if err := finalizer(pos); err != nil {
				errv.Store(err)
				continue
			}
			// process the results that were already received
			// ensuring they are processed in order
			for {
				pos++
				if pos == n || errv.Load() != nil {
					break
				}
				if _, ok := buffer[pos]; !ok {
					// no more result for the current position
					break
				}
				delete(buffer, pos)
				if err := finalizer(pos); err != nil {
					errv.Store(err)
					break
				}
			}
		}
		wgf.Done()
	}()

	// process all items in the list, with a concurrency of NumRoutines
	for i := 0; i < n; i++ {
		wg.Add(1)
		go func(idx int) {
			if errv.Load() == nil {
				if err := worker(idx); err != nil {
					errv.Store(err)
				} else {
					workc <- idx
				}
			}
			<-donec
			wg.Done()
		}(i)
		// throttling
		donec <- struct{}{}
		if errv.Load() != nil {
			break
		}
	}

	// wait for workers
	wg.Wait()
	// since workc is blocking, the finalizer has received all items
	// so we can safely close it and shutdown the finalizer routine
	close(workc)

	// wait for finalizer
	wgf.Wait()

	if err := errv.Load(); err != nil {
		return err.(error)
	}
	return nil
}
<commit_msg>fixed typo in comments<commit_after>// Package work eases concurrency patterns by providing often used helpers.
package work

import (
	"runtime"
	"sync"
	"sync/atomic"
)

// NumRoutines defines the maximum number of goroutines to be used per helper.
// It defaults to GOMAXPROCS.
var NumRoutines int

func init() {
	NumRoutines = runtime.GOMAXPROCS(0)
}

// do spawns workers with index 0 to n-1, limiting their numbers by NumRoutines.
func do(n int, worker func(int)) {
	var wg sync.WaitGroup
	if n <= NumRoutines {
		// spawn as many goroutines as number of workers
		wg.Add(n)
		for i := 0; i < n; i++ {
			go func(idx int) {
				worker(idx)
				wg.Done()
			}(i)
		}
		wg.Wait()
		return
	}

	// spawn the maximum number of goroutines
	wg.Add(NumRoutines)
	for i := 0; i < NumRoutines; i++ {
		go func(idx int) {
			for ; idx < n; idx += NumRoutines {
				worker(idx)
			}
			wg.Done()
		}(i)
	}
	wg.Wait()
}

// doWithError spawns workers with index 0 to n-1, limiting their numbers by NumRoutines.
// Similar to do but with error handling.
// The first error encountered aborts all processing and is then returned.
func doWithError(n int, worker func(int) error) error {
	var (
		errv atomic.Value // worker error
		wg   sync.WaitGroup
	)

	if n <= NumRoutines {
		// spawn as many goroutines as number of workers
		wg.Add(n)
		for i := 0; i < n; i++ {
			go func(idx int) {
				if errv.Load() == nil {
					if err := worker(idx); err != nil {
						errv.Store(err)
					}
				}
				wg.Done()
			}(i)
		}
		wg.Wait()

		if err := errv.Load(); err != nil {
			return err.(error)
		}
		return nil
	}

	// spawn the maximum number of goroutines
	wg.Add(NumRoutines)
	for i := 0; i < NumRoutines; i++ {
		go func(idx int) {
			for ; idx < n && errv.Load() == nil; idx += NumRoutines {
				if err := worker(idx); err != nil {
					errv.Store(err)
					break
				}
			}
			wg.Done()
		}(i)
	}
	wg.Wait()

	if err := errv.Load(); err != nil {
		return err.(error)
	}
	return nil
}

// Do spawns workers with index 0 to n-1, limiting their numbers by NumRoutines.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func Do(n int, worker, finalizer func(idx int)) {
	switch n {
	case 0:
		return
	case 1:
		worker(0)
		if finalizer != nil {
			finalizer(0)
		}
		return
	}

	if finalizer == nil {
		do(n, worker)
		return
	}

	var (
		donec = make(chan struct{}, NumRoutines) // worker throttling
		workc = make(chan int)                   // results from workers
		wg    sync.WaitGroup
	)

	// initialize the go routine managing the results and
	// dispatching to the finalizer in order
	wg.Add(1)
	go func() {
		defer wg.Done()
		// buffer holds results that cannot be finalized yet.
		buffer := make(map[int]struct{})
		pos := 0
		for idx := range workc {
			if pos < idx {
				// out of order result
				buffer[idx] = struct{}{}
				continue
			}
			finalizer(idx)
			// process the results that were already received
			// ensuring they are processed in order
			for {
				pos++
				if pos == n {
					return
				}
				if _, ok := buffer[pos]; !ok {
					// no more result for the current position
					break
				}
				delete(buffer, pos)
				finalizer(pos)
			}
		}
	}()

	// process all items in the list, with a concurrency of NumRoutines
	for i := 0; i < n; i++ {
		wg.Add(1)
		go func(idx int) {
			worker(idx)
			workc <- idx
			<-donec
			wg.Done()
		}(i)
		// throttling
		donec <- struct{}{}
	}

	// done when all go routines are
	wg.Wait()

	return
}

// DoWithError spawns workers with index 0 to n-1, limiting their numbers by NumRoutines.
// Similar to Do but with error handling.
// The first error encountered aborts all processing and is then returned.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func DoWithError(n int, worker, finalizer func(idx int) error) error {
	switch n {
	case 0:
		return nil
	case 1:
		if err := worker(0); err != nil {
			return err
		}
		if finalizer != nil {
			return finalizer(0)
		}
		return nil
	}

	if finalizer == nil {
		return doWithError(n, worker)
	}

	var (
		errv    atomic.Value                       // worker/finalizer error
		donec   = make(chan struct{}, NumRoutines) // worker done channel
		workc   = make(chan int)                   // results from workers
		wg, wgf sync.WaitGroup
	)

	// initialize the go routine managing the results and
	// dispatching to the finalizer in order
	wgf.Add(1)
	go func() {
		// buffer holds results that cannot be finalized yet.
		buffer := make(map[int]struct{})
		// current index to be processed
		pos := 0
		// the finalizer routine exits when the channel is closed
		// or when it has completed all work
		for idx := range workc {
			if errv.Load() != nil {
				// upon error, wait for the workers to complete
				// which will close the channel
				continue
			}
			if pos < idx {
				// out of order result
				buffer[idx] = struct{}{}
				continue
			}
			if err := finalizer(pos); err != nil {
				errv.Store(err)
				continue
			}
			// process the results that were already received
			// ensuring they are processed in order
			for {
				pos++
				if pos == n || errv.Load() != nil {
					break
				}
				if _, ok := buffer[pos]; !ok {
					// no more result for the current position
					break
				}
				delete(buffer, pos)
				if err := finalizer(pos); err != nil {
					errv.Store(err)
					break
				}
			}
		}
		wgf.Done()
	}()

	// process all items in the list, with a concurrency of NumRoutines
	for i := 0; i < n; i++ {
		wg.Add(1)
		go func(idx int) {
			if errv.Load() == nil {
				if err := worker(idx); err != nil {
					errv.Store(err)
				} else {
					workc <- idx
				}
			}
			<-donec
			wg.Done()
		}(i)
		// throttling
		donec <- struct{}{}
		if errv.Load() != nil {
			break
		}
	}

	// wait for workers
	wg.Wait()
	// since workc is blocking, the finalizer has received all items
	// so we can safely close it and shutdown the finalizer routine
	close(workc)

	// wait for finalizer
	wgf.Wait()

	if err := errv.Load(); err != nil {
		return err.(error)
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Ardan Studios. All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE handle.

// Package work manages a pool of routines to perform work.
package work

import (
	"errors"
	"fmt"
	"log"
	"sync"
	"sync/atomic"
	"time"
)

const (
	addRoutine = 1
	rmvRoutine = 3
)

// ErrorInvalidMinRoutines is the error for the invalid minRoutine parameter.
var ErrorInvalidMinRoutines = errors.New("Invalid minimum number of routines")

// ErrorInvalidIdleTime is the error for the invalid idle time parameter.
var ErrorInvalidIdleTime = errors.New("Invalid duration for idle time")

// ErrorInvalidStatTime is the error for the invalid stat time parameter.
var ErrorInvalidStatTime = errors.New("Invalid duration for stat time")

// Worker must be implemented by types that want to use
// this worker processes.
type Worker interface {
	Work(id int)
}

// Work provides a pool of routines that can execute any Worker
// tasks that are submitted.
type Work struct {
	minRoutines int            // Minumum number of routines always in the pool.
	idleTime    time.Duration  // Time for routines to die on idle.
	statTime    time.Duration  // Time to display stats.
	counter     int            // Maintains a running total number of routines ever created.
	tasks       chan Worker    // Unbuffered channel that work is sent into.
	control     chan int       // Unbuffered channel that work for the manager is send into.
	kill        chan struct{}  // Unbuffered channel to signal for a goroutine to die.
	shutdown    chan struct{}  // Closed when the Work pool is being shutdown.
	wg          sync.WaitGroup // Manages the number of routines for shutdown.
	routines    int64          // Number of routines
	active      int64          // Active number of routines in the work pool.
	pending     int64          // Pending number of routines waiting to submit work.
}

// manager controls changes to the work pool including stats
// and shutting down.
func (w *Work) manager() {
	w.wg.Add(1)

	go func() {
		log.Println("Work : manager : Started")

		// Create a timer to run stats.
		var stats <-chan time.Time
		timer := time.NewTimer(w.statTime)
		stats = timer.C

		for {
			select {
			case <-w.shutdown:
				// Capture the current number of routines.
				routines := int(atomic.LoadInt64(&w.routines))

				// Send a kill to all the existing routines.
				for i := 0; i < routines; i++ {
					w.kill <- struct{}{}
				}

				// Decrement the waitgroup and kill the manager.
				w.wg.Done()
				return

			case c := <-w.control:
				switch c {
				case addRoutine:
					log.Println("Work : manager : Info : Add Routine")

					// Capture a unique id.
					w.counter++

					// Create the routine.
					go w.work(w.counter)

				case rmvRoutine:
					log.Println("Work : manager : Info : Remove Routine")

					// Capture the number of routines.
					routines := int(atomic.LoadInt64(&w.routines))

					// Are there routines to remove.
					if routines <= w.minRoutines {
						break
					}

					// Send a kill signal to remove a routine.
					w.kill <- struct{}{}
				}

			case <-stats:
				// Capture the stats.
				routines := atomic.LoadInt64(&w.routines)
				pending := atomic.LoadInt64(&w.pending)
				active := atomic.LoadInt64(&w.active)

				// Display the stats.
				fmt.Printf("Work : manager : Stats : G[%d] P[%d] A[%d]\n", routines, pending, active)

				// Reset the clock.
				timer.Reset(w.statTime)
			}
		}
	}()
}

// New creates a new Worker.
func New(minRoutines int, idleTime time.Duration, statTime time.Duration) (*Work, error) {
	if minRoutines < 0 {
		return nil, ErrorInvalidMinRoutines
	}

	if idleTime < time.Minute {
		return nil, ErrorInvalidIdleTime
	}

	if statTime < time.Millisecond {
		return nil, ErrorInvalidStatTime
	}

	w := Work{
		minRoutines: minRoutines,
		idleTime:    idleTime,
		statTime:    statTime,
		tasks:       make(chan Worker),
		control:     make(chan int),
		kill:        make(chan struct{}),
		shutdown:    make(chan struct{}),
	}

	// Start the manager.
	w.manager()

	// Add the routines.
	w.Add(minRoutines)

	return &w, nil
}

// Add creates routines to process work or sets a count for
// routines to terminate.
func (w *Work) Add(routines int) {
	if routines == 0 {
		return
	}

	cmd := addRoutine
	if routines < 0 {
		routines = routines * -1
		cmd = rmvRoutine
	}

	for i := 0; i < routines; i++ {
		w.control <- cmd
	}
}

// work performs the users work and keeps stats.
func (w *Work) work(id int) {
	// Increment the counts.
	w.wg.Add(1)
	routine := int(atomic.AddInt64(&w.routines, 1))

	// Create a timer to track idle time.
	var idle <-chan time.Time
	var timer *time.Timer

	// Don't set the time until we are above min routines.
	if routine > w.minRoutines {
		timer = time.NewTimer(w.idleTime)
		idle = timer.C
	}

done:
	for {
		select {
		case t := <-w.tasks:
			atomic.AddInt64(&w.active, 1)
			{
				// Perform the work.
				t.Work(id)
			}
			atomic.AddInt64(&w.active, -1)

		case <-w.kill:
			break done

		case <-idle:
			break done
		}

		// If this goroutine can die on idle time
		// then reset the timer.
		if timer != nil {
			timer.Reset(w.idleTime)
		}
	}

	// Decrement the counts.
	atomic.AddInt64(&w.routines, -1)
	w.wg.Done()

	log.Println("Work : gr : Info : Shutdown")
}

// Run wait for the goroutine pool to take the work
// to be executed.
func (w *Work) Run(work Worker) {
	atomic.AddInt64(&w.pending, 1)
	{
		w.tasks <- work
	}
	atomic.AddInt64(&w.pending, -1)
}

// Shutdown waits for all the workers to finish.
func (w *Work) Shutdown() {
	close(w.shutdown)
	w.wg.Wait()
}
<commit_msg>Adjusted race conditions with reading count values.<commit_after>// Copyright 2014 Ardan Studios. All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE handle.

// Package work manages a pool of routines to perform work.
package work

import (
	"errors"
	"fmt"
	"log"
	"sync"
	"sync/atomic"
	"time"
)

const (
	addRoutine = 1
	rmvRoutine = 3
)

// ErrorInvalidMinRoutines is the error for the invalid minRoutine parameter.
var ErrorInvalidMinRoutines = errors.New("Invalid minimum number of routines")

// ErrorInvalidIdleTime is the error for the invalid idle time parameter.
var ErrorInvalidIdleTime = errors.New("Invalid duration for idle time")

// ErrorInvalidStatTime is the error for the invalid stat time parameter.
var ErrorInvalidStatTime = errors.New("Invalid duration for stat time")

// Worker must be implemented by types that want to use
// this worker processes.
type Worker interface {
	Work(id int)
}

// Work provides a pool of routines that can execute any Worker
// tasks that are submitted.
type Work struct {
	minRoutines int            // Minumum number of routines always in the pool.
	idleTime    time.Duration  // Time for routines to die on idle.
	statTime    time.Duration  // Time to display stats.
	counter     int            // Maintains a running total number of routines ever created.
	tasks       chan Worker    // Unbuffered channel that work is sent into.
	control     chan int       // Unbuffered channel that work for the manager is send into.
	kill        chan struct{}  // Unbuffered channel to signal for a goroutine to die.
	shutdown    chan struct{}  // Closed when the Work pool is being shutdown.
	wg          sync.WaitGroup // Manages the number of routines for shutdown.
	routines    int64          // Number of routines
	active      int64          // Active number of routines in the work pool.
	pending     int64          // Pending number of routines waiting to submit work.
}

// manager controls changes to the work pool including stats
// and shutting down.
func (w *Work) manager() {
	w.wg.Add(1)

	go func() {
		log.Println("Work : manager : Started")

		// Create a timer to run stats.
		var stats <-chan time.Time
		timer := time.NewTimer(w.statTime)
		stats = timer.C

		for {
			select {
			case <-w.shutdown:
				// Capture the current number of routines.
				routines := int(atomic.LoadInt64(&w.routines))

				// Send a kill to all the existing routines.
				for i := 0; i < routines; i++ {
					w.kill <- struct{}{}
				}

				// Decrement the waitgroup and kill the manager.
				w.wg.Done()
				return

			case c := <-w.control:
				switch c {
				case addRoutine:
					log.Println("Work : manager : Info : Add Routine")

					// Capture a unique id.
					w.counter++

					// Add to the counts.
					w.wg.Add(1)
					atomic.AddInt64(&w.routines, 1)

					// Create the routine.
					go w.work(w.counter)

				case rmvRoutine:
					log.Println("Work : manager : Info : Remove Routine")

					// Capture the number of routines.
					routines := int(atomic.LoadInt64(&w.routines))

					// Are there routines to remove.
					if routines <= w.minRoutines {
						break
					}

					// Send a kill signal to remove a routine.
					w.kill <- struct{}{}
				}

			case <-stats:
				// Capture the stats.
				routines := atomic.LoadInt64(&w.routines)
				pending := atomic.LoadInt64(&w.pending)
				active := atomic.LoadInt64(&w.active)

				// Display the stats.
				fmt.Printf("Work : manager : Stats : G[%d] P[%d] A[%d]\n", routines, pending, active)

				// Reset the clock.
				timer.Reset(w.statTime)
			}
		}
	}()
}

// New creates a new Worker.
func New(minRoutines int, idleTime time.Duration, statTime time.Duration) (*Work, error) {
	if minRoutines < 0 {
		return nil, ErrorInvalidMinRoutines
	}

	if idleTime < time.Minute {
		return nil, ErrorInvalidIdleTime
	}

	if statTime < time.Millisecond {
		return nil, ErrorInvalidStatTime
	}

	w := Work{
		minRoutines: minRoutines,
		idleTime:    idleTime,
		statTime:    statTime,
		tasks:       make(chan Worker),
		control:     make(chan int),
		kill:        make(chan struct{}),
		shutdown:    make(chan struct{}),
	}

	// Start the manager.
	w.manager()

	// Add the routines.
	w.Add(minRoutines)

	return &w, nil
}

// Add creates routines to process work or sets a count for
// routines to terminate.
func (w *Work) Add(routines int) {
	if routines == 0 {
		return
	}

	cmd := addRoutine
	if routines < 0 {
		routines = routines * -1
		cmd = rmvRoutine
	}

	for i := 0; i < routines; i++ {
		w.control <- cmd
	}
}

// work performs the users work and keeps stats.
func (w *Work) work(id int) {
	// Create a timer to track idle time.
	var idle <-chan time.Time
	var timer *time.Timer

	// Set the timer for routines about the min mark.
	if id > w.minRoutines {
		timer = time.NewTimer(w.idleTime)
		idle = timer.C
	}

done:
	for {
		select {
		case t := <-w.tasks:
			atomic.AddInt64(&w.active, 1)
			{
				// Perform the work.
				t.Work(id)
			}
			atomic.AddInt64(&w.active, -1)

		case <-w.kill:
			break done

		case <-idle:
			break done
		}

		// If this goroutine can die on idle time
		// then reset the timer.
		if timer != nil {
			timer.Reset(w.idleTime)
		}
	}

	// Decrement the counts.
	atomic.AddInt64(&w.routines, -1)
	w.wg.Done()

	log.Println("Work : gr : Info : Shutdown")
}

// Run wait for the goroutine pool to take the work
// to be executed.
func (w *Work) Run(work Worker) {
	atomic.AddInt64(&w.pending, 1)
	{
		w.tasks <- work
	}
	atomic.AddInt64(&w.pending, -1)
}

// Shutdown waits for all the workers to finish.
func (w *Work) Shutdown() {
	close(w.shutdown)
	w.wg.Wait()
}
<|endoftext|>