<commit_before>package cli

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/schollz/croc/v6/src/croc"
	"github.com/schollz/croc/v6/src/models"
	"github.com/schollz/croc/v6/src/tcp"
	"github.com/schollz/croc/v6/src/utils"
	log "github.com/schollz/logger"
	"github.com/urfave/cli"
)

var Version string

func Run() (err error) {
	// use all of the processors
	runtime.GOMAXPROCS(runtime.NumCPU())

	app := cli.NewApp()
	app.Name = "croc"
	if Version == "" {
		Version = "v6.0.12-fbbdd1b"
	}
	app.Version = Version
	app.Compiled = time.Now()
	app.Usage = "easily and securely transfer stuff from one computer to another"
	app.UsageText = "croc allows any two computers to directly and securely transfer files"
	// app.ArgsUsage = "[args and such]"
	app.Commands = []cli.Command{
		{
			Name:        "send",
			Usage:       "send a file",
			Description: "send a file over the relay",
			ArgsUsage:   "[filename]",
			Flags: []cli.Flag{
				cli.StringFlag{Name: "code, c", Usage: "codephrase used to connect to relay"},
				cli.BoolFlag{Name: "no-local", Usage: "disable local relay when sending"},
				cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the local relay (optional)"},
			},
			HelpName: "croc send",
			Action: func(c *cli.Context) error {
				return send(c)
			},
		},
		{
			Name:        "relay",
			Description: "start relay",
			HelpName:    "croc relay",
			Action: func(c *cli.Context) error {
				return relay(c)
			},
			Flags: []cli.Flag{
				cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the relay"},
			},
		},
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{Name: "remember", Usage: "save these settings to reuse next time"},
		cli.BoolFlag{Name: "debug", Usage: "increase verbosity (a lot)"},
		cli.BoolFlag{Name: "yes", Usage: "automatically agree to all prompts"},
		cli.BoolFlag{Name: "stdout", Usage: "redirect file to stdout"},
		cli.StringFlag{Name: "relay", Value: models.DEFAULT_RELAY, Usage: "address of the relay"},
		cli.StringFlag{Name: "out", Value: ".", Usage: "specify an output folder to receive the file"},
	}
	app.EnableBashCompletion = true
	app.HideHelp = false
	app.HideVersion = false
	app.BashComplete = func(c *cli.Context) {
		fmt.Fprintf(c.App.Writer, "send\nreceive\relay")
	}
	app.Action = func(c *cli.Context) error {
		// if trying to send but forgot send, let the user know
		if c.Args().First() != "" && utils.Exists(c.Args().First()) {
			_, fname := filepath.Split(c.Args().First())
			yn := utils.GetInput(fmt.Sprintf("Did you mean to send '%s'? (y/n) ", fname))
			if strings.ToLower(yn) == "y" {
				return send(c)
			}
		}
		return receive(c)
	}

	return app.Run(os.Args)
}

// func saveDefaultConfig(c *cli.Context) error {
// 	return croc.SaveDefaultConfig()
// }

func getConfigDir() (homedir string, err error) {
	homedir, err = os.UserHomeDir()
	if err != nil {
		log.Error(err)
		return
	}
	homedir = path.Join(homedir, ".config", "croc")
	if _, err := os.Stat(homedir); os.IsNotExist(err) {
		log.Debugf("creating home directory %s", homedir)
		err = os.MkdirAll(homedir, 0700)
	}
	return
}

func send(c *cli.Context) (err error) {
	if c.GlobalBool("debug") {
		log.SetLevel("debug")
		log.Debug("debug mode on")
	}
	configFile, err := getConfigDir()
	if err != nil {
		log.Error(err)
		return
	}
	configFile = path.Join(configFile, "send.json")

	crocOptions := croc.Options{
		SharedSecret: c.String("code"),
		IsSender:     true,
		Debug:        c.GlobalBool("debug"),
		NoPrompt:     c.GlobalBool("yes"),
		RelayAddress: c.GlobalString("relay"),
		Stdout:       c.GlobalBool("stdout"),
		DisableLocal: c.Bool("no-local"),
		RelayPorts:   strings.Split(c.String("ports"), ","),
	}
	b, errOpen := ioutil.ReadFile(configFile)
	if errOpen == nil && !c.GlobalBool("remember") {
		var rememberedOptions croc.Options
		err = json.Unmarshal(b, &rememberedOptions)
		if err != nil {
			log.Error(err)
			return
		}
		// update anything that isn't explicitly set
		if !c.GlobalIsSet("relay") {
			crocOptions.RelayAddress = rememberedOptions.RelayAddress
		}
		if !c.IsSet("no-local") {
			crocOptions.DisableLocal = rememberedOptions.DisableLocal
		}
		if !c.IsSet("ports") {
			crocOptions.RelayPorts = rememberedOptions.RelayPorts
		}
		if !c.IsSet("code") {
			crocOptions.SharedSecret = rememberedOptions.SharedSecret
		}
	}

	var fnames []string
	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) == 0 {
		f, err := ioutil.TempFile(".", "croc-stdin-")
		if err != nil {
			return err
		}
		_, err = io.Copy(f, os.Stdin)
		if err != nil {
			return err
		}
		err = f.Close()
		if err != nil {
			return err
		}
		fnames = []string{f.Name()}
		defer func() {
			err = os.Remove(fnames[0])
			if err != nil {
				log.Error(err)
			}
		}()
	} else {
		fnames = append([]string{c.Args().First()}, c.Args().Tail()...)
	}
	if len(fnames) == 0 {
		return errors.New("must specify file: croc send [filename]")
	}

	if len(crocOptions.SharedSecret) == 0 {
		// generate code phrase
		crocOptions.SharedSecret = utils.GetRandomName()
	}

	haveFolder := false
	paths := []string{}
	for _, fname := range fnames {
		stat, err := os.Stat(fname)
		if err != nil {
			return err
		}
		if stat.IsDir() {
			haveFolder = true
			err = filepath.Walk(fname,
				func(pathName string, info os.FileInfo, err error) error {
					if err != nil {
						return err
					}
					if !info.IsDir() {
						paths = append(paths, filepath.ToSlash(pathName))
					}
					return nil
				})
			if err != nil {
				return err
			}
		} else {
			paths = append(paths, filepath.ToSlash(fname))
		}
	}

	cr, err := croc.New(crocOptions)
	if err != nil {
		return
	}

	// save the config
	if c.GlobalBool("remember") {
		log.Debug("saving config file")
		var bConfig []byte
		// if the code wasn't set, don't save it
		if c.String("code") == "" {
			crocOptions.SharedSecret = ""
		}
		bConfig, err = json.MarshalIndent(crocOptions, "", "    ")
		if err != nil {
			log.Error(err)
			return
		}
		err = ioutil.WriteFile(configFile, bConfig, 0644)
		if err != nil {
			log.Error(err)
			return
		}
		log.Debugf("wrote %s", configFile)
	}

	err = cr.Send(croc.TransferOptions{
		PathToFiles:      paths,
		KeepPathInRemote: haveFolder,
	})

	return
}

func receive(c *cli.Context) (err error) {
	crocOptions := croc.Options{
		SharedSecret: c.String("code"),
		IsSender:     false,
		Debug:        c.GlobalBool("debug"),
		NoPrompt:     c.GlobalBool("yes"),
		RelayAddress: c.GlobalString("relay"),
		Stdout:       c.GlobalBool("stdout"),
	}
	if c.Args().First() != "" {
		crocOptions.SharedSecret = c.Args().First()
	}

	// load options here
	if c.GlobalBool("debug") {
		log.SetLevel("debug")
		log.Debug("debug mode on")
	}
	configFile, err := getConfigDir()
	if err != nil {
		log.Error(err)
		return
	}
	configFile = path.Join(configFile, "receive.json")
	b, errOpen := ioutil.ReadFile(configFile)
	if errOpen == nil && !c.GlobalBool("remember") {
		var rememberedOptions croc.Options
		err = json.Unmarshal(b, &rememberedOptions)
		if err != nil {
			log.Error(err)
			return
		}
		// update anything that isn't explicitly set
		if !c.GlobalIsSet("relay") {
			crocOptions.RelayAddress = rememberedOptions.RelayAddress
		}
		if crocOptions.SharedSecret == "" {
			crocOptions.SharedSecret = rememberedOptions.SharedSecret
		}
	}

	if crocOptions.SharedSecret == "" {
		crocOptions.SharedSecret = utils.GetInput("Enter receive code: ")
	}
	if c.GlobalString("out") != "" {
		os.Chdir(c.GlobalString("out"))
	}

	cr, err := croc.New(crocOptions)

	if err != nil {
		return
	}
	err = cr.Receive()
	return
}

func relay(c *cli.Context) (err error) {
	debugString := "info"
	if c.GlobalBool("debug") {
		debugString = "debug"
	}
	ports := strings.Split(c.String("ports"), ",")
	tcpPorts := strings.Join(ports[1:], ",")
	for i, port := range ports {
		if i == 0 {
			continue
		}
		go func(portStr string) {
			err = tcp.Run(debugString, portStr)
			if err != nil {
				panic(err)
			}
		}(port)
	}
	return tcp.Run(debugString, ports[0], tcpPorts)
}

// func dirSize(path string) (int64, error) {
// 	var size int64
// 	err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
// 		if !info.IsDir() {
// 			size += info.Size()
// 		}
// 		return err
// 	})
// 	return size, err
// }
<commit_msg>remove unused code<commit_after>package cli

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/schollz/croc/v6/src/croc"
	"github.com/schollz/croc/v6/src/models"
	"github.com/schollz/croc/v6/src/tcp"
	"github.com/schollz/croc/v6/src/utils"
	log "github.com/schollz/logger"
	"github.com/urfave/cli"
)

var Version string

func Run() (err error) {
	// use all of the processors
	runtime.GOMAXPROCS(runtime.NumCPU())

	app := cli.NewApp()
	app.Name = "croc"
	if Version == "" {
		Version = "v6.0.12-fbbdd1b"
	}
	app.Version = Version
	app.Compiled = time.Now()
	app.Usage = "easily and securely transfer stuff from one computer to another"
	app.UsageText = "croc allows any two computers to directly and securely transfer files"
	// app.ArgsUsage = "[args and such]"
	app.Commands = []cli.Command{
		{
			Name:        "send",
			Usage:       "send a file",
			Description: "send a file over the relay",
			ArgsUsage:   "[filename]",
			Flags: []cli.Flag{
				cli.StringFlag{Name: "code, c", Usage: "codephrase used to connect to relay"},
				cli.BoolFlag{Name: "no-local", Usage: "disable local relay when sending"},
				cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the local relay (optional)"},
			},
			HelpName: "croc send",
			Action: func(c *cli.Context) error {
				return send(c)
			},
		},
		{
			Name:        "relay",
			Description: "start relay",
			HelpName:    "croc relay",
			Action: func(c *cli.Context) error {
				return relay(c)
			},
			Flags: []cli.Flag{
				cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the relay"},
			},
		},
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{Name: "remember", Usage: "save these settings to reuse next time"},
		cli.BoolFlag{Name: "debug", Usage: "increase verbosity (a lot)"},
		cli.BoolFlag{Name: "yes", Usage: "automatically agree to all prompts"},
		cli.BoolFlag{Name: "stdout", Usage: "redirect file to stdout"},
		cli.StringFlag{Name: "relay", Value: models.DEFAULT_RELAY, Usage: "address of the relay"},
		cli.StringFlag{Name: "out", Value: ".", Usage: "specify an output folder to receive the file"},
	}
	app.EnableBashCompletion = true
	app.HideHelp = false
	app.HideVersion = false
	app.BashComplete = func(c *cli.Context) {
		fmt.Fprintf(c.App.Writer, "send\nreceive\relay")
	}
	app.Action = func(c *cli.Context) error {
		// if trying to send but forgot send, let the user know
		if c.Args().First() != "" && utils.Exists(c.Args().First()) {
			_, fname := filepath.Split(c.Args().First())
			yn := utils.GetInput(fmt.Sprintf("Did you mean to send '%s'? (y/n) ", fname))
			if strings.ToLower(yn) == "y" {
				return send(c)
			}
		}
		return receive(c)
	}

	return app.Run(os.Args)
}

func getConfigDir() (homedir string, err error) {
	homedir, err = os.UserHomeDir()
	if err != nil {
		log.Error(err)
		return
	}
	homedir = path.Join(homedir, ".config", "croc")
	if _, err := os.Stat(homedir); os.IsNotExist(err) {
		log.Debugf("creating home directory %s", homedir)
		err = os.MkdirAll(homedir, 0700)
	}
	return
}

