<commit_before>package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"runtime/debug"
	"strings"
	"time"

	"github.com/calmh/ini"
	"github.com/calmh/syncthing/discover"
	"github.com/calmh/syncthing/protocol"
)

const BlockSize = 128 * 1024

var cfg Configuration
var Version = "unknown-dev"

var (
	myID string
)

var (
	showVersion bool
	confDir     string
	verbose     bool
)

const (
	usage      = "syncthing [options]"
	extraUsage = `The following enviroment variables are interpreted by syncthing:

 STNORESTART  Do not attempt to restart when requested to, instead just exit.
              Set this variable when running under a service manager such as
              runit, launchd, etc.

 STPROFILER   Set to a listen address such as "127.0.0.1:9090" to start the
              profiler with HTTP access.

 STTRACE      A comma separated string of facilities to trace. The valid
              facility strings:
              - "discover" (the node discovery package)
              - "files"    (file set store)
              - "idx"      (index sending and receiving)
              - "mc"       (multicast beacon)
              - "need"     (file need calculations)
              - "net"      (connecting and disconnecting, network messages)
              - "pull"     (file pull activity)
              - "scanner"  (the file change scanner)
              `
)

func main() {
	flag.StringVar(&confDir, "home", getDefaultConfDir(), "Set configuration directory")
	flag.BoolVar(&showVersion, "version", false, "Show version")
	flag.BoolVar(&verbose, "v", false, "Be more verbose")
	flag.Usage = usageFor(flag.CommandLine, usage, extraUsage)
	flag.Parse()

	if len(os.Getenv("STRESTART")) > 0 {
		// Give the parent process time to exit and release sockets etc.
		time.Sleep(1 * time.Second)
	}

	if showVersion {
		fmt.Println(Version)
		os.Exit(0)
	}

	if len(os.Getenv("GOGC")) == 0 {
		debug.SetGCPercent(25)
	}

	if len(os.Getenv("GOMAXPROCS")) == 0 {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	confDir = expandTilde(confDir)

	// Ensure that our home directory exists and that we have a certificate and key.

	ensureDir(confDir, 0700)
	cert, err := loadCert(confDir)
	if err != nil {
		newCertificate(confDir)
		cert, err = loadCert(confDir)
		fatalErr(err)
	}

	myID = string(certID(cert.Certificate[0]))
	log.SetPrefix("[" + myID[0:5] + "] ")
	logger.SetPrefix("[" + myID[0:5] + "] ")

	infoln("Version", Version)
	infoln("My ID:", myID)

	// Prepare to be able to save configuration

	cfgFile := filepath.Join(confDir, "config.xml")
	go saveConfigLoop(cfgFile)

	// Load the configuration file, if it exists.
	// If it does not, create a template.

	cf, err := os.Open(cfgFile)
	if err == nil {
		// Read config.xml
		cfg, err = readConfigXML(cf)
		if err != nil {
			fatalln(err)
		}
		cf.Close()
	} else {
		// No config.xml, let's try the old syncthing.ini
		iniFile := filepath.Join(confDir, "syncthing.ini")
		cf, err := os.Open(iniFile)
		if err == nil {
			infoln("Migrating syncthing.ini to config.xml")
			iniCfg := ini.Parse(cf)
			cf.Close()
			Rename(iniFile, filepath.Join(confDir, "migrated_syncthing.ini"))

			cfg, _ = readConfigXML(nil)
			cfg.Repositories = []RepositoryConfiguration{
				{Directory: iniCfg.Get("repository", "dir")},
			}
			readConfigINI(iniCfg.OptionMap("settings"), &cfg.Options)
			for name, addrs := range iniCfg.OptionMap("nodes") {
				n := NodeConfiguration{
					NodeID:    name,
					Addresses: strings.Fields(addrs),
				}
				cfg.Repositories[0].Nodes = append(cfg.Repositories[0].Nodes, n)
			}

			saveConfig()
		}
	}

	if len(cfg.Repositories) == 0 {
		infoln("No config file; starting with empty defaults")

		cfg, err = readConfigXML(nil)
		cfg.Repositories = []RepositoryConfiguration{
			{
				ID:        "default",
				Directory: filepath.Join(getHomeDir(), "Sync"),
				Nodes: []NodeConfiguration{
					{NodeID: myID, Addresses: []string{"dynamic"}},
				},
			},
		}

		saveConfig()
		infof("Edit %s to taste or use the GUI\n", cfgFile)
	}

	if profiler := os.Getenv("STPROFILER"); len(profiler) > 0 {
		go func() {
			dlog.Println("Starting profiler on", profiler)
			err := http.ListenAndServe(profiler, nil)
			if err != nil {
				dlog.Fatal(err)
			}
		}()
	}

	// The TLS configuration is used for both the listening socket and outgoing
	// connections.

	tlsCfg := &tls.Config{
		Certificates:           []tls.Certificate{cert},
		NextProtos:             []string{"bep/1.0"},
		ServerName:             myID,
		ClientAuth:             tls.RequestClientCert,
		SessionTicketsDisabled: true,
		InsecureSkipVerify:     true,
		MinVersion:             tls.VersionTLS12,
	}

	m := NewModel(cfg.Options.MaxChangeKbps * 1000)
	if cfg.Options.MaxSendKbps > 0 {
		m.LimitRate(cfg.Options.MaxSendKbps)
	}

	for i := range cfg.Repositories {
		cfg.Repositories[i].Nodes = cleanNodeList(cfg.Repositories[i].Nodes, myID)
		dir := expandTilde(cfg.Repositories[i].Directory)
		ensureDir(dir, -1)
		m.AddRepo(cfg.Repositories[i].ID, dir, cfg.Repositories[i].Nodes)
	}

	// GUI
	if cfg.Options.GUIEnabled && cfg.Options.GUIAddress != "" {
		addr, err := net.ResolveTCPAddr("tcp", cfg.Options.GUIAddress)
		if err != nil {
			warnf("Cannot start GUI on %q: %v", cfg.Options.GUIAddress, err)
		} else {
			var hostOpen, hostShow string
			switch {
			case addr.IP == nil:
				hostOpen = "localhost"
				hostShow = "0.0.0.0"
			case addr.IP.IsUnspecified():
				hostOpen = "localhost"
				hostShow = addr.IP.String()
			default:
				hostOpen = addr.IP.String()
				hostShow = hostOpen
			}

			infof("Starting web GUI on http://%s:%d/", hostShow, addr.Port)
			startGUI(cfg.Options.GUIAddress, m)
			if cfg.Options.StartBrowser && len(os.Getenv("STRESTART")) == 0 {
				openURL(fmt.Sprintf("http://%s:%d", hostOpen, addr.Port))
			}
		}
	}

	// Walk the repository and update the local model before establishing any
	// connections to other nodes.

	if verbose {
		infoln("Populating repository index")
	}
	m.LoadIndexes(confDir)
	m.ScanRepos()
	m.SaveIndexes(confDir)

	connOpts := map[string]string{
		"clientId":      "syncthing",
		"clientVersion": Version,
		"clusterHash":   clusterHash(cfg.Repositories[0].Nodes),
	}

	// Routine to connect out to configured nodes
	if verbose {
		infoln("Attempting to connect to other nodes")
	}
	disc := discovery()
	go listenConnect(myID, disc, m, tlsCfg, connOpts)

	// Routine to pull blocks from other nodes to synchronize the local
	// repository. Does not run when we are in read only (publish only) mode.
	if cfg.Options.ReadOnly {
		if verbose {
			okln("Ready to synchronize (read only; no external updates accepted)")
		}
		m.StartRO()
	} else {
		if verbose {
			okln("Ready to synchronize (read-write)")
		}
		m.StartRW(cfg.Options.ParallelRequests)
	}

	select {}
}

func restart() {
	infoln("Restarting")
	if os.Getenv("SMF_FMRI") != "" || os.Getenv("STNORESTART") != "" {
		// Solaris SMF
		infoln("Service manager detected; exit instead of restart")
		os.Exit(0)
	}

	env := os.Environ()
	if len(os.Getenv("STRESTART")) == 0 {
		env = append(env, "STRESTART=1")
	}
	pgm, err := exec.LookPath(os.Args[0])
	if err != nil {
		warnln(err)
		return
	}
	proc, err := os.StartProcess(pgm, os.Args, &os.ProcAttr{
		Env:   env,
		Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
	})
	if err != nil {
		fatalln(err)
	}
	proc.Release()
	os.Exit(0)
}

var saveConfigCh = make(chan struct{})

func saveConfigLoop(cfgFile string) {
	for _ = range saveConfigCh {
		fd, err := os.Create(cfgFile + ".tmp")
		if err != nil {
			warnln(err)
			continue
		}

		err = writeConfigXML(fd, cfg)
		if err != nil {
			warnln(err)
			fd.Close()
			continue
		}

		err = fd.Close()
		if err != nil {
			warnln(err)
			continue
		}

		err = Rename(cfgFile+".tmp", cfgFile)
		if err != nil {
			warnln(err)
		}
	}
}

func saveConfig() {
	saveConfigCh <- struct{}{}
}

func listenConnect(myID string, disc *discover.Discoverer, m *Model, tlsCfg *tls.Config, connOpts map[string]string) {
	var conns = make(chan *tls.Conn)

	// Listen
	for _, addr := range cfg.Options.ListenAddress {
		addr := addr
		go func() {
			if debugNet {
				dlog.Println("listening on", addr)
			}
			l, err := tls.Listen("tcp", addr, tlsCfg)
			fatalErr(err)

			for {
				conn, err := l.Accept()
				if err != nil {
					warnln(err)
					continue
				}

				if debugNet {
					dlog.Println("connect from", conn.RemoteAddr())
				}

				tc := conn.(*tls.Conn)
				err = tc.Handshake()
				if err != nil {
					warnln(err)
					tc.Close()
					continue
				}

				conns <- tc
			}
		}()
	}

	// Connect
	go func() {
		for {
		nextNode:
			for _, nodeCfg := range cfg.Repositories[0].Nodes {
				if nodeCfg.NodeID == myID {
					continue
				}
				if m.ConnectedTo(nodeCfg.NodeID) {
					continue
				}
				for _, addr := range nodeCfg.Addresses {
					if addr == "dynamic" {
						if disc != nil {
							t := disc.Lookup(nodeCfg.NodeID)
							if len(t) == 0 {
								continue
							}
							addr = t[0] //XXX: Handle all of them
						}
					}

					if debugNet {
						dlog.Println("dial", nodeCfg.NodeID, addr)
					}
					conn, err := tls.Dial("tcp", addr, tlsCfg)
					if err != nil {
						if debugNet {
							dlog.Println(err)
						}
						continue
					}

					conns <- conn
					continue nextNode
				}
			}

			time.Sleep(time.Duration(cfg.Options.ReconnectIntervalS) * time.Second)
		}
	}()

next:
	for conn := range conns {
		remoteID := certID(conn.ConnectionState().PeerCertificates[0].Raw)

		if remoteID == myID {
			warnf("Connected to myself (%s) - should not happen", remoteID)
			conn.Close()
			continue
		}

		if m.ConnectedTo(remoteID) {
			warnf("Connected to already connected node (%s)", remoteID)
			conn.Close()
			continue
		}

		for _, nodeCfg := range cfg.Repositories[0].Nodes {
			if nodeCfg.NodeID == remoteID {
				protoConn := protocol.NewConnection(remoteID, conn, conn, m, connOpts)
				m.AddConnection(conn, protoConn)
				continue next
			}
		}
		conn.Close()
	}
}

func discovery() *discover.Discoverer {
	if !cfg.Options.LocalAnnEnabled {
		return nil
	}

	infoln("Sending local discovery announcements")

	if !cfg.Options.GlobalAnnEnabled {
		cfg.Options.GlobalAnnServer = ""
	} else if verbose {
		infoln("Sending external discovery announcements")
	}

	disc, err := discover.NewDiscoverer(myID, cfg.Options.ListenAddress, cfg.Options.GlobalAnnServer)

	if err != nil {
		warnf("No discovery possible (%v)", err)
	}

	return disc
}

func ensureDir(dir string, mode int) {
	fi, err := os.Stat(dir)
	if os.IsNotExist(err) {
		err := os.MkdirAll(dir, 0700)
		fatalErr(err)
	} else if mode >= 0 && err == nil && int(fi.Mode()&0777) != mode {
		err := os.Chmod(dir, os.FileMode(mode))
		fatalErr(err)
	}
}

func expandTilde(p string) string {
	if runtime.GOOS == "windows" {
		return p
	}

	if strings.HasPrefix(p, "~/") {
		return strings.Replace(p, "~", getUnixHomeDir(), 1)
	}
	return p
}

func getUnixHomeDir() string {
	home := os.Getenv("HOME")
	if home == "" {
		fatalln("No home directory?")
	}
	return home
}

func getHomeDir() string {
	if runtime.GOOS == "windows" {
		home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		if home == "" {
			home = os.Getenv("USERPROFILE")
		}
		return home
	}
	return getUnixHomeDir()
}

func getDefaultConfDir() string {
	if runtime.GOOS == "windows" {
		return filepath.Join(os.Getenv("AppData"), "syncthing")
	}
	return expandTilde("~/.syncthing")
}
<commit_msg>Remove pointless -v option<commit_after>package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"runtime/debug"
	"strings"
	"time"

	"github.com/calmh/ini"
	"github.com/calmh/syncthing/discover"
	"github.com/calmh/syncthing/protocol"
)

