<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"

	"github.com/urfave/cli"
	"github.com/motemen/ghq/utils"
)

var Commands = []cli.Command{
	commandGet,
	commandList,
	commandLook,
	commandImport,
	commandRoot,
}

var cloneFlags = []cli.Flag{
	cli.BoolFlag{Name: "update, u", Usage: "Update local repository if cloned already"},
	cli.BoolFlag{Name: "p", Usage: "Clone with SSH"},
	cli.BoolFlag{Name: "shallow", Usage: "Do a shallow clone"},
}

var commandGet = cli.Command{
	Name:  "get",
	Usage: "Clone/sync with a remote repository",
	Description: `
    Clone a GitHub repository under ghq root direcotry. If the repository is
    already cloned to local, nothing will happen unless '-u' ('--update')
    flag is supplied, in which case 'git remote update' is executed.
    When you use '-p' option, the repository is cloned via SSH.
`,
	Action: doGet,
	Flags:  cloneFlags,
}

var commandList = cli.Command{
	Name:  "list",
	Usage: "List local repositories",
	Description: `
    List locally cloned repositories. If a query argument is given, only
    repositories whose names contain that query text are listed. '-e'
    ('--exact') forces the match to be an exact one (i.e. the query equals to
    _project_ or _user_/_project_) If '-p' ('--full-path') is given, the full paths
    to the repository root are printed instead of relative ones.
`,
	Action: doList,
	Flags: []cli.Flag{
		cli.BoolFlag{Name: "exact, e", Usage: "Perform an exact match"},
		cli.BoolFlag{Name: "full-path, p", Usage: "Print full paths"},
		cli.BoolFlag{Name: "unique", Usage: "Print unique subpaths"},
	},
}

var commandLook = cli.Command{
	Name:  "look",
	Usage: "Look into a local repository",
	Description: `
    Look into a locally cloned repository with the shell.
`,
	Action: doLook,
}

var commandImport = cli.Command{
	Name:   "import",
	Usage:  "Bulk get repositories from stdin",
	Action: doImport,
	Flags:  cloneFlags,
}

var commandRoot = cli.Command{
	Name:   "root",
	Usage:  "Show repositories' root",
	Action: doRoot,
	Flags: []cli.Flag{
		cli.BoolFlag{Name: "all", Usage: "Show all roots"},
	},
}

type commandDoc struct {
	Parent    string
	Arguments string
}

var commandDocs = map[string]commandDoc{
	"get":    {"", "[-u] <repository URL> | [-u] [-p] <user>/<project>"},
	"list":   {"", "[-p] [-e] [<query>]"},
	"look":   {"", "<project> | <user>/<project> | <host>/<user>/<project>"},
	"import": {"", "< file"},
	"root":   {"", ""},
}

// Makes template conditionals to generate per-command documents.
func mkCommandsTemplate(genTemplate func(commandDoc) string) string {
	template := "{{if false}}"
	for _, command := range append(Commands) {
		template = template + fmt.Sprintf("{{else if (eq .Name %q)}}%s", command.Name, genTemplate(commandDocs[command.Name]))
	}
	return template + "{{end}}"
}

func init() {
	argsTemplate := mkCommandsTemplate(func(doc commandDoc) string { return doc.Arguments })
	parentTemplate := mkCommandsTemplate(func(doc commandDoc) string { return string(strings.TrimLeft(doc.Parent+" ", " ")) })

	cli.CommandHelpTemplate = `NAME:
    {{.Name}} - {{.Usage}}

USAGE:
    ghq ` + parentTemplate + `{{.Name}} ` + argsTemplate + `
{{if (len .Description)}}
DESCRIPTION: {{.Description}}
{{end}}{{if (len .Flags)}}
OPTIONS:
    {{range .Flags}}{{.}}
    {{end}}
{{end}}`
}

func doGet(c *cli.Context) error {
	argURL := c.Args().Get(0)
	doUpdate := c.Bool("update")
	isShallow := c.Bool("shallow")

	if argURL == "" {
		cli.ShowCommandHelp(c, "get")
		os.Exit(1)
	}

	// If argURL is a "./foo" or "../bar" form,
	// find repository name trailing after github.com/USER/.
	parts := strings.Split(argURL, string(filepath.Separator))
	if parts[0] == "." || parts[0] == ".." {
		if wd, err := os.Getwd(); err == nil {
			path := filepath.Clean(filepath.Join(wd, filepath.Join(parts...)))

			var repoPath string
			for _, r := range localRepositoryRoots() {
				p := strings.TrimPrefix(path, r+string(filepath.Separator))
				if p != path && (repoPath == "" || len(p) < len(repoPath)) {
					repoPath = p
				}
			}

			if repoPath != "" {
				// Guess it
				utils.Log("resolved", fmt.Sprintf("relative %q to %q", argURL, "https://"+repoPath))
				argURL = "https://" + repoPath
			}
		}
	}

	url, err := NewURL(argURL)
	utils.DieIf(err)

	isSSH := c.Bool("p")
	if isSSH {
		// Assume Git repository if `-p` is given.
		url, err = ConvertGitURLHTTPToSSH(url)
		utils.DieIf(err)
	}

	remote, err := NewRemoteRepository(url)
	utils.DieIf(err)

	if remote.IsValid() == false {
		utils.Log("error", fmt.Sprintf("Not a valid repository: %s", url))
		os.Exit(1)
	}

	getRemoteRepository(remote, doUpdate, isShallow)
	return nil
}

// getRemoteRepository clones or updates a remote repository remote.
// If doUpdate is true, updates the locally cloned repository. Otherwise does nothing.
// If isShallow is true, does shallow cloning. (no effect if already cloned or the VCS is Mercurial and git-svn)
func getRemoteRepository(remote RemoteRepository, doUpdate bool, isShallow bool) {
	remoteURL := remote.URL()
	local := LocalRepositoryFromURL(remoteURL)

	path := local.FullPath
	newPath := false

	_, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			newPath = true
			err = nil
		}
		utils.PanicIf(err)
	}

	if newPath {
		utils.Log("clone", fmt.Sprintf("%s -> %s", remoteURL, path))

		vcs := remote.VCS()
		if vcs == nil {
			utils.Log("error", fmt.Sprintf("Could not find version control system: %s", remoteURL))
			os.Exit(1)
		}

		err := vcs.Clone(remoteURL, path, isShallow)
		if err != nil {
			utils.Log("error", err.Error())
			os.Exit(1)
		}
	} else {
		if doUpdate {
			utils.Log("update", path)
			local.VCS().Update(path)
		} else {
			utils.Log("exists", path)
		}
	}
}

func doList(c *cli.Context) error {
	query := c.Args().First()
	exact := c.Bool("exact")
	printFullPaths := c.Bool("full-path")
	printUniquePaths := c.Bool("unique")

	var filterFn func(*LocalRepository) bool
	if query == "" {
		filterFn = func(_ *LocalRepository) bool {
			return true
		}
	} else if exact {
		filterFn = func(repo *LocalRepository) bool {
			return repo.Matches(query)
		}
	} else {
		filterFn = func(repo *LocalRepository) bool {
			return strings.Contains(repo.NonHostPath(), query)
		}
	}

	repos := []*LocalRepository{}

	walkLocalRepositories(func(repo *LocalRepository) {
		if filterFn(repo) == false {
			return
		}

		repos = append(repos, repo)
	})

	if printUniquePaths {
		subpathCount := map[string]int{} // Count duplicated subpaths (ex. foo/dotfiles and bar/dotfiles)
		reposCount := map[string]int{}   // Check duplicated repositories among roots

		// Primary first
		for _, repo := range repos {
			if reposCount[repo.RelPath] == 0 {
				for _, p := range repo.Subpaths() {
					subpathCount[p] = subpathCount[p] + 1
				}
			}

			reposCount[repo.RelPath] = reposCount[repo.RelPath] + 1
		}

		for _, repo := range repos {
			if reposCount[repo.RelPath] > 1 && repo.IsUnderPrimaryRoot() == false {
				continue
			}

			for _, p := range repo.Subpaths() {
				if subpathCount[p] == 1 {
					fmt.Println(p)
					break
				}
			}
		}
	} else {
		for _, repo := range repos {
			if printFullPaths {
				fmt.Println(repo.FullPath)
			} else {
				fmt.Println(repo.RelPath)
			}
		}
	}
	return nil
}

func doLook(c *cli.Context) error {
	name := c.Args().First()

	if name == "" {
		cli.ShowCommandHelp(c, "look")
		os.Exit(1)
	}

	reposFound := []*LocalRepository{}
	walkLocalRepositories(func(repo *LocalRepository) {
		if repo.Matches(name) {
			reposFound = append(reposFound, repo)
		}
	})

	if len(reposFound) == 0 {
		url, err := NewURL(name)

		if err == nil {
			repo := LocalRepositoryFromURL(url)
			_, err := os.Stat(repo.FullPath)

			// if the directory exists
			if err == nil {
				reposFound = append(reposFound, repo)
			}
		}
	}

	switch len(reposFound) {
	case 0:
		utils.Log("error", "No repository found")
		os.Exit(1)

	case 1:
		if runtime.GOOS == "windows" {
			cmd := exec.Command(os.Getenv("COMSPEC"))
			cmd.Stdin = os.Stdin
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			cmd.Dir = reposFound[0].FullPath
			err := cmd.Start()
			if err == nil {
				cmd.Wait()
				os.Exit(0)
			}
		} else {
			shell := os.Getenv("SHELL")
			if shell == "" {
				shell = "/bin/sh"
			}

			utils.Log("cd", reposFound[0].FullPath)
			err := os.Chdir(reposFound[0].FullPath)
			utils.PanicIf(err)

			env := append(syscall.Environ(), "GHQ_LOOK="+reposFound[0].RelPath)
			syscall.Exec(shell, []string{shell}, env)
		}

	default:
		utils.Log("error", "More than one repositories are found; Try more precise name")
		for _, repo := range reposFound {
			utils.Log("error", "- "+strings.Join(repo.PathParts, "/"))
		}
	}
	return nil
}

func doImport(c *cli.Context) error {
	var (
		doUpdate  = c.Bool("update")
		isSSH     = c.Bool("p")
		isShallow = c.Bool("shallow")
	)

	var (
		in       io.Reader
		finalize func() error
	)

	if len(c.Args()) == 0 {
		// `ghq import` reads URLs from stdin
		in = os.Stdin
		finalize = func() error { return nil }
	} else {
		// Handle `ghq import starred motemen` case
		// with `git config --global ghq.import.starred "!github-list-starred"`
		subCommand := c.Args().First()
		command, err := GitConfigSingle("ghq.import." + subCommand)
		if err == nil && command == "" {
			err = fmt.Errorf("ghq.import.%s configuration not found", subCommand)
		}
		utils.DieIf(err)

		// execute `sh -c 'COMMAND "$@"' -- ARG...`
		// TODO: Windows
		command = strings.TrimLeft(command, "!")
		shellCommand := append([]string{"sh", "-c", command + ` "$@"`, "--"}, c.Args().Tail()...)

		utils.Log("run", strings.Join(append([]string{command}, c.Args().Tail()...), " "))

		cmd := exec.Command(shellCommand[0], shellCommand[1:]...)
		cmd.Stderr = os.Stderr

		in, err = cmd.StdoutPipe()
		utils.DieIf(err)

		err = cmd.Start()
		utils.DieIf(err)

		finalize = cmd.Wait
	}

	scanner := bufio.NewScanner(in)
	for scanner.Scan() {
		line := scanner.Text()
		url, err := NewURL(line)
		if err != nil {
			utils.Log("error", fmt.Sprintf("Could not parse URL <%s>: %s", line, err))
			continue
		}
		if isSSH {
			url, err = ConvertGitURLHTTPToSSH(url)
			if err != nil {
				utils.Log("error", fmt.Sprintf("Could not convert URL <%s>: %s", url, err))
				continue
			}
		}

		remote, err := NewRemoteRepository(url)
		if utils.ErrorIf(err) {
			continue
		}
		if remote.IsValid() == false {
			utils.Log("error", fmt.Sprintf("Not a valid repository: %s", url))
			continue
		}

		getRemoteRepository(remote, doUpdate, isShallow)
	}
	if err := scanner.Err(); err != nil {
		utils.Log("error", fmt.Sprintf("While reading input: %s", err))
		os.Exit(1)
	}

	utils.DieIf(finalize())
	return nil
}

func doRoot(c *cli.Context) error {
	all := c.Bool("all")
	if all {
		for _, root := range localRepositoryRoots() {
			fmt.Println(root)
		}
	} else {
		fmt.Println(primaryLocalRepositoryRoot())
	}
	return nil
}
<commit_msg>fix typo direcotry -> directory<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"

	"github.com/urfave/cli"
	"github.com/motemen/ghq/utils"
)

var Commands = []cli.Command{
	commandGet,
	commandList,
	commandLook,
	commandImport,
	commandRoot,
}

var cloneFlags = []cli.Flag{
	cli.BoolFlag{Name: "update, u", Usage: "Update local repository if cloned already"},
	cli.BoolFlag{Name: "p", Usage: "Clone with SSH"},
	cli.BoolFlag{Name: "shallow", Usage: "Do a shallow clone"},
}

var commandGet = cli.Command{
	Name:  "get",
	Usage: "Clone/sync with a remote repository",
	Description: `
    Clone a GitHub repository under ghq root directory. If the repository is
    already cloned to local, nothing will happen unless '-u' ('--update')
    flag is supplied, in which case 'git remote update' is executed.
    When you use '-p' option, the repository is cloned via SSH.
`,
	Action: doGet,
	Flags:  cloneFlags,
}

var commandList = cli.Command{
	Name:  "list",
	Usage: "List local repositories",
	Description: `
    List locally cloned repositories. If a query argument is given, only
    repositories whose names contain that query text are listed. '-e'
    ('--exact') forces the match to be an exact one (i.e. the query equals to
    _project_ or _user_/_project_) If '-p' ('--full-path') is given, the full paths
    to the repository root are printed instead of relative ones.
`,
	Action: doList,
	Flags: []cli.Flag{
		cli.BoolFlag{Name: "exact, e", Usage: "Perform an exact match"},
		cli.BoolFlag{Name: "full-path, p", Usage: "Print full paths"},
		cli.BoolFlag{Name: "unique", Usage: "Print unique subpaths"},
	},
}