func send(c *cli.Context) (err error) {
	if c.GlobalBool("debug") {
		log.SetLevel("debug")
		log.Debug("debug mode on")
	}
	configFile, err := getConfigDir()
	if err != nil {
		log.Error(err)
		return
	}
	configFile = path.Join(configFile, "send.json")

	crocOptions := croc.Options{
		SharedSecret: c.String("code"),
		IsSender:     true,
		Debug:        c.GlobalBool("debug"),
		NoPrompt:     c.GlobalBool("yes"),
		RelayAddress: c.GlobalString("relay"),
		Stdout:       c.GlobalBool("stdout"),
		DisableLocal: c.Bool("no-local"),
		RelayPorts:   strings.Split(c.String("ports"), ","),
	}
	b, errOpen := ioutil.ReadFile(configFile)
	if errOpen == nil && !c.GlobalBool("remember") {
		var rememberedOptions croc.Options
		err = json.Unmarshal(b, &rememberedOptions)
		if err != nil {
			log.Error(err)
			return
		}
		// update anything that isn't explicitly set
		if !c.GlobalIsSet("relay") {
			crocOptions.RelayAddress = rememberedOptions.RelayAddress
		}
		if !c.IsSet("no-local") {
			crocOptions.DisableLocal = rememberedOptions.DisableLocal
		}
		if !c.IsSet("ports") {
			crocOptions.RelayPorts = rememberedOptions.RelayPorts
		}
		if !c.IsSet("code") {
			crocOptions.SharedSecret = rememberedOptions.SharedSecret
		}
	}

	var fnames []string
	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) == 0 {
		f, err := ioutil.TempFile(".", "croc-stdin-")
		if err != nil {
			return err
		}
		_, err = io.Copy(f, os.Stdin)
		if err != nil {
			return err
		}
		err = f.Close()
		if err != nil {
			return err
		}
		fnames = []string{f.Name()}
		defer func() {
			err = os.Remove(fnames[0])
			if err != nil {
				log.Error(err)
			}
		}()
	} else {
		fnames = append([]string{c.Args().First()}, c.Args().Tail()...)
	}
	if len(fnames) == 0 {
		return errors.New("must specify file: croc send [filename]")
	}

	if len(crocOptions.SharedSecret) == 0 {
		// generate code phrase
		crocOptions.SharedSecret = utils.GetRandomName()
	}

	haveFolder := false
	paths := []string{}
	for _, fname := range fnames {
		stat, err := os.Stat(fname)
		if err != nil {
			return err
		}
		if stat.IsDir() {
			haveFolder = true
			err = filepath.Walk(fname,
				func(pathName string, info os.FileInfo, err error) error {
					if err != nil {
						return err
					}
					if !info.IsDir() {
						paths = append(paths, filepath.ToSlash(pathName))
					}
					return nil
				})
			if err != nil {
				return err
			}
		} else {
			paths = append(paths, filepath.ToSlash(fname))
		}
	}

	cr, err := croc.New(crocOptions)
	if err != nil {
		return
	}

	// save the config
	if c.GlobalBool("remember") {
		log.Debug("saving config file")
		var bConfig []byte
		// if the code wasn't set, don't save it
		if c.String("code") == "" {
			crocOptions.SharedSecret = ""
		}
		bConfig, err = json.MarshalIndent(crocOptions, "", "    ")
		if err != nil {
			log.Error(err)
			return
		}
		err = ioutil.WriteFile(configFile, bConfig, 0644)
		if err != nil {
			log.Error(err)
			return
		}
		log.Debugf("wrote %s", configFile)
	}

	err = cr.Send(croc.TransferOptions{
		PathToFiles:      paths,
		KeepPathInRemote: haveFolder,
	})

	return
}

func receive(c *cli.Context) (err error) {
	crocOptions := croc.Options{
		SharedSecret: c.String("code"),
		IsSender:     false,
		Debug:        c.GlobalBool("debug"),
		NoPrompt:     c.GlobalBool("yes"),
		RelayAddress: c.GlobalString("relay"),
		Stdout:       c.GlobalBool("stdout"),
	}
	if c.Args().First() != "" {
		crocOptions.SharedSecret = c.Args().First()
	}

	// load options here
	if c.GlobalBool("debug") {
		log.SetLevel("debug")
		log.Debug("debug mode on")
	}
	configFile, err := getConfigDir()
	if err != nil {
		log.Error(err)
		return
	}
	configFile = path.Join(configFile, "receive.json")
	b, errOpen := ioutil.ReadFile(configFile)
	if errOpen == nil && !c.GlobalBool("remember") {
		var rememberedOptions croc.Options
		err = json.Unmarshal(b, &rememberedOptions)
		if err != nil {
			log.Error(err)
			return
		}
		// update anything that isn't explicitly set
		if !c.GlobalIsSet("relay") {
			crocOptions.RelayAddress = rememberedOptions.RelayAddress
		}
		if crocOptions.SharedSecret == "" {
			crocOptions.SharedSecret = rememberedOptions.SharedSecret
		}
	}

	if crocOptions.SharedSecret == "" {
		crocOptions.SharedSecret = utils.GetInput("Enter receive code: ")
	}
	if c.GlobalString("out") != "" {
		os.Chdir(c.GlobalString("out"))
	}

	cr, err := croc.New(crocOptions)

	if err != nil {
		return
	}
	err = cr.Receive()
	return
}

func relay(c *cli.Context) (err error) {
	debugString := "info"
	if c.GlobalBool("debug") {
		debugString = "debug"
	}
	ports := strings.Split(c.String("ports"), ",")
	tcpPorts := strings.Join(ports[1:], ",")
	for i, port := range ports {
		if i == 0 {
			continue
		}
		go func(portStr string) {
			err = tcp.Run(debugString, portStr)
			if err != nil {
				panic(err)
			}
		}(port)
	}
	return tcp.Run(debugString, ports[0], tcpPorts)
}
<|endoftext|>


<commit_before>package cli

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/schollz/croc/v6/src/croc"
	"github.com/schollz/croc/v6/src/tcp"
	"github.com/schollz/croc/v6/src/utils"
	"github.com/urfave/cli"
)

var Version string

func Run() (err error) {

	runtime.GOMAXPROCS(runtime.NumCPU())
	app := cli.NewApp()
	app.Name = "croc"
	if Version == "" {
		Version = "dev"
	}

	app.Version = Version
	app.Compiled = time.Now()
	app.Usage = "easily and securely transfer stuff from one computer to another"
	app.UsageText = "croc allows any two computers to directly and securely transfer files"
	// app.ArgsUsage = "[args and such]"
	app.Commands = []cli.Command{
		{
			Name:        "send",
			Usage:       "send a file",
			Description: "send a file over the relay",
			ArgsUsage:   "[filename]",
			Flags: []cli.Flag{
				cli.StringFlag{Name: "code, c", Usage: "codephrase used to connect to relay"},
			},
			HelpName: "croc send",
			Action: func(c *cli.Context) error {
				return send(c)
			},
		},
		{
			Name:        "relay",
			Description: "start relay",
			HelpName:    "croc relay",
			Action: func(c *cli.Context) error {
				return relay(c)
			},
			Flags: []cli.Flag{
				cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013,9014,9015,9016,9017,9018", Usage: "ports of the relay"},
			},
		},
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{Name: "debug", Usage: "increase verbosity (a lot)"},
		cli.BoolFlag{Name: "yes", Usage: "automatically agree to all prompts"},
		cli.BoolFlag{Name: "stdout", Usage: "redirect file to stdout"},
		cli.StringFlag{Name: "relay", Value: "198.199.67.130:9009", Usage: "address of the relay"},
		cli.StringFlag{Name: "out", Value: ".", Usage: "specify an output folder to receive the file"},
	}
	app.EnableBashCompletion = true
	app.HideHelp = false
	app.HideVersion = false
	app.BashComplete = func(c *cli.Context) {
		fmt.Fprintf(c.App.Writer, "send\nreceive\relay")
	}
	app.Action = func(c *cli.Context) error {
		// if trying to send but forgot send, let the user know
		if c.Args().First() != "" && utils.Exists(c.Args().First()) {
			_, fname := filepath.Split(c.Args().First())
			yn := utils.GetInput(fmt.Sprintf("Did you mean to send '%s'? (y/n) ", fname))
			if strings.ToLower(yn) == "y" {
				return send(c)
			}
		}
		return receive(c)
	}

	return app.Run(os.Args)
}

// func saveDefaultConfig(c *cli.Context) error {
// 	return croc.SaveDefaultConfig()
// }

func send(c *cli.Context) (err error) {

	var fnames []string
	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) == 0 {
		f, err := ioutil.TempFile(".", "croc-stdin-")
		if err != nil {
			return err
		}
		_, err = io.Copy(f, os.Stdin)
		if err != nil {
			return err
		}
		err = f.Close()
		if err != nil {
			return err
		}
		fnames = []string{f.Name()}
		defer func() {
			err = os.Remove(fnames[0])
			if err != nil {
				log.Println(err)
			}
		}()
	} else {
		fnames = append([]string{c.Args().First()}, c.Args().Tail()...)
	}
	if len(fnames) == 0 {
		return errors.New("must specify file: croc send [filename]")
	}

	var sharedSecret string
	if c.String("code") != "" {
		sharedSecret = c.String("code")
	}
	// cr.LoadConfig()
	if len(sharedSecret) == 0 {
		// generate code phrase
		sharedSecret = utils.GetRandomName()
	}

	haveFolder := false
	paths := []string{}
	for _, fname := range fnames {
		stat, err := os.Stat(fname)
		if err != nil {
			return err
		}
		if stat.IsDir() {
			haveFolder = true
			err = filepath.Walk(fname,
				func(pathName string, info os.FileInfo, err error) error {
					if err != nil {
						return err
					}
					if !info.IsDir() {
						paths = append(paths, filepath.ToSlash(pathName))
					}
					return nil
				})
			if err != nil {
				return err
			}
		} else {
			paths = append(paths, filepath.ToSlash(fname))
		}
	}
	cr, err := croc.New(croc.Options{
		SharedSecret: sharedSecret,
		IsSender:     true,
		Debug:        c.GlobalBool("debug"),
		NoPrompt:     c.GlobalBool("yes"),
		RelayAddress: c.GlobalString("relay"),
		Stdout:       c.GlobalBool("stdout"),
	})
	if err != nil {
		return
	}

	err = cr.Send(croc.TransferOptions{
		PathToFiles:      paths,
		KeepPathInRemote: haveFolder,
	})

	return
}

func receive(c *cli.Context) (err error) {
	var sharedSecret string
	if c.GlobalString("code") != "" {
		sharedSecret = c.GlobalString("code")
	}
	if c.Args().First() != "" {
		sharedSecret = c.Args().First()
	}
	if sharedSecret == "" {
		sharedSecret = utils.GetInput("Enter receive code: ")
	}
	if c.GlobalString("out") != "" {
		os.Chdir(c.GlobalString("out"))
	}

	cr, err := croc.New(croc.Options{
		SharedSecret: sharedSecret,
		IsSender:     false,
		Debug:        c.GlobalBool("debug"),
		NoPrompt:     c.GlobalBool("yes"),
		RelayAddress: c.GlobalString("relay"),
		Stdout:       c.GlobalBool("stdout"),
	})
	if err != nil {
		return
	}
	err = cr.Receive()
	return
}

func relay(c *cli.Context) (err error) {
	debugString := "warn"
	if c.GlobalBool("debug") {
		debugString = "debug"
	}
	ports := strings.Split(c.GlobalString("ports"), ",")
	tcpPorts := strings.Join(ports[1:], ",")
	for i, port := range ports {
		if i == 0 {
			continue
		}
		go func(portStr string) {
			err = tcp.Run(debugString, portStr, tcpPorts)
			if err != nil {
				panic(err)
			}
		}(port)
	}
	return tcp.Run(debugString, ports[0])
}

// func dirSize(path string) (int64, error) {
// 	var size int64
// 	err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
// 		if !info.IsDir() {
// 			size += info.Size()
// 		}
// 		return err
// 	})
// 	return size, err
// }
<commit_msg>ifix cliwq!<commit_after>package cli

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/schollz/croc/v6/src/croc"
	"github.com/schollz/croc/v6/src/tcp"
	"github.com/schollz/croc/v6/src/utils"
	"github.com/urfave/cli"
)

var Version string