const BlockSize = 128 * 1024

var cfg Configuration
var Version = "unknown-dev"

var (
	myID string
)

var (
	showVersion bool
	confDir     string
)

const (
	usage      = "syncthing [options]"
	extraUsage = `The following enviroment variables are interpreted by syncthing:

 STNORESTART  Do not attempt to restart when requested to, instead just exit.
              Set this variable when running under a service manager such as
              runit, launchd, etc.

 STPROFILER   Set to a listen address such as "127.0.0.1:9090" to start the
              profiler with HTTP access.

 STTRACE      A comma separated string of facilities to trace. The valid
              facility strings:
              - "discover" (the node discovery package)
              - "files"    (file set store)
              - "idx"      (index sending and receiving)
              - "mc"       (multicast beacon)
              - "need"     (file need calculations)
              - "net"      (connecting and disconnecting, network messages)
              - "pull"     (file pull activity)
              - "scanner"  (the file change scanner)
              `
)

func main() {
	flag.StringVar(&confDir, "home", getDefaultConfDir(), "Set configuration directory")
	flag.BoolVar(&showVersion, "version", false, "Show version")
	flag.Usage = usageFor(flag.CommandLine, usage, extraUsage)
	flag.Parse()

	if len(os.Getenv("STRESTART")) > 0 {
		// Give the parent process time to exit and release sockets etc.
		time.Sleep(1 * time.Second)
	}

	if showVersion {
		fmt.Println(Version)
		os.Exit(0)
	}

	if len(os.Getenv("GOGC")) == 0 {
		debug.SetGCPercent(25)
	}

	if len(os.Getenv("GOMAXPROCS")) == 0 {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	confDir = expandTilde(confDir)

	// Ensure that our home directory exists and that we have a certificate and key.

	ensureDir(confDir, 0700)
	cert, err := loadCert(confDir)
	if err != nil {
		newCertificate(confDir)
		cert, err = loadCert(confDir)
		fatalErr(err)
	}

	myID = string(certID(cert.Certificate[0]))
	log.SetPrefix("[" + myID[0:5] + "] ")
	logger.SetPrefix("[" + myID[0:5] + "] ")

	infoln("Version", Version)
	infoln("My ID:", myID)

	// Prepare to be able to save configuration

	cfgFile := filepath.Join(confDir, "config.xml")
	go saveConfigLoop(cfgFile)

	// Load the configuration file, if it exists.
	// If it does not, create a template.

	cf, err := os.Open(cfgFile)
	if err == nil {
		// Read config.xml
		cfg, err = readConfigXML(cf)
		if err != nil {
			fatalln(err)
		}
		cf.Close()
	} else {
		// No config.xml, let's try the old syncthing.ini
		iniFile := filepath.Join(confDir, "syncthing.ini")
		cf, err := os.Open(iniFile)
		if err == nil {
			infoln("Migrating syncthing.ini to config.xml")
			iniCfg := ini.Parse(cf)
			cf.Close()
			Rename(iniFile, filepath.Join(confDir, "migrated_syncthing.ini"))

			cfg, _ = readConfigXML(nil)
			cfg.Repositories = []RepositoryConfiguration{
				{Directory: iniCfg.Get("repository", "dir")},
			}
			readConfigINI(iniCfg.OptionMap("settings"), &cfg.Options)
			for name, addrs := range iniCfg.OptionMap("nodes") {
				n := NodeConfiguration{
					NodeID:    name,
					Addresses: strings.Fields(addrs),
				}
				cfg.Repositories[0].Nodes = append(cfg.Repositories[0].Nodes, n)
			}

			saveConfig()
		}
	}

	if len(cfg.Repositories) == 0 {
		infoln("No config file; starting with empty defaults")

		cfg, err = readConfigXML(nil)
		cfg.Repositories = []RepositoryConfiguration{
			{
				ID:        "default",
				Directory: filepath.Join(getHomeDir(), "Sync"),
				Nodes: []NodeConfiguration{
					{NodeID: myID, Addresses: []string{"dynamic"}},
				},
			},
		}

		saveConfig()
		infof("Edit %s to taste or use the GUI\n", cfgFile)
	}

	if profiler := os.Getenv("STPROFILER"); len(profiler) > 0 {
		go func() {
			dlog.Println("Starting profiler on", profiler)
			err := http.ListenAndServe(profiler, nil)
			if err != nil {
				dlog.Fatal(err)
			}
		}()
	}

	// The TLS configuration is used for both the listening socket and outgoing
	// connections.

	tlsCfg := &tls.Config{
		Certificates:           []tls.Certificate{cert},
		NextProtos:             []string{"bep/1.0"},
		ServerName:             myID,
		ClientAuth:             tls.RequestClientCert,
		SessionTicketsDisabled: true,
		InsecureSkipVerify:     true,
		MinVersion:             tls.VersionTLS12,
	}

	m := NewModel(cfg.Options.MaxChangeKbps * 1000)
	if cfg.Options.MaxSendKbps > 0 {
		m.LimitRate(cfg.Options.MaxSendKbps)
	}

	for i := range cfg.Repositories {
		cfg.Repositories[i].Nodes = cleanNodeList(cfg.Repositories[i].Nodes, myID)
		dir := expandTilde(cfg.Repositories[i].Directory)
		ensureDir(dir, -1)
		m.AddRepo(cfg.Repositories[i].ID, dir, cfg.Repositories[i].Nodes)
	}

	// GUI
	if cfg.Options.GUIEnabled && cfg.Options.GUIAddress != "" {
		addr, err := net.ResolveTCPAddr("tcp", cfg.Options.GUIAddress)
		if err != nil {
			warnf("Cannot start GUI on %q: %v", cfg.Options.GUIAddress, err)
		} else {
			var hostOpen, hostShow string
			switch {
			case addr.IP == nil:
				hostOpen = "localhost"
				hostShow = "0.0.0.0"
			case addr.IP.IsUnspecified():
				hostOpen = "localhost"
				hostShow = addr.IP.String()
			default:
				hostOpen = addr.IP.String()
				hostShow = hostOpen
			}

			infof("Starting web GUI on http://%s:%d/", hostShow, addr.Port)
			startGUI(cfg.Options.GUIAddress, m)
			if cfg.Options.StartBrowser && len(os.Getenv("STRESTART")) == 0 {
				openURL(fmt.Sprintf("http://%s:%d", hostOpen, addr.Port))
			}
		}
	}

	// Walk the repository and update the local model before establishing any
	// connections to other nodes.

	infoln("Populating repository index")
	m.LoadIndexes(confDir)
	m.ScanRepos()
	m.SaveIndexes(confDir)

	connOpts := map[string]string{
		"clientId":      "syncthing",
		"clientVersion": Version,
		"clusterHash":   clusterHash(cfg.Repositories[0].Nodes),
	}

	// Routine to connect out to configured nodes
	disc := discovery()
	go listenConnect(myID, disc, m, tlsCfg, connOpts)

	// Routine to pull blocks from other nodes to synchronize the local
	// repository. Does not run when we are in read only (publish only) mode.
	if cfg.Options.ReadOnly {
		okln("Ready to synchronize (read only; no external updates accepted)")
		m.StartRO()
	} else {
		okln("Ready to synchronize (read-write)")
		m.StartRW(cfg.Options.ParallelRequests)
	}

	select {}
}

func restart() {
	infoln("Restarting")
	if os.Getenv("SMF_FMRI") != "" || os.Getenv("STNORESTART") != "" {
		// Solaris SMF
		infoln("Service manager detected; exit instead of restart")
		os.Exit(0)
	}

	env := os.Environ()
	if len(os.Getenv("STRESTART")) == 0 {
		env = append(env, "STRESTART=1")
	}
	pgm, err := exec.LookPath(os.Args[0])
	if err != nil {
		warnln(err)
		return
	}
	proc, err := os.StartProcess(pgm, os.Args, &os.ProcAttr{
		Env:   env,
		Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
	})
	if err != nil {
		fatalln(err)
	}
	proc.Release()
	os.Exit(0)
}

var saveConfigCh = make(chan struct{})

func saveConfigLoop(cfgFile string) {
	for _ = range saveConfigCh {
		fd, err := os.Create(cfgFile + ".tmp")
		if err != nil {
			warnln(err)
			continue
		}

		err = writeConfigXML(fd, cfg)
		if err != nil {
			warnln(err)
			fd.Close()
			continue
		}

		err = fd.Close()
		if err != nil {
			warnln(err)
			continue
		}

		err = Rename(cfgFile+".tmp", cfgFile)
		if err != nil {
			warnln(err)
		}
	}
}

func saveConfig() {
	saveConfigCh <- struct{}{}
}

func listenConnect(myID string, disc *discover.Discoverer, m *Model, tlsCfg *tls.Config, connOpts map[string]string) {
	var conns = make(chan *tls.Conn)

	// Listen
	for _, addr := range cfg.Options.ListenAddress {
		addr := addr
		go func() {
			if debugNet {
				dlog.Println("listening on", addr)
			}
			l, err := tls.Listen("tcp", addr, tlsCfg)
			fatalErr(err)

			for {
				conn, err := l.Accept()
				if err != nil {
					warnln(err)
					continue
				}

				if debugNet {
					dlog.Println("connect from", conn.RemoteAddr())
				}

				tc := conn.(*tls.Conn)
				err = tc.Handshake()
				if err != nil {
					warnln(err)
					tc.Close()
					continue
				}

				conns <- tc
			}
		}()
	}

	// Connect
	go func() {
		for {
		nextNode:
			for _, nodeCfg := range cfg.Repositories[0].Nodes {
				if nodeCfg.NodeID == myID {
					continue
				}
				if m.ConnectedTo(nodeCfg.NodeID) {
					continue
				}
				for _, addr := range nodeCfg.Addresses {
					if addr == "dynamic" {
						if disc != nil {
							t := disc.Lookup(nodeCfg.NodeID)
							if len(t) == 0 {
								continue
							}
							addr = t[0] //XXX: Handle all of them
						}
					}

					if debugNet {
						dlog.Println("dial", nodeCfg.NodeID, addr)
					}
					conn, err := tls.Dial("tcp", addr, tlsCfg)
					if err != nil {
						if debugNet {
							dlog.Println(err)
						}
						continue
					}

					conns <- conn
					continue nextNode
				}
			}

			time.Sleep(time.Duration(cfg.Options.ReconnectIntervalS) * time.Second)
		}
	}()

next:
	for conn := range conns {
		remoteID := certID(conn.ConnectionState().PeerCertificates[0].Raw)

		if remoteID == myID {
			warnf("Connected to myself (%s) - should not happen", remoteID)
			conn.Close()
			continue
		}

		if m.ConnectedTo(remoteID) {
			warnf("Connected to already connected node (%s)", remoteID)
			conn.Close()
			continue
		}

		for _, nodeCfg := range cfg.Repositories[0].Nodes {
			if nodeCfg.NodeID == remoteID {
				protoConn := protocol.NewConnection(remoteID, conn, conn, m, connOpts)
				m.AddConnection(conn, protoConn)
				continue next
			}
		}
		conn.Close()
	}
}

func discovery() *discover.Discoverer {
	if !cfg.Options.LocalAnnEnabled {
		return nil
	}

	infoln("Sending local discovery announcements")

	if !cfg.Options.GlobalAnnEnabled {
		cfg.Options.GlobalAnnServer = ""
	} else {
		infoln("Sending external discovery announcements")
	}

	disc, err := discover.NewDiscoverer(myID, cfg.Options.ListenAddress, cfg.Options.GlobalAnnServer)

	if err != nil {
		warnf("No discovery possible (%v)", err)
	}

	return disc
}

func ensureDir(dir string, mode int) {
	fi, err := os.Stat(dir)
	if os.IsNotExist(err) {
		err := os.MkdirAll(dir, 0700)
		fatalErr(err)
	} else if mode >= 0 && err == nil && int(fi.Mode()&0777) != mode {
		err := os.Chmod(dir, os.FileMode(mode))
		fatalErr(err)
	}
}

func expandTilde(p string) string {
	if runtime.GOOS == "windows" {
		return p
	}

	if strings.HasPrefix(p, "~/") {
		return strings.Replace(p, "~", getUnixHomeDir(), 1)
	}
	return p
}

func getUnixHomeDir() string {
	home := os.Getenv("HOME")
	if home == "" {
		fatalln("No home directory?")
	}
	return home
}

func getHomeDir() string {
	if runtime.GOOS == "windows" {
		home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		if home == "" {
			home = os.Getenv("USERPROFILE")
		}
		return home
	}
	return getUnixHomeDir()
}

func getDefaultConfDir() string {
	if runtime.GOOS == "windows" {
		return filepath.Join(os.Getenv("AppData"), "syncthing")
	}
	return expandTilde("~/.syncthing")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"strconv"
	"strings"
	"time"

	"github.com/nelhage/taktician/ai"
	"github.com/nelhage/taktician/playtak"
	"github.com/nelhage/taktician/ptn"
	"github.com/nelhage/taktician/tak"
)

var (
	server   = flag.String("server", "playtak.com:10000", "playtak.com server to connect to")
	user     = flag.String("user", "", "username for login")
	pass     = flag.String("pass", "", "password for login")
	accept   = flag.String("accept", "", "accept a game from specified user")
	gameTime = flag.Duration("time", 20*time.Minute, "Length of game to offer")
	size     = flag.Int("size", 5, "size of game to offer")
	once     = flag.Bool("once", false, "play a single game and exit")
	takbot   = flag.String("takbot", "", "challenge TakBot AI")

	debug = flag.Int("debug", 1, "debug level")
	depth = flag.Int("depth", 5, "minimax depth")
	limit = flag.Duration("limit", time.Minute, "time limit per move")
	sort  = flag.Bool("sort", true, "sort moves via history heuristic")
	table = flag.Bool("table", true, "use the transposition table")
)

const ClientName = "Taktician AI"

func main() {
	flag.Parse()
	if *accept != "" || *takbot != "" {
		*once = true
	}

	backoff := 1 * time.Second
	for {
		client := &playtak.Client{
			Debug: true,
		}
		err := client.Connect(*server)
		if err != nil {
			goto reconnect
		}
		backoff = time.Second
		client.SendClient(ClientName)
		if *user != "" {
			err = client.Login(*user, *pass)
		} else {
			err = client.LoginGuest()
		}
		if err != nil {
			log.Fatal("login: ", err)
		}
		for {
			if *accept != "" {
				for line := range client.Recv {
					if strings.HasPrefix(line, "Seek new") {
						bits := strings.Split(line, " ")
						if bits[3] == *accept {
							log.Printf("accepting game %s from %s", bits[2], bits[3])
							client.SendCommand("Accept", bits[2])
							break
						}
					}
				}
			} else {
				client.SendCommand("Seek", strconv.Itoa(*size), strconv.Itoa(int(gameTime.Seconds())))
				if *takbot != "" {
					client.SendCommand("Shout", "takbot: play", *takbot)
				}
			}
			for line := range client.Recv {
				if strings.HasPrefix(line, "Game Start") {
					playGame(client, line)
					break
				}
			}
			if *once {
				return
			}
			if client.Error() != nil {
				log.Printf("Disconnected: %v", client.Error())
				break
			}
		}
	reconnect:
		log.Printf("sleeping %s before reconnect...", backoff)
		time.Sleep(backoff)
		backoff = backoff * 2
		if backoff > time.Minute {
			backoff = time.Minute
		}
	}
}

func timeBound(remaining time.Duration) time.Duration {
	return *limit
}

func playGame(c *playtak.Client, line string) {
	log.Println("New Game", line)
	bits := strings.Split(line, " ")
	size, _ := strconv.Atoi(bits[3])
	ai := ai.NewMinimax(ai.MinimaxConfig{
		Size:  size,
		Depth: *depth,
		Debug: *debug,

		NoSort:  !*sort,
		NoTable: !*table,
	})
	p := tak.New(tak.Config{Size: size})
	gameStr := fmt.Sprintf("Game#%s", bits[2])
	var color tak.Color
	switch bits[7] {
	case "white":
		color = tak.White
	case "black":
		color = tak.Black
	default:
		panic(fmt.Sprintf("bad color: %s", bits[7]))
	}
	timeLeft := *gameTime
	for {
		over, _ := p.GameOver()
		if color == p.ToMove() && !over {
			move := ai.GetMove(p, timeBound(timeLeft))
			next, err := p.Move(&move)
			if err != nil {
				log.Printf("ai returned bad move: %s: %s",
					ptn.FormatMove(&move), err)
				continue
			}
			p = next
			c.SendCommand(gameStr, playtak.FormatServer(&move))
		} else {
			var timeout <-chan time.Time
		theirMove:
			for {
				var line string
				select {
				case line = <-c.Recv:
				case <-timeout:
					break theirMove
				}

				if !strings.HasPrefix(line, gameStr) {
					continue
				}
				bits = strings.Split(line, " ")
				switch bits[1] {
				case "P", "M":
					move, err := playtak.ParseServer(strings.Join(bits[1:], " "))
					if err != nil {
						panic(err)
					}
					p, err = p.Move(&move)
					if err != nil {
						panic(err)
					}
					timeout = time.NewTimer(500 * time.Millisecond).C
				case "Abandoned.", "Over":
					return
				case "Time":
					w, b := bits[2], bits[3]
					var secsLeft int
					if color == tak.White {
						secsLeft, _ = strconv.Atoi(w)
					} else {
						secsLeft, _ = strconv.Atoi(b)
					}
					timeLeft = time.Duration(secsLeft) * time.Second
					break theirMove
				}
			}
		}
	}
}
<commit_msg>tweaked debug output from Taktician<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"strconv"
	"strings"
	"time"

	"github.com/nelhage/taktician/ai"
	"github.com/nelhage/taktician/playtak"
	"github.com/nelhage/taktician/ptn"
	"github.com/nelhage/taktician/tak"
)