var commandLook = cli.Command{
	Name:  "look",
	Usage: "Look into a local repository",
	Description: `
    Look into a locally cloned repository with the shell.
`,
	Action: doLook,
}

var commandImport = cli.Command{
	Name:   "import",
	Usage:  "Bulk get repositories from stdin",
	Action: doImport,
	Flags:  cloneFlags,
}

var commandRoot = cli.Command{
	Name:   "root",
	Usage:  "Show repositories' root",
	Action: doRoot,
	Flags: []cli.Flag{
		cli.BoolFlag{Name: "all", Usage: "Show all roots"},
	},
}

type commandDoc struct {
	Parent    string
	Arguments string
}

var commandDocs = map[string]commandDoc{
	"get":    {"", "[-u] <repository URL> | [-u] [-p] <user>/<project>"},
	"list":   {"", "[-p] [-e] [<query>]"},
	"look":   {"", "<project> | <user>/<project> | <host>/<user>/<project>"},
	"import": {"", "< file"},
	"root":   {"", ""},
}

// Makes template conditionals to generate per-command documents.
func mkCommandsTemplate(genTemplate func(commandDoc) string) string {
	template := "{{if false}}"
	for _, command := range append(Commands) {
		template = template + fmt.Sprintf("{{else if (eq .Name %q)}}%s", command.Name, genTemplate(commandDocs[command.Name]))
	}
	return template + "{{end}}"
}

func init() {
	argsTemplate := mkCommandsTemplate(func(doc commandDoc) string { return doc.Arguments })
	parentTemplate := mkCommandsTemplate(func(doc commandDoc) string { return string(strings.TrimLeft(doc.Parent+" ", " ")) })

	cli.CommandHelpTemplate = `NAME:
    {{.Name}} - {{.Usage}}

USAGE:
    ghq ` + parentTemplate + `{{.Name}} ` + argsTemplate + `
{{if (len .Description)}}
DESCRIPTION: {{.Description}}
{{end}}{{if (len .Flags)}}
OPTIONS:
    {{range .Flags}}{{.}}
    {{end}}
{{end}}`
}

func doGet(c *cli.Context) error {
	argURL := c.Args().Get(0)
	doUpdate := c.Bool("update")
	isShallow := c.Bool("shallow")

	if argURL == "" {
		cli.ShowCommandHelp(c, "get")
		os.Exit(1)
	}

	// If argURL is a "./foo" or "../bar" form,
	// find repository name trailing after github.com/USER/.
	parts := strings.Split(argURL, string(filepath.Separator))
	if parts[0] == "." || parts[0] == ".." {
		if wd, err := os.Getwd(); err == nil {
			path := filepath.Clean(filepath.Join(wd, filepath.Join(parts...)))

			var repoPath string
			for _, r := range localRepositoryRoots() {
				p := strings.TrimPrefix(path, r+string(filepath.Separator))
				if p != path && (repoPath == "" || len(p) < len(repoPath)) {
					repoPath = p
				}
			}

			if repoPath != "" {
				// Guess it
				utils.Log("resolved", fmt.Sprintf("relative %q to %q", argURL, "https://"+repoPath))
				argURL = "https://" + repoPath
			}
		}
	}

	url, err := NewURL(argURL)
	utils.DieIf(err)

	isSSH := c.Bool("p")
	if isSSH {
		// Assume Git repository if `-p` is given.
		url, err = ConvertGitURLHTTPToSSH(url)
		utils.DieIf(err)
	}

	remote, err := NewRemoteRepository(url)
	utils.DieIf(err)

	if remote.IsValid() == false {
		utils.Log("error", fmt.Sprintf("Not a valid repository: %s", url))
		os.Exit(1)
	}

	getRemoteRepository(remote, doUpdate, isShallow)
	return nil
}

// getRemoteRepository clones or updates a remote repository remote.
// If doUpdate is true, updates the locally cloned repository. Otherwise does nothing.
// If isShallow is true, does shallow cloning. (no effect if already cloned or the VCS is Mercurial and git-svn)
func getRemoteRepository(remote RemoteRepository, doUpdate bool, isShallow bool) {
	remoteURL := remote.URL()
	local := LocalRepositoryFromURL(remoteURL)

	path := local.FullPath
	newPath := false

	_, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			newPath = true
			err = nil
		}
		utils.PanicIf(err)
	}

	if newPath {
		utils.Log("clone", fmt.Sprintf("%s -> %s", remoteURL, path))

		vcs := remote.VCS()
		if vcs == nil {
			utils.Log("error", fmt.Sprintf("Could not find version control system: %s", remoteURL))
			os.Exit(1)
		}

		err := vcs.Clone(remoteURL, path, isShallow)
		if err != nil {
			utils.Log("error", err.Error())
			os.Exit(1)
		}
	} else {
		if doUpdate {
			utils.Log("update", path)
			local.VCS().Update(path)
		} else {
			utils.Log("exists", path)
		}
	}
}

func doList(c *cli.Context) error {
	query := c.Args().First()
	exact := c.Bool("exact")
	printFullPaths := c.Bool("full-path")
	printUniquePaths := c.Bool("unique")

	var filterFn func(*LocalRepository) bool
	if query == "" {
		filterFn = func(_ *LocalRepository) bool {
			return true
		}
	} else if exact {
		filterFn = func(repo *LocalRepository) bool {
			return repo.Matches(query)
		}
	} else {
		filterFn = func(repo *LocalRepository) bool {
			return strings.Contains(repo.NonHostPath(), query)
		}
	}

	repos := []*LocalRepository{}

	walkLocalRepositories(func(repo *LocalRepository) {
		if filterFn(repo) == false {
			return
		}

		repos = append(repos, repo)
	})

	if printUniquePaths {
		subpathCount := map[string]int{} // Count duplicated subpaths (ex. foo/dotfiles and bar/dotfiles)
		reposCount := map[string]int{}   // Check duplicated repositories among roots

		// Primary first
		for _, repo := range repos {
			if reposCount[repo.RelPath] == 0 {
				for _, p := range repo.Subpaths() {
					subpathCount[p] = subpathCount[p] + 1
				}
			}

			reposCount[repo.RelPath] = reposCount[repo.RelPath] + 1
		}

		for _, repo := range repos {
			if reposCount[repo.RelPath] > 1 && repo.IsUnderPrimaryRoot() == false {
				continue
			}

			for _, p := range repo.Subpaths() {
				if subpathCount[p] == 1 {
					fmt.Println(p)
					break
				}
			}
		}
	} else {
		for _, repo := range repos {
			if printFullPaths {
				fmt.Println(repo.FullPath)
			} else {
				fmt.Println(repo.RelPath)
			}
		}
	}
	return nil
}

func doLook(c *cli.Context) error {
	name := c.Args().First()

	if name == "" {
		cli.ShowCommandHelp(c, "look")
		os.Exit(1)
	}

	reposFound := []*LocalRepository{}
	walkLocalRepositories(func(repo *LocalRepository) {
		if repo.Matches(name) {
			reposFound = append(reposFound, repo)
		}
	})

	if len(reposFound) == 0 {
		url, err := NewURL(name)

		if err == nil {
			repo := LocalRepositoryFromURL(url)
			_, err := os.Stat(repo.FullPath)

			// if the directory exists
			if err == nil {
				reposFound = append(reposFound, repo)
			}
		}
	}

	switch len(reposFound) {
	case 0:
		utils.Log("error", "No repository found")
		os.Exit(1)

	case 1:
		if runtime.GOOS == "windows" {
			cmd := exec.Command(os.Getenv("COMSPEC"))
			cmd.Stdin = os.Stdin
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			cmd.Dir = reposFound[0].FullPath
			err := cmd.Start()
			if err == nil {
				cmd.Wait()
				os.Exit(0)
			}
		} else {
			shell := os.Getenv("SHELL")
			if shell == "" {
				shell = "/bin/sh"
			}

			utils.Log("cd", reposFound[0].FullPath)
			err := os.Chdir(reposFound[0].FullPath)
			utils.PanicIf(err)

			env := append(syscall.Environ(), "GHQ_LOOK="+reposFound[0].RelPath)
			syscall.Exec(shell, []string{shell}, env)
		}

	default:
		utils.Log("error", "More than one repositories are found; Try more precise name")
		for _, repo := range reposFound {
			utils.Log("error", "- "+strings.Join(repo.PathParts, "/"))
		}
	}
	return nil
}

func doImport(c *cli.Context) error {
	var (
		doUpdate  = c.Bool("update")
		isSSH     = c.Bool("p")
		isShallow = c.Bool("shallow")
	)

	var (
		in       io.Reader
		finalize func() error
	)

	if len(c.Args()) == 0 {
		// `ghq import` reads URLs from stdin
		in = os.Stdin
		finalize = func() error { return nil }
	} else {
		// Handle `ghq import starred motemen` case
		// with `git config --global ghq.import.starred "!github-list-starred"`
		subCommand := c.Args().First()
		command, err := GitConfigSingle("ghq.import." + subCommand)
		if err == nil && command == "" {
			err = fmt.Errorf("ghq.import.%s configuration not found", subCommand)
		}
		utils.DieIf(err)

		// execute `sh -c 'COMMAND "$@"' -- ARG...`
		// TODO: Windows
		command = strings.TrimLeft(command, "!")
		shellCommand := append([]string{"sh", "-c", command + ` "$@"`, "--"}, c.Args().Tail()...)

		utils.Log("run", strings.Join(append([]string{command}, c.Args().Tail()...), " "))

		cmd := exec.Command(shellCommand[0], shellCommand[1:]...)
		cmd.Stderr = os.Stderr

		in, err = cmd.StdoutPipe()
		utils.DieIf(err)

		err = cmd.Start()
		utils.DieIf(err)

		finalize = cmd.Wait
	}

	scanner := bufio.NewScanner(in)
	for scanner.Scan() {
		line := scanner.Text()
		url, err := NewURL(line)
		if err != nil {
			utils.Log("error", fmt.Sprintf("Could not parse URL <%s>: %s", line, err))
			continue
		}
		if isSSH {
			url, err = ConvertGitURLHTTPToSSH(url)
			if err != nil {
				utils.Log("error", fmt.Sprintf("Could not convert URL <%s>: %s", url, err))
				continue
			}
		}

		remote, err := NewRemoteRepository(url)
		if utils.ErrorIf(err) {
			continue
		}
		if remote.IsValid() == false {
			utils.Log("error", fmt.Sprintf("Not a valid repository: %s", url))
			continue
		}

		getRemoteRepository(remote, doUpdate, isShallow)
	}
	if err := scanner.Err(); err != nil {
		utils.Log("error", fmt.Sprintf("While reading input: %s", err))
		os.Exit(1)
	}

	utils.DieIf(finalize())
	return nil
}