func Run() (err error) {

	runtime.GOMAXPROCS(runtime.NumCPU())
	app := cli.NewApp()
	app.Name = "croc"
	if Version == "" {
		Version = "dev"
	}

	app.Version = Version
	app.Compiled = time.Now()
	app.Usage = "easily and securely transfer stuff from one computer to another"
	app.UsageText = "croc allows any two computers to directly and securely transfer files"
	// app.ArgsUsage = "[args and such]"
	app.Commands = []cli.Command{
		{
			Name:        "send",
			Usage:       "send a file",
			Description: "send a file over the relay",
			ArgsUsage:   "[filename]",
			Flags: []cli.Flag{
				cli.StringFlag{Name: "code, c", Usage: "codephrase used to connect to relay"},
			},
			HelpName: "croc send",
			Action: func(c *cli.Context) error {
				return send(c)
			},
		},
		{
			Name:        "relay",
			Description: "start relay",
			HelpName:    "croc relay",
			Action: func(c *cli.Context) error {
				return relay(c)
			},
			Flags: []cli.Flag{
				cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013,9014,9015,9016,9017,9018", Usage: "ports of the relay"},
			},
		},
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{Name: "debug", Usage: "increase verbosity (a lot)"},
		cli.BoolFlag{Name: "yes", Usage: "automatically agree to all prompts"},
		cli.BoolFlag{Name: "stdout", Usage: "redirect file to stdout"},
		cli.StringFlag{Name: "relay", Value: "198.199.67.130:9009", Usage: "address of the relay"},
		cli.StringFlag{Name: "out", Value: ".", Usage: "specify an output folder to receive the file"},
	}
	app.EnableBashCompletion = true
	app.HideHelp = false
	app.HideVersion = false
	app.BashComplete = func(c *cli.Context) {
		fmt.Fprintf(c.App.Writer, "send\nreceive\relay")
	}
	app.Action = func(c *cli.Context) error {
		// if trying to send but forgot send, let the user know
		if c.Args().First() != "" && utils.Exists(c.Args().First()) {
			_, fname := filepath.Split(c.Args().First())
			yn := utils.GetInput(fmt.Sprintf("Did you mean to send '%s'? (y/n) ", fname))
			if strings.ToLower(yn) == "y" {
				return send(c)
			}
		}
		return receive(c)
	}

	return app.Run(os.Args)
}

// func saveDefaultConfig(c *cli.Context) error {
// 	return croc.SaveDefaultConfig()
// }

func send(c *cli.Context) (err error) {

	var fnames []string
	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) == 0 {
		f, err := ioutil.TempFile(".", "croc-stdin-")
		if err != nil {
			return err
		}
		_, err = io.Copy(f, os.Stdin)
		if err != nil {
			return err
		}
		err = f.Close()
		if err != nil {
			return err
		}
		fnames = []string{f.Name()}
		defer func() {
			err = os.Remove(fnames[0])
			if err != nil {
				log.Println(err)
			}
		}()
	} else {
		fnames = append([]string{c.Args().First()}, c.Args().Tail()...)
	}
	if len(fnames) == 0 {
		return errors.New("must specify file: croc send [filename]")
	}

	var sharedSecret string
	if c.String("code") != "" {
		sharedSecret = c.String("code")
	}
	// cr.LoadConfig()
	if len(sharedSecret) == 0 {
		// generate code phrase
		sharedSecret = utils.GetRandomName()
	}

	haveFolder := false
	paths := []string{}
	for _, fname := range fnames {
		stat, err := os.Stat(fname)
		if err != nil {
			return err
		}
		if stat.IsDir() {
			haveFolder = true
			err = filepath.Walk(fname,
				func(pathName string, info os.FileInfo, err error) error {
					if err != nil {
						return err
					}
					if !info.IsDir() {
						paths = append(paths, filepath.ToSlash(pathName))
					}
					return nil
				})
			if err != nil {
				return err
			}
		} else {
			paths = append(paths, filepath.ToSlash(fname))
		}
	}
	cr, err := croc.New(croc.Options{
		SharedSecret: sharedSecret,
		IsSender:     true,
		Debug:        c.GlobalBool("debug"),
		NoPrompt:     c.GlobalBool("yes"),
		RelayAddress: c.GlobalString("relay"),
		Stdout:       c.GlobalBool("stdout"),
	})
	if err != nil {
		return
	}

	err = cr.Send(croc.TransferOptions{
		PathToFiles:      paths,
		KeepPathInRemote: haveFolder,
	})

	return
}

func receive(c *cli.Context) (err error) {
	var sharedSecret string
	if c.GlobalString("code") != "" {
		sharedSecret = c.GlobalString("code")
	}
	if c.Args().First() != "" {
		sharedSecret = c.Args().First()
	}
	if sharedSecret == "" {
		sharedSecret = utils.GetInput("Enter receive code: ")
	}
	if c.GlobalString("out") != "" {
		os.Chdir(c.GlobalString("out"))
	}

	cr, err := croc.New(croc.Options{
		SharedSecret: sharedSecret,
		IsSender:     false,
		Debug:        c.GlobalBool("debug"),
		NoPrompt:     c.GlobalBool("yes"),
		RelayAddress: c.GlobalString("relay"),
		Stdout:       c.GlobalBool("stdout"),
	})
	if err != nil {
		return
	}
	err = cr.Receive()
	return
}

func relay(c *cli.Context) (err error) {
	debugString := "warn"
	if c.GlobalBool("debug") {
		debugString = "debug"
	}
	ports := strings.Split(c.String("ports"), ",")
	tcpPorts := strings.Join(ports[1:], ",")
	for i, port := range ports {
		if i == 0 {
			continue
		}
		go func(portStr string) {
			err = tcp.Run(debugString, portStr)
			if err != nil {
				panic(err)
			}
		}(port)
	}
	return tcp.Run(debugString, ports[0],tcpPorts)
}

// func dirSize(path string) (int64, error) {
// 	var size int64
// 	err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
// 		if !info.IsDir() {
// 			size += info.Size()
// 		}
// 		return err
// 	})
// 	return size, err
// }
<|endoftext|>


<commit_before>package exn

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/printer"
	"go/token"
)

type Kind int

const (
	ErrLogic Kind = iota
	ErrNoImpl
	ErrUser
	ErrConv
)

type Error struct {
	Kind Kind
	Msg  string
}

func (e Error) Error() string {
	switch e.Kind {
	case ErrLogic:
		return "logical error: " + e.Msg
	case ErrNoImpl:
		return "unimplemented: " + e.Msg
	case ErrUser:
		return "error: " + e.Msg
	case ErrConv:
		return "translation error: " + e.Msg
	default:
		return "unknown error: " + e.Msg
	}
}

// Logic returns LogicError error.
func Logic(format string, args ...interface{}) Error {
	return errorf(ErrLogic, format, args...)
}

// NoImpl returns NoImplError error.
func NoImpl(format string, args ...interface{}) Error {
	return errorf(ErrNoImpl, format, args...)
}

// User returns ErrUser error.
func User(format string, args ...interface{}) Error {
	return errorf(ErrUser, format, args...)
}

// Conv returns rich ErrConv error.
func Conv(fileSet *token.FileSet, label string, node ast.Node) Error {
	var buf bytes.Buffer
	printer.Fprint(&buf, fileSet, node)
	pos := fileSet.Position(node.Pos())
	return errorf(
		ErrConv,
		"%s:%d: %s: `%s' (%T)",
		pos.Filename, pos.Line, label, buf.String(), node,
	)
}

func Catch(x interface{}) error {
	switch panicArg := x.(type) {
	case nil:
		return nil // No panic happened
	case Error:
		return panicArg
	default:
		panic(panicArg) // Unexpected panic
	}
}

func errorf(k Kind, format string, args ...interface{}) Error {
	return Error{
		Kind: k,
		Msg:  fmt.Sprintf(format, args...),
	}
}
<commit_msg>exn.Catch comment<commit_after>package exn

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/printer"
	"go/token"
)

type Kind int

const (
	ErrLogic Kind = iota
	ErrNoImpl
	ErrUser
	ErrConv
)

type Error struct {
	Kind Kind
	Msg  string
}

func (e Error) Error() string {
	switch e.Kind {
	case ErrLogic:
		return "logical error: " + e.Msg
	case ErrNoImpl:
		return "unimplemented: " + e.Msg
	case ErrUser:
		return "error: " + e.Msg
	case ErrConv:
		return "translation error: " + e.Msg
	default:
		return "unknown error: " + e.Msg
	}
}

// Logic returns LogicError error.
func Logic(format string, args ...interface{}) Error {
	return errorf(ErrLogic, format, args...)
}

// NoImpl returns NoImplError error.
func NoImpl(format string, args ...interface{}) Error {
	return errorf(ErrNoImpl, format, args...)
}

// User returns ErrUser error.
func User(format string, args ...interface{}) Error {
	return errorf(ErrUser, format, args...)
}

// Conv returns rich ErrConv error.
func Conv(fileSet *token.FileSet, label string, node ast.Node) Error {
	var buf bytes.Buffer
	printer.Fprint(&buf, fileSet, node)
	pos := fileSet.Position(node.Pos())
	return errorf(
		ErrConv,
		"%s:%d: %s: `%s' (%T)",
		pos.Filename, pos.Line, label, buf.String(), node,
	)
}

// Catch handles "recover()" return value.
func Catch(x interface{}) error {
	switch panicArg := x.(type) {
	case nil:
		return nil // No panic happened
	case Error:
		return panicArg
	default:
		panic(panicArg) // Unexpected panic
	}
}