var (
	server   = flag.String("server", "playtak.com:10000", "playtak.com server to connect to")
	user     = flag.String("user", "", "username for login")
	pass     = flag.String("pass", "", "password for login")
	accept   = flag.String("accept", "", "accept a game from specified user")
	gameTime = flag.Duration("time", 20*time.Minute, "Length of game to offer")
	size     = flag.Int("size", 5, "size of game to offer")
	once     = flag.Bool("once", false, "play a single game and exit")
	takbot   = flag.String("takbot", "", "challenge TakBot AI")

	debug = flag.Int("debug", 1, "debug level")
	depth = flag.Int("depth", 5, "minimax depth")
	limit = flag.Duration("limit", time.Minute, "time limit per move")
	sort  = flag.Bool("sort", true, "sort moves via history heuristic")
	table = flag.Bool("table", true, "use the transposition table")

	debugClient = flag.Bool("debug-client", false, "log debug output for playtak connection")
)

const ClientName = "Taktician AI"

func main() {
	flag.Parse()
	if *accept != "" || *takbot != "" {
		*once = true
	}

	backoff := 1 * time.Second
	for {
		client := &playtak.Client{
			Debug: *debugClient,
		}
		err := client.Connect(*server)
		if err != nil {
			goto reconnect
		}
		backoff = time.Second
		client.SendClient(ClientName)
		if *user != "" {
			err = client.Login(*user, *pass)
		} else {
			err = client.LoginGuest()
		}
		if err != nil {
			log.Fatal("login: ", err)
		}
		log.Printf("login OK")
		for {
			if *accept != "" {
				for line := range client.Recv {
					if strings.HasPrefix(line, "Seek new") {
						bits := strings.Split(line, " ")
						if bits[3] == *accept {
							log.Printf("accepting game %s from %s", bits[2], bits[3])
							client.SendCommand("Accept", bits[2])
							break
						}
					}
				}
			} else {
				client.SendCommand("Seek", strconv.Itoa(*size), strconv.Itoa(int(gameTime.Seconds())))
				log.Printf("Seek OK")
				if *takbot != "" {
					client.SendCommand("Shout", "takbot: play", *takbot)
				}
			}
			for line := range client.Recv {
				if strings.HasPrefix(line, "Game Start") {
					playGame(client, line)
					break
				}
			}
			if *once {
				return
			}
			if client.Error() != nil {
				log.Printf("Disconnected: %v", client.Error())
				break
			}
		}
	reconnect:
		log.Printf("sleeping %s before reconnect...", backoff)
		time.Sleep(backoff)
		backoff = backoff * 2
		if backoff > time.Minute {
			backoff = time.Minute
		}
	}
}