func doRoot(c *cli.Context) error {
	all := c.Bool("all")
	if all {
		for _, root := range localRepositoryRoots() {
			fmt.Println(root)
		}
	} else {
		fmt.Println(primaryLocalRepositoryRoot())
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/csv"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/AlexanderThaller/logger"
	"github.com/jinzhu/now"
	"github.com/juju/errgo"
)

type Command struct {
	Action        string
	Args          []string
	DataPath      string
	EndTime       time.Time
	Project       string
	SCM           string
	SCMAutoCommit bool
	SCMAutoPush   bool
	StartTime     time.Time
	TimeStamp     time.Time
	Value         string
}

const (
	CommitMessageTimeStampFormat = RecordTimeStampFormat
	DateFormat                   = "2006-01-02"
)

const (
	ActionDone         = "done"
	ActionListDates    = "listdates"
	ActionList         = "list"
	ActionListNotes    = "listnotes"
	ActionListProjects = "listprojects"
	ActionListTodos    = "listtodos"
	ActionNote         = "note"
	ActionTodo         = "todo"
)

func NewCommand() *Command {
	return new(Command)
}

func (com *Command) Run() error {
	if com.DataPath == "" {
		return errgo.New("the datapath can not be empty")
	}

	switch com.Action {
	case ActionDone:
		return com.runDone()
	case ActionNote:
		return com.runNote()
	case ActionListDates:
		return com.runListDates()
	case ActionList:
		return com.runList()
	case ActionListNotes:
		return com.runListNotes()
	case ActionListProjects:
		return com.runListProjects()
	case ActionListTodos:
		return com.runListTodos()
	case ActionTodo:
		return com.runTodo()
	default:
		return errgo.New("Do not recognize the action: " + com.Action)
	}
}

func (com *Command) runDone() error {
	l := logger.New(Name, "Command", "run", "Done")

	l.Trace("Args length: ", len(com.Args))
	if com.Value == "" {
		return errgo.New("todo command needs a value")
	}
	l.Trace("Project: ", com.Project)
	if com.Project == "" {
		return errgo.New("todo command needs an project")
	}

	done := new(Todo)
	done.Project = com.Project
	done.TimeStamp = com.TimeStamp
	done.Value = com.Value
	done.Done = true
	l.Trace("Done: ", fmt.Sprintf("%+v", done))

	return com.Write(done)
}

func (com *Command) runNote() error {
	l := logger.New(Name, "Command", "run", "Note")

	l.Trace("Args length: ", len(com.Args))
	if com.Value == "" {
		return errgo.New("note command needs a value")
	}
	l.Trace("Project: ", com.Project)
	if com.Project == "" {
		return errgo.New("note command needs an project")
	}

	note := new(Note)
	note.Project = com.Project
	note.TimeStamp = com.TimeStamp
	note.Value = com.Value
	l.Trace("Note: ", fmt.Sprintf("%+v", note))

	return com.Write(note)
}

func (com *Command) runTodo() error {
	l := logger.New(Name, "Command", "run", "Todo")

	l.Trace("Args length: ", len(com.Args))
	if com.Value == "" {
		return errgo.New("todo command needs a value")
	}
	l.Trace("Project: ", com.Project)
	if com.Project == "" {
		return errgo.New("todo command needs an project")
	}

	todo := new(Todo)
	todo.Project = com.Project
	todo.TimeStamp = com.TimeStamp
	todo.Value = com.Value
	todo.Done = false
	l.Trace("Todo: ", fmt.Sprintf("%+v", todo))

	return com.Write(todo)
}

func (com *Command) runList() error {
	if com.Project == "" {
		return com.runListProjects()
	} else {
		return com.runListProjectNotes(com.Project)
	}
}

func (com *Command) runListNotes() error {
	if com.Project != "" {
		return com.runListProjectNotes(com.Project)
	}

	projects, err := com.getProjects()
	if err != nil {
		return err
	}

	for _, project := range projects {
		err := com.runListProjectNotes(project)
		if err != nil {
			return err
		}
	}

	return nil
}

func (com *Command) runListTodos() error {
	if com.Project != "" {
		return com.runListProjectTodos(com.Project)
	}

	projects, err := com.getProjects()
	if err != nil {
		return err
	}

	for _, project := range projects {
		err := com.runListProjectTodos(project)
		if err != nil {
			return err
		}
	}

	return nil
}

func (com *Command) runListProjects() error {
	projects, err := com.getProjects()
	if err != nil {
		return err
	}

	for _, project := range projects {
		notes, err := com.getProjectNotes(project)
		if err != nil {
			return err
		}

		if len(notes) == 0 {
			continue
		}

		fmt.Println(project)
	}

	return nil
}

func (com *Command) runListDates() error {
	l := logger.New(Name, "Command", "run", "ListDates")

	var dates []string
	var err error

	if com.Project == "" {
		dates, err = com.getDates()
	} else {
		dates, err = com.getProjectDates(com.Project)
	}

	if err != nil {
		return err
	}

	sort.Strings(dates)
	for _, date := range dates {
		timestamp, err := now.Parse(date)
		if err != nil {
			l.Warning("Can not parse timestamp: ", errgo.Details(err))
			continue
		}

		if timestamp.Before(com.StartTime) {
			continue
		}

		if timestamp.After(com.EndTime) {
			continue
		}

		fmt.Println(date)
	}

	return nil
}

func (com *Command) getDates() ([]string, error) {
	projects, err := com.getProjects()
	if err != nil {
		return nil, err
	}

	datemap := make(map[string]struct{})
	for _, project := range projects {
		dates, err := com.getProjectDates(project)
		if err != nil {
			return nil, err
		}

		for _, date := range dates {
			datemap[date] = struct{}{}
		}
	}

	var out []string
	for date := range datemap {
		out = append(out, date)
	}

	return out, nil
}

func (com *Command) getProjectDates(project string) ([]string, error) {
	if com.DataPath == "" {
		return nil, errgo.New("datapath can not be empty")
	}
	if project == "" {
		return nil, errgo.New("project name can not be empty")
	}
	if !com.checkProjectExists(project) {
		return nil, errgo.New("project does not exist")
	}

	var out []string

	records, err := com.getProjectNotes(project)
	if err != nil {
		return nil, err
	}

	datemap := make(map[string]struct{})

	for _, record := range records {
		date, err := time.Parse(RecordTimeStampFormat, record.GetTimeStamp())
		if err != nil {
			return nil, err
		}

		datemap[date.Format(DateFormat)] = struct{}{}
	}

	for date := range datemap {
		out = append(out, date)
	}

	return out, nil
}

func (com *Command) getProjects() ([]string, error) {
	dir, err := ioutil.ReadDir(com.DataPath)
	if err != nil {
		return nil, err
	}

	var out []string
	for _, d := range dir {
		file := d.Name()

		// Skip dotfiles
		if strings.HasPrefix(file, ".") {
			continue
		}

		ext := filepath.Ext(file)
		name := file[0 : len(file)-len(ext)]

		out = append(out, name)
	}

	sort.Strings(out)
	return out, nil
}

func (com *Command) runListProjectTodos(project string) error {
	if project == "" {
		return errgo.New("project name can not be empty")
	}
	if !com.checkProjectExists(project) {
		return errgo.New("no notes for this project")
	}

	todos, err := com.getProjectTodos(project)
	if err != nil {
		return err
	}
	todos = com.filterTodos(todos)

	if len(todos) == 0 {
		return nil
	}

	fmt.Println("#", project, "- Todos")

	var out []string
	for _, todo := range todos {
		out = append(out, "  * "+todo.GetValue())
	}
	sort.Strings(out)

	for _, todo := range out {
		fmt.Println(todo)
	}
	fmt.Println("")

	return nil
}

func (com *Command) filterTodos(todos []Todo) []Todo {
	filter := make(map[string]Todo)

	sort.Sort(TodoByDate(todos))
	for _, todo := range todos {
		filter[todo.Value] = todo
	}

	var out []Todo
	for _, todo := range filter {
		if todo.Done {
			continue
		}

		out = append(out, todo)
	}

	return out
}

func (com *Command) runListProjectNotes(project string) error {
	if project == "" {
		return errgo.New("project name can not be empty")
	}
	if !com.checkProjectExists(project) {
		return errgo.New("no notes for this project")
	}

	notes, err := com.getProjectNotes(project)
	if err != nil {
		return err
	}

	if len(notes) == 0 {
		return nil
	}

	fmt.Println("#", project)
	sort.Sort(NotesByDate(notes))
	for _, note := range notes {
		fmt.Println("##", note.GetTimeStamp())
		fmt.Println(note.GetValue())
		fmt.Println("")
	}

	return nil
}

func (com *Command) checkProjectExists(project string) bool {
	projects, err := com.getProjects()
	if err != nil {
		return false
	}

	for _, d := range projects {
		if d == project {
			return true
		}
	}

	return false
}

func (com *Command) getProjectNotes(project string) ([]Note, error) {
	l := logger.New(Name, "Command", "get", "ProjectRecords")
	l.SetLevel(logger.Debug)

	if com.DataPath == "" {
		return nil, errgo.New("datapath can not be empty")
	}
	if project == "" {
		return nil, errgo.New("project name can not be empty")
	}
	if !com.checkProjectExists(project) {
		return nil, errgo.New("project does not exist")
	}

	filepath := filepath.Join(com.DataPath, project+".csv")
	file, err := os.OpenFile(filepath, os.O_RDONLY, 0640)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	reader := csv.NewReader(file)
	reader.FieldsPerRecord = 3

	var out []Note
	for {
		csv, err := reader.Read()
		if err != nil {
			if err == io.EOF {
				break
			}

			continue
		}

		note, err := NoteFromCSV(csv)
		if err != nil {
			continue
		}
		note.SetProject(project)

		if note.TimeStamp.Before(com.StartTime) {
			continue
		}

		if note.TimeStamp.After(com.EndTime) {
			continue
		}

		out = append(out, note)
	}

	return out, err
}

func (com *Command) getProjectTodos(project string) ([]Todo, error) {
	l := logger.New(Name, "Command", "get", "ProjectRecords")
	l.SetLevel(logger.Debug)

	if com.DataPath == "" {
		return nil, errgo.New("datapath can not be empty")
	}
	if project == "" {
		return nil, errgo.New("project name can not be empty")
	}
	if !com.checkProjectExists(project) {
		return nil, errgo.New("project does not exist")
	}

	filepath := filepath.Join(com.DataPath, project+".csv")
	file, err := os.OpenFile(filepath, os.O_RDONLY, 0640)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	reader := csv.NewReader(file)
	reader.FieldsPerRecord = 4

	var out []Todo
	for {
		csv, err := reader.Read()
		if err != nil {
			if err == io.EOF {
				break
			}

			continue
		}

		todo, err := TodoFromCSV(csv)
		if err != nil {
			continue
		}

		out = append(out, todo)
	}

	return out, err
}

func (com *Command) Write(record Record) error {
	if com.DataPath == "" {
		return errgo.New("datapath can not be empty")
	}

	if com.Project == "" {
		return errgo.New("project name can not be empty")
	}

	path := com.DataPath
	project := com.Project

	err := os.MkdirAll(path, 0750)
	if err != nil {
		return err
	}

	filepath := filepath.Join(path, project+".csv")
	file, err := os.OpenFile(filepath, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0640)
	if err != nil {
		return err
	}
	defer file.Close()

	writer := csv.NewWriter(file)
	err = writer.Write(record.CSV())
	if err != nil {
		return err
	}
	writer.Flush()

	err = com.Commit(record)
	if err != nil {
		return err
	}

	return nil
}

func (com *Command) Commit(record Record) error {
	if !com.SCMAutoCommit {
		return nil
	}

	if com.SCM == "" {
		return errgo.New("Can not use an empty scm for commiting")
	}

	filename := record.GetProject() + ".csv"
	err := scmAdd(com.SCM, com.DataPath, filename)
	if err != nil {
		return err
	}

	message := com.Project + " - "
	message += record.GetAction() + " - "
	message += com.TimeStamp.Format(CommitMessageTimeStampFormat)
	err = scmCommit(com.SCM, com.DataPath, message)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>will now list a project if it only has todos, will also list the date for which only todos have been recorded.<commit_after>package main

import (
	"encoding/csv"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/AlexanderThaller/logger"
	"github.com/jinzhu/now"
	"github.com/juju/errgo"
)

type Command struct {
	Action        string
	Args          []string
	DataPath      string
	EndTime       time.Time
	Project       string
	SCM           string
	SCMAutoCommit bool
	SCMAutoPush   bool
	StartTime     time.Time
	TimeStamp     time.Time
	Value         string
}

const (
	CommitMessageTimeStampFormat = RecordTimeStampFormat
	DateFormat                   = "2006-01-02"
)

const (
	ActionDone         = "done"
	ActionListDates    = "listdates"
	ActionList         = "list"
	ActionListNotes    = "listnotes"
	ActionListProjects = "listprojects"
	ActionListTodos    = "listtodos"
	ActionNote         = "note"
	ActionTodo         = "todo"
)

func NewCommand() *Command {
	return new(Command)
}

func (com *Command) Run() error {
	if com.DataPath == "" {
		return errgo.New("the datapath can not be empty")
	}

	switch com.Action {
	case ActionDone:
		return com.runDone()
	case ActionNote:
		return com.runNote()
	case ActionListDates:
		return com.runListDates()
	case ActionList:
		return com.runList()
	case ActionListNotes:
		return com.runListNotes()
	case ActionListProjects:
		return com.runListProjects()
	case ActionListTodos:
		return com.runListTodos()
	case ActionTodo:
		return com.runTodo()
	default:
		return errgo.New("Do not recognize the action: " + com.Action)
	}
}

func (com *Command) runDone() error {
	l := logger.New(Name, "Command", "run", "Done")

	l.Trace("Args length: ", len(com.Args))
	if com.Value == "" {
		return errgo.New("todo command needs a value")
	}
	l.Trace("Project: ", com.Project)
	if com.Project == "" {
		return errgo.New("todo command needs an project")
	}

	done := new(Todo)
	done.Project = com.Project
	done.TimeStamp = com.TimeStamp
	done.Value = com.Value
	done.Done = true
	l.Trace("Done: ", fmt.Sprintf("%+v", done))

	return com.Write(done)
}

func (com *Command) runNote() error {
	l := logger.New(Name, "Command", "run", "Note")

	l.Trace("Args length: ", len(com.Args))
	if com.Value == "" {
		return errgo.New("note command needs a value")
	}
	l.Trace("Project: ", com.Project)
	if com.Project == "" {
		return errgo.New("note command needs an project")
	}

	note := new(Note)
	note.Project = com.Project
	note.TimeStamp = com.TimeStamp
	note.Value = com.Value
	l.Trace("Note: ", fmt.Sprintf("%+v", note))

	return com.Write(note)
}

func (com *Command) runTodo() error {
	l := logger.New(Name, "Command", "run", "Todo")

	l.Trace("Args length: ", len(com.Args))
	if com.Value == "" {
		return errgo.New("todo command needs a value")
	}
	l.Trace("Project: ", com.Project)
	if com.Project == "" {
		return errgo.New("todo command needs an project")
	}

	todo := new(Todo)
	todo.Project = com.Project
	todo.TimeStamp = com.TimeStamp
	todo.Value = com.Value
	todo.Done = false
	l.Trace("Todo: ", fmt.Sprintf("%+v", todo))

	return com.Write(todo)
}

func (com *Command) runList() error {
	if com.Project == "" {
		return com.runListProjects()
	} else {
		return com.runListProjectNotes(com.Project)
	}
}

func (com *Command) runListNotes() error {
	if com.Project != "" {
		return com.runListProjectNotes(com.Project)
	}

	projects, err := com.getProjects()
	if err != nil {
		return err
	}

	for _, project := range projects {
		err := com.runListProjectNotes(project)
		if err != nil {
			return err
		}
	}

	return nil
}

func (com *Command) runListTodos() error {
	if com.Project != "" {
		return com.runListProjectTodos(com.Project)
	}

	projects, err := com.getProjects()
	if err != nil {
		return err
	}

	for _, project := range projects {
		err := com.runListProjectTodos(project)
		if err != nil {
			return err
		}
	}

	return nil
}

func (com *Command) runListProjects() error {
	projects, err := com.getProjects()
	if err != nil {
		return err
	}

	out := make(map[string]struct{})
	for _, project := range projects {
		notes, err := com.getProjectNotes(project)
		if err != nil {
			return err
		}

		if len(notes) == 0 {
			continue
		}

		out[project] = struct{}{}
	}

	for _, project := range projects {
		todos, err := com.getProjectTodos(project)
		if err != nil {
			return err
		}

		if len(todos) == 0 {
			continue
		}

		out[project] = struct{}{}
	}

	for project := range out {
		fmt.Println(project)
	}

	return nil
}

func (com *Command) runListDates() error {
	l := logger.New(Name, "Command", "run", "ListDates")

	var dates []string
	var err error

	if com.Project == "" {
		dates, err = com.getDates()
	} else {
		dates, err = com.getProjectDates(com.Project)
	}

	if err != nil {
		return err
	}

	sort.Strings(dates)
	for _, date := range dates {
		timestamp, err := now.Parse(date)
		if err != nil {
			l.Warning("Can not parse timestamp: ", errgo.Details(err))
			continue
		}

		if timestamp.Before(com.StartTime) {
			continue
		}

		if timestamp.After(com.EndTime) {
			continue
		}

		fmt.Println(date)
	}

	return nil
}

func (com *Command) getDates() ([]string, error) {
	projects, err := com.getProjects()
	if err != nil {
		return nil, err
	}

	datemap := make(map[string]struct{})
	for _, project := range projects {
		dates, err := com.getProjectDates(project)
		if err != nil {
			return nil, err
		}

		for _, date := range dates {
			datemap[date] = struct{}{}
		}
	}

	var out []string
	for date := range datemap {
		out = append(out, date)
	}

	return out, nil
}

func (com *Command) getProjectDates(project string) ([]string, error) {
	if com.DataPath == "" {
		return nil, errgo.New("datapath can not be empty")
	}
	if project == "" {
		return nil, errgo.New("project name can not be empty")
	}
	if !com.checkProjectExists(project) {
		return nil, errgo.New("project does not exist")
	}

	var out []string

	notes, err := com.getProjectNotes(project)
	if err != nil {
		return nil, err
	}

	todos, err := com.getProjectTodos(project)
	if err != nil {
		return nil, err
	}
	todos = com.filterTodos(todos)

	datemap := make(map[string]struct{})

	for _, note := range notes {
		date, err := time.Parse(RecordTimeStampFormat, note.GetTimeStamp())
		if err != nil {
			return nil, err
		}

		datemap[date.Format(DateFormat)] = struct{}{}
	}

	for _, todo := range todos {
		datemap[todo.TimeStamp.Format(DateFormat)] = struct{}{}
	}

	for date := range datemap {
		out = append(out, date)
	}

	return out, nil
}

func (com *Command) getProjects() ([]string, error) {
	dir, err := ioutil.ReadDir(com.DataPath)
	if err != nil {
		return nil, err
	}

	var out []string
	for _, d := range dir {
		file := d.Name()

		// Skip dotfiles
		if strings.HasPrefix(file, ".") {
			continue
		}

		ext := filepath.Ext(file)
		name := file[0 : len(file)-len(ext)]

		out = append(out, name)
	}

	sort.Strings(out)
	return out, nil
}

func (com *Command) runListProjectTodos(project string) error {
	if project == "" {
		return errgo.New("project name can not be empty")
	}
	if !com.checkProjectExists(project) {
		return errgo.New("no notes for this project")
	}

	todos, err := com.getProjectTodos(project)
	if err != nil {
		return err
	}
	todos = com.filterTodos(todos)

	if len(todos) == 0 {
		return nil
	}

	fmt.Println("#", project, "- Todos")

	var out []string
	for _, todo := range todos {
		out = append(out, "  * "+todo.GetValue())
	}
	sort.Strings(out)

	for _, todo := range out {
		fmt.Println(todo)
	}
	fmt.Println("")

	return nil
}

func (com *Command) filterTodos(todos []Todo) []Todo {
	filter := make(map[string]Todo)

	sort.Sort(TodoByDate(todos))
	for _, todo := range todos {
		filter[todo.Value] = todo
	}

	var out []Todo
	for _, todo := range filter {
		if todo.Done {
			continue
		}

		out = append(out, todo)
	}

	return out
}

func (com *Command) runListProjectNotes(project string) error {
	if project == "" {
		return errgo.New("project name can not be empty")
	}
	if !com.checkProjectExists(project) {
		return errgo.New("no notes for this project")
	}

	notes, err := com.getProjectNotes(project)
	if err != nil {
		return err
	}

	if len(notes) == 0 {
		return nil
	}

	fmt.Println("#", project)
	sort.Sort(NotesByDate(notes))
	for _, note := range notes {
		fmt.Println("##", note.GetTimeStamp())
		fmt.Println(note.GetValue())
		fmt.Println("")
	}

	return nil
}

func (com *Command) checkProjectExists(project string) bool {
	projects, err := com.getProjects()
	if err != nil {
		return false
	}

	for _, d := range projects {
		if d == project {
			return true
		}
	}

	return false
}

func (com *Command) getProjectNotes(project string) ([]Note, error) {
	l := logger.New(Name, "Command", "get", "ProjectRecords")
	l.SetLevel(logger.Debug)

	if com.DataPath == "" {
		return nil, errgo.New("datapath can not be empty")
	}
	if project == "" {
		return nil, errgo.New("project name can not be empty")
	}
	if !com.checkProjectExists(project) {
		return nil, errgo.New("project does not exist")
	}

	filepath := filepath.Join(com.DataPath, project+".csv")
	file, err := os.OpenFile(filepath, os.O_RDONLY, 0640)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	reader := csv.NewReader(file)
	reader.FieldsPerRecord = 3

	var out []Note
	for {
		csv, err := reader.Read()
		if err != nil {
			if err == io.EOF {
				break
			}

			continue
		}

		note, err := NoteFromCSV(csv)
		if err != nil {
			continue
		}
		note.SetProject(project)

		if note.TimeStamp.Before(com.StartTime) {
			continue
		}

		if note.TimeStamp.After(com.EndTime) {
			continue
		}

		out = append(out, note)
	}

	return out, err
}

func (com *Command) getProjectTodos(project string) ([]Todo, error) {
	l := logger.New(Name, "Command", "get", "ProjectRecords")
	l.SetLevel(logger.Debug)

	if com.DataPath == "" {
		return nil, errgo.New("datapath can not be empty")
	}
	if project == "" {
		return nil, errgo.New("project name can not be empty")
	}
	if !com.checkProjectExists(project) {
		return nil, errgo.New("project does not exist")
	}

	filepath := filepath.Join(com.DataPath, project+".csv")
	file, err := os.OpenFile(filepath, os.O_RDONLY, 0640)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	reader := csv.NewReader(file)
	reader.FieldsPerRecord = 4

	var out []Todo
	for {
		csv, err := reader.Read()
		if err != nil {
			if err == io.EOF {
				break
			}

			continue
		}

		todo, err := TodoFromCSV(csv)
		if err != nil {
			continue
		}

		out = append(out, todo)
	}

	return out, err
}

func (com *Command) Write(record Record) error {
	if com.DataPath == "" {
		return errgo.New("datapath can not be empty")
	}

	if com.Project == "" {
		return errgo.New("project name can not be empty")
	}

	path := com.DataPath
	project := com.Project

	err := os.MkdirAll(path, 0750)
	if err != nil {
		return err
	}

	filepath := filepath.Join(path, project+".csv")
	file, err := os.OpenFile(filepath, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0640)
	if err != nil {
		return err
	}
	defer file.Close()

	writer := csv.NewWriter(file)
	err = writer.Write(record.CSV())
	if err != nil {
		return err
	}
	writer.Flush()

	err = com.Commit(record)
	if err != nil {
		return err
	}

	return nil
}

func (com *Command) Commit(record Record) error {
	if !com.SCMAutoCommit {
		return nil
	}

	if com.SCM == "" {
		return errgo.New("Can not use an empty scm for commiting")
	}

	filename := record.GetProject() + ".csv"
	err := scmAdd(com.SCM, com.DataPath, filename)
	if err != nil {
		return err
	}

	message := com.Project + " - "
	message += record.GetAction() + " - "
	message += com.TimeStamp.Format(CommitMessageTimeStampFormat)
	err = scmCommit(com.SCM, com.DataPath, message)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/getwe/figlet4go"
	"github.com/timakin/ts/loader"
)

var Commands = []cli.Command{
	commandAll,
	commandHack,
	commandPH,
	commandTC,
	commandRE,
	commandHN,
	commandGH,
	commandMS,
	commandTNW,
	commandDN,
	commandFB,
	commandEJ,
	commandA16Z,
	commandHatena,
}

var commandAll = cli.Command{
	Name:        "pop",
	Usage:       "",
	Description: "Show today's news from major tech news sites, HN, PH, and subreddit of /programming.",
	Action:      doAll,
}

var commandHack = cli.Command{
	Name:  "hack",
	Usage: "",
	Description: `
`,
	Action: doHack,
}

var commandPH = cli.Command{
	Name:  "ph",
	Usage: "",
	Description: `
`,
	Action: doPH,
}

var commandHN = cli.Command{
	Name:  "hn",
	Usage: "",
	Description: `
`,
	Action: doHN,
}

var commandGH = cli.Command{
	Name:  "github",
	Usage: "",
	Description: `
`,
	Action: doGH,
}

var commandRE = cli.Command{
	Name:  "reddit",
	Usage: "",
	Description: `
`,
	Action: doRE,
}

var commandTC = cli.Command{
	Name:  "tc",
	Usage: "",
	Description: `
`,
	Action: doTC,
}

var commandMS = cli.Command{
	Name:  "ms",
	Usage: "",
	Description: `
`,
	Action: doMS,
}

var commandTNW = cli.Command{
	Name:  "tnw",
	Usage: "",
	Description: `
`,
	Action: doTNW,
}

var commandDN = cli.Command{
	Name:  "dn",
	Usage: "",
	Description: `
`,
	Action: doDN,
}

var commandFB = cli.Command{
	Name:  "forbes",
	Usage: "",
	Description: `
`,
	Action: doFB,
}

var commandEJ = cli.Command{
	Name:  "echojs",
	Usage: "",
	Description: `
`,
	Action: doEJ,
}

var commandA16Z = cli.Command{
	Name:  "a16z",
	Usage: "",
	Description: `
`,
	Action: doA16Z,
}

var commandHatena = cli.Command{
	Name:  "hatena",
	Usage: "",
	Description: `
`,
	Action: doHatena,
}

func pp(str string) {
	fmt.Printf(str)
}

func ppred(str string) {
	fmt.Printf("\033[1;31m" + str + "\033[0m")
}

func displayAA() {
	flag_str := flag.String("str", "TechStack", "input string")
	flag.Parse()
	str := *flag_str
	ascii := figlet4go.NewAsciiRender()
	renderStr, _ := ascii.Render(str)
	ppred(renderStr + "\n\n")
}

func displayUnitRssFeed(name string, uri string) {
	ppred("[" + name + "]\n")
	loader.GetUnitRssFeed(uri)
}

func displayUnitRssFeedWithDesc(name string, uri string) {
	ppred("[" + name + "]\n")
	loader.GetUnitRssFeedWithDesc(uri)
}

func doAll(c *cli.Context) {
	displayAA()
	ph := make(chan loader.ResultData)
	re := make(chan loader.ResultData)
	hn := make(chan loader.ResultData)
	gh := make(chan loader.ResultData)
	tc := make(chan loader.ResultData)
	ms := make(chan loader.ResultData)
	tnw := make(chan loader.ResultData)
	dn := make(chan loader.ResultData)
	fbs := make(chan loader.ResultData)
	ejs := make(chan loader.ResultData)
	a16z := make(chan loader.ResultData)
	go loader.GetPHFeed(ph)
	go loader.GetRedditFeed(re)
	go loader.GetRssFeed("HackerNews", "https://news.ycombinator.com/rss", hn)
	go loader.GetRdfFeedWithDesc("Github Trends", "http://github-trends.ryotarai.info/rss/github_trends_all_daily.rss", gh)
	go loader.GetRssFeed("TechCrunch", "http://feeds.feedburner.com/TechCrunch/", tc)
	go loader.GetRssFeed("Mashable", "http://feeds.mashable.com/Mashable", ms)
	go loader.GetRssFeed("The Next Web", "http://feeds2.feedburner.com/thenextweb", tnw)
	go loader.GetRssFeed("Designer News", "https://news.layervault.com/?format=rss", dn)
	go loader.GetRssFeed("Forbes - Tech", "http://www.forbes.com/technology/feed/", fbs)
	go loader.GetRssFeed("EchoJS", "http://www.echojs.com/rss", ejs)
	go loader.GetRssFeed("A16Z", "http://a16z.com/feed/", a16z)
	phres := <-ph
	reres := <-re
	hnres := <-hn
	ghres := <-gh
	tcres := <-tc
	msres := <-ms
	tnwres := <-tnw
	dnres := <-dn
	fbsres := <-fbs
	ejsres := <-ejs
	a16zres := <-a16z
	var PHData loader.Feed = &phres
	var REData loader.Feed = &reres
	var HNData loader.Feed = &hnres
	var GHData loader.Feed = &ghres
	var TCData loader.Feed = &tcres
	var MSData loader.Feed = &msres
	var TNWData loader.Feed = &tnwres
	var DNData loader.Feed = &dnres
	var FBSData loader.Feed = &fbsres
	var EJSData loader.Feed = &ejsres
	var A16ZData loader.Feed = &a16zres
	PHData.Display()
	REData.Display()
	HNData.Display()
	TCData.Display()
	GHData.Display()
	MSData.Display()
	TNWData.Display()
	DNData.Display()
	FBSData.Display()
	EJSData.Display()
	A16ZData.Display()
}

func doHack(c *cli.Context) {
	re := make(chan loader.ResultData)
	hn := make(chan loader.ResultData)
	gh := make(chan loader.ResultData)
	ejs := make(chan loader.ResultData)
	go loader.GetRedditFeed(re)
	go loader.GetRssFeed("HackerNews", "https://news.ycombinator.com/rss", hn)
	go loader.GetRdfFeedWithDesc("Github Trends", "http://github-trends.ryotarai.info/rss/github_trends_all_daily.rss", gh)
	go loader.GetRssFeed("EchoJS", "http://www.echojs.com/rss", ejs)
	reres := <-re
	hnres := <-hn
	ghres := <-gh
	ejsres := <-ejs
	var REData loader.Feed = &reres
	var HNData loader.Feed = &hnres
	var GHData loader.Feed = &ghres
	var EJSData loader.Feed = &ejsres
	REData.Display()
	HNData.Display()
	GHData.Display()
	EJSData.Display()
}

func doPH(c *cli.Context) {
	ph := make(chan loader.ResultData)
	go loader.GetPHFeed(ph)
	phres := <-ph
	var PHData loader.Feed = &phres
	PHData.Display()
}

func doRE(c *cli.Context) {
	re := make(chan loader.ResultData)
	go loader.GetRedditFeed(re)
	reres := <-re
	var REData loader.Feed = &reres
	REData.Display()
}

func doHN(c *cli.Context) {
	displayUnitRssFeed("HackerNews", "https://news.ycombinator.com/rss")
}

func doGH(c *cli.Context) {
	displayUnitRssFeedWithDesc("Github Trends", "http://github-trends.ryotarai.info/rss/github_trends_all_daily.rss")
}

func doTC(c *cli.Context) {
	displayUnitRssFeed("TechCrunch", "http://feeds.feedburner.com/TechCrunch/")
}

func doMS(c *cli.Context) {
	displayUnitRssFeed("Mashable", "http://feeds.mashable.com/Mashable")
}

func doTNW(c *cli.Context) {
	displayUnitRssFeed("The Next Web", "http://feeds2.feedburner.com/thenextweb")
}

func doDN(c *cli.Context) {
	displayUnitRssFeed("Designer News", "https://news.layervault.com/?format=rss")
}

func doFB(c *cli.Context) {
	displayUnitRssFeed("Forbes - Tech", "http://www.forbes.com/technology/feed/")
}

func doEJ(c *cli.Context) {
	displayUnitRssFeed("EchoJS", "http://www.echojs.com/rss")
}

func doA16Z(c *cli.Context) {
	displayUnitRssFeed("A16Z", "http://a16z.com/feed/")
}

func doHatena(c *cli.Context) {
	displayUnitRssFeed("Hatena", "http://b.hatena.ne.jp/search/tag?q=%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0&users=10&mode=rss")
}
<commit_msg>fix a panic<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/getwe/figlet4go"
	"github.com/timakin/ts/loader"
)

var Commands = []cli.Command{
	commandAll,
	commandHack,
	commandPH,
	commandTC,
	commandRE,
	commandHN,
	commandGH,
	commandMS,
	commandTNW,
	commandDN,
	commandFB,
	commandEJ,
	commandA16Z,
	commandHatena,
}

var commandAll = cli.Command{
	Name:        "pop",
	Usage:       "",
	Description: "Show today's news from major tech news sites, HN, PH, and subreddit of /programming.",
	Action:      doAll,
}

var commandHack = cli.Command{
	Name:  "hack",
	Usage: "",
	Description: `
`,
	Action: doHack,
}

var commandPH = cli.Command{
	Name:  "ph",
	Usage: "",
	Description: `
`,
	Action: doPH,
}

var commandHN = cli.Command{
	Name:  "hn",
	Usage: "",
	Description: `
`,
	Action: doHN,
}

var commandGH = cli.Command{
	Name:  "github",
	Usage: "",
	Description: `
`,
	Action: doGH,
}

var commandRE = cli.Command{
	Name:  "reddit",
	Usage: "",
	Description: `
`,
	Action: doRE,
}

var commandTC = cli.Command{
	Name:  "tc",
	Usage: "",
	Description: `
`,
	Action: doTC,
}

var commandMS = cli.Command{
	Name:  "ms",
	Usage: "",
	Description: `
`,
	Action: doMS,
}

var commandTNW = cli.Command{
	Name:  "tnw",
	Usage: "",
	Description: `
`,
	Action: doTNW,
}

var commandDN = cli.Command{
	Name:  "dn",
	Usage: "",
	Description: `
`,
	Action: doDN,
}

var commandFB = cli.Command{
	Name:  "forbes",
	Usage: "",
	Description: `
`,
	Action: doFB,
}

var commandEJ = cli.Command{
	Name:  "echojs",
	Usage: "",
	Description: `
`,
	Action: doEJ,
}

var commandA16Z = cli.Command{
	Name:  "a16z",
	Usage: "",
	Description: `
`,
	Action: doA16Z,
}

var commandHatena = cli.Command{
	Name:  "hatena",
	Usage: "",
	Description: `
`,
	Action: doHatena,
}

func pp(str string) {
	fmt.Printf(str)
}

func ppred(str string) {
	fmt.Printf("\033[1;31m" + str + "\033[0m")
}

func displayAA() {
	flag_str := flag.String("str", "TechStack", "input string")
	flag.Parse()
	str := *flag_str
	ascii := figlet4go.NewAsciiRender()
	renderStr, _ := ascii.Render(str)
	ppred(renderStr + "\n\n")
}

func displayUnitRssFeed(name string, uri string) {
	ppred("[" + name + "]\n")
	loader.GetUnitRssFeed(uri)
}

func displayUnitRssFeedWithDesc(name string, uri string) {
	ppred("[" + name + "]\n")
	loader.GetUnitRssFeedWithDesc(uri)
}

func doAll(c *cli.Context) {
	displayAA()
	ph := make(chan loader.ResultData)
	re := make(chan loader.ResultData)
	hn := make(chan loader.ResultData)
	gh := make(chan loader.ResultData)
	tc := make(chan loader.ResultData)
	ms := make(chan loader.ResultData)
	tnw := make(chan loader.ResultData)
	dn := make(chan loader.ResultData)
	fbs := make(chan loader.ResultData)
	ejs := make(chan loader.ResultData)
	a16z := make(chan loader.ResultData)
	go loader.GetPHFeed(ph)
	go loader.GetRedditFeed(re)
	go loader.GetRssFeed("HackerNews", "https://news.ycombinator.com/rss", hn)
	go loader.GetRssFeedWithDesc("Github Trends", "http://github-trends.ryotarai.info/rss/github_trends_all_daily.rss", gh)
	go loader.GetRssFeed("TechCrunch", "http://feeds.feedburner.com/TechCrunch/", tc)
	go loader.GetRssFeed("Mashable", "http://feeds.mashable.com/Mashable", ms)
	go loader.GetRssFeed("The Next Web", "http://feeds2.feedburner.com/thenextweb", tnw)
	go loader.GetRssFeed("Designer News", "https://news.layervault.com/?format=rss", dn)
	go loader.GetRssFeed("Forbes - Tech", "http://www.forbes.com/technology/feed/", fbs)
	go loader.GetRssFeed("EchoJS", "http://www.echojs.com/rss", ejs)
	go loader.GetRssFeed("A16Z", "http://a16z.com/feed/", a16z)
	phres := <-ph
	reres := <-re
	hnres := <-hn
	ghres := <-gh
	tcres := <-tc
	msres := <-ms
	tnwres := <-tnw
	dnres := <-dn
	fbsres := <-fbs
	ejsres := <-ejs
	a16zres := <-a16z
	var PHData loader.Feed = &phres
	var REData loader.Feed = &reres
	var HNData loader.Feed = &hnres
	var GHData loader.Feed = &ghres
	var TCData loader.Feed = &tcres
	var MSData loader.Feed = &msres
	var TNWData loader.Feed = &tnwres
	var DNData loader.Feed = &dnres
	var FBSData loader.Feed = &fbsres
	var EJSData loader.Feed = &ejsres
	var A16ZData loader.Feed = &a16zres
	PHData.Display()
	REData.Display()
	HNData.Display()
	TCData.Display()
	GHData.Display()
	MSData.Display()
	TNWData.Display()
	DNData.Display()
	FBSData.Display()
	EJSData.Display()
	A16ZData.Display()
}

func doHack(c *cli.Context) {
	re := make(chan loader.ResultData)
	hn := make(chan loader.ResultData)
	gh := make(chan loader.ResultData)
	ejs := make(chan loader.ResultData)
	go loader.GetRedditFeed(re)
	go loader.GetRssFeed("HackerNews", "https://news.ycombinator.com/rss", hn)
	go loader.GetRssFeedWithDesc("Github Trends", "http://github-trends.ryotarai.info/rss/github_trends_all_daily.rss", gh)
	go loader.GetRssFeed("EchoJS", "http://www.echojs.com/rss", ejs)
	reres := <-re
	hnres := <-hn
	ghres := <-gh
	ejsres := <-ejs
	var REData loader.Feed = &reres
	var HNData loader.Feed = &hnres
	var GHData loader.Feed = &ghres
	var EJSData loader.Feed = &ejsres
	REData.Display()
	HNData.Display()
	GHData.Display()
	EJSData.Display()
}

func doPH(c *cli.Context) {
	ph := make(chan loader.ResultData)
	go loader.GetPHFeed(ph)
	phres := <-ph
	var PHData loader.Feed = &phres
	PHData.Display()
}

func doRE(c *cli.Context) {
	re := make(chan loader.ResultData)
	go loader.GetRedditFeed(re)
	reres := <-re
	var REData loader.Feed = &reres
	REData.Display()
}

func doHN(c *cli.Context) {
	displayUnitRssFeed("HackerNews", "https://news.ycombinator.com/rss")
}

func doGH(c *cli.Context) {
	displayUnitRssFeedWithDesc("Github Trends", "http://github-trends.ryotarai.info/rss/github_trends_all_daily.rss")
}

func doTC(c *cli.Context) {
	displayUnitRssFeed("TechCrunch", "http://feeds.feedburner.com/TechCrunch/")
}

func doMS(c *cli.Context) {
	displayUnitRssFeed("Mashable", "http://feeds.mashable.com/Mashable")
}

func doTNW(c *cli.Context) {
	displayUnitRssFeed("The Next Web", "http://feeds2.feedburner.com/thenextweb")
}

func doDN(c *cli.Context) {
	displayUnitRssFeed("Designer News", "https://news.layervault.com/?format=rss")
}

func doFB(c *cli.Context) {
	displayUnitRssFeed("Forbes - Tech", "http://www.forbes.com/technology/feed/")
}

func doEJ(c *cli.Context) {
	displayUnitRssFeed("EchoJS", "http://www.echojs.com/rss")
}

func doA16Z(c *cli.Context) {
	displayUnitRssFeed("A16Z", "http://a16z.com/feed/")
}

func doHatena(c *cli.Context) {
	displayUnitRssFeed("Hatena", "http://b.hatena.ne.jp/search/tag?q=%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0&users=10&mode=rss")
}
<|endoftext|>


<commit_before>package box

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

type CommentCollection struct {
	TotalCount int        `json:"total_count"`
	Entries    []*Comment `json:"entries"`
}

type Comment struct {
	Type           string `json:"type"`
	Id             string `json:"id"`
	IsReplyComment bool   `json:"is_reply_comment"`
	Message        string `json:"message"`
	CreatedBy      *Item  `json:"created_by"` // TODO(ttacon): change this to user, this needs to be a mini-user struct
	Item           *Item  `json:"item"`
	CreatedAt      string `json:"created_at"`  // TODO(ttacon): change to time.Time
	ModifiedAt     string `json:"modified_at"` // TODO(ttacon): change to time.Time
}

// Documentation: https://developers.box.com/docs/#comments-add-a-comment-to-an-item
func (c *Client) AddComment(itemType, id, message, taggedMessage string) (*http.Response, *Comment, error) {
	var dataMap = map[string]interface{}{
		"item": map[string]string{
			"type": itemType,
			"id":   id,
		},
		"message":        message,
		"tagged_message": taggedMessage,
	}
	dataBytes, err := json.Marshal(dataMap)
	if err != nil {
		return nil, nil, err
	}

	req, err := http.NewRequest(
		"POST",
		fmt.Sprintf("%s/comments", BASE_URL),
		bytes.NewReader(dataBytes),
	)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.Trans.Client().Do(req)
	if err != nil {
		return resp, nil, err
	}

	var data Comment
	err = json.NewDecoder(resp.Body).Decode(&data)
	return resp, &data, err
}

// Documentation: https://developers.box.com/docs/#comments-change-a-comments-message
func (c *Client) ChangeCommentsMessage(commendId, message string) (*http.Response, *Comment, error) {
	var dataMap = map[string]string{
		"message": message,
	}
	dataBytes, err := json.Marshal(dataMap)
	if err != nil {
		return nil, nil, err
	}

	req, err := htt.NewRequest(
		"PUT",
		fmt.Sprintf("%s/comments/%s", BASE_URL, commendId),
		bytes.NewReader(dataBytes),
	)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.Trans.Client().Do(req)
	if err != nil {
		return resp, nil, err
	}

	var data Comment
	err = json.NewDecoder(resp.Body).Decode(&data)
	return resp, &data, err
}

// Documentation: https://developers.box.com/docs/#comments-get-information-about-a-comment
func (c *Client) GetComment(commentId string) (*http.Response, *Comment, error) {
	req, err := http.NewRequest(
		"GET",
		fmt.Sprintf("%s/comments/%s", BASE_URL, commentId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.Trans.Client().Do(req)
	if err != nil {
		return resp, nil, err
	}

	var data Comment
	err = json.NewDecoder(resp.Body).Decode(&data)
	return resp, &data, err
}
<commit_msg>Add DeleteComment<commit_after>package box

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

type CommentCollection struct {
	TotalCount int        `json:"total_count"`
	Entries    []*Comment `json:"entries"`
}

type Comment struct {
	Type           string `json:"type"`
	Id             string `json:"id"`
	IsReplyComment bool   `json:"is_reply_comment"`
	Message        string `json:"message"`
	CreatedBy      *Item  `json:"created_by"` // TODO(ttacon): change this to user, this needs to be a mini-user struct
	Item           *Item  `json:"item"`
	CreatedAt      string `json:"created_at"`  // TODO(ttacon): change to time.Time
	ModifiedAt     string `json:"modified_at"` // TODO(ttacon): change to time.Time
}

// Documentation: https://developers.box.com/docs/#comments-add-a-comment-to-an-item
func (c *Client) AddComment(itemType, id, message, taggedMessage string) (*http.Response, *Comment, error) {
	var dataMap = map[string]interface{}{
		"item": map[string]string{
			"type": itemType,
			"id":   id,
		},
		"message":        message,
		"tagged_message": taggedMessage,
	}
	dataBytes, err := json.Marshal(dataMap)
	if err != nil {
		return nil, nil, err
	}

	req, err := http.NewRequest(
		"POST",
		fmt.Sprintf("%s/comments", BASE_URL),
		bytes.NewReader(dataBytes),
	)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.Trans.Client().Do(req)
	if err != nil {
		return resp, nil, err
	}

	var data Comment
	err = json.NewDecoder(resp.Body).Decode(&data)
	return resp, &data, err
}

// Documentation: https://developers.box.com/docs/#comments-change-a-comments-message
func (c *Client) ChangeCommentsMessage(commendId, message string) (*http.Response, *Comment, error) {
	var dataMap = map[string]string{
		"message": message,
	}
	dataBytes, err := json.Marshal(dataMap)
	if err != nil {
		return nil, nil, err
	}

	req, err := htt.NewRequest(
		"PUT",
		fmt.Sprintf("%s/comments/%s", BASE_URL, commendId),
		bytes.NewReader(dataBytes),
	)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.Trans.Client().Do(req)
	if err != nil {
		return resp, nil, err
	}

	var data Comment
	err = json.NewDecoder(resp.Body).Decode(&data)
	return resp, &data, err
}

// Documentation: https://developers.box.com/docs/#comments-get-information-about-a-comment
func (c *Client) GetComment(commentId string) (*http.Response, *Comment, error) {
	req, err := http.NewRequest(
		"GET",
		fmt.Sprintf("%s/comments/%s", BASE_URL, commentId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.Trans.Client().Do(req)
	if err != nil {
		return resp, nil, err
	}

	var data Comment
	err = json.NewDecoder(resp.Body).Decode(&data)
	return resp, &data, err
}

// Documentation: https://developers.box.com/docs/#comments-delete-a-comment
func (c *Client) DeleteComment(commentId string) (*http.Response, error) {
	req, err := http.NewRequest(
		"DELETE",
		fmt.Sprintf("%s/comments/%s", BASE_URL, commentId),
		nil,
	)
	if err != nil {
		return nil, err
	}

	return c.Trans.Client().Do(req)
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

var (
	gCmdWords = []string{
		"set",
		"map",
		"cmd",
		"up",
		"half-up",
		"page-up",
		"down",
		"half-down",
		"page-down",
		"updir",
		"open",
		"quit",
		"top",
		"bottom",
		"toggle",
		"invert",
		"unselect",
		"copy",
		"cut",
		"paste",
		"delete",
		"clear",
		"draw",
		"redraw",
		"load",
		"reload",
		"read",
		"shell",
		"shell-pipe",
		"shell-wait",
		"shell-async",
		"find",
		"find-back",
		"find-next",
		"find-prev",
		"search",
		"search-back",
		"search-next",
		"search-prev",
		"mark-save",
		"mark-remove",
		"mark-load",
		"rename",
		"sync",
		"echo",
		"echomsg",
		"echoerr",
		"cd",
		"select",
		"glob-select",
		"glob-unselect",
		"source",
		"push",
	}

	gOptWords = []string{
		"anchorfind",
		"noanchorfind",
		"anchorfind!",
		"color256",
		"nocolor256",
		"color256!",
		"dircounts",
		"nodircounts",
		"dircounts!",
		"dirfirst",
		"nodirfirst",
		"dirfirst!",
		"drawbox",
		"nodrawbox",
		"drawbox!",
		"globsearch",
		"noglobsearch",
		"globsearch!",
		"icons",
		"noicons",
		"icons!",
		"hidden",
		"nohidden",
		"hidden!",
		"ignorecase",
		"noignorecase",
		"ignorecase!",
		"ignoredia",
		"noignoredia",
		"ignoredia!",
		"incsearch",
		"noincsearch",
		"incsearch!",
		"preview",
		"nopreview",
		"preview!",
		"reverse",
		"noreverse",
		"reverse!",
		"smartcase",
		"nosmartcase",
		"smartcase!",
		"smartdia",
		"nosmartdia",
		"smartdia!",
		"wrapscan",
		"nowrapscan",
		"wrapscan!",
		"wrapscroll",
		"nowrapscroll",
		"wrapscroll!",
		"number",
		"nonumber",
		"number!",
		"relativenumber",
		"norelativenumber",
		"relativenumber!",
		"findlen",
		"period",
		"scrolloff",
		"tabstop",
		"errorfmt",
		"filesep",
		"ifs",
		"previewer",
		"promptfmt",
		"shell",
		"sortby",
		"timefmt",
		"ratios",
		"info",
		"shellopts",
	}
)

func matchLongest(s1, s2 string) string {
	i := 0
	for ; i < len(s1) && i < len(s2); i++ {
		if s1[i] != s2[i] {
			break
		}
	}
	return s1[:i]
}

func matchWord(s string, words []string) (matches []string, longest string) {
	for _, w := range words {
		if !strings.HasPrefix(w, s) {
			continue
		}

		matches = append(matches, w)
		if longest != "" {
			longest = matchLongest(longest, w)
		} else if s != "" {
			longest = w + " "
		}
	}

	if longest == "" {
		longest = s
	}

	return
}

func matchExec(s string) (matches []string, longest string) {
	var words []string

	paths := strings.Split(envPath, string(filepath.ListSeparator))

	for _, p := range paths {
		if _, err := os.Stat(p); os.IsNotExist(err) {
			continue
		}

		files, err := ioutil.ReadDir(p)
		if err != nil {
			log.Printf("reading path: %s", err)
		}

		for _, f := range files {
			if !strings.HasPrefix(f.Name(), s) {
				continue
			}

			f, err = os.Stat(filepath.Join(p, f.Name()))
			if err != nil {
				log.Printf("getting file information: %s", err)
				continue
			}

			if !f.Mode().IsRegular() || !isExecutable(f) {
				continue
			}

			log.Print(f.Name())
			words = append(words, f.Name())
		}
	}

	sort.Strings(words)

	if len(words) > 0 {
		uniq := words[:1]
		for i := 1; i < len(words); i++ {
			if words[i] != words[i-1] {
				uniq = append(uniq, words[i])
			}
		}
		words = uniq
	}

	return matchWord(s, words)
}

func matchFile(s string) (matches []string, longest string) {
	dir := strings.Replace(s, "~", gUser.HomeDir, -1)

	if !filepath.IsAbs(dir) {
		wd, err := os.Getwd()
		if err != nil {
			log.Printf("getting current directory: %s", err)
		} else {
			dir = wd + string(filepath.Separator) + dir
		}
	}

	dir = unescape(filepath.Dir(dir))

	files, err := ioutil.ReadDir(dir)
	if err != nil {
		log.Printf("reading directory: %s", err)
	}

	for _, f := range files {
		f, err := os.Stat(filepath.Join(dir, f.Name()))
		if err != nil {
			log.Printf("getting file information: %s", err)
			return
		}

		_, last := filepath.Split(s)
		if !strings.HasPrefix(escape(f.Name()), last) {
			continue
		}

		name := f.Name()
		if isRoot(s) || filepath.Base(s) != s {
			name = filepath.Join(filepath.Dir(unescape(s)), f.Name())
		}
		name = escape(name)

		item := f.Name()
		if f.Mode().IsDir() {
			item += string(filepath.Separator)
		}
		matches = append(matches, item)

		if longest != "" {
			longest = matchLongest(longest, name)
		} else if s != "" {
			if f.Mode().IsRegular() {
				longest = name + " "
			} else {
				longest = name + string(filepath.Separator)
			}
		}
	}

	if longest == "" {
		longest = s
	}

	return
}

func completeCmd(acc []rune) (matches []string, longestAcc []rune) {
	s := string(acc)
	f := tokenize(s)

	var longest string

	switch len(f) {
	case 1:
		words := gCmdWords
		for c := range gOpts.cmds {
			words = append(words, c)
		}
		sort.Strings(words)
		j := 0
		for i := 1; i < len(words); i++ {
			if words[j] == words[i] {
				continue
			}
			j++
			words[i], words[j] = words[j], words[i]
		}
		words = words[:j+1]
		matches, longest = matchWord(s, words)
		longestAcc = []rune(longest)
	case 2:
		switch f[0] {
		case "set":
			matches, longest = matchWord(f[1], gOptWords)
			longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
		case "map", "cmd":
			longestAcc = acc
		default:
			matches, longest = matchFile(f[len(f)-1])
			longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
		}
	default:
		switch f[0] {
		case "set", "map", "cmd":
			longestAcc = acc
		default:
			matches, longest = matchFile(f[len(f)-1])
			longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
		}
	}

	return
}

func completeFile(acc []rune) (matches []string, longestAcc []rune) {
	s := string(acc)

	wd, err := os.Getwd()
	if err != nil {
		log.Printf("getting current directory: %s", err)
	}

	files, err := ioutil.ReadDir(wd)
	if err != nil {
		log.Printf("reading directory: %s", err)
	}

	var longest string

	for _, f := range files {
		if !strings.HasPrefix(f.Name(), s) {
			continue
		}

		matches = append(matches, f.Name())

		if longest != "" {
			longest = matchLongest(longest, f.Name())
		} else if s != "" {
			longest = f.Name()
		}
	}

	if longest == "" {
		longest = s
	}

	longestAcc = []rune(longest)

	return
}

func completeShell(acc []rune) (matches []string, longestAcc []rune) {
	s := string(acc)
	f := tokenize(s)

	var longest string

	switch len(f) {
	case 1:
		matches, longest = matchExec(s)
		longestAcc = []rune(longest)
	default:
		matches, longest = matchFile(f[len(f)-1])
		longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
	}

	return
}
<commit_msg>escape path separator on windows<commit_after>package main

import (
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

var (
	gCmdWords = []string{
		"set",
		"map",
		"cmd",
		"up",
		"half-up",
		"page-up",
		"down",
		"half-down",
		"page-down",
		"updir",
		"open",
		"quit",
		"top",
		"bottom",
		"toggle",
		"invert",
		"unselect",
		"copy",
		"cut",
		"paste",
		"delete",
		"clear",
		"draw",
		"redraw",
		"load",
		"reload",
		"read",
		"shell",
		"shell-pipe",
		"shell-wait",
		"shell-async",
		"find",
		"find-back",
		"find-next",
		"find-prev",
		"search",
		"search-back",
		"search-next",
		"search-prev",
		"mark-save",
		"mark-remove",
		"mark-load",
		"rename",
		"sync",
		"echo",
		"echomsg",
		"echoerr",
		"cd",
		"select",
		"glob-select",
		"glob-unselect",
		"source",
		"push",
	}

	gOptWords = []string{
		"anchorfind",
		"noanchorfind",
		"anchorfind!",
		"color256",
		"nocolor256",
		"color256!",
		"dircounts",
		"nodircounts",
		"dircounts!",
		"dirfirst",
		"nodirfirst",
		"dirfirst!",
		"drawbox",
		"nodrawbox",
		"drawbox!",
		"globsearch",
		"noglobsearch",
		"globsearch!",
		"icons",
		"noicons",
		"icons!",
		"hidden",
		"nohidden",
		"hidden!",
		"ignorecase",
		"noignorecase",
		"ignorecase!",
		"ignoredia",
		"noignoredia",
		"ignoredia!",
		"incsearch",
		"noincsearch",
		"incsearch!",
		"preview",
		"nopreview",
		"preview!",
		"reverse",
		"noreverse",
		"reverse!",
		"smartcase",
		"nosmartcase",
		"smartcase!",
		"smartdia",
		"nosmartdia",
		"smartdia!",
		"wrapscan",
		"nowrapscan",
		"wrapscan!",
		"wrapscroll",
		"nowrapscroll",
		"wrapscroll!",
		"number",
		"nonumber",
		"number!",
		"relativenumber",
		"norelativenumber",
		"relativenumber!",
		"findlen",
		"period",
		"scrolloff",
		"tabstop",
		"errorfmt",
		"filesep",
		"ifs",
		"previewer",
		"promptfmt",
		"shell",
		"sortby",
		"timefmt",
		"ratios",
		"info",
		"shellopts",
	}
)

func matchLongest(s1, s2 string) string {
	i := 0
	for ; i < len(s1) && i < len(s2); i++ {
		if s1[i] != s2[i] {
			break
		}
	}
	return s1[:i]
}

func matchWord(s string, words []string) (matches []string, longest string) {
	for _, w := range words {
		if !strings.HasPrefix(w, s) {
			continue
		}

		matches = append(matches, w)
		if longest != "" {
			longest = matchLongest(longest, w)
		} else if s != "" {
			longest = w + " "
		}
	}

	if longest == "" {
		longest = s
	}

	return
}

func matchExec(s string) (matches []string, longest string) {
	var words []string

	paths := strings.Split(envPath, string(filepath.ListSeparator))

	for _, p := range paths {
		if _, err := os.Stat(p); os.IsNotExist(err) {
			continue
		}

		files, err := ioutil.ReadDir(p)
		if err != nil {
			log.Printf("reading path: %s", err)
		}

		for _, f := range files {
			if !strings.HasPrefix(f.Name(), s) {
				continue
			}

			f, err = os.Stat(filepath.Join(p, f.Name()))
			if err != nil {
				log.Printf("getting file information: %s", err)
				continue
			}

			if !f.Mode().IsRegular() || !isExecutable(f) {
				continue
			}

			log.Print(f.Name())
			words = append(words, f.Name())
		}
	}

	sort.Strings(words)

	if len(words) > 0 {
		uniq := words[:1]
		for i := 1; i < len(words); i++ {
			if words[i] != words[i-1] {
				uniq = append(uniq, words[i])
			}
		}
		words = uniq
	}

	return matchWord(s, words)
}

func matchFile(s string) (matches []string, longest string) {
	dir := strings.Replace(s, "~", gUser.HomeDir, -1)

	if !filepath.IsAbs(dir) {
		wd, err := os.Getwd()
		if err != nil {
			log.Printf("getting current directory: %s", err)
		} else {
			dir = wd + string(filepath.Separator) + dir
		}
	}

	dir = unescape(filepath.Dir(dir))

	files, err := ioutil.ReadDir(dir)
	if err != nil {
		log.Printf("reading directory: %s", err)
	}

	for _, f := range files {
		f, err := os.Stat(filepath.Join(dir, f.Name()))
		if err != nil {
			log.Printf("getting file information: %s", err)
			return
		}

		_, last := filepath.Split(s)
		if !strings.HasPrefix(escape(f.Name()), last) {
			continue
		}

		name := f.Name()
		if isRoot(s) || filepath.Base(s) != s {
			name = filepath.Join(filepath.Dir(unescape(s)), f.Name())
		}
		name = escape(name)

		item := f.Name()
		if f.Mode().IsDir() {
			item += escape(string(filepath.Separator))
		}
		matches = append(matches, item)

		if longest != "" {
			longest = matchLongest(longest, name)
		} else if s != "" {
			if f.Mode().IsRegular() {
				longest = name + " "
			} else {
				longest = name + escape(string(filepath.Separator))
			}
		}
	}

	if longest == "" {
		longest = s
	}

	return
}

func completeCmd(acc []rune) (matches []string, longestAcc []rune) {
	s := string(acc)
	f := tokenize(s)

	var longest string

	switch len(f) {
	case 1:
		words := gCmdWords
		for c := range gOpts.cmds {
			words = append(words, c)
		}
		sort.Strings(words)
		j := 0
		for i := 1; i < len(words); i++ {
			if words[j] == words[i] {
				continue
			}
			j++
			words[i], words[j] = words[j], words[i]
		}
		words = words[:j+1]
		matches, longest = matchWord(s, words)
		longestAcc = []rune(longest)
	case 2:
		switch f[0] {
		case "set":
			matches, longest = matchWord(f[1], gOptWords)
			longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
		case "map", "cmd":
			longestAcc = acc
		default:
			matches, longest = matchFile(f[len(f)-1])
			longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
		}
	default:
		switch f[0] {
		case "set", "map", "cmd":
			longestAcc = acc
		default:
			matches, longest = matchFile(f[len(f)-1])
			longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
		}
	}

	return
}

func completeFile(acc []rune) (matches []string, longestAcc []rune) {
	s := string(acc)

	wd, err := os.Getwd()
	if err != nil {
		log.Printf("getting current directory: %s", err)
	}

	files, err := ioutil.ReadDir(wd)
	if err != nil {
		log.Printf("reading directory: %s", err)
	}

	var longest string

	for _, f := range files {
		if !strings.HasPrefix(f.Name(), s) {
			continue
		}

		matches = append(matches, f.Name())

		if longest != "" {
			longest = matchLongest(longest, f.Name())
		} else if s != "" {
			longest = f.Name()
		}
	}

	if longest == "" {
		longest = s
	}

	longestAcc = []rune(longest)

	return
}

func completeShell(acc []rune) (matches []string, longestAcc []rune) {
	s := string(acc)
	f := tokenize(s)

	var longest string

	switch len(f) {
	case 1:
		matches, longest = matchExec(s)
		longestAcc = []rune(longest)
	default:
		matches, longest = matchFile(f[len(f)-1])
		longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
	}

	return
}
<|endoftext|>


<commit_before>// This file contains functions for transpiling binary operator expressions.

package transpiler

import (
	"fmt"
	goast "go/ast"
	"go/token"
	"reflect"
	"strings"

	"github.com/elliotchance/c2go/ast"
	"github.com/elliotchance/c2go/program"
	"github.com/elliotchance/c2go/traverse"
	"github.com/elliotchance/c2go/types"
	"github.com/elliotchance/c2go/util"
)

func transpileBinaryOperator(n *ast.BinaryOperator, p *program.Program) (
	goast.Expr, string, []goast.Stmt, []goast.Stmt, error) {
	preStmts := []goast.Stmt{}
	postStmts := []goast.Stmt{}
	var err error

	left, leftType, newPre, newPost, err := transpileToExpr(n.Children[0], p)
	if err != nil {
		return nil, "", nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	right, rightType, newPre, newPost, err := transpileToExpr(n.Children[1], p)
	if err != nil {
		return nil, "", nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	operator := getTokenForOperator(n.Operator)
	returnType := types.ResolveTypeForBinaryOperator(p, n.Operator, leftType, rightType)

	if operator == token.LAND || operator == token.LOR {
		left, err = types.CastExpr(p, left, leftType, "bool")
		p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, left == nil))
		if left == nil {
			left = util.NewNil()
		}

		right, err = types.CastExpr(p, right, rightType, "bool")
		p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, right == nil))
		if right == nil {
			right = util.NewNil()
		}

		return util.NewBinaryExpr(left, operator, right), "bool",
			preStmts, postStmts, nil
	}

	// The right hand argument of the shift left or shift right operators
	// in Go must be unsigned integers. In C, shifting with a negative shift
	// count is undefined behaviour (so we should be able to ignore that case).
	// To handle this, cast the shift count to a uint64.
	if operator == token.SHL || operator == token.SHR {
		right, err = types.CastExpr(p, right, rightType, "unsigned long long")
		p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, right == nil))
		if right == nil {
			right = util.NewNil()
		}

		return util.NewBinaryExpr(left, operator, right), leftType,
			preStmts, postStmts, nil
	}

	if operator == token.NEQ || operator == token.EQL {
		// Convert "(0)" to "nil" when we are dealing with equality.
		if types.IsNullExpr(right) {
			right = util.NewNil()
		} else {
			// We may have to cast the right side to the same type as the left
			// side. This is a bit crude because we should make a better
			// decision of which type to cast to instead of only using the type
			// of the left side.
			right, err = types.CastExpr(p, right, rightType, leftType)
			p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, right == nil))
		}
	}

	if operator == token.ASSIGN {
		// Memory allocation is translated into the Go-style.
		allocSize := GetAllocationSizeNode(n.Children[1])

		if allocSize != nil {
			allocSizeExpr, _, newPre, newPost, err := transpileToExpr(allocSize, p)
			preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

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

			derefType, err := types.GetDereferenceType(leftType)
			if err != nil {
				return nil, "", preStmts, postStmts, err
			}

			toType, err := types.ResolveType(p, leftType)
			if err != nil {
				return nil, "", preStmts, postStmts, err
			}

			elementSize, err := types.SizeOf(p, derefType)
			if err != nil {
				return nil, "", preStmts, postStmts, err
			}

			right = util.NewCallExpr(
				"make",
				util.NewTypeIdent(toType),
				util.NewBinaryExpr(allocSizeExpr, token.QUO, util.NewIntLit(elementSize)),
			)
		} else {
			right, err = types.CastExpr(p, right, rightType, returnType)

			if _, ok := right.(*goast.UnaryExpr); ok {
				deref, err := types.GetDereferenceType(rightType)

				if !p.AddMessage(ast.GenerateWarningMessage(err, n)) {
					// This is some hackey to convert a reference to a variable
					// into a slice that points to the same location. It will
					// look similar to:
					//
					//     (*[1]int)(unsafe.Pointer(&a))[:]
					//
					p.AddImport("unsafe")
					right = &goast.SliceExpr{
						X: util.NewCallExpr(
							fmt.Sprintf("(*[1]%s)", deref),
							util.NewCallExpr("unsafe.Pointer", right),
						),
					}
				}
			}

			if p.AddMessage(ast.GenerateWarningMessage(err, n)) && right == nil {
				right = util.NewNil()
			}

			// Construct code for assigning value to an union field
			memberExpr, ok := n.Children[0].(*ast.MemberExpr)
			if ok {
				ref := memberExpr.GetDeclRefExpr()
				if ref != nil {
					union := p.GetStruct(ref.Type)
					if union != nil && union.IsUnion {
						funcName := fmt.Sprintf("%s.Set%s", ref.Name, strings.Title(memberExpr.Name))
						resExpr := util.NewCallExpr(funcName, right)
						resType := types.ResolveTypeForBinaryOperator(p, n.Operator, leftType, rightType)

						return resExpr, resType, preStmts, postStmts, nil
					}
				}
			}
		}
	}

	return util.NewBinaryExpr(left, operator, right),
		types.ResolveTypeForBinaryOperator(p, n.Operator, leftType, rightType),
		preStmts, postStmts, nil
}

// GetAllocationSizeNode returns the node that, if evaluated, would return the
// size (in bytes) of a memory allocation operation. For example:
//
//     (int *)malloc(sizeof(int))
//
// Would return the node that represents the "sizeof(int)".
//
// If the node does not represent an allocation operation (such as calling
// malloc, calloc, realloc, etc.) then nil is returned.
//
// In the case of calloc() it will return a new BinaryExpr that multiplies both
// arguments.
func GetAllocationSizeNode(node ast.Node) ast.Node {
	exprs := traverse.GetAllNodesOfType(node,
		reflect.TypeOf((*ast.CallExpr)(nil)))

	for _, expr := range exprs {
		functionName, _ := getNameOfFunctionFromCallExpr(expr.(*ast.CallExpr))

		if functionName == "malloc" {
			// Is 1 always the body in this case? Might need to be more careful
			// to find the correct node.
			return expr.(*ast.CallExpr).Children[1]
		}

		if functionName == "calloc" {
			return &ast.BinaryOperator{
				Type:     "int",
				Operator: "*",
				Children: expr.(*ast.CallExpr).Children[1:],
			}
		}

		// TODO: realloc() is not supported
		// https://github.com/elliotchance/c2go/issues/118
		//
		// Realloc will be treated as calloc which will almost certainly cause
		// bugs in your code.
		if functionName == "realloc" {
			return expr.(*ast.CallExpr).Children[2]
		}
	}

	return nil
}
<commit_msg>found place<commit_after>// This file contains functions for transpiling binary operator expressions.

package transpiler

import (
	"fmt"
	goast "go/ast"
	"go/token"
	"reflect"
	"strings"

	"github.com/elliotchance/c2go/ast"
	"github.com/elliotchance/c2go/program"
	"github.com/elliotchance/c2go/traverse"
	"github.com/elliotchance/c2go/types"
	"github.com/elliotchance/c2go/util"
)

func transpileBinaryOperator(n *ast.BinaryOperator, p *program.Program) (
	goast.Expr, string, []goast.Stmt, []goast.Stmt, error) {
	preStmts := []goast.Stmt{}
	postStmts := []goast.Stmt{}
	var err error

	left, leftType, newPre, newPost, err := transpileToExpr(n.Children[0], p)
	if err != nil {
		return nil, "", nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	right, rightType, newPre, newPost, err := transpileToExpr(n.Children[1], p)
	if err != nil {
		return nil, "", nil, nil, err
	}

	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

	operator := getTokenForOperator(n.Operator)
	returnType := types.ResolveTypeForBinaryOperator(p, n.Operator, leftType, rightType)

	if operator == token.LAND || operator == token.LOR {
		left, err = types.CastExpr(p, left, leftType, "bool")
		p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, left == nil))
		if left == nil {
			left = util.NewNil()
		}

		right, err = types.CastExpr(p, right, rightType, "bool")
		p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, right == nil))
		if right == nil {
			right = util.NewNil()
		}

		return util.NewBinaryExpr(left, operator, right), "bool",
			preStmts, postStmts, nil
	}

	// The right hand argument of the shift left or shift right operators
	// in Go must be unsigned integers. In C, shifting with a negative shift
	// count is undefined behaviour (so we should be able to ignore that case).
	// To handle this, cast the shift count to a uint64.
	if operator == token.SHL || operator == token.SHR {
		right, err = types.CastExpr(p, right, rightType, "unsigned long long")
		p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, right == nil))
		if right == nil {
			right = util.NewNil()
		}

		return util.NewBinaryExpr(left, operator, right), leftType,
			preStmts, postStmts, nil
	}

	if operator == token.NEQ || operator == token.EQL {
		// Convert "(0)" to "nil" when we are dealing with equality.
		if types.IsNullExpr(right) {
			right = util.NewNil()
		} else {
			// We may have to cast the right side to the same type as the left
			// side. This is a bit crude because we should make a better
			// decision of which type to cast to instead of only using the type
			// of the left side.
			right, err = types.CastExpr(p, right, rightType, leftType)
			p.AddMessage(ast.GenerateWarningOrErrorMessage(err, n, right == nil))
		}
	}

	if operator == token.ASSIGN {
		// Memory allocation is translated into the Go-style.
		allocSize := GetAllocationSizeNode(n.Children[1])

		if allocSize != nil {
			allocSizeExpr, _, newPre, newPost, err := transpileToExpr(allocSize, p)
			preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

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

			derefType, err := types.GetDereferenceType(leftType)
			if err != nil {
				return nil, "", preStmts, postStmts, err
			}

			toType, err := types.ResolveType(p, leftType)
			if err != nil {
				return nil, "", preStmts, postStmts, err
			}

			elementSize, err := types.SizeOf(p, derefType)
			if err != nil {
				return nil, "", preStmts, postStmts, err
			}

			right = util.NewCallExpr(
				"make",
				util.NewTypeIdent(toType),
				util.NewBinaryExpr(allocSizeExpr, token.QUO, util.NewIntLit(elementSize)),
			)
		} else {
			right, err = types.CastExpr(p, right, rightType, returnType)

			if _, ok := right.(*goast.UnaryExpr); ok {
				deref, err := types.GetDereferenceType(rightType)

				if !p.AddMessage(ast.GenerateWarningMessage(err, n)) {
					// This is some hackey to convert a reference to a variable
					// into a slice that points to the same location. It will
					// look similar to:
					//
					//     (*[1]int)(unsafe.Pointer(&a))[:]
					//
					p.AddImport("unsafe")
					right = &goast.SliceExpr{
						X: util.NewCallExpr(
							fmt.Sprintf("(*[1]%s)", deref),
							util.NewCallExpr("unsafe.Pointer", right),
						),
					}
				}
			}

			if p.AddMessage(ast.GenerateWarningMessage(err, n)) && right == nil {
				right = util.NewNil()
			}

			// Construct code for assigning value to an union field
			memberExpr, ok := n.Children[0].(*ast.MemberExpr)
			if ok {
				ref := memberExpr.GetDeclRefExpr()
				if ref != nil {
					union := p.GetStruct(ref.Type)
					if union != nil && union.IsUnion {
						funcName := fmt.Sprintf("%s.Set%s", ref.Name, strings.Title(memberExpr.Name))
						resExpr := util.NewCallExpr(funcName, right)
						resType := types.ResolveTypeForBinaryOperator(p, n.Operator, leftType, rightType)

						return resExpr, resType, preStmts, postStmts, nil
					}
				}
			}
		}
	}

	if operator == token.COMMA {
		//
		fmt.Println("COMMA")
	}

	return util.NewBinaryExpr(left, operator, right),
		types.ResolveTypeForBinaryOperator(p, n.Operator, leftType, rightType),
		preStmts, postStmts, nil
}