func errorf(k Kind, format string, args ...interface{}) Error {
	return Error{
		Kind: k,
		Msg:  fmt.Sprintf(format, args...),
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// +k8s:deepcopy-gen=package,register
// +k8s:conversion-gen=k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions
// +k8s:defaulter-gen=TypeMeta

// Package v1beta1 is the v1beta1 version of the API.
// +groupName=apiextensions.k8s.io
package v1beta1 // import "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
<commit_msg>Consolidate local OpenAPI specs and APIServices' spec into one data structure<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// +k8s:deepcopy-gen=package,register
// +k8s:conversion-gen=k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions
// +k8s:defaulter-gen=TypeMeta

// Package v1beta1 is the v1beta1 version of the API.
// +groupName=apiextensions.k8s.io
// +k8s:openapi-gen=true
package v1beta1 // import "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
<|endoftext|>


<commit_before>package servicing

import (
	"github.com/go-kit/kit/log"
	"github.com/prometheus/client_golang/prometheus"
	"k8s.io/apimachinery/pkg/labels"

	"github.com/sapcc/kubernikus/pkg/controller/config"
	"github.com/sapcc/kubernikus/pkg/controller/servicing/coreos"
	"github.com/sapcc/kubernikus/pkg/controller/servicing/flatcar"
	kubernikus_lister "github.com/sapcc/kubernikus/pkg/generated/listers/kubernikus/v1"
)

type servicingNodesCollector struct {
	klusters    kubernikus_lister.KlusterLister
	nodeListers *NodeListerFactory
	updating    *prometheus.Desc
	waiting     *prometheus.Desc
	kubelet     *prometheus.Desc
	proxy       *prometheus.Desc
	osimage     *prometheus.Desc
}

// RegisterServicingNodesCollector does what the method name sais
func RegisterServicingNodesCollector(logger log.Logger, factories config.Factories) {
	collector := &servicingNodesCollector{
		updating: prometheus.NewDesc(
			"kubernikus_servicing_nodes_state_updating",
			"Amount of nodes per servicing action/state",
			[]string{"kluster_id", "state"},
			prometheus.Labels{},
		),
		waiting: prometheus.NewDesc(
			"kubernikus_servicing_nodes_state_waiting",
			"Amount of nodes that are waiting for updates per state",
			[]string{"kluster_id", "state"},
			prometheus.Labels{},
		),
		kubelet: prometheus.NewDesc(
			"kubernikus_servicing_nodes_version_kubelet",
			"Update Status of Nodes per Kluster",
			[]string{"kluster_id", "version"},
			prometheus.Labels{},
		),
		proxy: prometheus.NewDesc(
			"kubernikus_servicing_nodes_version_proxy",
			"Update Status of Nodes per Kluster",
			[]string{"kluster_id", "version"},
			prometheus.Labels{},
		),
		osimage: prometheus.NewDesc(
			"kubernikus_servicing_nodes_version_osimage",
			"Update Status of Nodes per Kluster",
			[]string{"kluster_id", "version"},
			prometheus.Labels{},
		),
		klusters: factories.Kubernikus.Kubernikus().V1().Klusters().Lister(),
		nodeListers: &NodeListerFactory{
			Logger:          logger,
			NodeObservatory: factories.NodesObservatory.NodeInformer(),
			CoreOSVersion:   &coreos.Version{},
			CoreOSRelease:   &coreos.Release{},
			FlatcarVersion:  &flatcar.Version{},
			FlatcarRelease:  &flatcar.Release{},
		},
	}

	prometheus.MustRegister(collector)
}

//Each and every collector must implement the Describe function.
//It essentially writes all descriptors to the prometheus desc channel.
func (c *servicingNodesCollector) Describe(ch chan<- *prometheus.Desc) {
	ch <- c.updating
	ch <- c.waiting
	ch <- c.kubelet
	ch <- c.proxy
	ch <- c.osimage
}

//Collect implements required collect function for all promehteus collectors
func (c *servicingNodesCollector) Collect(ch chan<- prometheus.Metric) {
	klusters, err := c.klusters.List(labels.Everything())
	if err != nil {
		return
	}

	for _, kluster := range klusters {
		nodes, err := c.nodeListers.Make(kluster)
		if err != nil {
			return
		}

		updatingStarted := float64(len(nodes.Updating()))
		updatingFailed := float64(len(nodes.Failed()))
		updatingSuccessful := float64(len(nodes.Successful()))

		waitingReboot := float64(len(nodes.Reboot()))
		waitingReplace := float64(len(nodes.Replace()))
		waitingUptodate := float64(len(nodes.All())) - waitingReboot - waitingReplace

		kubeletVersions := map[string]int{}
		proxyVersions := map[string]int{}
		osVersions := map[string]int{}

		for _, node := range nodes.All() {
			kubeletVersions[node.Status.NodeInfo.KubeletVersion]++
			proxyVersions[node.Status.NodeInfo.KubeProxyVersion]++

			osVersion, err := coreos.ExractVersion(node)
			if err != nil {
				continue
			}
			osVersions[osVersion.String()]++
		}

		ch <- prometheus.MustNewConstMetric(c.updating, prometheus.GaugeValue, updatingStarted, kluster.GetName(), "started")
		ch <- prometheus.MustNewConstMetric(c.updating, prometheus.GaugeValue, updatingFailed, kluster.GetName(), "failed")
		ch <- prometheus.MustNewConstMetric(c.updating, prometheus.GaugeValue, updatingSuccessful, kluster.GetName(), "successful")
		ch <- prometheus.MustNewConstMetric(c.waiting, prometheus.GaugeValue, waitingReboot, kluster.GetName(), "reboot")
		ch <- prometheus.MustNewConstMetric(c.waiting, prometheus.GaugeValue, waitingReplace, kluster.GetName(), "replace")
		ch <- prometheus.MustNewConstMetric(c.waiting, prometheus.GaugeValue, waitingUptodate, kluster.GetName(), "uptodate")

		for version, count := range kubeletVersions {
			ch <- prometheus.MustNewConstMetric(c.kubelet, prometheus.GaugeValue, float64(count), kluster.GetName(), version)
		}

		for version, count := range proxyVersions {
			ch <- prometheus.MustNewConstMetric(c.proxy, prometheus.GaugeValue, float64(count), kluster.GetName(), version)
		}

		for version, count := range osVersions {
			ch <- prometheus.MustNewConstMetric(c.osimage, prometheus.GaugeValue, float64(count), kluster.GetName(), version)
		}
	}
}
<commit_msg>log errors in servicing nodes collector<commit_after>package servicing

import (
	"github.com/go-kit/kit/log"
	"github.com/prometheus/client_golang/prometheus"
	"k8s.io/apimachinery/pkg/labels"

	"github.com/sapcc/kubernikus/pkg/controller/config"
	"github.com/sapcc/kubernikus/pkg/controller/servicing/coreos"
	"github.com/sapcc/kubernikus/pkg/controller/servicing/flatcar"
	kubernikus_lister "github.com/sapcc/kubernikus/pkg/generated/listers/kubernikus/v1"
)

type servicingNodesCollector struct {
	klusters    kubernikus_lister.KlusterLister
	nodeListers *NodeListerFactory
	updating    *prometheus.Desc
	waiting     *prometheus.Desc
	kubelet     *prometheus.Desc
	proxy       *prometheus.Desc
	osimage     *prometheus.Desc
	logger      log.Logger
}

// RegisterServicingNodesCollector does what the method name sais
func RegisterServicingNodesCollector(logger log.Logger, factories config.Factories) {
	collector := &servicingNodesCollector{
		updating: prometheus.NewDesc(
			"kubernikus_servicing_nodes_state_updating",
			"Amount of nodes per servicing action/state",
			[]string{"kluster_id", "state"},
			prometheus.Labels{},
		),
		waiting: prometheus.NewDesc(
			"kubernikus_servicing_nodes_state_waiting",
			"Amount of nodes that are waiting for updates per state",
			[]string{"kluster_id", "state"},
			prometheus.Labels{},
		),
		kubelet: prometheus.NewDesc(
			"kubernikus_servicing_nodes_version_kubelet",
			"Update Status of Nodes per Kluster",
			[]string{"kluster_id", "version"},
			prometheus.Labels{},
		),
		proxy: prometheus.NewDesc(
			"kubernikus_servicing_nodes_version_proxy",
			"Update Status of Nodes per Kluster",
			[]string{"kluster_id", "version"},
			prometheus.Labels{},
		),
		osimage: prometheus.NewDesc(
			"kubernikus_servicing_nodes_version_osimage",
			"Update Status of Nodes per Kluster",
			[]string{"kluster_id", "version"},
			prometheus.Labels{},
		),
		klusters: factories.Kubernikus.Kubernikus().V1().Klusters().Lister(),
		nodeListers: &NodeListerFactory{
			Logger:          logger,
			NodeObservatory: factories.NodesObservatory.NodeInformer(),
			CoreOSVersion:   &coreos.Version{},
			CoreOSRelease:   &coreos.Release{},
			FlatcarVersion:  &flatcar.Version{},
			FlatcarRelease:  &flatcar.Release{},
		},
		logger: logger,
	}

	prometheus.MustRegister(collector)
}

//Each and every collector must implement the Describe function.
//It essentially writes all descriptors to the prometheus desc channel.
func (c *servicingNodesCollector) Describe(ch chan<- *prometheus.Desc) {
	ch <- c.updating
	ch <- c.waiting
	ch <- c.kubelet
	ch <- c.proxy
	ch <- c.osimage
}

//Collect implements required collect function for all promehteus collectors
func (c *servicingNodesCollector) Collect(ch chan<- prometheus.Metric) {
	klusters, err := c.klusters.List(labels.Everything())
	if err != nil {
		c.logger.Log("msg", "Failed to list klusters", "err", err)
		return
	}

	for _, kluster := range klusters {
		nodes, err := c.nodeListers.Make(kluster)
		if err != nil {
			c.logger.Log("msg", "Failed to list nodes", "kluster", kluster.Name, "err", err)
			continue
		}

		updatingStarted := float64(len(nodes.Updating()))
		updatingFailed := float64(len(nodes.Failed()))
		updatingSuccessful := float64(len(nodes.Successful()))

		waitingReboot := float64(len(nodes.Reboot()))
		waitingReplace := float64(len(nodes.Replace()))
		waitingUptodate := float64(len(nodes.All())) - waitingReboot - waitingReplace

		kubeletVersions := map[string]int{}
		proxyVersions := map[string]int{}
		osVersions := map[string]int{}

		for _, node := range nodes.All() {
			kubeletVersions[node.Status.NodeInfo.KubeletVersion]++
			proxyVersions[node.Status.NodeInfo.KubeProxyVersion]++

			osVersion, err := flatcar.ExractVersion(node)
			if err != nil {
				if osVersion, err = coreos.ExractVersion(node); err != nil {
					continue
				}
			}
			osVersions[osVersion.String()]++
		}

		ch <- prometheus.MustNewConstMetric(c.updating, prometheus.GaugeValue, updatingStarted, kluster.GetName(), "started")
		ch <- prometheus.MustNewConstMetric(c.updating, prometheus.GaugeValue, updatingFailed, kluster.GetName(), "failed")
		ch <- prometheus.MustNewConstMetric(c.updating, prometheus.GaugeValue, updatingSuccessful, kluster.GetName(), "successful")
		ch <- prometheus.MustNewConstMetric(c.waiting, prometheus.GaugeValue, waitingReboot, kluster.GetName(), "reboot")
		ch <- prometheus.MustNewConstMetric(c.waiting, prometheus.GaugeValue, waitingReplace, kluster.GetName(), "replace")
		ch <- prometheus.MustNewConstMetric(c.waiting, prometheus.GaugeValue, waitingUptodate, kluster.GetName(), "uptodate")

		for version, count := range kubeletVersions {
			ch <- prometheus.MustNewConstMetric(c.kubelet, prometheus.GaugeValue, float64(count), kluster.GetName(), version)
		}

		for version, count := range proxyVersions {
			ch <- prometheus.MustNewConstMetric(c.proxy, prometheus.GaugeValue, float64(count), kluster.GetName(), version)
		}

		for version, count := range osVersions {
			ch <- prometheus.MustNewConstMetric(c.osimage, prometheus.GaugeValue, float64(count), kluster.GetName(), version)
		}
	}
}
<|endoftext|>


<commit_before>/*
renamer.go

Renaming files script on various patterns

Author: Gianluca Fiore <forod.g@gmail.com> © 2013-2014
*/

package main

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

var usageMessage string = `
renamer.go (-p <prefix>|-s <suffix>|-i <indexname> -I <num>|-e|-l|-u) [-x <regexp>] [-t <target_dir>] -[cnr]

renamer.go will rename all files matching a regexp or all files in the
given directory (and optionally in all its subdirectories) by the flag
chosen. It can add a prefix/suffix, rename according to a
<name><numeric_index> pattern, make all low/uppercase, lowercase the
extension and copy files somewhere else leaving the originals untouched.

Arguments:

	-prefix|-p <prefix>
		Renames matching files by adding a prefix

	-suffix|-s <suffix>
		Renames matching files by adding a suffix

	-index|-i <name>
		Renames matching files to <name><num>. Requires '-I'

	-startnumber|-I <num>
		The <num> to start renaming in index mode. Requires '-i'

	-regexp|-x <regexp>
		The regular expression for matching files. Use double quotes
		around it

	-target-dir|-t <path>
		The directory where to rename/copy the files. Default is the
		current directory

	-copy|-c
		Copy instead of renaming

	-lower-extension|-e
		Lowercase the extension

	-lowercase|-l
		Lowercase the new filename. It is mutually exclusive with
		'-u'

	-uppercase|-u
		Uppercase the new filename. It is mutually exclusive with
		'-l'

	-dry-run|-n
		List files but don't actually perform any action

	-force|-f
		Overwrite existing files. The default is to not copy/rename
		if the target file already exists

	-recursive|-r
		Operate recursively on all subdirectories of target-dir
`

var regexpArg string         // the regexp argument
var fileRegex *regexp.Regexp // the files matching the regexp
var prefixArg string         // the prefix
var suffixArg string         // the suffix
var indexArg string          // the <name> in the <name><num> pattern
var numArg int               // the <num> in the <name><num> pattern
var targetArg string         // the target directory
var lowerExtArg bool         // the lower extension switch
var lowerArg bool            // the lowercase switch
var upperArg bool            // the uppercase switch
var copyArg bool             // the copy switch
var dryrunArg bool           // the dry-run switch
var forceArg bool            // the force switch
var recursiveArg bool        // the recursive switch

var operationSuccessful int // numeric flag to keep trace of what went
// wrong during the renaming

// Print a message and the usage instructions
func printUsage(msg string) {
	fmt.Fprintf(os.Stderr, msg+"\n")
	fmt.Fprintf(os.Stderr, usageMessage)
	os.Exit(2)
}

// Parse flags ("arguments")
func flagsInit() {
	// Use our custom usageMessage to print usage instructions
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, usageMessage)
	}

	const (
		def_regexp    = ""
		def_prefix    = ""
		def_suffix    = ""
		def_index     = ""
		def_num       = 1
		def_target    = "."
		def_lowerext  = false
		def_lowercase = false
		def_uppercase = false
		def_copy      = false
		def_dryrun    = false
		def_force     = false
		def_recursive = false
	)

	flag.StringVar(&regexpArg, "regexp", def_regexp, "")
	flag.StringVar(&regexpArg, "x", def_regexp, "")
	flag.StringVar(&prefixArg, "prefix", def_prefix, "")
	flag.StringVar(&prefixArg, "p", def_prefix, "")
	flag.StringVar(&suffixArg, "suffix", def_suffix, "")
	flag.StringVar(&suffixArg, "s", def_suffix, "")
	flag.StringVar(&indexArg, "index", def_index, "")
	flag.StringVar(&indexArg, "i", def_index, "")
	flag.IntVar(&numArg, "startnumber", def_num, "")
	flag.IntVar(&numArg, "I", def_num, "")
	flag.StringVar(&targetArg, "target", def_target, "")
	flag.StringVar(&targetArg, "t", def_target, "")
	flag.BoolVar(&lowerExtArg, "lower-extension", def_lowerext, "")
	flag.BoolVar(&lowerExtArg, "e", def_lowerext, "")
	flag.BoolVar(&lowerArg, "lowercase", def_lowercase, "")
	flag.BoolVar(&lowerArg, "l", def_lowercase, "")
	flag.BoolVar(&upperArg, "uppercase", def_uppercase, "")
	flag.BoolVar(&upperArg, "u", def_uppercase, "")
	flag.BoolVar(&copyArg, "copy", def_copy, "")
	flag.BoolVar(&copyArg, "c", def_copy, "")
	flag.BoolVar(&dryrunArg, "dry-run", def_dryrun, "")
	flag.BoolVar(&dryrunArg, "n", def_dryrun, "")
	flag.BoolVar(&forceArg, "force", def_force, "")
	flag.BoolVar(&forceArg, "f", def_force, "")
	flag.BoolVar(&recursiveArg, "recursive", def_recursive, "")
	flag.BoolVar(&recursiveArg, "r", def_recursive, "")

	flag.Parse()

	if regexpArg == "" && prefixArg == "" && suffixArg == "" && indexArg == "" && lowerExtArg == false && lowerArg == false && upperArg == false {
		printUsage("At least one of the mandatory actions must be given, nothing to do...")
	}
}