func timeBound(remaining time.Duration) time.Duration {
	return *limit
}

func playGame(c *playtak.Client, line string) {
	bits := strings.Split(line, " ")
	size, _ := strconv.Atoi(bits[3])
	ai := ai.NewMinimax(ai.MinimaxConfig{
		Size:  size,
		Depth: *depth,
		Debug: *debug,

		NoSort:  !*sort,
		NoTable: !*table,
	})
	p := tak.New(tak.Config{Size: size})
	id := bits[2]
	gameStr := fmt.Sprintf("Game#%s", id)
	var color tak.Color
	switch bits[7] {
	case "white":
		color = tak.White
	case "black":
		color = tak.Black
	default:
		panic(fmt.Sprintf("bad color: %s", bits[7]))
	}
	log.Printf("new game game-id=%q size=%s player1=%q player2=%q color=%q time=%q",
		bits[2], bits[3], bits[4], bits[6], bits[7], bits[8],
	)
	timeLeft := *gameTime
	for {
		over, _ := p.GameOver()
		if color == p.ToMove() && !over {
			move := ai.GetMove(p, timeBound(timeLeft))
			next, err := p.Move(&move)
			if err != nil {
				log.Printf("ai returned bad move: %s: %s",
					ptn.FormatMove(&move), err)
				continue
			}
			p = next
			c.SendCommand(gameStr, playtak.FormatServer(&move))
			log.Printf("my-move game-id=%s ply=%d ptn=%d.%s move=%q",
				id,
				p.MoveNumber(),
				p.MoveNumber()/2+1,
				strings.ToUpper(color.String()[:1]),
				ptn.FormatMove(&move))
		} else {
			var timeout <-chan time.Time
		theirMove:
			for {
				var line string
				select {
				case line = <-c.Recv:
				case <-timeout:
					break theirMove
				}

				if !strings.HasPrefix(line, gameStr) {
					continue
				}
				bits = strings.Split(line, " ")
				switch bits[1] {
				case "P", "M":
					move, err := playtak.ParseServer(strings.Join(bits[1:], " "))
					if err != nil {
						panic(err)
					}
					p, err = p.Move(&move)
					if err != nil {
						panic(err)
					}
					log.Printf("their-move game-id=%s ply=%d ptn=%d%s move=%q",
						id,
						p.MoveNumber(),
						p.MoveNumber()/2+1,
						strings.ToUpper(color.Flip().String()[:1]),
						ptn.FormatMove(&move))
					timeout = time.NewTimer(500 * time.Millisecond).C
				case "Abandoned.":
					log.Printf("game-over game-id=%s ply=%d result=abandoned",
						id, p.MoveNumber())
					return
				case "Over":
					log.Printf("game-over game-id=%s ply=%d result=%q",
						id, p.MoveNumber(), bits[2])
					return
				case "Time":
					w, b := bits[2], bits[3]
					var secsLeft int
					if color == tak.White {
						secsLeft, _ = strconv.Atoi(w)
					} else {
						secsLeft, _ = strconv.Atoi(b)
					}
					timeLeft = time.Duration(secsLeft) * time.Second
					break theirMove
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>// Multiplexer circuit
package iris2

import (
	"fmt"
)

type Mux struct {
	running     bool
	sources     []<-chan interface{}
	selector    <-chan Word
	Control     <-chan Word
	destination chan<- interface{}
	err         chan error
	Error       <-chan error
}

func NewMux(control, selector <-chan Word, dest chan<- interface{}, src0 <-chan interface{}, srcs ...<-chan interface{}) *Mux {
	var mux Mux
	mux.err = make(chan error)
	mux.Error = mux.err
	mux.sources = []<-chan interface{}{src0}
	mux.sources = append(mux.sources, srcs...)
	mux.selector = selector
	mux.destination = dest
	mux.Control = control
	return &mux
}

func (this *Mux) body() {
	for this.running {
		select {
		case index := <-this.selector:
			if index >= Word(len(this.sources)) {
				this.err <- fmt.Errorf("Selected non existent source: %d", index)
			} else if index < 0 {
				this.err <- fmt.Errorf("Select source %d is less than zero!", index)
			} else {
				this.destination <- <-this.sources[index]
			}
		}
	}
}

func (this *Mux) queryControl() {
	<-this.Control
	if err := this.shutdown(); err != nil {
		this.err <- err
	}
}

func (this *Mux) shutdown() error {
	if !this.running {
		return fmt.Errorf("Attempted to shutdown a multiplexer which isn't running")
	} else {
		this.running = false
		return nil
	}
}

func (this *Mux) Startup() error {
	if this.running {
		return fmt.Errorf("Given multiplexer is already running!")
	} else {
		this.running = true
		go this.body()
		go this.queryControl()
		return nil
	}
}
<commit_msg>Mux sources can now be easily added<commit_after>// Multiplexer circuit
package iris2

import (
	"fmt"
)

type Mux struct {
	running     bool
	sources     []<-chan interface{}
	selector    <-chan Word
	Control     <-chan Word
	destination chan<- interface{}
	err         chan error
	Error       <-chan error
}

func NewMux(control, selector <-chan Word, dest chan<- interface{}) *Mux {
	var mux Mux
	mux.err = make(chan error)
	mux.Error = mux.err
	mux.selector = selector
	mux.destination = dest
	mux.Control = control
	return &mux
}
func (this *Mux) AddSource(src <-chan interface{}) {
	this.sources = append(this.sources, src)
}
func (this *Mux) body() {
	for this.running {
		select {
		case index := <-this.selector:
			if index >= Word(len(this.sources)) {
				this.err <- fmt.Errorf("Selected non existent source: %d", index)
			} else if index < 0 {
				this.err <- fmt.Errorf("Select source %d is less than zero!", index)
			} else {
				this.destination <- <-this.sources[index]
			}
		}
	}
}

func (this *Mux) queryControl() {
	<-this.Control
	if err := this.shutdown(); err != nil {
		this.err <- err
	}
}

func (this *Mux) shutdown() error {
	if !this.running {
		return fmt.Errorf("Attempted to shutdown a multiplexer which isn't running")
	} else {
		this.running = false
		return nil
	}
}

func (this *Mux) Startup() error {
	if this.running {
		return fmt.Errorf("Given multiplexer is already running!")
	} else {
		this.running = true
		go this.body()
		go this.queryControl()
		return nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/base64"
	"errors"
	"fmt"
	"github.com/masterzen/winrm/winrm"
	"github.com/mitchellh/packer/common/uuid"
	"github.com/mitchellh/packer/packer"
	"io"
	"strings"
)

// A Communicator is the interface used to communicate with the machine
// that exists that will eventually be packaged into an image. Communicators
// allow you to execute remote commands, upload files, etc.
//
// Communicators must be safe for concurrency, meaning multiple calls to
// Start or any other method may be called at the same time.
type Communicator struct {
	host string
	user string
	pass string
}

// Start takes a RemoteCmd and starts it. The RemoteCmd must not be
// modified after being used with Start, and it must not be used with
// Start again. The Start method returns immediately once the command
// is started. It does not wait for the command to complete. The
// RemoteCmd.Exited field should be used for this.
func (c *Communicator) Start(rc *packer.RemoteCmd) (err error) {

	client := winrm.NewClient(c.host, c.user, c.pass)
	shell, err := client.CreateShell()
	if err != nil {
		return
	}
	defer shell.Close()

	cmd, err := shell.Execute(rc.Command)
	if err != nil {
		return
	}

	go io.Copy(rc.Stdout, cmd.Stdout)
	go io.Copy(rc.Stderr, cmd.Stderr)

	cmd.Wait()
	rc.SetExited(cmd.ExitCode())
	return
}

// Upload uploads a file to the machine to the given path with the
// contents coming from the given reader. This method will block until
// it completes.
func (c *Communicator) Upload(path string, r io.Reader) (err error) {

	client := winrm.NewClient(c.host, c.user, c.pass)
	shell, err := client.CreateShell()
	if err != nil {
		return
	}
	defer shell.Close()

	temp, err := runCommand(client, fmt.Sprintf(
		`echo %%TEMP%%\packer-%s.tmp`, uuid.TimeOrderedUUID()))

	if err != nil {
		return
	}

	temp = strings.TrimSpace(temp)
	bytes := make([]byte, 8000-len(temp))
	for {
		read, _ := r.Read(bytes)
		if read == 0 {
			break
		}

		_, err = runCommand(client, fmt.Sprintf(
			`echo %s >> %s`, base64.StdEncoding.EncodeToString(bytes[:read]), temp))

		if err != nil {
			return
		}
	}

	_, err = runPowershell(client, fmt.Sprintf(`
		$path = "%s"
		$temp = "%s"

		$dir = [System.IO.Path]::GetDirectoryName($path)
		if (-Not (Test-Path $dir)) {
			mkdir $dir
		} elseif (Test-Path $path) {
			rm $path
		}

		$lines = Get-Content $temp
		$value = [string]::join("",$lines)
		$bytes = [System.Convert]::FromBase64String($value)

		$file = [System.IO.Path]::GetFullPath($path)
		[System.IO.File]::WriteAllBytes($file, $bytes)

		del $temp
	`, path, temp))
	return
}

// UploadDir uploads the contents of a directory recursively to
// the remote path. It also takes an optional slice of paths to
// ignore when uploading.
//
// The folder name of the source folder should be created unless there
// is a trailing slash on the source "/". For example: "/tmp/src" as
// the source will create a "src" directory in the destination unless
// a trailing slash is added. This is identical behavior to rsync(1).
func (c *Communicator) UploadDir(dst string, src string, exclude []string) error {
	panic("not implemented yet")
}

// Download downloads a file from the machine from the given remote path
// with the contents writing to the given writer. This method will
// block until it completes.
func (c *Communicator) Download(path string, w io.Writer) error {
	panic("not implemented yet")
}

func runCommand(client *winrm.Client, text string) (string, error) {
	stdout, stderr, err := client.RunWithString(text, "")

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

	if stderr != "" {
		return "", errors.New("Error running command on guest: " + stderr)
	}

	return stdout, nil
}

func runPowershell(client *winrm.Client, text string) (string, error) {
	var bytes []byte
	for _, c := range []byte(text) {
		bytes = append(bytes, c, 0)
	}

	encoded := "powershell -NoProfile -EncodedCommand " +
		base64.StdEncoding.EncodeToString(bytes)

	return runCommand(client, encoded)
}
<commit_msg>Communicator can upload large files<commit_after>package main

import (
	"bytes"
	"encoding/base64"
	"errors"
	"fmt"
	"github.com/masterzen/winrm/winrm"
	"github.com/mitchellh/packer/common/uuid"
	"github.com/mitchellh/packer/packer"
	"io"
	"io/ioutil"
	"strings"
)

// A Communicator is the interface used to communicate with the machine
// that exists that will eventually be packaged into an image. Communicators
// allow you to execute remote commands, upload files, etc.
//
// Communicators must be safe for concurrency, meaning multiple calls to
// Start or any other method may be called at the same time.
type Communicator struct {
	host string
	user string
	pass string
}

// Start takes a RemoteCmd and starts it. The RemoteCmd must not be
// modified after being used with Start, and it must not be used with
// Start again. The Start method returns immediately once the command
// is started. It does not wait for the command to complete. The
// RemoteCmd.Exited field should be used for this.
func (c *Communicator) Start(rc *packer.RemoteCmd) (err error) {

	client := winrm.NewClient(c.host, c.user, c.pass)
	shell, err := client.CreateShell()
	if err != nil {
		return
	}
	defer shell.Close()

	cmd, err := shell.Execute(rc.Command)
	if err != nil {
		return
	}

	go io.Copy(rc.Stdout, cmd.Stdout)
	go io.Copy(rc.Stderr, cmd.Stderr)

	cmd.Wait()
	rc.SetExited(cmd.ExitCode())
	return
}

// Upload uploads a file to the machine to the given path with the
// contents coming from the given reader. This method will block until
// it completes.
func (c *Communicator) Upload(path string, r io.Reader) (err error) {

	client := winrm.NewClient(c.host, c.user, c.pass)
	shell, err := client.CreateShell()
	if err != nil {
		return
	}
	defer shell.Close()

	temp, err := runCommand(shell, fmt.Sprintf(
		`echo %%TEMP%%\packer-%s.tmp`, uuid.TimeOrderedUUID()))

	if err != nil {
		return
	}

	bytes, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	temp = strings.TrimSpace(temp)
	for _, chunk := range encodeChunks(bytes, 8000-len(temp)) {

		_, err = runCommand(shell,
			fmt.Sprintf(`echo %s >> %s`, chunk, temp))

		if err != nil {
			return
		}
	}

	_, err = runPowershell(shell, fmt.Sprintf(`
		$path = "%s"
		$temp = "%s"

		$dir = [System.IO.Path]::GetDirectoryName($path)
		if (-Not (Test-Path $dir)) {
			mkdir $dir
		} elseif (Test-Path $path) {
			rm $path
		}

		$lines = Get-Content $temp
		$value = [string]::join("",$lines)
		$bytes = [System.Convert]::FromBase64String($value)

		$file = [System.IO.Path]::GetFullPath($path)
		[System.IO.File]::WriteAllBytes($file, $bytes)

		del $temp
	`, path, temp))
	return
}

// UploadDir uploads the contents of a directory recursively to
// the remote path. It also takes an optional slice of paths to
// ignore when uploading.
//
// The folder name of the source folder should be created unless there
// is a trailing slash on the source "/". For example: "/tmp/src" as
// the source will create a "src" directory in the destination unless
// a trailing slash is added. This is identical behavior to rsync(1).
func (c *Communicator) UploadDir(dst string, src string, exclude []string) error {
	panic("not implemented yet")
}

// Download downloads a file from the machine from the given remote path
// with the contents writing to the given writer. This method will
// block until it completes.
func (c *Communicator) Download(path string, w io.Writer) error {
	panic("not implemented yet")
}

func runCommand(shell *winrm.Shell, text string) (string, error) {
	cmd, err := shell.Execute(text)

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

	var stdout, stderr bytes.Buffer
	go io.Copy(&stdout, cmd.Stdout)
	go io.Copy(&stderr, cmd.Stderr)

	cmd.Wait()

	if stderr.Len() > 0 {
		return "", errors.New("Error running command on guest: " + stderr.String())
	}

	return stdout.String(), nil
}

func runPowershell(shell *winrm.Shell, text string) (string, error) {
	var bytes []byte
	for _, c := range []byte(text) {
		bytes = append(bytes, c, 0)
	}

	encoded := "powershell -NoProfile -EncodedCommand " +
		base64.StdEncoding.EncodeToString(bytes)

	return runCommand(shell, encoded)
}

func encodeChunks(bytes []byte, chunkSize int) []string {
	text := base64.StdEncoding.EncodeToString(bytes)
	reader := strings.NewReader(text)

	var chunks []string
	chunk := make([]byte, chunkSize)

	for {
		n, _ := reader.Read(chunk)
		if n == 0 {
			break
		}

		chunks = append(chunks, string(chunk[:n]))
	}

	return chunks
}
<|endoftext|>


<commit_before>package main

import (
	"image"
	"image/color"
	"log"

	"github.com/crgimenes/metal/pcloader/fonts"
	"github.com/hajimehoshi/ebiten"
)

var square *ebiten.Image

func block(screen *ebiten.Image) {
	if square == nil {

		// Create an 16x16 image
		square, _ = ebiten.NewImage(16, 16, ebiten.FilterNearest)
	}

	// Fill the square with the white color

	square.Fill(color.White)

	// Create an empty option struct
	opts := &ebiten.DrawImageOptions{}

	// Add the Translate effect to the option struct.
	w, h := screen.Size()
	w = (w / 2) - (16 / 2)
	h = (h / 2) - (16 / 2)
	opts.GeoM.Translate(float64(w), float64(h))

	// Draw the square image to the screen with an empty option
	screen.DrawImage(square, opts)
}

const screenWidth = 320  // 40 columns
const screenHeight = 240 // 30 rows

const rows = 30
const columns = 40

var img *image.RGBA

var videoChar [rows * columns]byte

var font fonts.Font8x8

func drawPix(x, y int) {
	pos := 4*y*screenWidth + 4*x
	img.Pix[pos] = 0xff
	img.Pix[pos+1] = 0xff
	img.Pix[pos+2] = 0xff
	img.Pix[pos+3] = 0xff
}

func getBit(n int, pos uint64) bool {
	// from right to left
	val := n & (1 << pos)
	return (val > 0)
}

func drawChar(index byte, x, y int) {
	var a, b uint64
	for a = 0; a < 8; a++ {
		for b = 0; b < 8; b++ {
			if font.Bitmap[index][b]&(0x80>>a) != 0 {
				drawPix(int(a)+x, int(b)+y)
			}
		}
	}
}

func drawVideoTextMode() {
	i := 0
	for r := 0; r < rows; r++ {
		for c := 0; c < columns; c++ {
			drawChar(videoChar[i], c*8, r*8)
			i++
		}
	}
}

func update(screen *ebiten.Image) error {

	screen.Fill(color.NRGBA{0x00, 0x00, 0xff, 0xff})
	////
	/*
		drawPix(100, 100)
		drawPix(101, 100)
		drawPix(102, 100)
		drawPix(103, 100)
		drawPix(104, 100)
		drawPix(105, 100)
		drawPix(100, 100)
		drawPix(101, 101)
		drawPix(102, 102)
		drawPix(103, 103)
		drawPix(104, 104)
		drawPix(105, 105)
	*/
	//drawChar(0, 100, 100)
	//drawChar(1, 100+8, 100)
	//drawChar(2, 100+8+8, 100)

	drawVideoTextMode()
	screen.ReplacePixels(img.Pix)

	//block(screen)
	/*
		////
		if ebiten.IsKeyPressed(ebiten.KeyUp) {
			ebitenutil.DebugPrint(screen, "You're pressing the 'UP' button.")
		}
		// When the "down arrow key" is pressed..
		if ebiten.IsKeyPressed(ebiten.KeyDown) {
			ebitenutil.DebugPrint(screen, "\nYou're pressing the 'DOWN' button.")
		}
		// When the "left arrow key" is pressed..
		if ebiten.IsKeyPressed(ebiten.KeyLeft) {
			ebitenutil.DebugPrint(screen, "\n\nYou're pressing the 'LEFT' button.")
		}
		// When the "right arrow key" is pressed..
		if ebiten.IsKeyPressed(ebiten.KeyRight) {
			ebitenutil.DebugPrint(screen, "\n\n\nYou're pressing the 'RIGHT' button.")
		}

		// When the "left mouse button" is pressed...
		if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
			ebitenutil.DebugPrint(screen, "You're pressing the 'LEFT' mouse button.")
		}
		// When the "right mouse button" is pressed...
		if ebiten.IsMouseButtonPressed(ebiten.MouseButtonRight) {
			ebitenutil.DebugPrint(screen, "\nYou're pressing the 'RIGHT' mouse button.")
		}
		// When the "middle mouse button" is pressed...
		if ebiten.IsMouseButtonPressed(ebiten.MouseButtonMiddle) {
			ebitenutil.DebugPrint(screen, "\n\nYou're pressing the 'MIDDLE' mouse button.")
		}

		x, y := ebiten.CursorPosition()

		// Display the information with "X: xx, Y: xx" format
		ebitenutil.DebugPrint(screen, fmt.Sprintf("X: %d, Y: %d", x, y))
	*/
	return nil
}

func main() {

	i := 0
	c := byte(0)
	tot := rows * columns
	for {
		videoChar[i] = c
		c++
		if c > 2 {
			c = 0
		}
		i++
		if i >= tot {
			break
		}
	}

	font.Load()

	img = image.NewRGBA(image.Rect(0, 0, screenWidth, screenHeight))
	if err := ebiten.Run(update, screenWidth, screenHeight, 2, "METAL BASIC 0.01"); err != nil {
		log.Fatal(err)
	}

	if err := ebiten.Run(update, 320, 240, 2, "Metal"); err != nil {
		panic(err)
	}

}
<commit_msg>add clear and putchar<commit_after>package main

import (
	"image"
	"image/color"
	"log"

	"github.com/crgimenes/metal/pcloader/fonts"
	"github.com/hajimehoshi/ebiten"
)

var square *ebiten.Image

func block(screen *ebiten.Image) {
	if square == nil {

		// Create an 16x16 image
		square, _ = ebiten.NewImage(16, 16, ebiten.FilterNearest)
	}

	// Fill the square with the white color

	square.Fill(color.White)

	// Create an empty option struct
	opts := &ebiten.DrawImageOptions{}

	// Add the Translate effect to the option struct.
	w, h := screen.Size()
	w = (w / 2) - (16 / 2)
	h = (h / 2) - (16 / 2)
	opts.GeoM.Translate(float64(w), float64(h))

	// Draw the square image to the screen with an empty option
	screen.DrawImage(square, opts)
}

const screenWidth = 320  // 40 columns
const screenHeight = 240 // 30 rows

const rows = 30
const columns = 40

var img *image.RGBA

var videoChar [rows * columns]byte

var font fonts.Font8x8
var cursor int

func drawPix(x, y int) {
	pos := 4*y*screenWidth + 4*x
	img.Pix[pos] = 0xff
	img.Pix[pos+1] = 0xff
	img.Pix[pos+2] = 0xff
	img.Pix[pos+3] = 0xff
}

func drawOffPix(x, y int) {
	pos := 4*y*screenWidth + 4*x
	img.Pix[pos] = 0x00
	img.Pix[pos+1] = 0x00
	img.Pix[pos+2] = 0x00
	img.Pix[pos+3] = 0x00
}

func getBit(n int, pos uint64) bool {
	// from right to left
	val := n & (1 << pos)
	return (val > 0)
}

func drawChar(index byte, x, y int) {
	var a, b uint64
	for a = 0; a < 8; a++ {
		for b = 0; b < 8; b++ {
			if font.Bitmap[index][b]&(0x80>>a) != 0 {
				drawPix(int(a)+x, int(b)+y)
			} else {
				drawOffPix(int(a)+x, int(b)+y)
			}
		}
	}
}

func drawVideoTextMode() {
	i := 0
	for r := 0; r < rows; r++ {
		for c := 0; c < columns; c++ {
			drawChar(videoChar[i], c*8, r*8)
			i++
		}
	}
}

func clearVideoTextMode() {
	for i := 0; i < rows*columns; i++ {
		videoChar[i] = 0
	}
}

func putChar(c byte) {
	videoChar[cursor] = c
	cursor++
	if cursor >= rows*columns {
		// move chars 1 row up
		// subtract one row fron cursor
		cursor = 0
	}
}

func update(screen *ebiten.Image) error {

	screen.Fill(color.NRGBA{0x00, 0x00, 0xff, 0xff})
	////
	/*
		drawPix(100, 100)
		drawPix(101, 100)
		drawPix(102, 100)
		drawPix(103, 100)
		drawPix(104, 100)
		drawPix(105, 100)
		drawPix(100, 100)
		drawPix(101, 101)
		drawPix(102, 102)
		drawPix(103, 103)
		drawPix(104, 104)
		drawPix(105, 105)
	*/
	//drawChar(0, 100, 100)
	//drawChar(1, 100+8, 100)
	//drawChar(2, 100+8+8, 100)

	putChar(1)
	putChar(0)

	drawVideoTextMode()
	screen.ReplacePixels(img.Pix)

	//block(screen)
	/*
		////
		if ebiten.IsKeyPressed(ebiten.KeyUp) {
			ebitenutil.DebugPrint(screen, "You're pressing the 'UP' button.")
		}
		// When the "down arrow key" is pressed..
		if ebiten.IsKeyPressed(ebiten.KeyDown) {
			ebitenutil.DebugPrint(screen, "\nYou're pressing the 'DOWN' button.")
		}
		// When the "left arrow key" is pressed..
		if ebiten.IsKeyPressed(ebiten.KeyLeft) {
			ebitenutil.DebugPrint(screen, "\n\nYou're pressing the 'LEFT' button.")
		}
		// When the "right arrow key" is pressed..
		if ebiten.IsKeyPressed(ebiten.KeyRight) {
			ebitenutil.DebugPrint(screen, "\n\n\nYou're pressing the 'RIGHT' button.")
		}

		// When the "left mouse button" is pressed...
		if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
			ebitenutil.DebugPrint(screen, "You're pressing the 'LEFT' mouse button.")
		}
		// When the "right mouse button" is pressed...
		if ebiten.IsMouseButtonPressed(ebiten.MouseButtonRight) {
			ebitenutil.DebugPrint(screen, "\nYou're pressing the 'RIGHT' mouse button.")
		}
		// When the "middle mouse button" is pressed...
		if ebiten.IsMouseButtonPressed(ebiten.MouseButtonMiddle) {
			ebitenutil.DebugPrint(screen, "\n\nYou're pressing the 'MIDDLE' mouse button.")
		}

		x, y := ebiten.CursorPosition()

		// Display the information with "X: xx, Y: xx" format
		ebitenutil.DebugPrint(screen, fmt.Sprintf("X: %d, Y: %d", x, y))
	*/
	return nil
}

func main() {

	i := 0
	c := byte(0)
	tot := rows * columns
	for {
		videoChar[i] = c
		c++
		if c > 3 {
			c = 0
		}
		i++
		if i >= tot {
			break
		}
	}

	font.Load()

	img = image.NewRGBA(image.Rect(0, 0, screenWidth, screenHeight))
	if err := ebiten.Run(update, screenWidth, screenHeight, 2, "METAL BASIC 0.01"); err != nil {
		log.Fatal(err)
	}

	if err := ebiten.Run(update, 320, 240, 2, "Metal"); err != nil {
		panic(err)
	}

}
<|endoftext|>


<commit_before>package controllers

import (
	"bytes"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/eirka/eirka-libs/audit"
	"github.com/eirka/eirka-libs/config"
	"github.com/eirka/eirka-libs/db"
	"github.com/eirka/eirka-libs/user"

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

func init() {

	// Database connection settings
	dbase := db.Database{

		User:           local.Settings.Database.User,
		Password:       local.Settings.Database.Password,
		Proto:          local.Settings.Database.Proto,
		Host:           local.Settings.Database.Host,
		Database:       local.Settings.Database.Database,
		MaxIdle:        local.Settings.Database.MaxIdle,
		MaxConnections: local.Settings.Database.MaxConnections,
	}

	// Set up DB connection
	dbase.NewDb()

	// Get limits and stuff from database
	config.GetDatabaseSettings()

}

func performJsonRequest(r http.Handler, method, path string, body []byte) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func errorMessage(err error) string {
	return fmt.Sprintf(`{"error_message":"%s"}`, err)
}

func successMessage(message string) string {
	return fmt.Sprintf(`{"success_message":"%s"}`, message)
}

func TestAddTagController(t *testing.T) {

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(false))

	router.POST("/tag/add", AddTagController)

	first := performRequest(router, "POST", "/tag/add")

	assert.Equal(t, first.Code, 400, "HTTP request code should match")

	request1 := []byte(`{"ib": 1, "tag": 1, "image": 1}`)

	second := performJsonRequest(router, "POST", "/tag/add", request1)

	assert.Equal(t, second.Code, 200, "HTTP request code should match")
	assert.JSONEq(t, second.Body.String(), successMessage(audit.AuditAddTag), "HTTP response should match")

}
<commit_msg>tests with mock database<commit_after>package controllers

import (
	"bytes"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"gopkg.in/DATA-DOG/go-sqlmock.v1"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/eirka/eirka-libs/audit"
	"github.com/eirka/eirka-libs/config"
	"github.com/eirka/eirka-libs/db"
	"github.com/eirka/eirka-libs/user"

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

func performJsonRequest(r http.Handler, method, path string, body []byte) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func errorMessage(err error) string {
	return fmt.Sprintf(`{"error_message":"%s"}`, err)
}

func successMessage(message string) string {
	return fmt.Sprintf(`{"success_message":"%s"}`, message)
}

func TestAddTagController(t *testing.T) {

	var err error

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	statusrows := sqlmock.NewRows([]string{"count"}).AddRow(1)
	mock.ExpectQuery(`SELECT count\(1\) FROM images`).WillReturnRows(statusrows)

	duperows := sqlmock.NewRows([]string{"count"}).AddRow(0)
	mock.ExpectQuery(`select count\(1\) from tagmap`).WillReturnRows(duperows)

	mock.ExpectExec("INSERT into tagmap").
		WithArgs(1, 1).
		WillReturnResult(sqlmock.NewResult(1, 1))

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(false))

	router.POST("/tag/add", AddTagController)

	first := performRequest(router, "POST", "/tag/add")

	assert.Equal(t, first.Code, 400, "HTTP request code should match")

	request1 := []byte(`{"ib": 1, "tag": 1, "image": 1}`)

	second := performJsonRequest(router, "POST", "/tag/add", request1)

	assert.Equal(t, second.Code, 200, "HTTP request code should match")
	assert.JSONEq(t, second.Body.String(), successMessage(audit.AuditAddTag), "HTTP response should match")

}
<|endoftext|>


<commit_before>package sortedmap

import (
	"testing"
	"time"
)

const (
	nilBoundValsErr  = "accepted nil bound values"
	generalBoundsErr = "between bounds error"
	nilValErr        = "nil value!"
	nonNilValErr     = "non-nil value"
	runawayIterErr   = "runaway iterator!"
)

var maxTime = time.Unix(1<<63-62135596801, 999999999)

func TestIterCh(t *testing.T) {
	if _, _, err := newSortedMapFromRandRecords(1000); err != nil {
		t.Fatal(err)
	}
}

func TestIterChTimeout(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}
	timeout := 1 * time.Microsecond
	sleepDur := 10 * time.Millisecond

	params := IterChParams{
		SendTimeout: timeout,
	}

	ch, err := sm.CustomIterCh(params)
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			for i := 0; i < 5; i++ {
				time.Sleep(sleepDur)
				rec := <-ch.Records()
				if i > 1 && rec.Key != nil {
					t.Fatalf("TestIterChTimeout failed: %v: %v", nonNilValErr, rec.Key)
				}
			}
		}()
	}

	params.LowerBound = time.Time{}
	params.UpperBound = maxTime

	ch, err = sm.CustomIterCh(params)
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			for i := 0; i < 5; i++ {
				time.Sleep(sleepDur)
				rec := <-ch.Records()
				if i > 1 && rec.Key != nil {
					t.Fatalf("TestIterChTimeout failed: %v: %v", nonNilValErr, rec.Key)
				}
			}
		}()
	}
}

func TestReversedIterChTimeout(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}
	timeout := 1 * time.Microsecond
	sleepDur := 10 * time.Millisecond

	params := IterChParams{
		Reversed:    true,
		SendTimeout: timeout,
	}

	ch, err := sm.CustomIterCh(params)
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			for i := 0; i < 5; i++ {
				time.Sleep(sleepDur)
				rec := <-ch.Records()
				if i > 1 && rec.Key != nil {
					t.Fatalf("TestReversedIterChTimeout failed: %v: %v", nonNilValErr, rec.Key)
				}
			}
		}()
	}

	params.LowerBound = time.Time{}
	params.UpperBound = maxTime

	ch, err = sm.CustomIterCh(params)
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			for i := 0; i < 5; i++ {
				time.Sleep(sleepDur)
				rec := <-ch.Records()
				if i > 1 && rec.Key != nil {
					t.Fatalf("TestReversedIterChTimeout failed: %v: %v", nonNilValErr, rec.Key)
				}
			}
		}()
	}
}