// GetAllocationSizeNode returns the node that, if evaluated, would return the
// size (in bytes) of a memory allocation operation. For example:
//
//     (int *)malloc(sizeof(int))
//
// Would return the node that represents the "sizeof(int)".
//
// If the node does not represent an allocation operation (such as calling
// malloc, calloc, realloc, etc.) then nil is returned.
//
// In the case of calloc() it will return a new BinaryExpr that multiplies both
// arguments.
func GetAllocationSizeNode(node ast.Node) ast.Node {
	exprs := traverse.GetAllNodesOfType(node,
		reflect.TypeOf((*ast.CallExpr)(nil)))

	for _, expr := range exprs {
		functionName, _ := getNameOfFunctionFromCallExpr(expr.(*ast.CallExpr))

		if functionName == "malloc" {
			// Is 1 always the body in this case? Might need to be more careful
			// to find the correct node.
			return expr.(*ast.CallExpr).Children[1]
		}

		if functionName == "calloc" {
			return &ast.BinaryOperator{
				Type:     "int",
				Operator: "*",
				Children: expr.(*ast.CallExpr).Children[1:],
			}
		}

		// TODO: realloc() is not supported
		// https://github.com/elliotchance/c2go/issues/118
		//
		// Realloc will be treated as calloc which will almost certainly cause
		// bugs in your code.
		if functionName == "realloc" {
			return expr.(*ast.CallExpr).Children[2]
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package triangle

import (
	"math/big"

	"github.com/krasin/g3"
)

type Point [3]int64
type Vector [3]int64
type Triangle [3]Point
type Line [2]Point
type Cube [2]Point

const MaxJ = 10

type VolumeSetter interface {
	Set16(node g3.Node, val uint16)
}

func NewVector(p1, p2 Point) Vector {
	return Vector{p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]}
}

func VectorProduct(a, b Vector) Vector {
	return Vector{
		a[1]*b[2] - a[2]*b[1],
		a[2]*b[0] - a[0]*b[2],
		a[0]*b[1] - a[1]*b[0],
	}
}

func ScalarProduct(a, b Vector) (s *big.Int) {
	s = big.NewInt(0)
	for i := 0; i < 3; i++ {
		tmp := big.NewInt(0)
		s.Add(s, tmp.Mul(big.NewInt(a[i]), big.NewInt(b[i])))
	}
	return
}

func DotInPlane(p, a, b, c Point, r int64) bool {
	va := NewVector(c, a)
	vb := NewVector(c, b)
	vc := NewVector(c, p)
	v := VectorProduct(va, vb)

	s := ScalarProduct(vc, v)
	s.Mul(s, s)
	s.Mul(s, big.NewInt(4))

	r2 := big.NewInt(r)
	r2.Mul(r2, r2)

	v2 := ScalarProduct(v, v)

	t := big.NewInt(0)
	t.Mul(r2, v2)

	return t.Cmp(s) >= 0
}

func len2(v Vector) (s *big.Int) {
	s = big.NewInt(0)
	for i := 0; i < 3; i++ {
		tmp := big.NewInt(v[i])
		tmp.Mul(tmp, tmp)
		s.Add(s, tmp)
	}
	return
}

func sameSide(p, a, b, c Point, r int64) bool {
	ab := NewVector(a, b)
	ac := NewVector(a, c)
	ap := NewVector(a, p)
	v1 := VectorProduct(ab, ac)
	v2 := VectorProduct(ab, ap)
	s := ScalarProduct(v1, v2)
	if s.Cmp(big.NewInt(0)) >= 0 {
		return true
	}
	h2 := len2(v2)
	h2.Mul(h2, big.NewInt(4))
	r2 := big.NewInt(r)
	r2.Mul(r2, r2)
	r2.Mul(r2, len2(ab))
	return r2.Cmp(h2) >= 0
}

func inplaneDotInTriangle(p, a, b, c Point, r int64) bool {
	return sameSide(p, a, b, c, r) &&
		sameSide(p, b, c, a, r) &&
		sameSide(p, c, a, b, r)
}

func DotInTriangle(p, a, b, c Point, r int64) bool {
	return DotInPlane(p, a, b, c, r) && inplaneDotInTriangle(p, a, b, c, r)
}

func to(a, n int64) int64 {
	if a >= 0 {
		return (a + (n-1)/2) / n
	}
	return -((-a + n/2) / n)
}

func toGrid(p Point, scale int64) Point {
	return Point{to(p[0], scale), to(p[1], scale), to(p[2], scale)}
}

func scalePoint(p Point, scale int64) Point {
	return Point{p[0] * scale, p[1] * scale, p[2] * scale}
}

func findJ(p1, p2 Point, scale int64) (j uint) {
	for j = 0; j < 31; j++ {
		var r2 int64
		for z := 0; z < 3; z++ {
			diff := int64(p1[z] - p2[z])
			r2 += diff * diff
		}
		if r2 < (int64(scale)*int64(scale))<<(2*j) {
			return j + 2
		}
	}
	panic("unreachable")
}

func peq(p1, p2 Point) bool {
	for z := 0; z < 3; z++ {
		if p1[z] != p2[z] {
			return false
		}
	}
	return true
}

type pointSlice []Point

func (ps pointSlice) Len() int {
	return len(ps)
}

func (ps pointSlice) Less(i, j int) (res bool) {
	for z := 0; z < 3; z++ {
		if ps[i][z] < ps[j][z] {
			return true
		}
		if ps[i][z] > ps[j][z] {
			return false
		}
	}
	return false
}

func (ps pointSlice) Swap(i, j int) {
	ps[i], ps[j] = ps[j], ps[i]
}

func AddDot(a, b, c Point, scale int64, vol VolumeSetter, i0, i1 int64, j0, j1 uint, last1 Point, color uint16) Point {
	m := j0
	if m < j1 {
		m = j1
	}
	i2 := 1<<m - i0*(1<<(m-j0)) - i1*(1<<(m-j1))
	var p Point
	for z := 0; z < 3; z++ {
		p[z] = int64(i0)*(int64(1)<<uint(m-j0))*a[z] +
			int64(i1)*(int64(1)<<uint(m-j1))*b[z] +
			int64(i2)*c[z]
		p[z] >>= m
	}

	p = toGrid(p, scale)
	vol.Set16(g3.Node{int(p[0]), int(p[1]), int(p[2])}, color)

	return p
}

func AllTriangleDots(a, b, c Point, scale int64, vol VolumeSetter, color uint16) {
	j0 := findJ(a, c, scale)
	j1 := findJ(a, b, scale)

	m := j0
	if m < j1 {
		m = j1
	}

	for i0 := 0; i0 <= 1<<j0; i0++ {
		var last1 Point
		for i1 := 0; i0*(1<<(m-j0))+i1*(1<<(m-j1)) <= 1<<m; i1++ {
			last1 = AddDot(a, b, c, scale, vol, int64(i0), int64(i1), j0, j1, last1, color)

		}
	}
}

func det3(v0, v1, v2 Vector) int64 {
	return v0[0]*v1[1]*v2[2] + v0[1]*v1[2]*v2[0] + v0[2]*v1[0]*v2[1] -
		v0[0]*v1[2]*v2[1] - v0[1]*v1[0]*v2[2] - v0[2]*v1[1]*v2[0]
}

func MeshVolume(triangles []Triangle, scale int64) (res int64) {
	for _, t := range triangles {
		res += det3(Vector(t[0]), Vector(t[1]), Vector(t[2]))
	}
	return res / 6
}
<commit_msg>VolumeSetter -> SpaceSetter<commit_after>package triangle

import (
	"math/big"

	"github.com/krasin/g3"
)

type Point [3]int64
type Vector [3]int64
type Triangle [3]Point
type Line [2]Point
type Cube [2]Point

const MaxJ = 10

type SpaceSetter interface {
	Set16(node g3.Node, val uint16)
}

func NewVector(p1, p2 Point) Vector {
	return Vector{p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]}
}

func VectorProduct(a, b Vector) Vector {
	return Vector{
		a[1]*b[2] - a[2]*b[1],
		a[2]*b[0] - a[0]*b[2],
		a[0]*b[1] - a[1]*b[0],
	}
}

func ScalarProduct(a, b Vector) (s *big.Int) {
	s = big.NewInt(0)
	for i := 0; i < 3; i++ {
		tmp := big.NewInt(0)
		s.Add(s, tmp.Mul(big.NewInt(a[i]), big.NewInt(b[i])))
	}
	return
}

func DotInPlane(p, a, b, c Point, r int64) bool {
	va := NewVector(c, a)
	vb := NewVector(c, b)
	vc := NewVector(c, p)
	v := VectorProduct(va, vb)

	s := ScalarProduct(vc, v)
	s.Mul(s, s)
	s.Mul(s, big.NewInt(4))

	r2 := big.NewInt(r)
	r2.Mul(r2, r2)

	v2 := ScalarProduct(v, v)

	t := big.NewInt(0)
	t.Mul(r2, v2)

	return t.Cmp(s) >= 0
}

func len2(v Vector) (s *big.Int) {
	s = big.NewInt(0)
	for i := 0; i < 3; i++ {
		tmp := big.NewInt(v[i])
		tmp.Mul(tmp, tmp)
		s.Add(s, tmp)
	}
	return
}

func sameSide(p, a, b, c Point, r int64) bool {
	ab := NewVector(a, b)
	ac := NewVector(a, c)
	ap := NewVector(a, p)
	v1 := VectorProduct(ab, ac)
	v2 := VectorProduct(ab, ap)
	s := ScalarProduct(v1, v2)
	if s.Cmp(big.NewInt(0)) >= 0 {
		return true
	}
	h2 := len2(v2)
	h2.Mul(h2, big.NewInt(4))
	r2 := big.NewInt(r)
	r2.Mul(r2, r2)
	r2.Mul(r2, len2(ab))
	return r2.Cmp(h2) >= 0
}

func inplaneDotInTriangle(p, a, b, c Point, r int64) bool {
	return sameSide(p, a, b, c, r) &&
		sameSide(p, b, c, a, r) &&
		sameSide(p, c, a, b, r)
}

func DotInTriangle(p, a, b, c Point, r int64) bool {
	return DotInPlane(p, a, b, c, r) && inplaneDotInTriangle(p, a, b, c, r)
}

func to(a, n int64) int64 {
	if a >= 0 {
		return (a + (n-1)/2) / n
	}
	return -((-a + n/2) / n)
}

func toGrid(p Point, scale int64) Point {
	return Point{to(p[0], scale), to(p[1], scale), to(p[2], scale)}
}

func scalePoint(p Point, scale int64) Point {
	return Point{p[0] * scale, p[1] * scale, p[2] * scale}
}

func findJ(p1, p2 Point, scale int64) (j uint) {
	for j = 0; j < 31; j++ {
		var r2 int64
		for z := 0; z < 3; z++ {
			diff := int64(p1[z] - p2[z])
			r2 += diff * diff
		}
		if r2 < (int64(scale)*int64(scale))<<(2*j) {
			return j + 2
		}
	}
	panic("unreachable")
}

func peq(p1, p2 Point) bool {
	for z := 0; z < 3; z++ {
		if p1[z] != p2[z] {
			return false
		}
	}
	return true
}

type pointSlice []Point

func (ps pointSlice) Len() int {
	return len(ps)
}

func (ps pointSlice) Less(i, j int) (res bool) {
	for z := 0; z < 3; z++ {
		if ps[i][z] < ps[j][z] {
			return true
		}
		if ps[i][z] > ps[j][z] {
			return false
		}
	}
	return false
}

func (ps pointSlice) Swap(i, j int) {
	ps[i], ps[j] = ps[j], ps[i]
}

func AddDot(a, b, c Point, scale int64, vol SpaceSetter, i0, i1 int64, j0, j1 uint, last1 Point, color uint16) Point {
	m := j0
	if m < j1 {
		m = j1
	}
	i2 := 1<<m - i0*(1<<(m-j0)) - i1*(1<<(m-j1))
	var p Point
	for z := 0; z < 3; z++ {
		p[z] = int64(i0)*(int64(1)<<uint(m-j0))*a[z] +
			int64(i1)*(int64(1)<<uint(m-j1))*b[z] +
			int64(i2)*c[z]
		p[z] >>= m
	}

	p = toGrid(p, scale)
	vol.Set16(g3.Node{int(p[0]), int(p[1]), int(p[2])}, color)

	return p
}

func AllTriangleDots(a, b, c Point, scale int64, vol SpaceSetter, color uint16) {
	j0 := findJ(a, c, scale)
	j1 := findJ(a, b, scale)

	m := j0
	if m < j1 {
		m = j1
	}

	for i0 := 0; i0 <= 1<<j0; i0++ {
		var last1 Point
		for i1 := 0; i0*(1<<(m-j0))+i1*(1<<(m-j1)) <= 1<<m; i1++ {
			last1 = AddDot(a, b, c, scale, vol, int64(i0), int64(i1), j0, j1, last1, color)

		}
	}
}

func det3(v0, v1, v2 Vector) int64 {
	return v0[0]*v1[1]*v2[2] + v0[1]*v1[2]*v2[0] + v0[2]*v1[0]*v2[1] -
		v0[0]*v1[2]*v2[1] - v0[1]*v1[0]*v2[2] - v0[2]*v1[1]*v2[0]
}

func MeshVolume(triangles []Triangle, scale int64) (res int64) {
	for _, t := range triangles {
		res += det3(Vector(t[0]), Vector(t[1]), Vector(t[2]))
	}
	return res / 6
}
<|endoftext|>