// Write a renamed or a copy of a file to disk
func writeFile(oldname, newname string) {
	// check if the new filename is already present
	_, lstat_err := os.Lstat(newname)
	if lstat_err == nil && forceArg == false {
		fmt.Fprintf(os.Stderr, "File %s already exist! Use -force to override it\n", newname)
		operationSuccessful = operationSuccessful + 1
	}
	if dryrunArg {
		// if dry-run was given, just output the renaming operation
		fmt.Fprintf(os.Stdout, "Renaming %s to %s (dry-run)\n", oldname, newname)
		operationSuccessful = operationSuccessful + 0
	} else {
		if copyArg {
			copyf, create_err := os.Create(newname)
			if create_err != nil {
				fmt.Fprintf(os.Stderr, create_err.Error())
				operationSuccessful = operationSuccessful + 1
			}
			originalf, open_err := os.Open(oldname)
			if open_err != nil {
				fmt.Fprintf(os.Stderr, open_err.Error())
				operationSuccessful = operationSuccessful + 1
			}
			_, copy_err := io.Copy(copyf, originalf)
			if copy_err != nil {
				fmt.Fprintf(os.Stderr, "An error occurred during the copy of %s to %s\n", oldname, newname)
				fmt.Fprintf(os.Stderr, copy_err.Error())
				operationSuccessful = operationSuccessful + 1
			} else {
				fmt.Fprintf(os.Stdout, "Copying %s to %s\n", oldname, newname)
			}
		} else {
			rename_err := os.Rename(oldname, newname)
			if rename_err != nil {
				fmt.Fprintf(os.Stderr, "An error occurred during the renaming of %s to %s\n", oldname, newname)
				fmt.Fprintf(os.Stderr, rename_err.Error())
				operationSuccessful = operationSuccessful + 1
			} else {
				fmt.Fprintf(os.Stdout, "Renaming %s to %s\n", oldname, newname)
			}
		}
	}
}

// Add a prefix string to a name
func addPrefix(names []string, prefix string) int {
	var finalname, dirname string
	for _, f := range names {
		dirname = filepath.Dir(f)
		finalname = filepath.Join(dirname, prefix+filepath.Base(f))
		writeFile(f, finalname)
	}
	return 0
}

// Add a suffix string to a name
func addSuffix(names []string, suffix string) int {
	var finalname, dirname, justname, ext string
	for _, f := range names {
		ext = filepath.Ext(f)
		dirname = filepath.Dir(f)
		justname = strings.TrimSuffix(filepath.Base(f), ext)
		finalname = filepath.Join(dirname, justname+suffix+ext)
		writeFile(f, finalname)
	}
	return 0
}

// Rename a slice of filenames to <newname><count>.<extension>
func indexName(names []string, newname string, count int) int {
	var finalname, dirname, ext string
	for _, f := range names {
		ext = filepath.Ext(f)
		dirname = filepath.Dir(f)
		finalname = fmt.Sprintf("%s/%s%03d%s", dirname, newname, count, ext)
		writeFile(f, finalname)
		count++
	}
	return 0
}

// Make extensions lowercase
func lowercaseExtension(names []string) int {
	var finalname, dirname, basename, ext string
	for _, f := range names {
		dirname = filepath.Dir(f)
		basename = filepath.Base(f)
		ext = filepath.Ext(f)
		finalname = filepath.Join(dirname, strings.TrimSuffix(basename, ext)+strings.ToLower(strings.TrimSuffix(ext, basename)))
	}
	return 0
}

// Make filenames all lowercase
func lowercaseFiles(names []string) int {
	var finalname, dirname string
	for _, f := range names {
		dirname = filepath.Dir(f)
		finalname = filepath.Join(dirname, strings.ToLower(filepath.Base(f)))
		writeFile(f, finalname)
	}
	return 0
}

// Make filenames all uppercase
func uppercaseFiles(names []string) int {
	var finalname, dirname string
	for _, f := range names {
		dirname = filepath.Dir(f)
		finalname = filepath.Join(dirname, strings.ToUpper(filepath.Base(f)))
		writeFile(f, finalname)
	}
	return 0
}

// Get all files and directories
func getFilesFromDir(dirname string) ([]string, []string) {
	var complete_path string                // final, absolute, path
	var filesindir = make([]os.FileInfo, 0) // files & directories found in path
	var allfiles = make([]string, 0)
	var alldirectories = make([]string, 0)

	dirinfo, lerr := os.Lstat(dirname)
	if lerr != nil {
		fmt.Fprintf(os.Stderr, lerr.Error())
		return alldirectories, allfiles
	}

	// check whether targetArg is an absolute path AND a directory
	if filepath.IsAbs(dirname) && dirinfo.IsDir() {
		complete_path = dirname
	} else {
		abs_path, err := filepath.Abs(dirname)
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
		}
		complete_path = abs_path
	}
	dir, err := os.Open(complete_path)
	defer dir.Close()
	if err != nil {
		err = errors.New(fmt.Sprintf("Target directory %s is not a directory or can't be accessed\n", complete_path))
		fmt.Fprintf(os.Stderr, err.Error())
		return alldirectories, allfiles
	}

	// scan for files/directories in path
	filesindir, read_err := dir.Readdir(0)
	if read_err != nil {
		fmt.Fprintf(os.Stderr, read_err.Error())
		return alldirectories, allfiles
	}

	// check in filesindir slice and separate directories from files in
	// 2 different slices
	for _, f := range filesindir {
		if f.IsDir() {
			alldirectories = append(alldirectories, filepath.Join(complete_path, f.Name()))
		} else {
			allfiles = append(allfiles, filepath.Join(complete_path, f.Name()))
		}
	}

	return alldirectories, allfiles
}

func renameFiles(dir, files []string) int {
	var basename string
	var matchingfiles []string // a slice containing only the files
	// matching the regexp passed as argument (if)
	var result int // the integer returned by each functions,
	// signaling success or failure

	// recursively search on every directory in dir for other
	// files/directories if recursiveArg switch has been enabled
	if dir != nil && recursiveArg == true {
		for _, d := range dir {
			nd, nf := getFilesFromDir(d)
			// if it's a dir, append to []dir
			if len(nd) > 0 {
				for _, i := range nd {
					dir = append(dir, i)
				}
			}
			// if it's a file, append to []files
			if len(nf) > 0 {
				for _, i := range nf {
					files = append(files, i)
				}
			}
		}
	}

	// check if the files should match a given regexp
	if regexpArg != "" {
		for _, f := range files {
			basename = filepath.Base(f)
			// Compile and check if the regexp is a valid one
			compRegexp, err := regexp.Compile(regexpArg)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Invalid regexp: %s\n", regexpArg)
				printUsage("You must give a valid regexp (or none, to operate on all files). Alternatively, add -force to force renaming all files, whether they match the regexp or not")
				return 1
			} else {
				if compRegexp.MatchString(basename) == false {
					continue
				} else {
					matchingfiles = append(matchingfiles, f)
				}
			}
		}
	}

	// if matchingfiles contains something, iterate over it.
	// Otherwise, we simply use the generic files slice
	if len(matchingfiles) >= 1 {
		files = matchingfiles
	} else {
		// different messages whether we have forceArg == true or false
		if regexpArg != "" {
			if forceArg {
				fmt.Fprintf(os.Stdout, "No files matched, including all files anyway (-force enabled)\n")
			} else {
				fmt.Fprintf(os.Stderr, "No files matched, check the correctness of your regexp\n")
				return 1
			}
		}
	}

	if prefixArg != "" {
		result = addPrefix(files, prefixArg)
	}
	if suffixArg != "" {
		result = addSuffix(files, suffixArg)
	}
	if indexArg != "" {
		result = indexName(files, indexArg, numArg)
	}
	if lowerArg == true && upperArg == true {
		// can't use both
		printUsage("Can't use both lowercase and uppercase, choose one only!")
		return 1
	}
	if lowerExtArg == true {
		result = lowercaseExtension(files)
	}
	if lowerArg == true {
		result = lowercaseFiles(files)
	}
	if upperArg == true {
		result = uppercaseFiles(files)
	}

	return result
}

func main() {
	var success_rename int
	var directories, files []string

	flagsInit()

	directories, files = getFilesFromDir(targetArg)

	success_rename = renameFiles(directories, files)

	// check that everything went smoothly
	if success_rename == 0 && operationSuccessful == 0 {
		fmt.Fprintf(os.Stdout, "\nRenaming complete\n")
	} else {
		fmt.Fprintf(os.Stdout, "\nNot all files were correctly renamed, check the previous error messages")
	}
}
<commit_msg>Forgot writeFile call in lowercaseExtension<commit_after>/*
renamer.go

Renaming files script on various patterns

Author: Gianluca Fiore <forod.g@gmail.com> © 2013-2014
*/

package main

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

var usageMessage string = `
renamer.go (-p <prefix>|-s <suffix>|-i <indexname> -I <num>|-e|-l|-u) [-x <regexp>] [-t <target_dir>] -[cnr]

renamer.go will rename all files matching a regexp or all files in the
given directory (and optionally in all its subdirectories) by the flag
chosen. It can add a prefix/suffix, rename according to a
<name><numeric_index> pattern, make all low/uppercase, lowercase the
extension and copy files somewhere else leaving the originals untouched.

Arguments:

	-prefix|-p <prefix>
		Renames matching files by adding a prefix

	-suffix|-s <suffix>
		Renames matching files by adding a suffix

	-index|-i <name>
		Renames matching files to <name><num>. Requires '-I'

	-startnumber|-I <num>
		The <num> to start renaming in index mode. Requires '-i'

	-regexp|-x <regexp>
		The regular expression for matching files. Use double quotes
		around it

	-target-dir|-t <path>
		The directory where to rename/copy the files. Default is the
		current directory

	-copy|-c
		Copy instead of renaming

	-lower-extension|-e
		Lowercase the extension

	-lowercase|-l
		Lowercase the new filename. It is mutually exclusive with
		'-u'

	-uppercase|-u
		Uppercase the new filename. It is mutually exclusive with
		'-l'

	-dry-run|-n
		List files but don't actually perform any action

	-force|-f
		Overwrite existing files. The default is to not copy/rename
		if the target file already exists

	-recursive|-r
		Operate recursively on all subdirectories of target-dir
`

var regexpArg string         // the regexp argument
var fileRegex *regexp.Regexp // the files matching the regexp
var prefixArg string         // the prefix
var suffixArg string         // the suffix
var indexArg string          // the <name> in the <name><num> pattern
var numArg int               // the <num> in the <name><num> pattern
var targetArg string         // the target directory
var lowerExtArg bool         // the lower extension switch
var lowerArg bool            // the lowercase switch
var upperArg bool            // the uppercase switch
var copyArg bool             // the copy switch
var dryrunArg bool           // the dry-run switch
var forceArg bool            // the force switch
var recursiveArg bool        // the recursive switch

var operationSuccessful int // numeric flag to keep trace of what went
// wrong during the renaming

// Print a message and the usage instructions
func printUsage(msg string) {
	fmt.Fprintf(os.Stderr, msg+"\n")
	fmt.Fprintf(os.Stderr, usageMessage)
	os.Exit(2)
}

// Parse flags ("arguments")
func flagsInit() {
	// Use our custom usageMessage to print usage instructions
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, usageMessage)
	}

	const (
		def_regexp    = ""
		def_prefix    = ""
		def_suffix    = ""
		def_index     = ""
		def_num       = 1
		def_target    = "."
		def_lowerext  = false
		def_lowercase = false
		def_uppercase = false
		def_copy      = false
		def_dryrun    = false
		def_force     = false
		def_recursive = false
	)

	flag.StringVar(&regexpArg, "regexp", def_regexp, "")
	flag.StringVar(&regexpArg, "x", def_regexp, "")
	flag.StringVar(&prefixArg, "prefix", def_prefix, "")
	flag.StringVar(&prefixArg, "p", def_prefix, "")
	flag.StringVar(&suffixArg, "suffix", def_suffix, "")
	flag.StringVar(&suffixArg, "s", def_suffix, "")
	flag.StringVar(&indexArg, "index", def_index, "")
	flag.StringVar(&indexArg, "i", def_index, "")
	flag.IntVar(&numArg, "startnumber", def_num, "")
	flag.IntVar(&numArg, "I", def_num, "")
	flag.StringVar(&targetArg, "target", def_target, "")
	flag.StringVar(&targetArg, "t", def_target, "")
	flag.BoolVar(&lowerExtArg, "lower-extension", def_lowerext, "")
	flag.BoolVar(&lowerExtArg, "e", def_lowerext, "")
	flag.BoolVar(&lowerArg, "lowercase", def_lowercase, "")
	flag.BoolVar(&lowerArg, "l", def_lowercase, "")
	flag.BoolVar(&upperArg, "uppercase", def_uppercase, "")
	flag.BoolVar(&upperArg, "u", def_uppercase, "")
	flag.BoolVar(&copyArg, "copy", def_copy, "")
	flag.BoolVar(&copyArg, "c", def_copy, "")
	flag.BoolVar(&dryrunArg, "dry-run", def_dryrun, "")
	flag.BoolVar(&dryrunArg, "n", def_dryrun, "")
	flag.BoolVar(&forceArg, "force", def_force, "")
	flag.BoolVar(&forceArg, "f", def_force, "")
	flag.BoolVar(&recursiveArg, "recursive", def_recursive, "")
	flag.BoolVar(&recursiveArg, "r", def_recursive, "")

	flag.Parse()

	if regexpArg == "" && prefixArg == "" && suffixArg == "" && indexArg == "" && lowerExtArg == false && lowerArg == false && upperArg == false {
		printUsage("At least one of the mandatory actions must be given, nothing to do...")
	}
}