func TestBoundedIterCh(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}

	reversed := false
	earlierDate := time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)
	laterDate := time.Date(5321, 1, 1, 0, 0, 0, 0, time.UTC)

	ch, err := sm.BoundedIterCh(reversed, nil, nil)
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			if err := verifyRecords(ch.Records(), reversed); err != nil {
				t.Fatal(err)
			}
		}()
	}

	ch, err = sm.BoundedIterCh(reversed, time.Time{}, maxTime)
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			if err := verifyRecords(ch.Records(), reversed); err != nil {
				t.Fatal(err)
			}
		}()
	}

	ch, err = sm.BoundedIterCh(reversed, earlierDate, time.Now())
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			if err := verifyRecords(ch.Records(), reversed); err != nil {
				t.Fatal(err)
			}
		}()
	}

	ch, err = sm.BoundedIterCh(reversed, time.Now(), laterDate)
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			if err := verifyRecords(ch.Records(), reversed); err != nil {
				t.Fatal(err)
			}
		}()
	}

	if _, err := sm.BoundedIterCh(reversed, laterDate, laterDate); err == nil {
		t.Fatalf("TestBoundedIterCh failed: %v", "equal bounds values were accepted error")
	}
}

func TestCustomIterCh(t *testing.T) {
	const (
		nilBoundValsErr  = "accepted two nil bound values"
		generalBoundsErr = "only one bound value"
	)
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}

	reversed := true
	buffSize := 64

	earlierDate := time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)
	laterDate := time.Now()

	params := IterChParams{
		Reversed: reversed,
		BufSize:  buffSize,
	}

	func() {
		ch, err := sm.CustomIterCh(params)
		if err != nil {
			t.Fatal(err)
		}
		defer ch.Close()

		if err := verifyRecords(ch.Records(), params.Reversed); err != nil {
			t.Fatal(err)
		}
	}()

	params = IterChParams{
		Reversed:   reversed,
		BufSize:    buffSize,
		LowerBound: earlierDate,
		UpperBound: laterDate,
	}

	func() {
		ch, err := sm.CustomIterCh(params)
		if err != nil {
			t.Fatal(err)
		}
		defer ch.Close()

		if err := verifyRecords(ch.Records(), params.Reversed); err != nil {
			t.Fatal(err)
		}
	}()

	params = IterChParams{
		Reversed:   reversed,
		BufSize:    buffSize,
		LowerBound: laterDate,
		UpperBound: earlierDate,
	}

	func() {
		_, err := sm.CustomIterCh(params)
		if err == nil {
			t.Fatal(err)
		}
	}()

	reversed = false
	params = IterChParams{
		Reversed:   reversed,
		BufSize:    0,
		LowerBound: laterDate,
		UpperBound: earlierDate,
	}

	func() {
		_, err := sm.CustomIterCh(params)
		if err == nil {
			t.Fatal(err)
		}
	}()
}