// Write a renamed or a copy of a file to disk
func writeFile(oldname, newname string) {
	// check if the new filename is already present
	_, lstat_err := os.Lstat(newname)
	if lstat_err == nil && forceArg == false {
		fmt.Fprintf(os.Stderr, "File %s already exist! Use -force to override it\n", newname)
		operationSuccessful = operationSuccessful + 1
	}
	if dryrunArg {
		// if dry-run was given, just output the renaming operation
		fmt.Fprintf(os.Stdout, "Renaming %s to %s (dry-run)\n", oldname, newname)
		operationSuccessful = operationSuccessful + 0
	} else {
		if copyArg {
			copyf, create_err := os.Create(newname)
			if create_err != nil {
				fmt.Fprintf(os.Stderr, create_err.Error())
				operationSuccessful = operationSuccessful + 1
			}
			originalf, open_err := os.Open(oldname)
			if open_err != nil {
				fmt.Fprintf(os.Stderr, open_err.Error())
				operationSuccessful = operationSuccessful + 1
			}
			_, copy_err := io.Copy(copyf, originalf)
			if copy_err != nil {
				fmt.Fprintf(os.Stderr, "An error occurred during the copy of %s to %s\n", oldname, newname)
				fmt.Fprintf(os.Stderr, copy_err.Error())
				operationSuccessful = operationSuccessful + 1
			} else {
				fmt.Fprintf(os.Stdout, "Copying %s to %s\n", oldname, newname)
			}
		} else {
			rename_err := os.Rename(oldname, newname)
			if rename_err != nil {
				fmt.Fprintf(os.Stderr, "An error occurred during the renaming of %s to %s\n", oldname, newname)
				fmt.Fprintf(os.Stderr, rename_err.Error())
				operationSuccessful = operationSuccessful + 1
			} else {
				fmt.Fprintf(os.Stdout, "Renaming %s to %s\n", oldname, newname)
			}
		}
	}
}

// Add a prefix string to a name
func addPrefix(names []string, prefix string) int {
	var finalname, dirname string
	for _, f := range names {
		dirname = filepath.Dir(f)
		finalname = filepath.Join(dirname, prefix+filepath.Base(f))
		writeFile(f, finalname)
	}
	return 0
}

// Add a suffix string to a name
func addSuffix(names []string, suffix string) int {
	var finalname, dirname, justname, ext string
	for _, f := range names {
		ext = filepath.Ext(f)
		dirname = filepath.Dir(f)
		justname = strings.TrimSuffix(filepath.Base(f), ext)
		finalname = filepath.Join(dirname, justname+suffix+ext)
		writeFile(f, finalname)
	}
	return 0
}

// Rename a slice of filenames to <newname><count>.<extension>
func indexName(names []string, newname string, count int) int {
	var finalname, dirname, ext string
	for _, f := range names {
		ext = filepath.Ext(f)
		dirname = filepath.Dir(f)
		finalname = fmt.Sprintf("%s/%s%03d%s", dirname, newname, count, ext)
		writeFile(f, finalname)
		count++
	}
	return 0
}

// Make extensions lowercase
func lowercaseExtension(names []string) int {
	var finalname, dirname, basename, ext string
	for _, f := range names {
		dirname = filepath.Dir(f)
		basename = filepath.Base(f)
		ext = filepath.Ext(f)
		finalname = filepath.Join(dirname, strings.TrimSuffix(basename, ext)+strings.ToLower(strings.TrimSuffix(ext, basename)))
		writeFile(f, finalname)
	}
	return 0
}

// Make filenames all lowercase
func lowercaseFiles(names []string) int {
	var finalname, dirname string
	for _, f := range names {
		dirname = filepath.Dir(f)
		finalname = filepath.Join(dirname, strings.ToLower(filepath.Base(f)))
		writeFile(f, finalname)
	}
	return 0
}

// Make filenames all uppercase
func uppercaseFiles(names []string) int {
	var finalname, dirname string
	for _, f := range names {
		dirname = filepath.Dir(f)
		finalname = filepath.Join(dirname, strings.ToUpper(filepath.Base(f)))
		writeFile(f, finalname)
	}
	return 0
}

// Get all files and directories
func getFilesFromDir(dirname string) ([]string, []string) {
	var complete_path string                // final, absolute, path
	var filesindir = make([]os.FileInfo, 0) // files & directories found in path
	var allfiles = make([]string, 0)
	var alldirectories = make([]string, 0)

	dirinfo, lerr := os.Lstat(dirname)
	if lerr != nil {
		fmt.Fprintf(os.Stderr, lerr.Error())
		return alldirectories, allfiles
	}

	// check whether targetArg is an absolute path AND a directory
	if filepath.IsAbs(dirname) && dirinfo.IsDir() {
		complete_path = dirname
	} else {
		abs_path, err := filepath.Abs(dirname)
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
		}
		complete_path = abs_path
	}
	dir, err := os.Open(complete_path)
	defer dir.Close()
	if err != nil {
		err = errors.New(fmt.Sprintf("Target directory %s is not a directory or can't be accessed\n", complete_path))
		fmt.Fprintf(os.Stderr, err.Error())
		return alldirectories, allfiles
	}

	// scan for files/directories in path
	filesindir, read_err := dir.Readdir(0)
	if read_err != nil {
		fmt.Fprintf(os.Stderr, read_err.Error())
		return alldirectories, allfiles
	}

	// check in filesindir slice and separate directories from files in
	// 2 different slices
	for _, f := range filesindir {
		if f.IsDir() {
			alldirectories = append(alldirectories, filepath.Join(complete_path, f.Name()))
		} else {
			allfiles = append(allfiles, filepath.Join(complete_path, f.Name()))
		}
	}

	return alldirectories, allfiles
}

func renameFiles(dir, files []string) int {
	var basename string
	var matchingfiles []string // a slice containing only the files
	// matching the regexp passed as argument (if)
	var result int // the integer returned by each functions,
	// signaling success or failure

	// recursively search on every directory in dir for other
	// files/directories if recursiveArg switch has been enabled
	if dir != nil && recursiveArg == true {
		for _, d := range dir {
			nd, nf := getFilesFromDir(d)
			// if it's a dir, append to []dir
			if len(nd) > 0 {
				for _, i := range nd {
					dir = append(dir, i)
				}
			}
			// if it's a file, append to []files
			if len(nf) > 0 {
				for _, i := range nf {
					files = append(files, i)
				}
			}
		}
	}

	// check if the files should match a given regexp
	if regexpArg != "" {
		for _, f := range files {
			basename = filepath.Base(f)
			// Compile and check if the regexp is a valid one
			compRegexp, err := regexp.Compile(regexpArg)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Invalid regexp: %s\n", regexpArg)
				printUsage("You must give a valid regexp (or none, to operate on all files). Alternatively, add -force to force renaming all files, whether they match the regexp or not")
				return 1
			} else {
				if compRegexp.MatchString(basename) == false {
					continue
				} else {
					matchingfiles = append(matchingfiles, f)
				}
			}
		}
	}

	// if matchingfiles contains something, iterate over it.
	// Otherwise, we simply use the generic files slice
	if len(matchingfiles) >= 1 {
		files = matchingfiles
	} else {
		// different messages whether we have forceArg == true or false
		if regexpArg != "" {
			if forceArg {
				fmt.Fprintf(os.Stdout, "No files matched, including all files anyway (-force enabled)\n")
			} else {
				fmt.Fprintf(os.Stderr, "No files matched, check the correctness of your regexp\n")
				return 1
			}
		}
	}

	if prefixArg != "" {
		result = addPrefix(files, prefixArg)
	}
	if suffixArg != "" {
		result = addSuffix(files, suffixArg)
	}
	if indexArg != "" {
		result = indexName(files, indexArg, numArg)
	}
	if lowerArg == true && upperArg == true {
		// can't use both
		printUsage("Can't use both lowercase and uppercase, choose one only!")
		return 1
	}
	if lowerExtArg == true {
		result = lowercaseExtension(files)
	}
	if lowerArg == true {
		result = lowercaseFiles(files)
	}
	if upperArg == true {
		result = uppercaseFiles(files)
	}

	return result
}

func main() {
	var success_rename int
	var directories, files []string

	flagsInit()

	directories, files = getFilesFromDir(targetArg)

	success_rename = renameFiles(directories, files)

	// check that everything went smoothly
	if success_rename == 0 && operationSuccessful == 0 {
		fmt.Fprintf(os.Stdout, "\nRenaming complete\n")
	} else {
		fmt.Fprintf(os.Stdout, "\nNot all files were correctly renamed, check the previous error messages")
	}
}
<|endoftext|>


<commit_before>package db

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
)

type response struct {
	Ok bool `json:"ok"`
}

func CreateDatabase(dbname, host string) error {

	req, err := http.NewRequest("PUT", fmt.Sprintf("%s/%s", host, dbname), nil)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusCreated {
		return errors.New("Cannot create database")
	}

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

	res := response{}
	err = json.Unmarshal(body, &res)
	if err != nil {
		return err
	}

	if !res.Ok {
		return errors.New("Cannot create database")
	}

	return nil
}

func DeleteDatabase(dbname, host string) error {
	fmt.Print(fmt.Sprintf("URL %s", host))
	req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/%s", host, dbname), nil)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	res := response{}
	err = json.Unmarshal(body, &res)
	if err != nil {
		return err
	}

	if !res.Ok {
		return errors.New("Cannot delete database")
	}

	return nil
}
<commit_msg>Refactor to use golang's url constructor<commit_after>package db

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
)

type response struct {
	Ok bool `json:"ok"`
}

func CreateDatabase(dbname, host string) error {

	url, err := url.Parse(host)
	if err != nil {
		return err
	}

	url.Path = dbname
	req, err := http.NewRequest("PUT", url.String(), nil)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusCreated {
		return errors.New("Cannot create database")
	}

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

	res := response{}
	err = json.Unmarshal(body, &res)
	if err != nil {
		return err
	}

	if !res.Ok {
		return errors.New("Cannot create database")
	}

	return nil
}

func DeleteDatabase(dbname, host string) error {

	url, err := url.Parse(host)
	if err != nil {
		return err
	}

	url.Path = dbname

	req, err := http.NewRequest("DELETE", url.String(), nil)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	res := response{}
	err = json.Unmarshal(body, &res)
	if err != nil {
		return err
	}

	if !res.Ok {
		return errors.New("Cannot delete database")
	}

	return nil
}
<|endoftext|>


<commit_before>package db

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

const (
	TRADB      = ".trago.db"
	chars      = "abcdefghijklmnopqrstuvwxyz1234567890"
	currentDir = "."
)

var (
	ErrFileNotFound = errors.New("Couldn't find .trago.db")
)

type TraDb struct {
	ReplicaId  string
	VersionVec map[string]int
	Files      map[string]FileState
}

type FileState struct {
	Size    int
	MTime   int64
	Version int
	Replica string
	Hash    string
	Mode    uint32
}

type FileData struct {
	Name string
	Data []byte
	Mode uint32
}

type Label uint8

type FileTag struct {
	Label Label
	Mode  uint32
}

type TagList struct {
	Files map[string]FileTag
	Dirs  map[string]FileTag
}

const (
	File = Label(iota)
	Conflict
	Directory
	Deleted
)

// Parse parses a TraDb structure.
// Fails if the given string is not in the correct format.
func Parse(data string) (*TraDb, error) {
	tradb := &TraDb{}
	versionVector := make(map[string]int)

	tradb.Files = make(map[string]FileState)

	for _, line := range strings.Split(data, "\n") {
		line = strings.TrimSpace(line)

		if strings.HasPrefix(line, "#") {
			continue
		}

		fields := strings.Fields(line)
		if len(fields) == 0 {
			continue
		}

		switch fields[0] {
		case "file": // file name size mtime replica:version hash mode
			if len(fields) != 7 {
				continue
			}

			size, err := strconv.Atoi(fields[2])
			if err != nil {
				return nil, err
			}

			mtime, err := strconv.ParseInt(fields[3], 10, 64)
			if err != nil {
				return nil, err
			}

			pair := strings.Split(fields[4], ":")
			replicaId := pair[0]
			ver, err := strconv.Atoi(pair[1])
			if err != nil {
				return nil, err
			}

			sum := fields[5]

			mode, err := strconv.ParseUint(fields[6], 10, 32)
			if err != nil {
				return nil, err
			}

			tradb.Files[fields[1]] = FileState{size, mtime, ver, replicaId, sum, uint32(mode)}
		case "version": // version r1:v1 r2:v2 ...
			for _, entry := range fields[1:] {
				pair := strings.Split(entry, ":") // replica:version pair

				v, err := strconv.Atoi(pair[1])
				if err != nil {
					return nil, err
				}

				versionVector[pair[0]] = v
			}
			tradb.VersionVec = versionVector

		case "replica": // replica replica-id
			if len(fields) != 2 {
				continue
			}
			tradb.ReplicaId = fields[1]
		}
	}

	return tradb, nil
}