func TestCancelCustomIterCh(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}

	earlierDate := time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)
	laterDate := time.Now()

	func() {
		params := IterChParams{
			LowerBound: earlierDate,
			UpperBound: laterDate,
		}

		ch, err := sm.CustomIterCh(params)
		if err != nil {
			t.Fatal(err)
		}
		defer ch.Close()

		go func() {
			if err := verifyRecords(ch.Records(), params.Reversed); err != nil {
				if err.Error() != "Channel was nil." {
					t.Fatal(err)
				}
			} else {
				t.Fatal("Channel was not closed.")
			}
		}()
		ch.Close()
	}()
}

func TestIterFunc(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}
	sm.IterFunc(false, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestIterFunc failed: %v", nilValErr)
		}
		return true
	})
	sm.IterFunc(true, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestIterFunc failed: %v", nilValErr)
		}
		return true
	})
	i := 0
	sm.IterFunc(false, func(rec Record) bool {
		if i > 0 {
			t.Fatalf("TestIterFunc failed: %v", runawayIterErr)
		}
		i++
		return false
	})
	i = 0
	sm.IterFunc(true, func(rec Record) bool {
		if i > 0 {
			t.Fatalf("TestIterFunc failed: %v", runawayIterErr)
		}
		i++
		return false
	})
}

func TestBoundedIterFunc(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}

	earlierDate := time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)
	laterDate := time.Now()

	if err := sm.BoundedIterFunc(false, nil, nil, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestBoundedIterFunc failed: %v", err)
	}

	if err := sm.BoundedIterFunc(false, nil, laterDate, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestBoundedIterFunc failed: %v", err)
	}

	if err := sm.BoundedIterFunc(false, laterDate, nil, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestBoundedIterFunc failed: %v", err)
	}

	if err := sm.BoundedIterFunc(false, earlierDate, laterDate, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestBoundedIterFunc failed: %v", err)
	}
}

func TestBoundedIterFuncWithNoBoundsReturned(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}
	if err := sm.BoundedIterFunc(false, time.Date(5783, 1, 1, 0, 0, 0, 0, time.UTC), time.Now(), func(rec Record) bool {
		if rec.Key == nil {
			t.Fatal(nilValErr)
		}
		return false
	}); err == nil {
		t.Fatal("Values fall between or are equal to the given bounds when it should not have returned bounds.")
	}
}

func TestReversedBoundedIterFunc(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}

	earlierDate := time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)
	laterDate := time.Now()

	if err := sm.BoundedIterFunc(true, nil, nil, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestReversedBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestReversedBoundedIterFunc failed: %v", err)
	}

	if err := sm.BoundedIterFunc(true, nil, laterDate, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestReversedBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestReversedBoundedIterFunc failed: %v", err)
	}

	if err := sm.BoundedIterFunc(true, laterDate, nil, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestReversedBoundedIterFunc failed: %v", err)
	}

	if err := sm.BoundedIterFunc(true, earlierDate, laterDate, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestReversedBoundedIterFunc failed: %v", err)
	}
}
<commit_msg>Increase test coverage<commit_after>package sortedmap

import (
	"testing"
	"time"

	"github.com/umpc/go-sortedmap/asc"
)

const (
	nilBoundValsErr  = "accepted nil bound values"
	generalBoundsErr = "between bounds error"
	nilValErr        = "nil value!"
	nonNilValErr     = "non-nil value"
	runawayIterErr   = "runaway iterator!"
)

var maxTime = time.Unix(1<<63-62135596801, 999999999)

func TestIterCh(t *testing.T) {
	if _, _, err := newSortedMapFromRandRecords(1000); err != nil {
		t.Fatal(err)
	}
}

func TestIterChTimeout(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}
	timeout := 1 * time.Microsecond
	sleepDur := 10 * time.Millisecond

	params := IterChParams{
		SendTimeout: timeout,
	}

	ch, err := sm.CustomIterCh(params)
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			for i := 0; i < 5; i++ {
				time.Sleep(sleepDur)
				rec := <-ch.Records()
				if i > 1 && rec.Key != nil {
					t.Fatalf("TestIterChTimeout failed: %v: %v", nonNilValErr, rec.Key)
				}
			}
		}()
	}

	params.LowerBound = time.Time{}
	params.UpperBound = maxTime

	ch, err = sm.CustomIterCh(params)
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			for i := 0; i < 5; i++ {
				time.Sleep(sleepDur)
				rec := <-ch.Records()
				if i > 1 && rec.Key != nil {
					t.Fatalf("TestIterChTimeout failed: %v: %v", nonNilValErr, rec.Key)
				}
			}
		}()
	}
}

func TestReversedIterChTimeout(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}
	timeout := 1 * time.Microsecond
	sleepDur := 10 * time.Millisecond

	params := IterChParams{
		Reversed:    true,
		SendTimeout: timeout,
	}

	ch, err := sm.CustomIterCh(params)
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			for i := 0; i < 5; i++ {
				time.Sleep(sleepDur)
				rec := <-ch.Records()
				if i > 1 && rec.Key != nil {
					t.Fatalf("TestReversedIterChTimeout failed: %v: %v", nonNilValErr, rec.Key)
				}
			}
		}()
	}

	params.LowerBound = time.Time{}
	params.UpperBound = maxTime

	ch, err = sm.CustomIterCh(params)
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			for i := 0; i < 5; i++ {
				time.Sleep(sleepDur)
				rec := <-ch.Records()
				if i > 1 && rec.Key != nil {
					t.Fatalf("TestReversedIterChTimeout failed: %v: %v", nonNilValErr, rec.Key)
				}
			}
		}()
	}
}

func TestBoundedIterCh(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}

	reversed := false
	earlierDate := time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)
	laterDate := time.Date(5321, 1, 1, 0, 0, 0, 0, time.UTC)

	ch, err := sm.BoundedIterCh(reversed, nil, nil)
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			if err := verifyRecords(ch.Records(), reversed); err != nil {
				t.Fatal(err)
			}
		}()
	}

	ch, err = sm.BoundedIterCh(reversed, time.Time{}, maxTime)
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			if err := verifyRecords(ch.Records(), reversed); err != nil {
				t.Fatal(err)
			}
		}()
	}

	ch, err = sm.BoundedIterCh(reversed, earlierDate, time.Now())
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			if err := verifyRecords(ch.Records(), reversed); err != nil {
				t.Fatal(err)
			}
		}()
	}

	ch, err = sm.BoundedIterCh(reversed, time.Now(), laterDate)
	if err != nil {
		t.Fatal(err)
	} else {
		func() {
			defer ch.Close()

			if err := verifyRecords(ch.Records(), reversed); err != nil {
				t.Fatal(err)
			}
		}()
	}

	if _, err := sm.BoundedIterCh(reversed, laterDate, laterDate); err == nil {
		t.Fatalf("TestBoundedIterCh failed: %v", "equal bounds values were accepted error")
	}
}

func TestBounds(t *testing.T) {
	sm := New(4, asc.Time)

	obsd     := time.Date(1995, 10, 18,  8, 37, 1, 0, time.UTC)
	unixtime := time.Date(1970,  1,  1,  0,  0, 0, 0, time.UTC)
	linux    := time.Date(1991,  8, 25, 20, 57, 8, 0, time.UTC)
	github   := time.Date(2008,  4, 10,  0,  0, 0, 0, time.UTC)

	sm.Insert("OpenBSD",  obsd)
	sm.Insert("UnixTime", unixtime)
	sm.Insert("Linux",    linux)
	sm.Insert("GitHub",   github)

	reversed := false

	iterCh, err := sm.BoundedIterCh(reversed, time.Time{}, unixtime)
	if err != nil {
		t.Fatal(err)
	} else {
		defer iterCh.Close()

		if err := verifyRecords(iterCh.Records(), reversed); err != nil {
			t.Fatal(err)
		}
	}

	iterCh, err = sm.BoundedIterCh(reversed, obsd, github)
	if err != nil {
		t.Fatal(err)
	} else {
		defer iterCh.Close()

		if err := verifyRecords(iterCh.Records(), reversed); err != nil {
			t.Fatal(err)
		}
	}

	_, err = sm.BoundedIterCh(reversed, obsd, obsd)
	if err == nil {
		t.Fatal("equal bounds values were accepted error")
	}
}