// ParseFile parses a TraDb from the db file in the current directory.
func ParseFile() (*TraDb, error) {
	tradb := &TraDb{}

	dbfile, err := os.Open(TRADB)
	if os.IsNotExist(err) {
		log.Println(ErrFileNotFound.Error())
		tradb, err = New()
		if err == nil {
			return tradb, ErrFileNotFound
		} else {
			return nil, err
		}
	} else if err != nil {
		return nil, err
	}
	defer func() { err = dbfile.Close() }()

	bs, err := ioutil.ReadFile(TRADB)
	if err != nil {
		return nil, err
	}

	tradb, err = Parse(string(bs))
	return tradb, err
}

// New creates a new TraDb.
//
// The replica ID is a random string, and the version
// number is set to 1. Checks for files in the current
// directory and stores relevant file state in a map.
func New() (*TraDb, error) {
	replicaId := make([]byte, 16)
	versionVector := make(map[string]int)

	rand.Seed(time.Now().UTC().UnixNano())
	for i, _ := range replicaId {
		replicaId[i] = chars[rand.Intn(len(chars))]
	}
	versionVector[string(replicaId)] = 1 // TODO: check for duplicates

	files, err := readDir(currentDir)
	if err != nil {
		return nil, err
	}

	filemap := make(map[string]FileState)

	for filename, file := range files {
		var err error
		var hashString string

		if !file.IsDir() {
			hashString, err = hash(filename)
			if err != nil {
				return nil, err
			}
		} else {
			hashString = "[dir]"
		}

		fs := FileState{
			Size:    int(file.Size()),
			MTime:   file.ModTime().UTC().UnixNano(),
			Version: 1,
			Replica: string(replicaId),
			Hash:    hashString,
			Mode:    uint32(file.Mode()),
		}
		filemap[filename] = fs
	}

	return &TraDb{string(replicaId), versionVector, filemap}, nil
}

func readDir(dir string) (map[string]os.FileInfo, error) {
	files := make(map[string]os.FileInfo)

	if err := readDirRecursive(dir, files); err != nil {
		return nil, err
	}
	delete(files, TRADB)

	return files, nil
}

func readDirRecursive(dir string, filemap map[string]os.FileInfo) error {
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}

	for _, fileinfo := range files {
		name := filepath.Join(dir, fileinfo.Name())

		filemap[name] = fileinfo
		if fileinfo.IsDir() {
			err := readDirRecursive(name, filemap)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// Write writes a TraDb to the db file .trago.db.
func (tradb *TraDb) Write() error {
	var pairs []string

	for replicaId, version := range tradb.VersionVec {
		entry := strings.Join([]string{replicaId, strconv.Itoa(version)}, ":")
		pairs = append(pairs, entry)
	}

	versionVector := strings.Join(pairs, " ")

	preamble := fmt.Sprintf(
		"replica %s\nversion %s\n# files\n",
		tradb.ReplicaId,
		versionVector,
	)

	fileEntries := make([]string, len(tradb.Files))

	i := 0
	for filename, info := range tradb.Files {
		var err error
		var hashString string

		if mode := os.FileMode(info.Mode); mode.IsDir() {
			hashString = "[dir]"
		} else {
			hashString, err = hash(filename)
			if err != nil {
				return err
			}
		}

		fileEntries[i] = fmt.Sprintf(
			"file %s %d %d %s:%d %s %d",
			filename,
			info.Size,
			info.MTime,
			info.Replica,
			info.Version,
			hashString,
			info.Mode,
		)
		i++
	}

	entryString := strings.Join(fileEntries, "\n")
	dataToWrite := []byte(preamble + entryString)

	return ioutil.WriteFile(TRADB, dataToWrite, 0644)
}

// Update looks for modified files in the current directory
// and updates the filemap accordingly.
func (db *TraDb) Update() error {
	files, err := readDir(currentDir)
	if err != nil {
		return err
	}

	visitedFiles := make(map[string]bool)
	ourVersion := db.VersionVec[db.ReplicaId]

	for filename, file := range files {
		var err error
		var hashString string

		if file.IsDir() {
			hashString = "[dir]"
		} else {
			hashString, err = hash(filename)
			if err != nil {
				return err
			}
		}

		dbRecord := db.Files[filename]
		if dbRecord.Version == 0 {
			log.Printf("found a new file: %s\n", filename)

			db.Files[filename] = FileState{
				Size:    int(file.Size()),
				MTime:   file.ModTime().UTC().UnixNano(),
				Version: ourVersion,
				Replica: db.ReplicaId,
				Hash:    hashString,
				Mode:    uint32(file.Mode()),
			}
		} else if file.IsDir() {
			visitedFiles[filename] = true
			continue
		} else if dbRecord.MTime < file.ModTime().UTC().UnixNano() {
			log.Printf("found an updated file: %s\n", filename)
			dbRecord.MTime = file.ModTime().UTC().UnixNano()
			dbRecord.Version = ourVersion
			dbRecord.Mode = uint32(file.Mode())
			dbRecord.Replica = db.ReplicaId
			dbRecord.Hash = hashString
			db.Files[filename] = dbRecord
		} else {
			log.Printf("file unchanged: %s\n", filename)
		}
		visitedFiles[filename] = true
	}

	// Check for deleted files and directories.
	for filename, _ := range db.Files {
		if !visitedFiles[filename] {
			log.Printf("update: deleting entry for %s\n", filename)
			delete(db.Files, filename)
		}
	}

	return nil
}

// Compare compares two TraDbs.
// Returns a TagList which gives the FileTag for each changed file.
func (local *TraDb) Compare(remote *TraDb) (TagList, error) {
	var tags TagList

	tags.Dirs = make(map[string]FileTag)
	tags.Files = make(map[string]FileTag)
	remoteFiles := remote.Files

	for file, state := range local.Files {
		isDir := os.FileMode(state.Mode).IsDir()
		remoteState := remoteFiles[file]

		// File or directory doesn't exist on the remote replica.
		if remoteState.Version == 0 {
			if state.Version <= remote.VersionVec[state.Replica] {
				log.Printf("deleting: %s\n", file)

				if isDir {
					tags.Dirs[file] = FileTag{Deleted, 0}
				} else {
					tags.Files[file] = FileTag{Deleted, 0}
				}
				continue
			}
		}

		if isDir {
			continue
		}

		changed, err := isFileChanged(state, remoteState)
		if err != nil {
			return tags, err
		}

		if changed {
			if local.VersionVec[remoteState.Replica] >= remoteState.Version {
				log.Printf("keeping: %s\n", file)
			} else if remote.VersionVec[state.Replica] >= state.Version {
				log.Printf("downloading: %s\n", file)
				tags.Files[file] = FileTag{File, remoteFiles[file].Mode}

				dir := filepath.Dir(file)
				tags.Dirs[dir] = FileTag{Directory, remoteFiles[dir].Mode}
			} else {
				log.Printf("conflict: %s\n", file)
				tags.Files[file] = FileTag{Conflict, 0}
			}
		} else {
			log.Printf("unchanged: %s\n", file)
		}
	}

	for file, state := range remoteFiles {
		if mode := os.FileMode(state.Mode); mode.IsDir() {
			continue
		}

		if local.Files[file].Version > 0 {
			continue
		} else if state.Version > local.VersionVec[state.Replica] {
			log.Printf("downloading new file: %s\n", file)
			tags.Files[file] = FileTag{File, remoteFiles[file].Mode}

			dir := filepath.Dir(file)
			tags.Dirs[dir] = FileTag{Directory, remoteFiles[dir].Mode}
		}
	}

	return tags, nil
}

func (db *TraDb) UpdateMTimes() error {
	files, err := readDir(currentDir)
	if err != nil {
		return err
	}

	for filename, file := range files {
		if file.IsDir() {
			continue
		}

		dbRecord := db.Files[filename]
		if mtime := file.ModTime().UTC().UnixNano(); mtime > dbRecord.MTime {
			log.Printf("updating mtime: %s\n", filename)
			dbRecord.MTime = mtime
			db.Files[filename] = dbRecord
		}
	}

	return nil
}

func CombineVectors(v1 map[string]int, v2 map[string]int) {
	for replica, version := range v1 {
		if v2[replica] > version {
			v1[replica] = v2[replica]
		}
	}

	for replica, version := range v2 {
		if v1[replica] < version {
			v1[replica] = version
		}
	}
}

func hash(filename string) (string, error) {
	f, err := os.Open(filename)
	if err != nil {
		return filename, err
	}
	defer func() { err = f.Close() }()

	h := md5.New()
	_, err = io.Copy(h, f)
	if err != nil {
		return filename, err
	}

	return hex.EncodeToString(h.Sum(nil)), err
}

func isFileChanged(fs1 FileState, fs2 FileState) (bool, error) {
	if fs1.MTime != fs2.MTime || fs1.Size != fs2.Size {
		h1, err := hex.DecodeString(fs1.Hash)
		if err != nil {
			return false, err
		}

		h2, err := hex.DecodeString(fs2.Hash)
		if err != nil {
			return false, err
		}

		return !bytes.Equal(h1, h2), nil
	}
	return false, nil
}
<commit_msg>detect new directories correctly<commit_after>package db

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

const (
	TRADB      = ".trago.db"
	chars      = "abcdefghijklmnopqrstuvwxyz1234567890"
	currentDir = "."
)

var (
	ErrFileNotFound = errors.New("Couldn't find .trago.db")
)

type TraDb struct {
	ReplicaId  string
	VersionVec map[string]int
	Files      map[string]FileState
}

type FileState struct {
	Size    int
	MTime   int64
	Version int
	Replica string
	Hash    string
	Mode    uint32
}

type FileData struct {
	Name string
	Data []byte
	Mode uint32
}

type Label uint8

type FileTag struct {
	Label Label
	Mode  uint32
}

type TagList struct {
	Files map[string]FileTag
	Dirs  map[string]FileTag
}

const (
	File = Label(iota)
	Conflict
	Directory
	Deleted
)

// Parse parses a TraDb structure.
// Fails if the given string is not in the correct format.
func Parse(data string) (*TraDb, error) {
	tradb := &TraDb{}
	versionVector := make(map[string]int)

	tradb.Files = make(map[string]FileState)

	for _, line := range strings.Split(data, "\n") {
		line = strings.TrimSpace(line)

		if strings.HasPrefix(line, "#") {
			continue
		}

		fields := strings.Fields(line)
		if len(fields) == 0 {
			continue
		}

		switch fields[0] {
		case "file": // file name size mtime replica:version hash mode
			if len(fields) != 7 {
				continue
			}

			size, err := strconv.Atoi(fields[2])
			if err != nil {
				return nil, err
			}

			mtime, err := strconv.ParseInt(fields[3], 10, 64)
			if err != nil {
				return nil, err
			}

			pair := strings.Split(fields[4], ":")
			replicaId := pair[0]
			ver, err := strconv.Atoi(pair[1])
			if err != nil {
				return nil, err
			}

			sum := fields[5]

			mode, err := strconv.ParseUint(fields[6], 10, 32)
			if err != nil {
				return nil, err
			}

			tradb.Files[fields[1]] = FileState{size, mtime, ver, replicaId, sum, uint32(mode)}
		case "version": // version r1:v1 r2:v2 ...
			for _, entry := range fields[1:] {
				pair := strings.Split(entry, ":") // replica:version pair

				v, err := strconv.Atoi(pair[1])
				if err != nil {
					return nil, err
				}

				versionVector[pair[0]] = v
			}
			tradb.VersionVec = versionVector

		case "replica": // replica replica-id
			if len(fields) != 2 {
				continue
			}
			tradb.ReplicaId = fields[1]
		}
	}

	return tradb, nil
}

// ParseFile parses a TraDb from the db file in the current directory.
func ParseFile() (*TraDb, error) {
	tradb := &TraDb{}

	dbfile, err := os.Open(TRADB)
	if os.IsNotExist(err) {
		log.Println(ErrFileNotFound.Error())
		tradb, err = New()
		if err == nil {
			return tradb, ErrFileNotFound
		} else {
			return nil, err
		}
	} else if err != nil {
		return nil, err
	}
	defer func() { err = dbfile.Close() }()

	bs, err := ioutil.ReadFile(TRADB)
	if err != nil {
		return nil, err
	}

	tradb, err = Parse(string(bs))
	return tradb, err
}

// New creates a new TraDb.
//
// The replica ID is a random string, and the version
// number is set to 1. Checks for files in the current
// directory and stores relevant file state in a map.
func New() (*TraDb, error) {
	replicaId := make([]byte, 16)
	versionVector := make(map[string]int)

	rand.Seed(time.Now().UTC().UnixNano())
	for i, _ := range replicaId {
		replicaId[i] = chars[rand.Intn(len(chars))]
	}
	versionVector[string(replicaId)] = 1 // TODO: check for duplicates

	files, err := readDir(currentDir)
	if err != nil {
		return nil, err
	}

	filemap := make(map[string]FileState)

	for filename, file := range files {
		var err error
		var hashString string

		if !file.IsDir() {
			hashString, err = hash(filename)
			if err != nil {
				return nil, err
			}
		} else {
			hashString = "[dir]"
		}

		fs := FileState{
			Size:    int(file.Size()),
			MTime:   file.ModTime().UTC().UnixNano(),
			Version: 1,
			Replica: string(replicaId),
			Hash:    hashString,
			Mode:    uint32(file.Mode()),
		}
		filemap[filename] = fs
	}

	return &TraDb{string(replicaId), versionVector, filemap}, nil
}

func readDir(dir string) (map[string]os.FileInfo, error) {
	files := make(map[string]os.FileInfo)

	if err := readDirRecursive(dir, files); err != nil {
		return nil, err
	}
	delete(files, TRADB)

	return files, nil
}

func readDirRecursive(dir string, filemap map[string]os.FileInfo) error {
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}

	for _, fileinfo := range files {
		name := filepath.Join(dir, fileinfo.Name())

		filemap[name] = fileinfo
		if fileinfo.IsDir() {
			err := readDirRecursive(name, filemap)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// Write writes a TraDb to the db file .trago.db.
func (tradb *TraDb) Write() error {
	var pairs []string

	for replicaId, version := range tradb.VersionVec {
		entry := strings.Join([]string{replicaId, strconv.Itoa(version)}, ":")
		pairs = append(pairs, entry)
	}

	versionVector := strings.Join(pairs, " ")

	preamble := fmt.Sprintf(
		"replica %s\nversion %s\n# files\n",
		tradb.ReplicaId,
		versionVector,
	)

	fileEntries := make([]string, len(tradb.Files))

	i := 0
	for filename, info := range tradb.Files {
		var err error
		var hashString string

		if mode := os.FileMode(info.Mode); mode.IsDir() {
			hashString = "[dir]"
		} else {
			hashString, err = hash(filename)
			if err != nil {
				return err
			}
		}

		fileEntries[i] = fmt.Sprintf(
			"file %s %d %d %s:%d %s %d",
			filename,
			info.Size,
			info.MTime,
			info.Replica,
			info.Version,
			hashString,
			info.Mode,
		)
		i++
	}

	entryString := strings.Join(fileEntries, "\n")
	dataToWrite := []byte(preamble + entryString)

	return ioutil.WriteFile(TRADB, dataToWrite, 0644)
}

// Update looks for modified files in the current directory
// and updates the filemap accordingly.
func (db *TraDb) Update() error {
	files, err := readDir(currentDir)
	if err != nil {
		return err
	}

	visitedFiles := make(map[string]bool)
	ourVersion := db.VersionVec[db.ReplicaId]

	for filename, file := range files {
		var err error
		var hashString string

		if file.IsDir() {
			hashString = "[dir]"
		} else {
			hashString, err = hash(filename)
			if err != nil {
				return err
			}
		}

		dbRecord := db.Files[filename]
		if dbRecord.Version == 0 {
			log.Printf("found a new file: %s\n", filename)

			db.Files[filename] = FileState{
				Size:    int(file.Size()),
				MTime:   file.ModTime().UTC().UnixNano(),
				Version: ourVersion,
				Replica: db.ReplicaId,
				Hash:    hashString,
				Mode:    uint32(file.Mode()),
			}
		} else if file.IsDir() {
			visitedFiles[filename] = true
			continue
		} else if dbRecord.MTime < file.ModTime().UTC().UnixNano() {
			log.Printf("found an updated file: %s\n", filename)
			dbRecord.MTime = file.ModTime().UTC().UnixNano()
			dbRecord.Version = ourVersion
			dbRecord.Mode = uint32(file.Mode())
			dbRecord.Replica = db.ReplicaId
			dbRecord.Hash = hashString
			db.Files[filename] = dbRecord
		} else {
			log.Printf("file unchanged: %s\n", filename)
		}
		visitedFiles[filename] = true
	}

	// Check for deleted files and directories.
	for filename, _ := range db.Files {
		if !visitedFiles[filename] {
			log.Printf("update: deleting entry for %s\n", filename)
			delete(db.Files, filename)
		}
	}

	return nil
}

// Compare compares two TraDbs.
// Returns a TagList which gives the FileTag for each changed file.
func (local *TraDb) Compare(remote *TraDb) (TagList, error) {
	var tags TagList

	tags.Dirs = make(map[string]FileTag)
	tags.Files = make(map[string]FileTag)
	remoteFiles := remote.Files

	for file, state := range local.Files {
		isDir := os.FileMode(state.Mode).IsDir()
		remoteState := remoteFiles[file]

		// File or directory doesn't exist on the remote replica.
		if remoteState.Version == 0 {
			if state.Version <= remote.VersionVec[state.Replica] {
				log.Printf("deleting: %s\n", file)

				if isDir {
					tags.Dirs[file] = FileTag{Deleted, 0}
				} else {
					tags.Files[file] = FileTag{Deleted, 0}
				}
				continue
			}
		}

		if isDir {
			continue
		}

		changed, err := isFileChanged(state, remoteState)
		if err != nil {
			return tags, err
		}

		if changed {
			if local.VersionVec[remoteState.Replica] >= remoteState.Version {
				log.Printf("keeping: %s\n", file)
			} else if remote.VersionVec[state.Replica] >= state.Version {
				log.Printf("downloading: %s\n", file)
				tags.Files[file] = FileTag{File, remoteFiles[file].Mode}
			} else {
				log.Printf("conflict: %s\n", file)
				tags.Files[file] = FileTag{Conflict, 0}
			}
		} else {
			log.Printf("unchanged: %s\n", file)
		}
	}

	for file, state := range remoteFiles {
		if local.Files[file].Version > 0 {
			continue
		} else if state.Version > local.VersionVec[state.Replica] {
			if mode := os.FileMode(state.Mode); mode.IsDir() {
				log.Printf("new directory: %s\n", file)
				tags.Dirs[file] = FileTag{Directory, remoteFiles[file].Mode}
			} else {
				log.Printf("downloading new file: %s\n", file)
				tags.Files[file] = FileTag{File, remoteFiles[file].Mode}
			}
		}
	}

	return tags, nil
}

func (db *TraDb) UpdateMTimes() error {
	files, err := readDir(currentDir)
	if err != nil {
		return err
	}

	for filename, file := range files {
		if file.IsDir() {
			continue
		}

		dbRecord := db.Files[filename]
		if mtime := file.ModTime().UTC().UnixNano(); mtime > dbRecord.MTime {
			log.Printf("updating mtime: %s\n", filename)
			dbRecord.MTime = mtime
			db.Files[filename] = dbRecord
		}
	}

	return nil
}

func CombineVectors(v1 map[string]int, v2 map[string]int) {
	for replica, version := range v1 {
		if v2[replica] > version {
			v1[replica] = v2[replica]
		}
	}

	for replica, version := range v2 {
		if v1[replica] < version {
			v1[replica] = version
		}
	}
}

func hash(filename string) (string, error) {
	f, err := os.Open(filename)
	if err != nil {
		return filename, err
	}
	defer func() { err = f.Close() }()

	h := md5.New()
	_, err = io.Copy(h, f)
	if err != nil {
		return filename, err
	}

	return hex.EncodeToString(h.Sum(nil)), err
}

func isFileChanged(fs1 FileState, fs2 FileState) (bool, error) {
	if fs1.MTime != fs2.MTime || fs1.Size != fs2.Size {
		h1, err := hex.DecodeString(fs1.Hash)
		if err != nil {
			return false, err
		}

		h2, err := hex.DecodeString(fs2.Hash)
		if err != nil {
			return false, err
		}

		return !bytes.Equal(h1, h2), nil
	}
	return false, nil
}
<|endoftext|>


<commit_before>package db

import "fmt"

//QLQeryer implements the Queryer interface for ql database.
type QLQeryer struct {
}

//CreateSession retruns a Query for creating new session.
func (ql QLQeryer) CreateSession(table string) Query {
	var query = `
	BEGIN TRANSACTION;
	  INSERT INTO %s (key, data, created_on, updated_on, expires_on)
		VALUES ($1,$2,now(),now(),$3);
	COMMIT;
	`
	query = fmt.Sprintf(query, table)
	return NewQuery(query, true, "key", "data", "expires_on")
}

//FindSessionByKey returns a query for finding a session by key.
func (ql QLQeryer) FindSessionByKey(table string) Query {
	var query = `
	SELECT * from %s WHERE key=$1 LIMIT 1;
	`
	query = fmt.Sprintf(query, table)
	return NewQuery(query, false, "key")
}

//UpdateSession updaates session data.
func (ql QLQeryer) UpdateSession(table string) Query {
	var query = `
BEGIN TRANSACTION;
  UPDATE %s
    data = $2,
    updated_on = now(),
  WHERE key==$1;
COMMIT;
	`
	query = fmt.Sprintf(query, table)
	return NewQuery(query, true, "key", "data")
}

//DeleteSession deletes a session.
func (ql QLQeryer) DeleteSession(table string) Query {
	var query = `
BEGIN TRANSACTION;
   DELETE FROM %s
  WHERE key==$1;
COMMIT;
	`
	query = fmt.Sprintf(query, table)
	return NewQuery(query, true, "key")
}

//CreateUser creates a new user.
func (ql QLQeryer) CreateUser(table string) Query {
	var query = `
	BEGIN TRANSACTION;
	  INSERT INTO %s (username,password,email,created_at,updated_at)
		VALUES ($1,$2,$3,now(),now());
	COMMIT;
	`
	query = fmt.Sprintf(query, table)
	return NewQuery(query, true, "username", "password", "email")
}

//FindUserBy finds a user by the speicified field.
func (ql QLQeryer) FindUserBy(table, field string) Query {
	var query = `
	SELECT id(),username,password,email,created_at,updated_at
	from %s WHERE %s LIKE $1 LIMIT 1;
	`
	query = fmt.Sprintf(query, table, field)
	return NewQuery(query, false, field)
}
<commit_msg>Use Eqlaity in Queries<commit_after>package db

import "fmt"

//QLQeryer implements the Queryer interface for ql database.
type QLQeryer struct {
}

//CreateSession retruns a Query for creating new session.
func (ql QLQeryer) CreateSession(table string) Query {
	var query = `
	BEGIN TRANSACTION;
	  INSERT INTO %s (key, data, created_on, updated_on, expires_on)
		VALUES ($1,$2,now(),now(),$3);
	COMMIT;
	`
	query = fmt.Sprintf(query, table)
	return NewQuery(query, true, "key", "data", "expires_on")
}

//FindSessionByKey returns a query for finding a session by key.
func (ql QLQeryer) FindSessionByKey(table string) Query {
	var query = `
	SELECT * from %s WHERE key=$1 LIMIT 1;
	`
	query = fmt.Sprintf(query, table)
	return NewQuery(query, false, "key")
}

//UpdateSession updaates session data.
func (ql QLQeryer) UpdateSession(table string) Query {
	var query = `
BEGIN TRANSACTION;
  UPDATE %s
    data = $2,
    updated_on = now(),
  WHERE key=$1;
COMMIT;
	`
	query = fmt.Sprintf(query, table)
	return NewQuery(query, true, "key", "data")
}

//DeleteSession deletes a session.
func (ql QLQeryer) DeleteSession(table string) Query {
	var query = `
BEGIN TRANSACTION;
   DELETE FROM %s
  WHERE key=$1;
COMMIT;
	`
	query = fmt.Sprintf(query, table)
	return NewQuery(query, true, "key")
}

//CreateUser creates a new user.
func (ql QLQeryer) CreateUser(table string) Query {
	var query = `
	BEGIN TRANSACTION;
	  INSERT INTO %s (username,password,email,created_at,updated_at)
		VALUES ($1,$2,$3,now(),now());
	COMMIT;
	`
	query = fmt.Sprintf(query, table)
	return NewQuery(query, true, "username", "password", "email")
}

//FindUserBy finds a user by the speicified field.
func (ql QLQeryer) FindUserBy(table, field string) Query {
	var query = `
	SELECT id(),username,password,email,created_at,updated_at
	from %s WHERE %s=$1 LIMIT 1;
	`
	query = fmt.Sprintf(query, table, field)
	return NewQuery(query, false, field)
}
<|endoftext|>