func TestCustomIterCh(t *testing.T) {
	const (
		nilBoundValsErr  = "accepted two nil bound values"
		generalBoundsErr = "only one bound value"
	)
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}

	reversed := true
	buffSize := 64

	earlierDate := time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)
	laterDate := time.Now()

	params := IterChParams{
		Reversed: reversed,
		BufSize:  buffSize,
	}

	func() {
		ch, err := sm.CustomIterCh(params)
		if err != nil {
			t.Fatal(err)
		}
		defer ch.Close()

		if err := verifyRecords(ch.Records(), params.Reversed); err != nil {
			t.Fatal(err)
		}
	}()

	params = IterChParams{
		Reversed:   reversed,
		BufSize:    buffSize,
		LowerBound: earlierDate,
		UpperBound: laterDate,
	}

	func() {
		ch, err := sm.CustomIterCh(params)
		if err != nil {
			t.Fatal(err)
		}
		defer ch.Close()

		if err := verifyRecords(ch.Records(), params.Reversed); err != nil {
			t.Fatal(err)
		}
	}()

	params = IterChParams{
		Reversed:   reversed,
		BufSize:    buffSize,
		LowerBound: laterDate,
		UpperBound: earlierDate,
	}

	func() {
		_, err := sm.CustomIterCh(params)
		if err == nil {
			t.Fatal(err)
		}
	}()

	reversed = false
	params = IterChParams{
		Reversed:   reversed,
		BufSize:    0,
		LowerBound: laterDate,
		UpperBound: earlierDate,
	}

	func() {
		_, err := sm.CustomIterCh(params)
		if err == nil {
			t.Fatal(err)
		}
	}()
}

func TestCancelCustomIterCh(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}

	earlierDate := time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)
	laterDate := time.Now()

	func() {
		params := IterChParams{
			LowerBound: earlierDate,
			UpperBound: laterDate,
		}

		ch, err := sm.CustomIterCh(params)
		if err != nil {
			t.Fatal(err)
		}
		defer ch.Close()

		go func() {
			if err := verifyRecords(ch.Records(), params.Reversed); err != nil {
				if err.Error() != "Channel was nil." {
					t.Fatal(err)
				}
			} else {
				t.Fatal("Channel was not closed.")
			}
		}()
		ch.Close()
	}()
}

func TestIterFunc(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}
	sm.IterFunc(false, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestIterFunc failed: %v", nilValErr)
		}
		return true
	})
	sm.IterFunc(true, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestIterFunc failed: %v", nilValErr)
		}
		return true
	})
	i := 0
	sm.IterFunc(false, func(rec Record) bool {
		if i > 0 {
			t.Fatalf("TestIterFunc failed: %v", runawayIterErr)
		}
		i++
		return false
	})
	i = 0
	sm.IterFunc(true, func(rec Record) bool {
		if i > 0 {
			t.Fatalf("TestIterFunc failed: %v", runawayIterErr)
		}
		i++
		return false
	})
}

func TestBoundedIterFunc(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}

	earlierDate := time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)
	laterDate := time.Now()

	if err := sm.BoundedIterFunc(false, nil, nil, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestBoundedIterFunc failed: %v", err)
	}

	if err := sm.BoundedIterFunc(false, nil, laterDate, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestBoundedIterFunc failed: %v", err)
	}

	if err := sm.BoundedIterFunc(false, laterDate, nil, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestBoundedIterFunc failed: %v", err)
	}

	if err := sm.BoundedIterFunc(false, earlierDate, laterDate, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestBoundedIterFunc failed: %v", err)
	}
}

func TestBoundedIterFuncWithNoBoundsReturned(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}
	if err := sm.BoundedIterFunc(false, time.Date(5783, 1, 1, 0, 0, 0, 0, time.UTC), time.Now(), func(rec Record) bool {
		if rec.Key == nil {
			t.Fatal(nilValErr)
		}
		return false
	}); err == nil {
		t.Fatal("Values fall between or are equal to the given bounds when it should not have returned bounds.")
	}
}

func TestReversedBoundedIterFunc(t *testing.T) {
	sm, _, err := newSortedMapFromRandRecords(1000)
	if err != nil {
		t.Fatal(err)
	}

	earlierDate := time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)
	laterDate := time.Now()

	if err := sm.BoundedIterFunc(true, nil, nil, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestReversedBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestReversedBoundedIterFunc failed: %v", err)
	}

	if err := sm.BoundedIterFunc(true, nil, laterDate, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestReversedBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestReversedBoundedIterFunc failed: %v", err)
	}

	if err := sm.BoundedIterFunc(true, laterDate, nil, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestReversedBoundedIterFunc failed: %v", err)
	}

	if err := sm.BoundedIterFunc(true, earlierDate, laterDate, func(rec Record) bool {
		if rec.Key == nil {
			t.Fatalf("TestBoundedIterFunc failed: %v", nilValErr)
		}
		return false
	}); err != nil {
		t.Fatalf("TestReversedBoundedIterFunc failed: %v", err)
	}
}
<|endoftext|>


<commit_before>// Paquete unzip encargado de descomprimir un archivo zip comprimido en 32 bits
package unzip

import (
	"archive/zip"
	"io"
	"os"
	"time"
)

// Estructura enacargada de archivar la información del directorio/arhivo comprimido/descomprimio
type ZipContent struct {
	FileName    string    `json:"file_name"`
	IsDirectory bool      `json:"is_directory"`
	Size        int64     `json:"size"`
	Error       error     `json:"error"`
	CreatedAt   time.Time `json:"created_at"`
}

var (
	files map[int]ZipFiles
)

// Función encargada de crear un directorio
func createDirectory(path string, mode os.FileMode) (string, error) {
	if err := os.MkdirAll(path, mode); err != nil {
		return path, err
	}
	return path, nil
}

// Función encargada de crear un archivo
func createFile(path string) (*os.File, error) {
	return os.Create(path)
}

// Función encargada de copiar un directorio
func copyDirectory(path string, dir *zip.File) (string, error) {
	return createDirectory(path+dir.Name, dir.Mode())
}

// Función enacrgada de copiar un archivo
func copyFile(path string, file *zip.File) (int64, string, error) {
	// Creamos archivo destino
	newFile, err := createFile(path + file.Name)
	if err != nil {
		return 0, (path + file.Name), err
	}
	// Cerramos el archivo destino
	defer newFile.Close()

	// Abrimos archivo original
	originalFile, err := file.Open()
	if err != nil {
		return 0, (path + file.Name), err
	}

	// Copiamos archivo
	_, err = io.Copy(newFile, originalFile)

	fileInfo, err := newFile.Stat()
	if err != nil {
		return 0, (path + file.Name), err
	}

	return fileInfo.Size(), (path + file.Name), nil
}

// Función encargada de descomprimir un archivo zip (32 bits)
func Unzip(pathFile, pathDestination string) (map[int]ZipContent, error) {
	files = make(map[int]ZipContent)

	// Creamos la ruta de destino
	_, err := createDirectory(pathDestination, os.ModePerm)
	if err != nil {
		return files, err
	}

	// Abriendo archivo ZIP
	reader, err := zip.OpenReader(os.Args[1])
	if err != nil {
		return files, err
	}
	defer reader.Close()

	// Recorremos el contenido del archivo ZIP
	for i, f := range reader.File {

		if f.FileInfo().IsDir() {
			// Creamos directorio
			path, err := copyDirectory(pathDestination, f)

			files[i] = compress.ZipContent{
				FileName:    path,
				IsDirectory: true,
				Size:        0,
				Error:       err,
				CreatedAt:   time.Now(),
			}
		} else {
			// Copiamos archivo
			size, path, err := copyFile(pathDestination, f)

			files[i] = compress.ZipContent{
				FileName:    path,
				IsDirectory: false,
				Size:        size,
				Error:       err,
				CreatedAt:   time.Now(),
			}
		}
	}

	return files, nil
}
<commit_msg>format<commit_after>// Paquete unzip encargado de descomprimir un archivo zip comprimido en 32 bits
package unzip

import (
	"archive/zip"
	"io"
	"os"
	"time"
)

// Estructura enacargada de archivar la información del directorio/arhivo comprimido/descomprimio
type ZipContent struct {
	FileName    string    `json:"file_name"`
	IsDirectory bool      `json:"is_directory"`
	Size        int64     `json:"size"`
	Error       error     `json:"error"`
	CreatedAt   time.Time `json:"created_at"`
}

var (
	files map[int]ZipFiles
)

// Función encargada de crear un directorio
func createDirectory(path string, mode os.FileMode) (string, error) {
	if err := os.MkdirAll(path, mode); err != nil {
		return path, err
	}
	return path, nil
}

// Función encargada de crear un archivo
func createFile(path string) (*os.File, error) {
	return os.Create(path)
}

// Función encargada de copiar un directorio
func copyDirectory(path string, dir *zip.File) (string, error) {
	return createDirectory(path+dir.Name, dir.Mode())
}

// Función enacrgada de copiar un archivo
func copyFile(path string, file *zip.File) (int64, string, error) {
	// Creamos archivo destino
	newFile, err := createFile(path + file.Name)
	if err != nil {
		return 0, (path + file.Name), err
	}
	// Cerramos el archivo destino
	defer newFile.Close()

	// Abrimos archivo original
	originalFile, err := file.Open()
	if err != nil {
		return 0, (path + file.Name), err
	}

	// Copiamos archivo
	_, err = io.Copy(newFile, originalFile)

	fileInfo, err := newFile.Stat()
	if err != nil {
		return 0, (path + file.Name), err
	}

	return fileInfo.Size(), (path + file.Name), nil
}

// Función encargada de descomprimir un archivo zip (32 bits)
func Unzip(pathFile, pathDestination string) (map[int]ZipContent, error) {
	files = make(map[int]ZipContent)

	// Creamos la ruta de destino
	_, err := createDirectory(pathDestination, os.ModePerm)
	if err != nil {
		return files, err
	}

	// Abriendo archivo ZIP
	reader, err := zip.OpenReader(os.Args[1])
	if err != nil {
		return files, err
	}
	defer reader.Close()

	// Recorremos el contenido del archivo ZIP
	for i, f := range reader.File {

		if f.FileInfo().IsDir() {
			// Creamos directorio
			path, err := copyDirectory(pathDestination, f)

			files[i] = compress.ZipContent{
				FileName:    path,
				IsDirectory: true,
				Size:        0,
				Error:       err,
				CreatedAt:   time.Now(),
			}
		} else {
			// Copiamos archivo
			size, path, err := copyFile(pathDestination, f)

			files[i] = compress.ZipContent{
				FileName:    path,
				IsDirectory: false,
				Size:        size,
				Error:       err,
				CreatedAt:   time.Now(),
			}
		}
	}

	return files, nil
}
<|endoftext|>