<commit_before>package main

import (
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

type LinkState int8

const (
	NotLink LinkState = iota
	Working
	Broken
)

type File struct {
	os.FileInfo
	LinkState LinkState
	Path      string
}

func getFilesSorted(path string) []*File {
	fi, err := readdir(path)
	if err != nil {
		log.Printf("reading directory: %s", err)
	}

	switch gOpts.sortby {
	case "name":
		sortFilesStable(fi, func(i, j int) bool {
			return strings.EqualFold(fi[i].Name(), fi[j].Name())
		})
	case "size":
		sortFilesStable(fi, func(i, j int) bool {
			return fi[i].Size() < fi[j].Size()
		})
	case "time":
		sortFilesStable(fi, func(i, j int) bool {
			return fi[i].ModTime().Before(fi[j].ModTime())
		})
	default:
		log.Printf("unknown sorting type: %s", gOpts.sortby)
	}

	if gOpts.dirfirst {
		sortFilesStable(fi, func(i, j int) bool {
			if fi[i].IsDir() == fi[j].IsDir() {
				return i < j
			}
			return fi[i].IsDir()
		})
	}
	//TODO this should be optional
	sortFilesStable(fi, func(i, j int) bool {
		nums1, rest1, numFirst1 := extractNums(fi[i].Name())
		nums2, rest2, numFirst2 := extractNums(fi[j].Name())

		if numFirst1 != numFirst2 {
			return i < j
		}

		if numFirst1 {
			if nums1[0] != nums2[0] {
				return nums1[0] < nums2[0]
			}
			nums1 = nums1[1:]
			nums2 = nums2[1:]
		}

		for k := 0; k < len(nums1) && k < len(nums2); k++ {
			if rest1[k] != rest2[k] {
				return i < j
			}
			if nums1[k] != nums2[k] {
				return nums1[k] < nums2[k]
			}
		}

		return i < j
	})

	return fi
}

func readdir(path string) ([]*File, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}

	names, err := f.Readdirnames(-1)
	fi := make([]*File, 0, len(names))
	for _, filename := range names {
		if !gOpts.hidden && filename[0] == '.' {
			continue
		}

		fpath := filepath.Join(path, filename)

		lstat, lerr := os.Lstat(fpath)
		if os.IsNotExist(lerr) {
			continue
		}
		if lerr != nil {
			return fi, lerr
		}

		var linkState LinkState

		if lstat.Mode()&os.ModeSymlink != 0 {
			stat, serr := os.Stat(fpath)
			if serr == nil {
				linkState = Working
				lstat = stat
			} else {
				linkState = Broken
				log.Printf("getting link destination info: %s", serr)
			}
		}

		fi = append(fi, &File{
			FileInfo:  lstat,
			LinkState: linkState,
			Path:      fpath,
		})
	}
	return fi, err
}

type Dir struct {
	ind  int // which entry is highlighted
	pos  int // which line in the ui highlighted entry is
	path string
	fi   []*File
}

func newDir(path string) *Dir {
	return &Dir{
		path: path,
		fi:   getFilesSorted(path),
	}
}

func (dir *Dir) renew(height int) {
	var name string
	if len(dir.fi) != 0 {
		name = dir.fi[dir.ind].Name()
	}

	dir.fi = getFilesSorted(dir.path)

	dir.load(dir.ind, dir.pos, height, name)
}

func (dir *Dir) load(ind, pos, height int, name string) {
	if len(dir.fi) == 0 {
		dir.ind, dir.pos = 0, 0
		return
	}

	ind = max(0, min(ind, len(dir.fi)-1))

	if dir.fi[ind].Name() != name {
		for i, f := range dir.fi {
			if f.Name() == name {
				ind = i
				break
			}
		}

		edge := min(gOpts.scrolloff, len(dir.fi)-ind-1)
		pos = min(ind, height-edge-1)
	}

	dir.ind = ind
	dir.pos = pos
}

type Nav struct {
	dirs   []*Dir
	inds   map[string]int
	poss   map[string]int
	names  map[string]string
	marks  map[string]bool
	saves  map[string]bool
	height int
}

func getDirs(wd string, height int) []*Dir {
	var dirs []*Dir

	for curr, base := wd, ""; !isRoot(base); curr, base = filepath.Dir(curr), filepath.Base(curr) {
		dir := newDir(curr)
		for i, f := range dir.fi {
			if f.Name() == base {
				dir.ind = i
				edge := min(gOpts.scrolloff, len(dir.fi)-dir.ind-1)
				dir.pos = min(i, height-edge-1)
				break
			}
		}
		dirs = append(dirs, dir)
	}

	for i, j := 0, len(dirs)-1; i < j; i, j = i+1, j-1 {
		dirs[i], dirs[j] = dirs[j], dirs[i]
	}

	return dirs
}

func newNav(height int) *Nav {
	wd, err := os.Getwd()
	if err != nil {
		log.Printf("getting current directory: %s", err)
	}

	dirs := getDirs(wd, height)

	return &Nav{
		dirs:   dirs,
		inds:   make(map[string]int),
		poss:   make(map[string]int),
		names:  make(map[string]string),
		marks:  make(map[string]bool),
		saves:  make(map[string]bool),
		height: height,
	}
}

func (nav *Nav) renew(height int) {
	nav.height = height
	for _, d := range nav.dirs {
		d.renew(nav.height)
	}

	for m := range nav.marks {
		if _, err := os.Stat(m); os.IsNotExist(err) {
			delete(nav.marks, m)
		}
	}
}

func (nav *Nav) up(dist int) {
	dir := nav.currDir()

	if dir.ind == 0 {
		return
	}

	dir.ind -= dist
	dir.ind = max(0, dir.ind)

	dir.pos -= dist
	edge := min(gOpts.scrolloff, dir.ind)
	dir.pos = max(dir.pos, edge)
}

func (nav *Nav) down(dist int) {
	dir := nav.currDir()

	maxind := len(dir.fi) - 1

	if dir.ind >= maxind {
		return
	}

	dir.ind += dist
	dir.ind = min(maxind, dir.ind)

	dir.pos += dist
	edge := min(gOpts.scrolloff, maxind-dir.ind)

	// use a smaller value when the height is even and scrolloff is maxed
	// in order to stay at the same row as much as possible while up/down
	edge = min(edge, nav.height/2+nav.height%2-1)

	dir.pos = min(dir.pos, nav.height-edge-1)
	dir.pos = min(dir.pos, maxind)
}

func (nav *Nav) updir() error {
	if len(nav.dirs) <= 1 {
		return nil
	}

	dir := nav.currDir()

	nav.inds[dir.path] = dir.ind
	nav.poss[dir.path] = dir.pos

	if len(dir.fi) != 0 {
		nav.names[dir.path] = dir.fi[dir.ind].Name()
	}

	nav.dirs = nav.dirs[:len(nav.dirs)-1]

	if err := os.Chdir(filepath.Dir(dir.path)); err != nil {
		return fmt.Errorf("updir: %s", err)
	}

	return nil
}

var ErrNotDir = fmt.Errorf("not a directory")

func (nav *Nav) open() error {
	curr, err := nav.currFile()
	if err != nil {
		return err
	}
	if !curr.IsDir() {
		return ErrNotDir
	}
	path := curr.Path

	dir := newDir(path)

	dir.load(nav.inds[path], nav.poss[path], nav.height, nav.names[path])

	nav.dirs = append(nav.dirs, dir)

	if err := os.Chdir(path); err != nil {
		return fmt.Errorf("open: %s", err)
	}

	return nil
}

func (nav *Nav) bot() {
	dir := nav.currDir()

	dir.ind = len(dir.fi) - 1
	dir.pos = min(dir.ind, nav.height-1)
}

func (nav *Nav) top() {
	dir := nav.currDir()

	dir.ind = 0
	dir.pos = 0
}

func (nav *Nav) cd(wd string) error {
	wd = strings.Replace(wd, "~", envHome, -1)
	wd = filepath.Clean(wd)

	if !filepath.IsAbs(wd) {
		wd = filepath.Join(nav.currDir().path, wd)
	}

	if err := os.Chdir(wd); err != nil {
		return fmt.Errorf("cd: %s", err)
	}

	nav.dirs = getDirs(wd, nav.height)

	// TODO: save/load ind and pos from the map

	return nil
}

func (nav *Nav) toggleMark(path string) {
	if nav.marks[path] {
		delete(nav.marks, path)
	} else {
		nav.marks[path] = true
	}
}

func (nav *Nav) toggle() {
	curr, err := nav.currFile()
	if err != nil {
		return
	}

	nav.toggleMark(curr.Path)

	nav.down(1)
}

func (nav *Nav) invert() {
	last := nav.currDir()
	for _, f := range last.fi {
		path := filepath.Join(last.path, f.Name())
		nav.toggleMark(path)
	}
}

func (nav *Nav) save(copy bool) error {
	if len(nav.marks) == 0 {
		curr, err := nav.currFile()
		if err != nil {
			return errors.New("no file selected")
		}

		if err := saveFiles([]string{curr.Path}, copy); err != nil {
			return err
		}

		nav.saves = make(map[string]bool)
		nav.saves[curr.Path] = copy
	} else {
		var fs []string
		for f := range nav.marks {
			fs = append(fs, f)
		}

		if err := saveFiles(fs, copy); err != nil {
			return err
		}

		nav.saves = make(map[string]bool)
		for f := range nav.marks {
			nav.saves[f] = copy
		}
	}

	return nil
}

func (nav *Nav) put() error {
	list, copy, err := loadFiles()
	if err != nil {
		return err
	}

	if len(list) == 0 {
		return errors.New("no file in yank/delete buffer")
	}

	dir := nav.currDir()

	var args []string

	var sh string
	if copy {
		sh = "cp"
		args = append(args, "-r")
	} else {
		sh = "mv"
	}

	args = append(args, "--backup=numbered")
	args = append(args, list...)
	args = append(args, dir.path)

	cmd := exec.Command(sh, args...)

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("%s: %s", sh, err)
	}

	// TODO: async?

	return nil
}

func (nav *Nav) sync() error {
	list, copy, err := loadFiles()
	if err != nil {
		return err
	}

	nav.saves = make(map[string]bool)
	for _, f := range list {
		nav.saves[f] = copy
	}

	return nil
}

func (nav *Nav) currDir() *Dir {
	return nav.dirs[len(nav.dirs)-1]
}

func (nav *Nav) currFile() (*File, error) {
	last := nav.dirs[len(nav.dirs)-1]

	if len(last.fi) == 0 {
		return nil, fmt.Errorf("empty directory")
	}
	return last.fi[last.ind], nil
}

func (nav *Nav) currMarks() []string {
	marks := make([]string, 0, len(nav.marks))
	for m := range nav.marks {
		marks = append(marks, m)
	}
	return marks
}
<commit_msg>fix less function for name sorting<commit_after>package main

import (
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

type LinkState int8

const (
	NotLink LinkState = iota
	Working
	Broken
)

type File struct {
	os.FileInfo
	LinkState LinkState
	Path      string
}

func getFilesSorted(path string) []*File {
	fi, err := readdir(path)
	if err != nil {
		log.Printf("reading directory: %s", err)
	}

	switch gOpts.sortby {
	case "name":
		sortFilesStable(fi, func(i, j int) bool {
			return strings.ToLower(fi[i].Name()) < strings.ToLower(fi[j].Name())
		})
	case "size":
		sortFilesStable(fi, func(i, j int) bool {
			return fi[i].Size() < fi[j].Size()
		})
	case "time":
		sortFilesStable(fi, func(i, j int) bool {
			return fi[i].ModTime().Before(fi[j].ModTime())
		})
	default:
		log.Printf("unknown sorting type: %s", gOpts.sortby)
	}

	if gOpts.dirfirst {
		sortFilesStable(fi, func(i, j int) bool {
			if fi[i].IsDir() == fi[j].IsDir() {
				return i < j
			}
			return fi[i].IsDir()
		})
	}
	//TODO this should be optional
	sortFilesStable(fi, func(i, j int) bool {
		nums1, rest1, numFirst1 := extractNums(fi[i].Name())
		nums2, rest2, numFirst2 := extractNums(fi[j].Name())

		if numFirst1 != numFirst2 {
			return i < j
		}

		if numFirst1 {
			if nums1[0] != nums2[0] {
				return nums1[0] < nums2[0]
			}
			nums1 = nums1[1:]
			nums2 = nums2[1:]
		}

		for k := 0; k < len(nums1) && k < len(nums2); k++ {
			if rest1[k] != rest2[k] {
				return i < j
			}
			if nums1[k] != nums2[k] {
				return nums1[k] < nums2[k]
			}
		}

		return i < j
	})

	return fi
}

func readdir(path string) ([]*File, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}

	names, err := f.Readdirnames(-1)
	fi := make([]*File, 0, len(names))
	for _, filename := range names {
		if !gOpts.hidden && filename[0] == '.' {
			continue
		}

		fpath := filepath.Join(path, filename)

		lstat, lerr := os.Lstat(fpath)
		if os.IsNotExist(lerr) {
			continue
		}
		if lerr != nil {
			return fi, lerr
		}

		var linkState LinkState

		if lstat.Mode()&os.ModeSymlink != 0 {
			stat, serr := os.Stat(fpath)
			if serr == nil {
				linkState = Working
				lstat = stat
			} else {
				linkState = Broken
				log.Printf("getting link destination info: %s", serr)
			}
		}

		fi = append(fi, &File{
			FileInfo:  lstat,
			LinkState: linkState,
			Path:      fpath,
		})
	}
	return fi, err
}

type Dir struct {
	ind  int // which entry is highlighted
	pos  int // which line in the ui highlighted entry is
	path string
	fi   []*File
}

func newDir(path string) *Dir {
	return &Dir{
		path: path,
		fi:   getFilesSorted(path),
	}
}

func (dir *Dir) renew(height int) {
	var name string
	if len(dir.fi) != 0 {
		name = dir.fi[dir.ind].Name()
	}

	dir.fi = getFilesSorted(dir.path)

	dir.load(dir.ind, dir.pos, height, name)
}

func (dir *Dir) load(ind, pos, height int, name string) {
	if len(dir.fi) == 0 {
		dir.ind, dir.pos = 0, 0
		return
	}

	ind = max(0, min(ind, len(dir.fi)-1))

	if dir.fi[ind].Name() != name {
		for i, f := range dir.fi {
			if f.Name() == name {
				ind = i
				break
			}
		}

		edge := min(gOpts.scrolloff, len(dir.fi)-ind-1)
		pos = min(ind, height-edge-1)
	}

	dir.ind = ind
	dir.pos = pos
}

type Nav struct {
	dirs   []*Dir
	inds   map[string]int
	poss   map[string]int
	names  map[string]string
	marks  map[string]bool
	saves  map[string]bool
	height int
}

func getDirs(wd string, height int) []*Dir {
	var dirs []*Dir

	for curr, base := wd, ""; !isRoot(base); curr, base = filepath.Dir(curr), filepath.Base(curr) {
		dir := newDir(curr)
		for i, f := range dir.fi {
			if f.Name() == base {
				dir.ind = i
				edge := min(gOpts.scrolloff, len(dir.fi)-dir.ind-1)
				dir.pos = min(i, height-edge-1)
				break
			}
		}
		dirs = append(dirs, dir)
	}

	for i, j := 0, len(dirs)-1; i < j; i, j = i+1, j-1 {
		dirs[i], dirs[j] = dirs[j], dirs[i]
	}

	return dirs
}

func newNav(height int) *Nav {
	wd, err := os.Getwd()
	if err != nil {
		log.Printf("getting current directory: %s", err)
	}

	dirs := getDirs(wd, height)

	return &Nav{
		dirs:   dirs,
		inds:   make(map[string]int),
		poss:   make(map[string]int),
		names:  make(map[string]string),
		marks:  make(map[string]bool),
		saves:  make(map[string]bool),
		height: height,
	}
}

func (nav *Nav) renew(height int) {
	nav.height = height
	for _, d := range nav.dirs {
		d.renew(nav.height)
	}

	for m := range nav.marks {
		if _, err := os.Stat(m); os.IsNotExist(err) {
			delete(nav.marks, m)
		}
	}
}

func (nav *Nav) up(dist int) {
	dir := nav.currDir()

	if dir.ind == 0 {
		return
	}

	dir.ind -= dist
	dir.ind = max(0, dir.ind)

	dir.pos -= dist
	edge := min(gOpts.scrolloff, dir.ind)
	dir.pos = max(dir.pos, edge)
}

func (nav *Nav) down(dist int) {
	dir := nav.currDir()

	maxind := len(dir.fi) - 1

	if dir.ind >= maxind {
		return
	}

	dir.ind += dist
	dir.ind = min(maxind, dir.ind)

	dir.pos += dist
	edge := min(gOpts.scrolloff, maxind-dir.ind)

	// use a smaller value when the height is even and scrolloff is maxed
	// in order to stay at the same row as much as possible while up/down
	edge = min(edge, nav.height/2+nav.height%2-1)

	dir.pos = min(dir.pos, nav.height-edge-1)
	dir.pos = min(dir.pos, maxind)
}

func (nav *Nav) updir() error {
	if len(nav.dirs) <= 1 {
		return nil
	}

	dir := nav.currDir()

	nav.inds[dir.path] = dir.ind
	nav.poss[dir.path] = dir.pos

	if len(dir.fi) != 0 {
		nav.names[dir.path] = dir.fi[dir.ind].Name()
	}

	nav.dirs = nav.dirs[:len(nav.dirs)-1]

	if err := os.Chdir(filepath.Dir(dir.path)); err != nil {
		return fmt.Errorf("updir: %s", err)
	}

	return nil
}

var ErrNotDir = fmt.Errorf("not a directory")

func (nav *Nav) open() error {
	curr, err := nav.currFile()
	if err != nil {
		return err
	}
	if !curr.IsDir() {
		return ErrNotDir
	}
	path := curr.Path

	dir := newDir(path)

	dir.load(nav.inds[path], nav.poss[path], nav.height, nav.names[path])

	nav.dirs = append(nav.dirs, dir)

	if err := os.Chdir(path); err != nil {
		return fmt.Errorf("open: %s", err)
	}

	return nil
}

func (nav *Nav) bot() {
	dir := nav.currDir()

	dir.ind = len(dir.fi) - 1
	dir.pos = min(dir.ind, nav.height-1)
}

func (nav *Nav) top() {
	dir := nav.currDir()

	dir.ind = 0
	dir.pos = 0
}

func (nav *Nav) cd(wd string) error {
	wd = strings.Replace(wd, "~", envHome, -1)
	wd = filepath.Clean(wd)

	if !filepath.IsAbs(wd) {
		wd = filepath.Join(nav.currDir().path, wd)
	}

	if err := os.Chdir(wd); err != nil {
		return fmt.Errorf("cd: %s", err)
	}

	nav.dirs = getDirs(wd, nav.height)

	// TODO: save/load ind and pos from the map

	return nil
}

func (nav *Nav) toggleMark(path string) {
	if nav.marks[path] {
		delete(nav.marks, path)
	} else {
		nav.marks[path] = true
	}
}

func (nav *Nav) toggle() {
	curr, err := nav.currFile()
	if err != nil {
		return
	}

	nav.toggleMark(curr.Path)

	nav.down(1)
}

func (nav *Nav) invert() {
	last := nav.currDir()
	for _, f := range last.fi {
		path := filepath.Join(last.path, f.Name())
		nav.toggleMark(path)
	}
}

func (nav *Nav) save(copy bool) error {
	if len(nav.marks) == 0 {
		curr, err := nav.currFile()
		if err != nil {
			return errors.New("no file selected")
		}

		if err := saveFiles([]string{curr.Path}, copy); err != nil {
			return err
		}

		nav.saves = make(map[string]bool)
		nav.saves[curr.Path] = copy
	} else {
		var fs []string
		for f := range nav.marks {
			fs = append(fs, f)
		}

		if err := saveFiles(fs, copy); err != nil {
			return err
		}

		nav.saves = make(map[string]bool)
		for f := range nav.marks {
			nav.saves[f] = copy
		}
	}

	return nil
}

func (nav *Nav) put() error {
	list, copy, err := loadFiles()
	if err != nil {
		return err
	}

	if len(list) == 0 {
		return errors.New("no file in yank/delete buffer")
	}

	dir := nav.currDir()

	var args []string

	var sh string
	if copy {
		sh = "cp"
		args = append(args, "-r")
	} else {
		sh = "mv"
	}

	args = append(args, "--backup=numbered")
	args = append(args, list...)
	args = append(args, dir.path)

	cmd := exec.Command(sh, args...)

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("%s: %s", sh, err)
	}

	// TODO: async?

	return nil
}

func (nav *Nav) sync() error {
	list, copy, err := loadFiles()
	if err != nil {
		return err
	}

	nav.saves = make(map[string]bool)
	for _, f := range list {
		nav.saves[f] = copy
	}

	return nil
}

func (nav *Nav) currDir() *Dir {
	return nav.dirs[len(nav.dirs)-1]
}

func (nav *Nav) currFile() (*File, error) {
	last := nav.dirs[len(nav.dirs)-1]

	if len(last.fi) == 0 {
		return nil, fmt.Errorf("empty directory")
	}
	return last.fi[last.ind], nil
}

func (nav *Nav) currMarks() []string {
	marks := make([]string, 0, len(nav.marks))
	for m := range nav.marks {
		marks = append(marks, m)
	}
	return marks
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
)

const (
	createDatabaseMigrationName = "Create-Database"
	createTablesMigrationName   = "Create-Tables"
)

// RunNew creates a new fragmenta project given the argument
// Usage: fragmenta new [app|cms|api| valid repo path e.g. github.com/fragmenta/fragmenta-cms]
func RunNew(args []string) {

	// Remove fragmenta backup from args list
	args = args[2:]

	// We expect two args left:
	if len(args) < 2 {
		log.Printf("Both a project path and a project type or URL are required to create a new site\n")
		return
	}

	repo := args[0]
	projectPath, err := filepath.Abs(args[1])
	if err != nil {
		log.Printf("Error expanding file path\n")
		return
	}

	if fileExists(projectPath) {
		log.Printf("A folder already exists at path %s\n", projectPath)
		return
	}

	switch repo {
	case "app":
		repo = "github.com/fragmenta/fragmenta-app"
	case "cms":
		repo = "github.com/fragmenta/fragmenta-cms"
	case "blog":
		repo = "github.com/fragmenta/fragmenta-blog"
	default:
		// TODO clean repo if it contains https or .git...
	}

	// Go get the project url, to make sure it is up to date, should use -u
	result, err := runCommand("go", "get", repo)
	if err != nil {
		log.Printf("Error calling go get %s", err)
		return
	}
	log.Printf("%s", string(result))

	// Copy the pristine new site over
	goProjectPath := path.Join(os.Getenv("GOPATH"), "src", repo)
	err = copyNewSite(goProjectPath, projectPath)
	if err != nil {
		log.Printf("Error copying project %s", err)
		return
	}

	// Generate config files
	err = generateConfig(projectPath)
	if err != nil {
		log.Printf("Error generating config %s", err)
		return
	}

	// Generate a migration AND run it
	err = generateCreateSQL(projectPath)
	if err != nil {
		log.Printf("Error generating migrations %s", err)
		return
	}

	// Output instructions to let them change setup first if they wish
	showNewSiteHelp(projectPath)

}

func copyNewSite(goProjectPath, projectPath string) error {

	// Now copy that over to a new project at projectPath - it should be in GOPATH/src/repo
	// Unfortunately there is no simple facility for this in golang stdlib, so we use unix command (sorry windows!)
	// FIXME - do not rely on unix commands

	result, err := runCommand("cp", "-r", goProjectPath, projectPath)
	if err != nil {
		log.Printf("Error copying site %s", err)
		return err
	}
	log.Printf("%s", string(result))

	// Delete the .git folder at that path
	gitPath := path.Join(projectPath, ".git")
	log.Printf("Removing all at:%s", gitPath)
	err = os.RemoveAll(gitPath)
	if err != nil {
		return err
	}

	// Run git init to get a new git repo here
	result, err = runCommand("git", "init", projectPath)
	if err != nil {
		return err
	}
	log.Printf("Initialising new git repo at:%s", projectPath)

	// Now reifyNewSite
	return reifyNewSite(goProjectPath, projectPath)
}

// reifyNewSite changes import refs within go files to the correct format
func reifyNewSite(goProjectPath, projectPath string) error {
	files, err := collectFiles(projectPath, []string{".go"})
	if err != nil {
		return err
	}

	// For each go file within project, make sure the refs are to the new site, not to the template site
	relGoProjectPath := projectPathRelative(goProjectPath)
	relProjectPath := projectPathRelative(projectPath)
	for _, f := range files {
		// Load the file, if it contains refs to goprojectpath, replace them with relative project path imports
		data, err := ioutil.ReadFile(f)
		if err != nil {
			return err
		}

		// Substitutions - consider reifying instead if it is any more complex
		fileString := string(data)
		if strings.Contains(fileString, relGoProjectPath) {
			fileString = strings.Replace(fileString, relGoProjectPath, relProjectPath, -1)
		}

		err = ioutil.WriteFile(f, []byte(fileString), permissions)
		if err != nil {
			return err
		}

	}

	return nil
}

// the user should be prompted to:

func showNewSiteHelp(projectPath string) {
	helpString := fragmentaDivider
	helpString += "Congratulations, we've made a new website at " + projectPathRelative(projectPath)
	helpString += "\n  if you wish you can edit the database config at secrets/fragmenta.json and sql at db/migrate"
	helpString += "\n  To get started, run the following commands:"
	helpString += "\n  cd " + projectPath
	helpString += "\n  fragmenta migrate"
	helpString += "\n  fragmenta"
	helpString += fragmentaDivider
	log.Print(helpString)
}

// generateCreateSQL generates an SQL migration file to create the database user and database referred to in config
func generateCreateSQL(projectPath string) error {

	// Set up a Create-Database migration, which comes first
	name := path.Base(projectPath)
	d := ConfigDevelopment["db"]
	u := ConfigDevelopment["db_user"]
	p := ConfigDevelopment["db_pass"]
	sql := fmt.Sprintf("/* Setup database for %s */\nCREATE USER \"%s\" WITH PASSWORD '%s';\nCREATE DATABASE \"%s\" WITH OWNER \"%s\";", name, u, p, d, u)

	// Generate a migration to create db with today's date
	file := migrationPath(projectPath, createDatabaseMigrationName)
	err := ioutil.WriteFile(file, []byte(sql), 0744)
	if err != nil {
		return err
	}

	// If we have a Create-Tables file, copy it out to a new migration with today's date
	createTablesPath := path.Join(projectPath, "db", "migrate", createTablesMigrationName+".sql.tmpl")
	if fileExists(createTablesPath) {
		sql, err := ioutil.ReadFile(createTablesPath)
		if err != nil {
			return err
		}

		// Now vivify the template, for now we just replace one key
		sqlString := reifyString(string(sql))

		file = migrationPath(projectPath, createTablesMigrationName)
		err = ioutil.WriteFile(file, []byte(sqlString), 0744)
		if err != nil {
			return err
		}
		// Remove the old file
		os.Remove(createTablesPath)

	} else {
		fmt.Printf("NO TABLES %s", createTablesPath)
	}

	return nil
}

func projectPathRelative(projectPath string) string {
	goSrc := os.Getenv("GOPATH") + "/src/"
	return strings.Replace(projectPath, goSrc, "", 1)
}

func generateConfig(projectPath string) error {
	configPath := configPath(projectPath)
	prefix := path.Base(projectPath)
	log.Printf("Generating new config at %s", configPath)

	ConfigProduction = map[string]string{}
	ConfigDevelopment = map[string]string{}
	ConfigTest = map[string]string{
		"port":            "3000",
		"log":             "log/test.log",
		"db_adapter":      "postgres",
		"db":              prefix + "_test",
		"db_user":         prefix + "_server",
		"db_pass":         randomKey(8),
		"assets_compiled": "no",
		"path":            projectPathRelative(projectPath),
		"hmac_key":        randomKey(32),
		"secret_key":      randomKey(32),
	}

	// Should we ask for db prefix when setting up?
	// hmm, in fact can we do this setup here at all!!
	for k, v := range ConfigTest {
		ConfigDevelopment[k] = v
		ConfigProduction[k] = v
	}
	ConfigDevelopment["db"] = prefix + "_development"
	ConfigDevelopment["log"] = "log/development.log"
	ConfigDevelopment["hmac_key"] = randomKey(32)
	ConfigDevelopment["secret_key"] = randomKey(32)

	ConfigProduction["db"] = prefix + "_production"
	ConfigProduction["log"] = "log/production.log"
	ConfigProduction["port"] = "80"
	ConfigProduction["assets_compiled"] = "yes"
	ConfigProduction["hmac_key"] = randomKey(32)
	ConfigProduction["secret_key"] = randomKey(32)

	configs := map[string]map[string]string{
		"production":  ConfigProduction,
		"development": ConfigDevelopment,
		"test":        ConfigTest,
	}

	configJSON, err := json.MarshalIndent(configs, "", "\t")
	if err != nil {
		log.Printf("Error parsing config %s %v", configPath, err)
		return err
	}

	// Write the config json file
	err = ioutil.WriteFile(configPath, configJSON, permissions)
	if err != nil {
		log.Printf("Error writing config %s %v", configPath, err)
		return err
	}

	return nil
}

// Generate a random 32 byte key encoded in base64
func randomKey(l int64) string {
	k := make([]byte, l)
	if _, err := io.ReadFull(rand.Reader, k); err != nil {
		return ""
	}
	return hex.EncodeToString(k)
}

// Collect the files with these extensions under src
func collectFiles(dir string, extensions []string) ([]string, error) {

	files := []string{}

	err := filepath.Walk(dir, func(file string, info os.FileInfo, err error) error {
		// If we have an err pass it up
		if err != nil {
			return err
		}

		// Deal with files only
		if !info.IsDir() {
			// Check for go files
			name := path.Base(file)
			if !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") {
				files = append(files, file)
			}
		}

		return nil
	})

	if err != nil {
		return files, err
	}

	return files, nil

}
<commit_msg>add restriction for /Users/spozoga/work/src<commit_after>package main

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
)

const (
	createDatabaseMigrationName = "Create-Database"
	createTablesMigrationName   = "Create-Tables"
)

// RunNew creates a new fragmenta project given the argument
// Usage: fragmenta new [app|cms|api| valid repo path e.g. github.com/fragmenta/fragmenta-cms]
func RunNew(args []string) {

	// Remove fragmenta backup from args list
	args = args[2:]

	// We expect two args left:
	if len(args) < 2 {
		log.Printf("Both a project path and a project type or URL are required to create a new site\n")
		return
	}

	repo := args[0]
	projectPath, err := filepath.Abs(args[1])
	if err != nil {
		log.Printf("Error expanding file path\n")
		return
	}

	if !strings.HasPrefix(projectPath, path.Join(os.Getenv("GOPATH"), "src")) {
		log.Printf("Create project in $GOPATH/src \n")
		return
	}

	if fileExists(projectPath) {
		log.Printf("A folder already exists at path %s\n", projectPath)
		return
	}

	switch repo {
	case "app":
		repo = "github.com/fragmenta/fragmenta-app"
	case "cms":
		repo = "github.com/fragmenta/fragmenta-cms"
	case "blog":
		repo = "github.com/fragmenta/fragmenta-blog"
	default:
		// TODO clean repo if it contains https or .git...
	}

	// Go get the project url, to make sure it is up to date, should use -u
	result, err := runCommand("go", "get", repo)
	if err != nil {
		log.Printf("Error calling go get %s", err)
		return
	}
	log.Printf("%s", string(result))

	// Copy the pristine new site over
	goProjectPath := path.Join(os.Getenv("GOPATH"), "src", repo)
	err = copyNewSite(goProjectPath, projectPath)
	if err != nil {
		log.Printf("Error copying project %s", err)
		return
	}

	// Generate config files
	err = generateConfig(projectPath)
	if err != nil {
		log.Printf("Error generating config %s", err)
		return
	}

	// Generate a migration AND run it
	err = generateCreateSQL(projectPath)
	if err != nil {
		log.Printf("Error generating migrations %s", err)
		return
	}

	// Output instructions to let them change setup first if they wish
	showNewSiteHelp(projectPath)

}

func copyNewSite(goProjectPath, projectPath string) error {

	// Now copy that over to a new project at projectPath - it should be in GOPATH/src/repo
	// Unfortunately there is no simple facility for this in golang stdlib, so we use unix command (sorry windows!)
	// FIXME - do not rely on unix commands

	result, err := runCommand("cp", "-r", goProjectPath, projectPath)
	if err != nil {
		log.Printf("Error copying site %s", err)
		return err
	}
	log.Printf("%s", string(result))

	// Delete the .git folder at that path
	gitPath := path.Join(projectPath, ".git")
	log.Printf("Removing all at:%s", gitPath)
	err = os.RemoveAll(gitPath)
	if err != nil {
		return err
	}

	// Run git init to get a new git repo here
	result, err = runCommand("git", "init", projectPath)
	if err != nil {
		return err
	}
	log.Printf("Initialising new git repo at:%s", projectPath)

	// Now reifyNewSite
	return reifyNewSite(goProjectPath, projectPath)
}

// reifyNewSite changes import refs within go files to the correct format
func reifyNewSite(goProjectPath, projectPath string) error {
	files, err := collectFiles(projectPath, []string{".go"})
	if err != nil {
		return err
	}

	// For each go file within project, make sure the refs are to the new site, not to the template site
	relGoProjectPath := projectPathRelative(goProjectPath)
	relProjectPath := projectPathRelative(projectPath)
	for _, f := range files {
		// Load the file, if it contains refs to goprojectpath, replace them with relative project path imports
		data, err := ioutil.ReadFile(f)
		if err != nil {
			return err
		}

		// Substitutions - consider reifying instead if it is any more complex
		fileString := string(data)
		if strings.Contains(fileString, relGoProjectPath) {
			fileString = strings.Replace(fileString, relGoProjectPath, relProjectPath, -1)
		}

		err = ioutil.WriteFile(f, []byte(fileString), permissions)
		if err != nil {
			return err
		}

	}

	return nil
}

// the user should be prompted to:

func showNewSiteHelp(projectPath string) {
	helpString := fragmentaDivider
	helpString += "Congratulations, we've made a new website at " + projectPathRelative(projectPath)
	helpString += "\n  if you wish you can edit the database config at secrets/fragmenta.json and sql at db/migrate"
	helpString += "\n  To get started, run the following commands:"
	helpString += "\n  cd " + projectPath
	helpString += "\n  fragmenta migrate"
	helpString += "\n  fragmenta"
	helpString += fragmentaDivider
	log.Print(helpString)
}

// generateCreateSQL generates an SQL migration file to create the database user and database referred to in config
func generateCreateSQL(projectPath string) error {

	// Set up a Create-Database migration, which comes first
	name := path.Base(projectPath)
	d := ConfigDevelopment["db"]
	u := ConfigDevelopment["db_user"]
	p := ConfigDevelopment["db_pass"]
	sql := fmt.Sprintf("/* Setup database for %s */\nCREATE USER \"%s\" WITH PASSWORD '%s';\nCREATE DATABASE \"%s\" WITH OWNER \"%s\";", name, u, p, d, u)

	// Generate a migration to create db with today's date
	file := migrationPath(projectPath, createDatabaseMigrationName)
	err := ioutil.WriteFile(file, []byte(sql), 0744)
	if err != nil {
		return err
	}

	// If we have a Create-Tables file, copy it out to a new migration with today's date
	createTablesPath := path.Join(projectPath, "db", "migrate", createTablesMigrationName+".sql.tmpl")
	if fileExists(createTablesPath) {
		sql, err := ioutil.ReadFile(createTablesPath)
		if err != nil {
			return err
		}

		// Now vivify the template, for now we just replace one key
		sqlString := reifyString(string(sql))

		file = migrationPath(projectPath, createTablesMigrationName)
		err = ioutil.WriteFile(file, []byte(sqlString), 0744)
		if err != nil {
			return err
		}
		// Remove the old file
		os.Remove(createTablesPath)

	} else {
		fmt.Printf("NO TABLES %s", createTablesPath)
	}

	return nil
}

func projectPathRelative(projectPath string) string {
	goSrc := os.Getenv("GOPATH") + "/src/"
	return strings.Replace(projectPath, goSrc, "", 1)
}

func generateConfig(projectPath string) error {
	configPath := configPath(projectPath)
	prefix := path.Base(projectPath)
	log.Printf("Generating new config at %s", configPath)

	ConfigProduction = map[string]string{}
	ConfigDevelopment = map[string]string{}
	ConfigTest = map[string]string{
		"port":            "3000",
		"log":             "log/test.log",
		"db_adapter":      "postgres",
		"db":              prefix + "_test",
		"db_user":         prefix + "_server",
		"db_pass":         randomKey(8),
		"assets_compiled": "no",
		"path":            projectPathRelative(projectPath),
		"hmac_key":        randomKey(32),
		"secret_key":      randomKey(32),
	}

	// Should we ask for db prefix when setting up?
	// hmm, in fact can we do this setup here at all!!
	for k, v := range ConfigTest {
		ConfigDevelopment[k] = v
		ConfigProduction[k] = v
	}
	ConfigDevelopment["db"] = prefix + "_development"
	ConfigDevelopment["log"] = "log/development.log"
	ConfigDevelopment["hmac_key"] = randomKey(32)
	ConfigDevelopment["secret_key"] = randomKey(32)

	ConfigProduction["db"] = prefix + "_production"
	ConfigProduction["log"] = "log/production.log"
	ConfigProduction["port"] = "80"
	ConfigProduction["assets_compiled"] = "yes"
	ConfigProduction["hmac_key"] = randomKey(32)
	ConfigProduction["secret_key"] = randomKey(32)

	configs := map[string]map[string]string{
		"production":  ConfigProduction,
		"development": ConfigDevelopment,
		"test":        ConfigTest,
	}

	configJSON, err := json.MarshalIndent(configs, "", "\t")
	if err != nil {
		log.Printf("Error parsing config %s %v", configPath, err)
		return err
	}

	// Write the config json file
	err = ioutil.WriteFile(configPath, configJSON, permissions)
	if err != nil {
		log.Printf("Error writing config %s %v", configPath, err)
		return err
	}

	return nil
}

// Generate a random 32 byte key encoded in base64
func randomKey(l int64) string {
	k := make([]byte, l)
	if _, err := io.ReadFull(rand.Reader, k); err != nil {
		return ""
	}
	return hex.EncodeToString(k)
}

// Collect the files with these extensions under src
func collectFiles(dir string, extensions []string) ([]string, error) {

	files := []string{}

	err := filepath.Walk(dir, func(file string, info os.FileInfo, err error) error {
		// If we have an err pass it up
		if err != nil {
			return err
		}

		// Deal with files only
		if !info.IsDir() {
			// Check for go files
			name := path.Base(file)
			if !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") {
				files = append(files, file)
			}
		}

		return nil
	})

	if err != nil {
		return files, err
	}

	return files, nil

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/ParsePlatform/parse-cli/herokucmd"
	"github.com/ParsePlatform/parse-cli/parsecli"
	"github.com/ParsePlatform/parse-cli/parsecmd"
	"github.com/facebookgo/parse"
	"github.com/facebookgo/stackerr"
	"github.com/spf13/cobra"
)

type newCmd struct {
	addApplication bool

	noCode       bool   // do not setup a Cloud Code project
	configOnly   bool   // create a Cloud Code project with only configuration, no code
	createNewApp bool   // create a new app
	parseAppName string // name of parse app
	codeLocation string // location of Cloud Code project
}

func (n *newCmd) curlCommand(e *parsecli.Env, app *parsecli.App) string {
	args := "{}"
	if e.Type == parsecli.HerokuFormat {
		args = `{"a": "Adventurous ", "b": "Parser"}`
	}

	return fmt.Sprintf(
		`curl -X POST \
 -H "X-Parse-Application-Id: %s" \
 -H "X-Parse-REST-API-Key: %s" \
 -H "Content-Type: application/json" \
 -d '%s' \
 https://api.parse.com/1/functions/hello
`,
		app.ApplicationID,
		app.RestKey,
		args,
	)
}

func (n *newCmd) cloudCodeHelpMessage(e *parsecli.Env, app *parsecli.App) string {
	code := "Cloud Code"
	if e.Type == parsecli.HerokuFormat {
		code = "server code"
	}
	return fmt.Sprintf(
		`Your %s has been created at %s.

This includes a "Hello world" cloud function, so once you deploy,
you can test that it works, with the printed curl command.

Next, you might want to deploy this code with:

	cd %s
	parse deploy

Once deployed you can test that it works by running:
%s
`,
		code,
		e.Root,
		e.Root,
		n.curlCommand(e, app),
	)
}

func (n *newCmd) getCloudCodeDir(e *parsecli.Env, appName string, isNew bool) (string, error) {
	var cloudCodeDir string
	if isNew {
		fmt.Fprintf(e.Out,
			`Awesome! Now it's time to set up some Cloud Code for the app: %q,
Next we will create a directory to hold your Cloud Code.
Please enter the name to use for this directory,
or hit ENTER to use %q as the directory name.

Directory Name: `,
			appName,
			appName,
		)
	} else {
		fmt.Fprintf(e.Out,
			`Please enter the name of the folder where we can download the latest deployed
Cloud Code for your app %q

Directory Name: `,
			appName,
		)
	}

	fmt.Fscanf(e.In, "%s\n", &cloudCodeDir)
	cloudCodeDir = strings.TrimSpace(cloudCodeDir)
	if cloudCodeDir == "" {
		cloudCodeDir = appName
	}

	helpMsg := "Next time, please select the same app you selected now."
	if isNew {
		helpMsg = `Next time, please select to add Cloud Code to an 'existing' app
and select the app you just created.`
	}

	configFile := filepath.Join(e.Root, cloudCodeDir, parsecli.LegacyConfigFile)
	if _, err := os.Lstat(configFile); err == nil {
		return "", stackerr.Newf(
			`Sorry, we are unable to create Cloud Code at %s.
It seems that you already have Cloud Code at %s.
Please run "parse new" again.
%s
Please choose a different name for your Cloud Code directory,
so it does not conflict with any other Cloud Code in the current directory.
`,
			cloudCodeDir,
			cloudCodeDir,
			helpMsg,
		)
	}

	cloudCodeDirInfo, err := os.Stat(filepath.Join(e.Root, cloudCodeDir))
	if err == nil {
		if !cloudCodeDirInfo.IsDir() {
			return "", stackerr.Newf(`Sorry, we are unable to create Cloud Code at %s.
In the current directory a file named: %s already exists.
Please run "parse new" again.
%s
Please choose a different name for your Cloud Code directory,
so it does not conflict with any other Cloud Code in the current directory.
`,
				cloudCodeDir,
				cloudCodeDir,
				helpMsg,
			)

		}
		return "", nil
	}
	if !os.IsNotExist(err) {
		return "", stackerr.Wrap(err)
	}
	return cloudCodeDir, nil
}

func (n *newCmd) promptCreateNewApp(e *parsecli.Env, nonInteractive bool) (string, error) {
	if nonInteractive || n.noCode {
		if n.createNewApp {
			return "new", nil
		}
		return "existing", nil
	}

	msg := `"new" and "existing" are the only valid options.
Please try again ...`

	var decision string
	for i := 0; i < 3; i++ {
		fmt.Fprintf(e.Out,
			`Would you like to create a new app, or add Cloud Code to an existing app?
Type "(n)ew" or "(e)xisting": `,
		)
		fmt.Fscanf(e.In, "%s\n", &decision)
		decision = strings.ToLower(decision)

		if decision == "new" || decision == "n" || decision == "existing" || decision == "e" {
			return decision, nil
		}
		fmt.Fprintln(e.Err, msg)
	}
	return "", stackerr.New(msg)
}

func (n *newCmd) setupSample(
	e *parsecli.Env,
	name string,
	appConfig parsecli.AppConfig,
	isNew bool,
	nonInteractive bool,
) (bool, error) {
	found := parsecli.IsProjectDir(parsecli.GetProjectRoot(e, e.Root))
	if !found {
		root := parsecli.GetLegacyProjectRoot(e, e.Root)
		_, err := os.Lstat(filepath.Join(root, parsecli.LegacyConfigFile))
		found = err == nil
	}
	if found {
		return false, stackerr.New(
			`Detected that you are already inside a Parse project.
Please refrain from creating a Parse project inside another Parse project.
`,
		)
	}

	var (
		cloudCodeDir string
		err          error
	)

	if nonInteractive {
		cloudCodeDir = n.codeLocation
	} else if n.configOnly {
		cloudCodeDir = "" // ensures that "parse new --init" inits the current directory
	} else {
		cloudCodeDir, err = n.getCloudCodeDir(e, name, isNew)
		if err != nil {
			return false, err
		}
	}
	e.Root = filepath.Join(e.Root, cloudCodeDir)

	switch e.Type {
	case parsecli.ParseFormat:
		if !n.configOnly {
			var decision string
			if isNew {
				fmt.Fprint(e.Out, `
You can either set up a blank project or create a sample Cloud Code project.
Please type "(b)lank" if you wish to setup a blank project, otherwise press ENTER: `)
			} else {
				fmt.Fprint(e.Out, `
You can either set up a blank project or download the current deployed Cloud Code.
Please type "(b)lank" if you wish to setup a blank project, otherwise press ENTER: `)
			}
			fmt.Fscanf(e.In, "%s\n", &decision)
			decision = strings.ToLower(strings.TrimSpace(decision))
			if decision != "" && decision == "b" || decision == "blank" {
				n.configOnly = true
			}
		}
		return parsecmd.CloneSampleCloudCode(e, isNew, n.configOnly, appConfig)
	case parsecli.HerokuFormat:
		if !n.configOnly {
			var decision string
			if isNew {
				fmt.Fprint(e.Out, `
You can either set up a blank project or create a sample Node.js project.
Please type "(b)lank" if you wish to setup a blank project, otherwise press ENTER: `)
			} else {
				fmt.Fprint(e.Out, `
You can either set up a blank project or download the server code currently deployed to Heroku.
Please type "(b)lank" if you wish to setup a blank project, otherwise press ENTER: `)
			}
			fmt.Fscanf(e.In, "%s\n", &decision)
			decision = strings.ToLower(strings.TrimSpace(decision))
			if decision != "" && decision == "b" || decision == "blank" {
				n.configOnly = true
			}
		}
		return herokucmd.CloneNodeCode(e, isNew, n.configOnly, appConfig)
	}
	return false, stackerr.Newf("Unknown project type: %d", e.Type)
}

func (n *newCmd) configureSample(
	add *addCmd,
	name string,
	appConfig parsecli.AppConfig,
	args []string,
	e *parsecli.Env,
) error {
	if err := add.addSelectedApp(name, appConfig, args, e); err != nil {
		return err
	}

	masterKey, err := appConfig.GetMasterKey(e)
	if err != nil {
		return err
	}
	e.ParseAPIClient = e.ParseAPIClient.WithCredentials(
		parse.MasterKey{
			ApplicationID: appConfig.GetApplicationID(),
			MasterKey:     masterKey,
		},
	)

	if e.Type == parsecli.ParseFormat {
		return parsecmd.UseLatestJSSDK(e)
	}
	return nil
}

func (n *newCmd) run(e *parsecli.Env) error {
	apps := &parsecli.Apps{}
	addCmd := &addCmd{MakeDefault: true, apps: apps}

	if err := apps.Login.AuthUser(e, false); err != nil {
		return err
	}
	var (
		app *parsecli.App
		err error
	)

	nonInteractive := n.parseAppName != "" && n.codeLocation != ""
	decision, err := n.promptCreateNewApp(e, nonInteractive)
	if err != nil {
		return err
	}
	isNew := false

	switch decision {
	case "new", "n":
		isNew = true
		var createRetries int
		if n.noCode {
			createRetries = 1
		}
		// we pass retries so that even in non interactive mode we can create an app,
		// and failure does not print 3 times to the screen
		app, err = apps.CreateApp(e, n.parseAppName, createRetries)
		if err != nil {
			return err
		}
		if n.noCode {
			fmt.Fprintln(e.Out, "Successfully created the app.")
			return apps.PrintApp(e, app)
		}
	case "existing", "e":
		app, err = addCmd.selectApp(e, n.parseAppName)
		if err != nil {
			return err
		}
		if n.noCode {
			fmt.Fprintln(e.Out, "Successfully selected the app.")
			return apps.PrintApp(e, app)
		}
	}

	projectType, err := herokucmd.PromptCreateWebhooks(e)
	if err != nil {
		return err
	}

	var appConfig parsecli.AppConfig
	switch projectType {
	case "heroku":
		e.Type = parsecli.HerokuFormat
		var newHerokuApp bool
		newHerokuApp, appConfig, err = herokucmd.GetLinkedHerokuAppConfig(app, e)
		if err != nil {
			return err
		}
		isNew = isNew || newHerokuApp

	case "parse":
		e.Type = parsecli.ParseFormat
		appConfig = parsecmd.GetParseAppConfig(app)
	}

	dumpTemplate, err := n.setupSample(e, app.Name, appConfig, isNew, nonInteractive)
	if err != nil {
		return err
	}
	if err := n.configureSample(addCmd, app.Name, appConfig, nil, e); err != nil {
		return err
	}
	if token := apps.Login.Credentials.Token; token != "" {
		email, err := apps.Login.AuthToken(e, token)
		if err != nil {
			return err
		}
		if err := parsecli.SetParserEmail(e, email); err != nil {
			return err
		}
	}

	if dumpTemplate {
		fmt.Fprintf(e.Out, n.cloudCodeHelpMessage(e, app))
	}

	return nil
}

func NewNewCmd(e *parsecli.Env) *cobra.Command {
	nc := newCmd{addApplication: true}
	cmd := &cobra.Command{
		Use:   "new",
		Short: "Adds Cloud Code to an existing Parse app, additional can create a new Parse app",
		Long: `Adds Cloud Code to an existing Parse app, additional can create a new Parse app.
You can also use it in non-interactive mode by using the various flags available.
`,
		Run: parsecli.RunNoArgs(e, nc.run),
	}
	cmd.Flags().BoolVarP(&nc.configOnly, "init", "i", nc.configOnly,
		"Create a Cloud Code project only with configuration.")
	cmd.Flags().BoolVarP(&nc.noCode, "nocode", "n", nc.noCode,
		"Do not set up a Cloud Code project for the app. Typically used in conjunction with 'create' option")
	cmd.Flags().BoolVarP(&nc.createNewApp, "create", "c", nc.createNewApp,
		"Set this flag to true if you want to create a new Parse app.")
	cmd.Flags().StringVarP(&nc.parseAppName, "app", "a", nc.parseAppName,
		"Name of the Parse app you want to create or set up Cloud Code project for.")
	cmd.Flags().StringVarP(&nc.codeLocation, "loc", "l", nc.codeLocation,
		"Location, relative to the current directory, at which the Cloud Code project will be set up.")
	return cmd
}
<commit_msg>abort new workflow when dir exists<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/ParsePlatform/parse-cli/herokucmd"
	"github.com/ParsePlatform/parse-cli/parsecli"
	"github.com/ParsePlatform/parse-cli/parsecmd"
	"github.com/facebookgo/parse"
	"github.com/facebookgo/stackerr"
	"github.com/spf13/cobra"
)

type newCmd struct {
	addApplication bool

	noCode       bool   // do not setup a Cloud Code project
	configOnly   bool   // create a Cloud Code project with only configuration, no code
	createNewApp bool   // create a new app
	parseAppName string // name of parse app
	codeLocation string // location of Cloud Code project
}

func (n *newCmd) curlCommand(e *parsecli.Env, app *parsecli.App) string {
	args := "{}"
	if e.Type == parsecli.HerokuFormat {
		args = `{"a": "Adventurous ", "b": "Parser"}`
	}

	return fmt.Sprintf(
		`curl -X POST \
 -H "X-Parse-Application-Id: %s" \
 -H "X-Parse-REST-API-Key: %s" \
 -H "Content-Type: application/json" \
 -d '%s' \
 https://api.parse.com/1/functions/hello
`,
		app.ApplicationID,
		app.RestKey,
		args,
	)
}

func (n *newCmd) cloudCodeHelpMessage(e *parsecli.Env, app *parsecli.App) string {
	code := "Cloud Code"
	if e.Type == parsecli.HerokuFormat {
		code = "server code"
	}
	return fmt.Sprintf(
		`Your %s has been created at %s.

This includes a "Hello world" cloud function, so once you deploy,
you can test that it works, with the printed curl command.

Next, you might want to deploy this code with:

	cd %s
	parse deploy

Once deployed you can test that it works by running:
%s
`,
		code,
		e.Root,
		e.Root,
		n.curlCommand(e, app),
	)
}

func (n *newCmd) getCloudCodeDir(e *parsecli.Env, appName string, isNew bool) (string, error) {
	var cloudCodeDir string
	if isNew {
		fmt.Fprintf(e.Out,
			`Awesome! Now it's time to set up some Cloud Code for the app: %q,
Next we will create a directory to hold your Cloud Code.
Please enter the name to use for this directory,
or hit ENTER to use %q as the directory name.

Directory Name: `,
			appName,
			appName,
		)
	} else {
		fmt.Fprintf(e.Out,
			`Please enter the name of the folder where we can download the latest deployed
Cloud Code for your app %q

Directory Name: `,
			appName,
		)
	}

	fmt.Fscanf(e.In, "%s\n", &cloudCodeDir)
	cloudCodeDir = strings.TrimSpace(cloudCodeDir)
	if cloudCodeDir == "" {
		cloudCodeDir = appName
	}

	helpMsg := "Next time, please select the same app you selected now."
	if isNew {
		helpMsg = `Next time, please select to add Cloud Code to an 'existing' app
and select the app you just created.`
	}

	configFile := filepath.Join(e.Root, cloudCodeDir, parsecli.LegacyConfigFile)
	if _, err := os.Lstat(configFile); err == nil {
		return "", stackerr.Newf(
			`Sorry, we are unable to create Cloud Code at %s.
It seems that you already have Cloud Code at %s.
Please run "parse new" again.
%s
Please choose a different name for your Cloud Code directory,
so it does not conflict with any other Cloud Code in the current directory.
`,
			cloudCodeDir,
			cloudCodeDir,
			helpMsg,
		)
	}

	cloudCodeDirInfo, err := os.Stat(filepath.Join(e.Root, cloudCodeDir))
	if err == nil {
		fileType := "file"
		if cloudCodeDirInfo.IsDir() {
			fileType = "directory"
		}
		return "", stackerr.Newf(`Sorry, we are unable to create Cloud Code at %s.
In the current directory a %s named: %q already exists.
Please run "parse new" again.
%s
Please choose a different name for your Cloud Code directory,
so it does not conflict with any other Cloud Code in the current directory.
`,
			cloudCodeDir,
			fileType,
			cloudCodeDir,
			helpMsg,
		)
	}
	if !os.IsNotExist(err) {
		return "", stackerr.Wrap(err)
	}
	return cloudCodeDir, nil
}

func (n *newCmd) promptCreateNewApp(e *parsecli.Env, nonInteractive bool) (string, error) {
	if nonInteractive || n.noCode {
		if n.createNewApp {
			return "new", nil
		}
		return "existing", nil
	}

	msg := `"new" and "existing" are the only valid options.
Please try again ...`

	var decision string
	for i := 0; i < 3; i++ {
		fmt.Fprintf(e.Out,
			`Would you like to create a new app, or add Cloud Code to an existing app?
Type "(n)ew" or "(e)xisting": `,
		)
		fmt.Fscanf(e.In, "%s\n", &decision)
		decision = strings.ToLower(decision)

		if decision == "new" || decision == "n" || decision == "existing" || decision == "e" {
			return decision, nil
		}
		fmt.Fprintln(e.Err, msg)
	}
	return "", stackerr.New(msg)
}

func (n *newCmd) setupSample(
	e *parsecli.Env,
	name string,
	appConfig parsecli.AppConfig,
	isNew bool,
	nonInteractive bool,
) (bool, error) {
	found := parsecli.IsProjectDir(parsecli.GetProjectRoot(e, e.Root))
	if !found {
		root := parsecli.GetLegacyProjectRoot(e, e.Root)
		_, err := os.Lstat(filepath.Join(root, parsecli.LegacyConfigFile))
		found = err == nil
	}
	if found {
		return false, stackerr.New(
			`Detected that you are already inside a Parse project.
Please refrain from creating a Parse project inside another Parse project.
`,
		)
	}

	var (
		cloudCodeDir string
		err          error
	)

	if nonInteractive {
		cloudCodeDir = n.codeLocation
	} else if n.configOnly {
		cloudCodeDir = "" // ensures that "parse new --init" inits the current directory
	} else {
		cloudCodeDir, err = n.getCloudCodeDir(e, name, isNew)
		if err != nil {
			return false, err
		}
	}
	e.Root = filepath.Join(e.Root, cloudCodeDir)

	switch e.Type {
	case parsecli.ParseFormat:
		if !n.configOnly {
			var decision string
			if isNew {
				fmt.Fprint(e.Out, `
You can either set up a blank project or create a sample Cloud Code project.
Please type "(b)lank" if you wish to setup a blank project, otherwise press ENTER: `)
			} else {
				fmt.Fprint(e.Out, `
You can either set up a blank project or download the current deployed Cloud Code.
Please type "(b)lank" if you wish to setup a blank project, otherwise press ENTER: `)
			}
			fmt.Fscanf(e.In, "%s\n", &decision)
			decision = strings.ToLower(strings.TrimSpace(decision))
			if decision != "" && decision == "b" || decision == "blank" {
				n.configOnly = true
			}
		}
		return parsecmd.CloneSampleCloudCode(e, isNew, n.configOnly, appConfig)
	case parsecli.HerokuFormat:
		if !n.configOnly {
			var decision string
			if isNew {
				fmt.Fprint(e.Out, `
You can either set up a blank project or create a sample Node.js project.
Please type "(b)lank" if you wish to setup a blank project, otherwise press ENTER: `)
			} else {
				fmt.Fprint(e.Out, `
You can either set up a blank project or download the server code currently deployed to Heroku.
Please type "(b)lank" if you wish to setup a blank project, otherwise press ENTER: `)
			}
			fmt.Fscanf(e.In, "%s\n", &decision)
			decision = strings.ToLower(strings.TrimSpace(decision))
			if decision != "" && decision == "b" || decision == "blank" {
				n.configOnly = true
			}
		}
		return herokucmd.CloneNodeCode(e, isNew, n.configOnly, appConfig)
	}
	return false, stackerr.Newf("Unknown project type: %d", e.Type)
}

func (n *newCmd) configureSample(
	add *addCmd,
	name string,
	appConfig parsecli.AppConfig,
	args []string,
	e *parsecli.Env,
) error {
	if err := add.addSelectedApp(name, appConfig, args, e); err != nil {
		return err
	}

	masterKey, err := appConfig.GetMasterKey(e)
	if err != nil {
		return err
	}
	e.ParseAPIClient = e.ParseAPIClient.WithCredentials(
		parse.MasterKey{
			ApplicationID: appConfig.GetApplicationID(),
			MasterKey:     masterKey,
		},
	)

	if e.Type == parsecli.ParseFormat {
		return parsecmd.UseLatestJSSDK(e)
	}
	return nil
}

func (n *newCmd) run(e *parsecli.Env) error {
	apps := &parsecli.Apps{}
	addCmd := &addCmd{MakeDefault: true, apps: apps}

	if err := apps.Login.AuthUser(e, false); err != nil {
		return err
	}
	var (
		app *parsecli.App
		err error
	)

	nonInteractive := n.parseAppName != "" && n.codeLocation != ""
	decision, err := n.promptCreateNewApp(e, nonInteractive)
	if err != nil {
		return err
	}
	isNew := false

	switch decision {
	case "new", "n":
		isNew = true
		var createRetries int
		if n.noCode {
			createRetries = 1
		}
		// we pass retries so that even in non interactive mode we can create an app,
		// and failure does not print 3 times to the screen
		app, err = apps.CreateApp(e, n.parseAppName, createRetries)
		if err != nil {
			return err
		}
		if n.noCode {
			fmt.Fprintln(e.Out, "Successfully created the app.")
			return apps.PrintApp(e, app)
		}
	case "existing", "e":
		app, err = addCmd.selectApp(e, n.parseAppName)
		if err != nil {
			return err
		}
		if n.noCode {
			fmt.Fprintln(e.Out, "Successfully selected the app.")
			return apps.PrintApp(e, app)
		}
	}

	projectType, err := herokucmd.PromptCreateWebhooks(e)
	if err != nil {
		return err
	}

	var appConfig parsecli.AppConfig
	switch projectType {
	case "heroku":
		e.Type = parsecli.HerokuFormat
		var newHerokuApp bool
		newHerokuApp, appConfig, err = herokucmd.GetLinkedHerokuAppConfig(app, e)
		if err != nil {
			return err
		}
		isNew = isNew || newHerokuApp

	case "parse":
		e.Type = parsecli.ParseFormat
		appConfig = parsecmd.GetParseAppConfig(app)
	}

	dumpTemplate, err := n.setupSample(e, app.Name, appConfig, isNew, nonInteractive)
	if err != nil {
		return err
	}
	if err := n.configureSample(addCmd, app.Name, appConfig, nil, e); err != nil {
		return err
	}
	if token := apps.Login.Credentials.Token; token != "" {
		email, err := apps.Login.AuthToken(e, token)
		if err != nil {
			return err
		}
		if err := parsecli.SetParserEmail(e, email); err != nil {
			return err
		}
	}

	if dumpTemplate {
		fmt.Fprintf(e.Out, n.cloudCodeHelpMessage(e, app))
	}

	return nil
}

func NewNewCmd(e *parsecli.Env) *cobra.Command {
	nc := newCmd{addApplication: true}
	cmd := &cobra.Command{
		Use:   "new",
		Short: "Adds Cloud Code to an existing Parse app, additional can create a new Parse app",
		Long: `Adds Cloud Code to an existing Parse app, additional can create a new Parse app.
You can also use it in non-interactive mode by using the various flags available.
`,
		Run: parsecli.RunNoArgs(e, nc.run),
	}
	cmd.Flags().BoolVarP(&nc.configOnly, "init", "i", nc.configOnly,
		"Create a Cloud Code project only with configuration.")
	cmd.Flags().BoolVarP(&nc.noCode, "nocode", "n", nc.noCode,
		"Do not set up a Cloud Code project for the app. Typically used in conjunction with 'create' option")
	cmd.Flags().BoolVarP(&nc.createNewApp, "create", "c", nc.createNewApp,
		"Set this flag to true if you want to create a new Parse app.")
	cmd.Flags().StringVarP(&nc.parseAppName, "app", "a", nc.parseAppName,
		"Name of the Parse app you want to create or set up Cloud Code project for.")
	cmd.Flags().StringVarP(&nc.codeLocation, "loc", "l", nc.codeLocation,
		"Location, relative to the current directory, at which the Cloud Code project will be set up.")
	return cmd
}
<|endoftext|>


<commit_before>package fmom

import (
	"fmt"
	"math"
)

// Equal returns true if p1==p2
func Equal(p1, p2 P4) bool {
	return p4equal(p1, p2, 1e-14)
}

func p4equal(p1, p2 P4, epsilon float64) bool {
	if cmpeq(p1.E(), p2.E(), epsilon) &&
		cmpeq(p1.Px(), p2.Px(), epsilon) &&
		cmpeq(p1.Py(), p2.Py(), epsilon) &&
		cmpeq(p1.Pz(), p2.Pz(), epsilon) {
		return true
	}
	return false
}

func cmpeq(x, y, epsilon float64) bool {
	if x == y {
		return true
	}

	return math.Abs(x-y) < epsilon
}

// Add returns the sum p1+p2.
func Add(p1, p2 P4) P4 {
	// FIXME(sbinet):
	// dispatch most efficient/less-lossy addition
	// based on type(dst) (and, optionally, type(src))
	var sum P4
	switch p1 := p1.(type) {

	case *PxPyPzE:
		p := NewPxPyPzE(p1.Px()+p2.Px(), p1.Py()+p2.Py(), p1.Pz()+p2.Pz(), p1.E()+p2.E())
		sum = &p

	case *EEtaPhiM:
		p := NewPxPyPzE(p1.Px()+p2.Px(), p1.Py()+p2.Py(), p1.Pz()+p2.Pz(), p1.E()+p2.E())
		var pp EEtaPhiM
		pp.Set(&p)
		sum = &pp

	case *EtEtaPhiM:
		p := NewPxPyPzE(p1.Px()+p2.Px(), p1.Py()+p2.Py(), p1.Pz()+p2.Pz(), p1.E()+p2.E())
		var pp EtEtaPhiM
		pp.Set(&p)
		sum = &pp

	case *PtEtaPhiM:
		p := NewPxPyPzE(p1.Px()+p2.Px(), p1.Py()+p2.Py(), p1.Pz()+p2.Pz(), p1.E()+p2.E())
		var pp PtEtaPhiM
		pp.Set(&p)
		sum = &pp

	case *IPtCotThPhiM:
		p := NewPxPyPzE(p1.Px()+p2.Px(), p1.Py()+p2.Py(), p1.Pz()+p2.Pz(), p1.E()+p2.E())
		var pp IPtCotThPhiM
		pp.Set(&p)
		sum = &pp

	default:
		panic(fmt.Errorf("fmom: invalid P4 concrete value: %#v", p1))
	}
	return sum
}

// IAdd adds src into dst, and returns dst
func IAdd(dst, src P4) P4 {
	// FIXME(sbinet):
	// dispatch most efficient/less-lossy addition
	// based on type(dst) (and, optionally, type(src))
	var sum P4
	var p4 *PxPyPzE = nil
	switch p1 := dst.(type) {

	case *PxPyPzE:
		p4 = p1
		sum = dst

	case *EEtaPhiM:
		p := NewPxPyPzE(p1.Px(), p1.Py(), p1.Pz(), p1.E())
		p4 = &p
		sum = dst

	case *EtEtaPhiM:
		p := NewPxPyPzE(p1.Px(), p1.Py(), p1.Pz(), p1.E())
		p4 = &p
		sum = dst

	case *PtEtaPhiM:
		p := NewPxPyPzE(p1.Px(), p1.Py(), p1.Pz(), p1.E())
		p4 = &p
		sum = dst

	case *IPtCotThPhiM:
		p := NewPxPyPzE(p1.Px(), p1.Py(), p1.Pz(), p1.E())
		p4 = &p
		sum = dst

	default:
		panic(fmt.Errorf("fmom: invalid P4 concrete value: %#v", dst))
	}
	p4[0] += src.Px()
	p4[1] += src.Py()
	p4[2] += src.Pz()
	p4[3] += src.E()
	sum.Set(p4)
	return sum
}
<commit_msg>fmom: add Scale(alpha, p4)<commit_after>package fmom

import (
	"fmt"
	"math"
)

// Equal returns true if p1==p2
func Equal(p1, p2 P4) bool {
	return p4equal(p1, p2, 1e-14)
}

func p4equal(p1, p2 P4, epsilon float64) bool {
	if cmpeq(p1.E(), p2.E(), epsilon) &&
		cmpeq(p1.Px(), p2.Px(), epsilon) &&
		cmpeq(p1.Py(), p2.Py(), epsilon) &&
		cmpeq(p1.Pz(), p2.Pz(), epsilon) {
		return true
	}
	return false
}

func cmpeq(x, y, epsilon float64) bool {
	if x == y {
		return true
	}

	return math.Abs(x-y) < epsilon
}

// Add returns the sum p1+p2.
func Add(p1, p2 P4) P4 {
	// FIXME(sbinet):
	// dispatch most efficient/less-lossy addition
	// based on type(dst) (and, optionally, type(src))
	var sum P4
	switch p1 := p1.(type) {

	case *PxPyPzE:
		p := NewPxPyPzE(p1.Px()+p2.Px(), p1.Py()+p2.Py(), p1.Pz()+p2.Pz(), p1.E()+p2.E())
		sum = &p

	case *EEtaPhiM:
		p := NewPxPyPzE(p1.Px()+p2.Px(), p1.Py()+p2.Py(), p1.Pz()+p2.Pz(), p1.E()+p2.E())
		var pp EEtaPhiM
		pp.Set(&p)
		sum = &pp

	case *EtEtaPhiM:
		p := NewPxPyPzE(p1.Px()+p2.Px(), p1.Py()+p2.Py(), p1.Pz()+p2.Pz(), p1.E()+p2.E())
		var pp EtEtaPhiM
		pp.Set(&p)
		sum = &pp

	case *PtEtaPhiM:
		p := NewPxPyPzE(p1.Px()+p2.Px(), p1.Py()+p2.Py(), p1.Pz()+p2.Pz(), p1.E()+p2.E())
		var pp PtEtaPhiM
		pp.Set(&p)
		sum = &pp

	case *IPtCotThPhiM:
		p := NewPxPyPzE(p1.Px()+p2.Px(), p1.Py()+p2.Py(), p1.Pz()+p2.Pz(), p1.E()+p2.E())
		var pp IPtCotThPhiM
		pp.Set(&p)
		sum = &pp

	default:
		panic(fmt.Errorf("fmom: invalid P4 concrete value: %#v", p1))
	}
	return sum
}

// IAdd adds src into dst, and returns dst
func IAdd(dst, src P4) P4 {
	// FIXME(sbinet):
	// dispatch most efficient/less-lossy addition
	// based on type(dst) (and, optionally, type(src))
	var sum P4
	var p4 *PxPyPzE = nil
	switch p1 := dst.(type) {

	case *PxPyPzE:
		p4 = p1
		sum = dst

	case *EEtaPhiM:
		p := NewPxPyPzE(p1.Px(), p1.Py(), p1.Pz(), p1.E())
		p4 = &p
		sum = dst

	case *EtEtaPhiM:
		p := NewPxPyPzE(p1.Px(), p1.Py(), p1.Pz(), p1.E())
		p4 = &p
		sum = dst

	case *PtEtaPhiM:
		p := NewPxPyPzE(p1.Px(), p1.Py(), p1.Pz(), p1.E())
		p4 = &p
		sum = dst

	case *IPtCotThPhiM:
		p := NewPxPyPzE(p1.Px(), p1.Py(), p1.Pz(), p1.E())
		p4 = &p
		sum = dst

	default:
		panic(fmt.Errorf("fmom: invalid P4 concrete value: %#v", dst))
	}
	p4[0] += src.Px()
	p4[1] += src.Py()
	p4[2] += src.Pz()
	p4[3] += src.E()
	sum.Set(p4)
	return sum
}

// Scale returns a*p
func Scale(a float64, p P4) P4 {
	// FIXME(sbinet):
	// dispatch most efficient/less-lossy operation
	// based on type(dst) (and, optionally, type(src))
	var out P4
	switch p := p.(type) {

	case *PxPyPzE:
		dst := NewPxPyPzE(a*p.Px(), a*p.Py(), a*p.Pz(), a*p.E())
		out = &dst

	case *EEtaPhiM:
		dst := NewPxPyPzE(a*p.Px(), a*p.Py(), a*p.Pz(), a*p.E())
		var pp EEtaPhiM
		pp.Set(&dst)
		out = &pp

	case *EtEtaPhiM:
		dst := NewPxPyPzE(a*p.Px(), a*p.Py(), a*p.Pz(), a*p.E())
		var pp EtEtaPhiM
		pp.Set(&dst)
		out = &pp

	case *PtEtaPhiM:
		dst := NewPxPyPzE(a*p.Px(), a*p.Py(), a*p.Pz(), a*p.E())
		var pp PtEtaPhiM
		pp.Set(&dst)
		out = &pp

	case *IPtCotThPhiM:
		dst := NewPxPyPzE(a*p.Px(), a*p.Py(), a*p.Pz(), a*p.E())
		var pp IPtCotThPhiM
		pp.Set(&dst)
		out = &pp

	default:
		panic(fmt.Errorf("fmom: invalid P4 concrete value: %#v", p))
	}

	return out
}
<|endoftext|>


<commit_before>package otp

import (
	"fmt"
)

type Pad struct {
	pages       [][]byte
	currentPage int
}

func NewPad(material []byte, pageSize int, startPage int) (*Pad, error) {
	if startPage < 1 {
		return nil, fmt.Errorf("minimum start page is 1")
	}

	if len(material)%pageSize != 0 {
		return nil, fmt.Errorf("pad size must be divisible by page size")
	}

	var pages [][]byte
	for i := 0; i < len(material); i += pageSize {
		pages = append(pages, material[i:i+pageSize])
	}

	p := Pad{
		pages:       pages,
		currentPage: startPage - 1,
	}

	return &p, nil
}

func (p *Pad) TotalPages() int {
	return len(p.pages)
}

func (p *Pad) RemainingPages() int {
	return len(p.pages) - p.currentPage
}

func (p *Pad) UsedPages() int {
	return p.currentPage + 1
}

func (p *Pad) PreviousPage() ([]byte, error) {
	if p.currentPage == 0 {
		return nil, fmt.Errorf("no previous pages")
	}
	return p.pages[p.currentPage-1], nil
}

func (p *Pad) CurrentPage() []byte {
	return p.pages[p.currentPage]
}

func (p *Pad) NextPage() ([]byte, error) {
	if p.RemainingPages() == 0 {
		return nil, fmt.Errorf("pad depleted")
	}
	p.currentPage++
	return p.CurrentPage(), nil
}

func (p *Pad) PeekNextPage() ([]byte, error) {
	if p.RemainingPages() == 0 {
		return nil, fmt.Errorf("pad depleted")
	}
	return p.pages[p.currentPage+1], nil
}
<commit_msg>guard against initializing an out-of-bounds page<commit_after>package otp

import (
	"fmt"
)

type Pad struct {
	pages       [][]byte
	currentPage int
}

func NewPad(material []byte, pageSize int, startPage int) (*Pad, error) {
	if len(material)%pageSize != 0 {
		return nil, fmt.Errorf("pad size must be divisible by page size")
	}

	var pages [][]byte
	for i := 0; i < len(material); i += pageSize {
		pages = append(pages, material[i:i+pageSize])
	}

	if startPage < 1 || startPage > len(pages) {
		return nil, fmt.Errorf("start page (%d) out of bounds", startPage)
	}

	p := Pad{
		pages:       pages,
		currentPage: startPage - 1,
	}

	return &p, nil
}

func (p *Pad) TotalPages() int {
	return len(p.pages)
}

func (p *Pad) RemainingPages() int {
	return len(p.pages) - p.currentPage
}

func (p *Pad) UsedPages() int {
	return p.currentPage + 1
}

func (p *Pad) PreviousPage() ([]byte, error) {
	if p.currentPage == 0 {
		return nil, fmt.Errorf("no previous pages")
	}
	return p.pages[p.currentPage-1], nil
}

func (p *Pad) CurrentPage() []byte {
	return p.pages[p.currentPage]
}

func (p *Pad) NextPage() ([]byte, error) {
	if p.RemainingPages() == 0 {
		return nil, fmt.Errorf("pad depleted")
	}
	p.currentPage++
	return p.CurrentPage(), nil
}

func (p *Pad) PeekNextPage() ([]byte, error) {
	if p.RemainingPages() == 0 {
		return nil, fmt.Errorf("pad depleted")
	}
	return p.pages[p.currentPage+1], nil
}
<|endoftext|>


<commit_before>package gootp

import (
	"bytes"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha1"
	"encoding/base32"
	"encoding/binary"
	"errors"
	"fmt"
	"hash"
	"math"
	"strconv"
	"strings"
	"time"
)

func hmacMsg(c, key []byte, h func() hash.Hash) ([]byte, error) {
	//Hash the message derived from the time with the key from the TOTP secret
	mac := hmac.New(h, key)
	_, err := mac.Write(c)
	if err != nil {
		return make([]byte, 0), err
	}
	return mac.Sum(nil), nil
}

func generateHashKey(s string) ([]byte, error) {
	//Decode and convert the base32 encoded secret string to a byte array
	key, err := base32.StdEncoding.DecodeString(strings.ToUpper(s))
	if err != nil {
		return make([]byte, 0), err
	}
	return key, nil
}

func generateHashCountFromTime(t time.Time) ([]byte, error) {
	//The message to be hashed is the count of the number of time intervals
	c := t.Unix() / 30
	return generateHashCount(c)
}

func generateHashCount(c int64) ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, c)
	if err != nil {
		return make([]byte, 0), err
	}
	return buf.Bytes(), nil
}

func extractFromHash(hash []byte) uint32 {
	//Get the last 4 bits of the hash (a value ranging from 0-15)
	// This will be the index into the 20-byte value
	iBits := hash[len(hash)-1] & 0xf
	iInt := int64(iBits)

	//Extract the next 4 bytes starting at the index and convert to uint32
	r := bytes.NewReader(hash)
	var b [4]byte
	r.ReadAt(b[:], iInt)
	b[0] = b[0] & 0x7f
	return binary.BigEndian.Uint32(b[:])
}

// Get the HMAC-based One Time Password (RFC 4226). Providing the following inputs:
//    - Secret string at least 16 bytes / 128 bits in length
//
//    - Counter value, the moving factor (see RFC 4226 section 5.2).  This counter MUST be synchronized between the HOTP generator (client) and the HOTP validator (server).
//
//    - A hash function to use, eg SHA1, SHA256, SHA512
//
//    - The number of digits to be returned in the OTP. Must be a minimum of 6.
//
// Note that the returned OTP is a string as a leading zero is valid so an integer type is not appropriate
func GetHOTP(secret string, count int64, mode func() hash.Hash, digits int) (otp string, err error) {
	if digits < 6 {
		err = errors.New("The number of digits of the OTP generated must be at least 6")
		return
	}
	if len(secret) < 16 {
		err = errors.New("The secret string used to generate the OTP must be at least 128 bits")
		return
	}
	key, err := generateHashKey(secret)
	if err != nil {
		return
	}
	msg, err := generateHashCount(count)
	if err != nil {
		return
	}
	if mode == nil {
		mode = sha1.New
	}
	hash, err := hmacMsg(msg, key, mode)
	if err != nil {
		return
	}
	otpInt := int(math.Mod(float64(extractFromHash(hash)), math.Pow(10, float64(digits))))
	otp = fmt.Sprintf("%0"+strconv.Itoa(digits)+"d", otpInt)
	return
}

// Get the Time-based One Time Password (RFC 6238) for the current time. Providing the following inputs:
//    - Secret string at least 16 bytes / 128 bits in length.
//    - A hash function to use, eg SHA1, SHA256, SHA512.
//    - The number of digits to be returned in the OTP. Must be a minimum of 6.
//
//
// Note that the returned OTP is a string as a leading zero is valid so an integer type is not appropriate.
//
// The number of seconds the OTP is valid for is also returned.
func GetTOTPNow(secret string, mode func() hash.Hash, digits int) (otp string, timeRemaining int, err error) {
	otp, timeRemaining, err = GetTOTPAt(secret, time.Now().UTC(), mode, digits)
	return
}

// Get the Time-based One Time Password (RFC 6238) for a specific time. Providing the following inputs:
//    - Secret string at least 16 bytes / 128 bits in length.
//    - The UTC time for which the TOTP should be generated.
//    - A hash function to use, eg SHA1, SHA256, SHA512.
//    - The number of digits to be returned in the OTP. Must be a minimum of 6.
//
//
// Note that the returned OTP is a string as a leading zero is valid so an integer type is not appropriate.
//
// The number of seconds the OTP is valid for is also returned.
func GetTOTPAt(secret string, t time.Time, h func() hash.Hash, digits int) (otp string, timeRemaining int, err error) {
	key, err := generateHashKey(secret)
	if err != nil {
		return
	}
	msg, err := generateHashCountFromTime(t)
	if err != nil {
		return
	}
	hash, err := hmacMsg(msg, key, h)
	if err != nil {
		return
	}
	timeRemaining = int(30 - math.Mod(float64(t.Unix()), 30))
	otpInt := int(math.Mod(float64(extractFromHash(hash)), math.Pow(10, float64(digits))))
	otp = fmt.Sprintf("%0"+strconv.Itoa(digits)+"d", otpInt)
	return
}

// Get a Time-based One Time Password history (RFC 6238). Providing the following inputs:
//
//    - Secret string at least 16 bytes / 128 bits in length.
//
//    - A hash function to use, eg SHA1, SHA256, SHA512.
//
//    - The number of digits to be returned in the OTP. Must be a minimum of 6.
//
//
// Note that the returned OTP is an array of strings as a leading zero is valid so an integer type is not appropriate. The first element in the array is the current OTP.
//
// The number of seconds the current OTP is valid for is also returned.
func GetTOTPHistory(secret string, h func() hash.Hash, digits int, history int) (otps []string, timeRemaining int, err error) {
	key, err := generateHashKey(secret)
	if err != nil {
		return
	}
	c := time.Now().UTC().Unix() / 30
	timeRemaining = int(30 - math.Mod(float64(time.Now().UTC().Unix()), 30))
	for i := 0; i < history; i++ {
		c = c - int64(i)
		msg, ierr := generateHashCount(c)
		if ierr != nil {
			err = ierr
			return
		}
		hash, ierr := hmacMsg(msg, key, h)
		if ierr != nil {
			err = ierr
			return
		}
		otpInt := int(math.Mod(float64(extractFromHash(hash)), math.Pow(10, float64(digits))))
		otp := fmt.Sprintf("%0"+strconv.Itoa(digits)+"d", otpInt)
		otps = append(otps, otp)
	}
	return
}

// Generate a base32 encoded secret string to be shared between the client and the server for OTPs
// Specify the length of the secret to generate in bytes. Note this needs to be at least 16 bytes / 128 bits.
func GenerateOTPSecret(s int) (string, error) {
	if s < 16 {
		return "", errors.New("The secret size needs to be at least 16 bytes / 128 bits")
	}
	b, err := generateRandomBytes(s)
	return base32.StdEncoding.EncodeToString(b), err
}

func generateRandomBytes(n int) ([]byte, error) {
	b := make([]byte, n)
	_, err := rand.Read(b)
	if err != nil {
		return nil, err
	}

	return b, nil
}
<commit_msg>update<commit_after>package gootp

import (
	"bytes"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha1"
	"encoding/base32"
	"encoding/binary"
	"errors"
	"fmt"
	"hash"
	"math"
	"strconv"
	"strings"
	"time"
)

func hmacMsg(c, key []byte, h func() hash.Hash) ([]byte, error) {
	//Hash the message derived from the time with the key from the TOTP secret
	mac := hmac.New(h, key)
	_, err := mac.Write(c)
	if err != nil {
		return make([]byte, 0), err
	}
	return mac.Sum(nil), nil
}

func generateHashKey(s string) ([]byte, error) {
	//Decode and convert the base32 encoded secret string to a byte array
	key, err := base32.StdEncoding.DecodeString(strings.ToUpper(s))
	if err != nil {
		return make([]byte, 0), err
	}
	return key, nil
}

func generateHashCountFromTime(t time.Time) ([]byte, error) {
	//The message to be hashed is the count of the number of time intervals
	c := t.Unix() / 30
	return generateHashCount(c)
}

func generateHashCount(c int64) ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, c)
	if err != nil {
		return make([]byte, 0), err
	}
	return buf.Bytes(), nil
}

func extractFromHash(hash []byte) uint32 {
	//Get the last 4 bits of the hash (a value ranging from 0-15)
	// This will be the index into the 20-byte value
	iBits := hash[len(hash)-1] & 0xf
	iInt := int64(iBits)

	//Extract the next 4 bytes starting at the index and convert to uint32
	r := bytes.NewReader(hash)
	var b [4]byte
	r.ReadAt(b[:], iInt)
	b[0] = b[0] & 0x7f
	return binary.BigEndian.Uint32(b[:])
}

// Get the HMAC-based One Time Password (RFC 4226). Providing the following inputs:
//    - Secret string at least 16 bytes / 128 bits in length
//    - Counter value, the moving factor (see RFC 4226 section 5.2).  This counter MUST be synchronized between the HOTP generator (client) and the HOTP validator (server).
//    - A hash function to use, eg SHA1, SHA256, SHA512
//    - The number of digits to be returned in the OTP. Must be a minimum of 6.
//
// Note that the returned OTP is a string as a leading zero is valid so an integer type is not appropriate
func GetHOTP(secret string, count int64, mode func() hash.Hash, digits int) (otp string, err error) {
	if digits < 6 {
		err = errors.New("The number of digits of the OTP generated must be at least 6")
		return
	}
	if len(secret) < 16 {
		err = errors.New("The secret string used to generate the OTP must be at least 128 bits")
		return
	}
	key, err := generateHashKey(secret)
	if err != nil {
		return
	}
	msg, err := generateHashCount(count)
	if err != nil {
		return
	}
	if mode == nil {
		mode = sha1.New
	}
	hash, err := hmacMsg(msg, key, mode)
	if err != nil {
		return
	}
	otpInt := int(math.Mod(float64(extractFromHash(hash)), math.Pow(10, float64(digits))))
	otp = fmt.Sprintf("%0"+strconv.Itoa(digits)+"d", otpInt)
	return
}

// Get the Time-based One Time Password (RFC 6238) for the current time. Providing the following inputs:
//    - Secret string at least 16 bytes / 128 bits in length.
//    - A hash function to use, eg SHA1, SHA256, SHA512.
//    - The number of digits to be returned in the OTP. Must be a minimum of 6.
//
// Note that the returned OTP is a string as a leading zero is valid so an integer type is not appropriate.
//
// The number of seconds the OTP is valid for is also returned.
func GetTOTPNow(secret string, mode func() hash.Hash, digits int) (otp string, timeRemaining int, err error) {
	otp, timeRemaining, err = GetTOTPAt(secret, time.Now().UTC(), mode, digits)
	return
}

// Get the Time-based One Time Password (RFC 6238) for a specific time. Providing the following inputs:
//    - Secret string at least 16 bytes / 128 bits in length.
//    - The UTC time for which the TOTP should be generated.
//    - A hash function to use, eg SHA1, SHA256, SHA512.
//    - The number of digits to be returned in the OTP. Must be a minimum of 6.
//
// Note that the returned OTP is a string as a leading zero is valid so an integer type is not appropriate.
//
// The number of seconds the OTP is valid for is also returned.
func GetTOTPAt(secret string, t time.Time, h func() hash.Hash, digits int) (otp string, timeRemaining int, err error) {
	key, err := generateHashKey(secret)
	if err != nil {
		return
	}
	msg, err := generateHashCountFromTime(t)
	if err != nil {
		return
	}
	hash, err := hmacMsg(msg, key, h)
	if err != nil {
		return
	}
	timeRemaining = int(30 - math.Mod(float64(t.Unix()), 30))
	otpInt := int(math.Mod(float64(extractFromHash(hash)), math.Pow(10, float64(digits))))
	otp = fmt.Sprintf("%0"+strconv.Itoa(digits)+"d", otpInt)
	return
}

// Get a Time-based One Time Password history (RFC 6238). Providing the following inputs:
//
//    - Secret string at least 16 bytes / 128 bits in length.
//    - A hash function to use, eg SHA1, SHA256, SHA512.
//    - The number of digits to be returned in the OTP. Must be a minimum of 6.
//
// Note that the returned OTP is an array of strings as a leading zero is valid so an integer type is not appropriate. The first element in the array is the current OTP.
//
// The number of seconds the current OTP is valid for is also returned.
func GetTOTPHistory(secret string, h func() hash.Hash, digits int, history int) (otps []string, timeRemaining int, err error) {
	key, err := generateHashKey(secret)
	if err != nil {
		return
	}
	c := time.Now().UTC().Unix() / 30
	timeRemaining = int(30 - math.Mod(float64(time.Now().UTC().Unix()), 30))
	for i := 0; i < history; i++ {
		c = c - int64(i)
		msg, ierr := generateHashCount(c)
		if ierr != nil {
			err = ierr
			return
		}
		hash, ierr := hmacMsg(msg, key, h)
		if ierr != nil {
			err = ierr
			return
		}
		otpInt := int(math.Mod(float64(extractFromHash(hash)), math.Pow(10, float64(digits))))
		otp := fmt.Sprintf("%0"+strconv.Itoa(digits)+"d", otpInt)
		otps = append(otps, otp)
	}
	return
}

// Generate a base32 encoded secret string to be shared between the client and the server for OTPs
// Specify the length of the secret to generate in bytes. Note this needs to be at least 16 bytes / 128 bits.
func GenerateOTPSecret(s int) (string, error) {
	if s < 16 {
		return "", errors.New("The secret size needs to be at least 16 bytes / 128 bits")
	}
	b, err := generateRandomBytes(s)
	return base32.StdEncoding.EncodeToString(b), err
}

func generateRandomBytes(n int) ([]byte, error) {
	b := make([]byte, n)
	_, err := rand.Read(b)
	if err != nil {
		return nil, err
	}

	return b, nil
}
<|endoftext|>


<commit_before>package main

import "bytes"
import "code.google.com/p/go.crypto/twofish"
import "crypto/cipher"
import "crypto/sha256"
import "encoding/binary"
import "flag"
import "fmt"
import "io"
import "os"

var passwordFile *os.File
var salt []byte
var iter int

/**
* Inspired from https://metacpan.org/source/TLINDEN/Crypt-PWSafe3-1.14/lib/Crypt/PWSafe3.pm
* ,http://sourceforge.net/p/passwordsafe/code/HEAD/tree/trunk/pwsafe/pwsafe/docs/formatV3.txt,
* https://github.com/sommer/loxodo
*/
func readChunk(size int) (chunk []byte){
    chunk = make([]byte, size)
    bytesRead, err := passwordFile.Read(chunk)
    if err != nil && err != io.EOF {
        panic(err)
    }
    if bytesRead == 0 {
      //no bytes read
    }
    return
}

func openPasswordFile(fileName string){
    var err error
    passwordFile, err = os.Open(fileName)
    if err != nil {
        panic(err)
    }
}

func stretchPassword(password string) []byte {
    stretched := sha256.Sum256(append([]byte(password), salt[:]...))
    for i:= 0; i < iter ;  i++ {
        stretched = sha256.Sum256(stretched[:])
    }
    return stretched[:]
}

func verifyTag(){
    passwordSafe3Tag := readChunk(4)
    if (string(passwordSafe3Tag) != "PWS3") {
        fmt.Printf("%s is not a password safe version 3 file.", passwordFile.Name())
        os.Exit(1)
    }
}

func main() {
    flag.Parse()
    fileName := flag.Args()[0]
    password := flag.Args()[1]
    openPasswordFile(fileName)
    verifyTag()
    salt = readChunk(32)
    iter = int(binary.LittleEndian.Uint32(readChunk(4)))

    storedPassword := readChunk(32)
    stretchedPassword := stretchPassword(password)

    shaps := sha256.Sum256(stretchedPassword[:])
    if !bytes.Equal(storedPassword, shaps[:]) {
        fmt.Println("Passwords do not match")
        os.Exit(1)
    }
    b1 := readChunk(16)
    b2 := readChunk(16)
    b3 := readChunk(16)
    b4 := readChunk(16)

    block, err := twofish.NewCipher(stretchedPassword)
    if err != nil {
        panic(err)
    }

    block.Decrypt(b1,b1)
    block.Decrypt(b2,b2)
    block.Decrypt(b3,b3)
    block.Decrypt(b4,b4)
    keyk := append(b1, b2...)
    //keyl := append(b3, b4...)
    iv := readChunk(16)

    block, err = twofish.NewCipher(keyk)
    if err != nil {
        panic(err)
    }
    mode := cipher.NewCBCDecrypter(block, iv)
    data := readChunk(16);

    if len(data) < 16 {
        panic("EOF encountered when parsing record field")
    }

    if string(data) == "PWS3-EOFPWS3-EOF" {
        //return 0
    }

    mode.CryptBlocks(data, data)
    raw_length := binary.LittleEndian.Uint32(data[0:4])
    raw_type := data[4]
    raw_data := data[5:]
    fmt.Printf("%d\n", raw_length)
    fmt.Printf("%d\n", raw_type)
    fmt.Printf("%x\n", binary.LittleEndian.Uint16(raw_data[:raw_length]))
}
<commit_msg>Read full header now<commit_after>package main

import "bytes"
import "code.google.com/p/go.crypto/twofish"
import "crypto/cipher"
import "crypto/sha256"
import "encoding/binary"
import "errors"
import "flag"
import "fmt"
import "io"
import "os"

var passwordFile *os.File
var salt []byte
var iter int
var vault Vault

type Header struct {
    Version int
}

type Field struct {
    raw_length int
    raw_type byte
    raw_value []byte
}

type Record struct {
    Group,Title,URL,Username,Password,Notes string
}

type Vault struct {
    header Header
    records []Record
}

func NewVault(size int) *Vault {
    return &Vault{records:make([]Record, size)}
}
/**
* Inspired from https://metacpan.org/source/TLINDEN/Crypt-PWSafe3-1.14/lib/Crypt/PWSafe3.pm
* ,http://sourceforge.net/p/passwordsafe/code/HEAD/tree/trunk/pwsafe/pwsafe/docs/formatV3.txt,
* https://github.com/sommer/loxodo
*/

func readChunk(size int) (chunk []byte){
    chunk = make([]byte, size)
    bytesRead, err := passwordFile.Read(chunk)
    if err != nil && err != io.EOF {
        panic(err)
    }
    if bytesRead == 0 {
        //no bytes read
    }
    return
}

func openPasswordFile(fileName string){
    var err error
    passwordFile, err = os.Open(fileName)
    if err != nil {
        panic(err)
    }
}

func stretchPassword(password string) []byte {
    stretched := sha256.Sum256(append([]byte(password), salt[:]...))
    for i:= 0; i < iter ;  i++ {
        stretched = sha256.Sum256(stretched[:])
    }
    return stretched[:]
}

func verifyTag(){
    passwordSafe3Tag := readChunk(4)
    if (string(passwordSafe3Tag) != "PWS3") {
        fmt.Printf("%s is not a password safe version 3 file.", passwordFile.Name())
        os.Exit(1)
    }
}

func main() {
    flag.Parse()
    fileName := flag.Args()[0]
    password := flag.Args()[1]
    openPasswordFile(fileName)
    verifyTag()
    salt = readChunk(32)
    iter = int(binary.LittleEndian.Uint32(readChunk(4)))

    storedPassword := readChunk(32)
    stretchedPassword := stretchPassword(password)

    shaps := sha256.Sum256(stretchedPassword[:])
    if !bytes.Equal(storedPassword, shaps[:]) {
        fmt.Println("Passwords do not match")
        os.Exit(1)
    }
    b1 := readChunk(16)
    b2 := readChunk(16)
    b3 := readChunk(16)
    b4 := readChunk(16)

    block, err := twofish.NewCipher(stretchedPassword)
    if err != nil {
        panic(err)
    }

    block.Decrypt(b1,b1)
    block.Decrypt(b2,b2)
    block.Decrypt(b3,b3)
    block.Decrypt(b4,b4)
    keyk := append(b1, b2...)
    //keyl := append(b3, b4...)
    iv := readChunk(16)

    block, err = twofish.NewCipher(keyk)
    if err != nil {
        panic(err)
    }
    mode := cipher.NewCBCDecrypter(block, iv)
    for {
        field, _ := readFieldInfo(mode)
        if field != nil {
            fmt.Printf("%d\n", field.raw_length)
            fmt.Printf("%d\n", field.raw_type)
            //fmt.Printf("%x\n", binary.LittleEndian.Uint16(field.raw_value))
        }
        if (field.raw_type == 0xff){
            break
        }
    }
}

func readFieldInfo(mode cipher.BlockMode) (*Field, error) {
    data := readChunk(16);

    if len(data) < 16 {
        return nil, errors.New("EOF encountered when parsing record field")
    }

    if string(data) == "PWS3-EOFPWS3-EOF" {
        return nil, nil
    }

    mode.CryptBlocks(data, data)
    raw_length := int(binary.LittleEndian.Uint32(data[0:4]))
    raw_type := data[4]
    raw_value := data[5:]
    if (raw_length > 11){
        for i := 0; i < ((raw_length + 4)/16); i++{
            data = readChunk(16)
            if len(data) < 16 {
                return nil, errors.New("EOF encountered when parsing record field")
            }
            mode.CryptBlocks(data,data)
            raw_value = data[:]
        }
        raw_value = raw_value[:raw_length]
    }
    return &Field{raw_length, raw_type, raw_value[:raw_length]}, nil
}
<|endoftext|>


<commit_before>// qst - run things quickly
//
// Given a file or directory, guesses the project type and runs
// it for you. Restarts on changes. Intended for small experiments
// and working with unfamilar build systems.
package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/heyLu/qst/detect"
	"github.com/heyLu/qst/fileutil"
)

var delay = flag.Duration("delay", 1*time.Second, "time to wait until restart")
var autoRestart = flag.Bool("autorestart", true, "automatically restart after command exists")
var command = flag.String("command", "", "command to run ({file} will be substituted)")
var projectType = flag.String("type", "", "project type to use (autodetected if not present)")
var step = flag.String("step", "run", "which step to run (build, run or test)")
var justDetect = flag.Bool("detect", false, "detect the project type and exit")
var remote = flag.Bool("remote", false, "fetch and run a remote project")

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s <file>\n\n", os.Args[0])
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nSupported project types: \n")
		for _, project := range detect.ProjectTypes {
			paddedId := fmt.Sprintf("%s%s", project.Id, strings.Repeat(" ", 30-len(project.Id)))
			fmt.Fprintf(os.Stderr, "\t%s- %v\n", paddedId, project.Commands[*step])
		}
	}

	flag.Parse()
	args := flag.Args()

	if len(args) < 1 {
		flag.Usage()
		os.Exit(1)
	}

	file := args[0]

	if *remote {
		f, err := fetchFromGit(file)
		if err != nil {
			log.Fatal(err)
		}
		file = *f
	}

	if *justDetect {
		projects := detect.DetectAll(file)
		if len(projects) == 0 {
			log.Fatal("unknown project type")
		}
		for _, project := range projects {
			fmt.Println(project.Id)
		}
		os.Exit(0)
	}

	var cmd string
	if !flagEmpty(command) {
		cmd = *command
	} else {
		project, err := detect.Detect(file)
		if !flagEmpty(projectType) {
			project = detect.GetById(*projectType)
			if project == nil {
				log.Fatalf("unknown type: `%s'", *projectType)
			}
		}
		if err != nil {
			log.Fatal("error: ", err)
		}
		log.Printf("detected a %s project", project.Id)
		projectCmd, found := project.Commands[*step]
		if !found {
			log.Fatalf("%s doesn't support `%s'", project.Id, *step)
		}
		cmd = projectCmd
	}
	file, _ = filepath.Abs(file)
	cmd = strings.Replace(cmd, "{file}", file, -1)
	if err := os.Chdir(fileutil.Dir(file)); err != nil {
		log.Fatal(err)
	}
	log.Printf("command to run: `%s'", cmd)

	runner := MakeRunner(cmd, *autoRestart)
	go runCmd(file, runner)

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM)
	s := <-c
	log.Printf("got signal: %s, exiting...", s)
	runner.Stop()
}

func flagEmpty(stringFlag *string) bool {
	return stringFlag == nil || strings.TrimSpace(*stringFlag) == ""
}

func fetchFromGit(file string) (*string, error) {
	if !strings.HasPrefix(file, "github.com") {
		return nil, errors.New("only github repos supported for now, sorry")
	}
	repo, dir, err := splitGithubUrl(file)
	if err != nil {
		return nil, err
	}
	log.Printf("cloning %s", *repo)
	cloneCmd := exec.Command("git", "clone", *repo)
	cloneCmd.Stderr = os.Stderr
	cloneCmd.Stdout = os.Stdout
	err = cloneCmd.Run()
	if err != nil {
		return nil, err
	}
	return dir, nil
}

func splitGithubUrl(repo string) (*string, *string, error) {
	repoWithPath := strings.TrimPrefix(strings.TrimPrefix(repo, "git://"), "github.com/")
	parts := strings.SplitN(repoWithPath, "/", 3)
	if len(parts) < 2 || parts[0] == "" || parts[1] == "" {
		return nil, nil, errors.New(fmt.Sprint("error: invalid repo: ", repo))
	}
	repoUrl := fmt.Sprintf("git://github.com/%s/%s", parts[0], parts[1])
	dir := parts[1]
	if len(parts) == 3 {
		dir = path.Join(dir, parts[2])
	}
	return &repoUrl, &dir, nil
}

func runCmd(file string, runner *Runner) {
	runner.Start()
	lastMtime := time.Now()
	for {
		info, err := os.Stat(file)
		if os.IsNotExist(err) {
			log.Fatalf("`%s' disappeared, exiting", file)
		}

		mtime := info.ModTime()
		if mtime.After(lastMtime) {
			log.Printf("`%s' changed, trying to restart", file)
			runner.Restart()
		}

		lastMtime = mtime
		time.Sleep(1 * time.Second)
	}
}

type Runner struct {
	cmd         *exec.Cmd
	shellCmd    string
	started     bool
	autoRestart bool
	restarting  bool
}

func MakeRunner(shellCmd string, autoRestart bool) *Runner {
	return &Runner{nil, shellCmd, false, autoRestart, false}
}

func (r *Runner) Start() error {
	if r.started {
		return errors.New("already started, use Restart()")
	}

	r.started = true
	go func() {
		for {
			log.Printf("[runner] starting command")
			r.cmd = exec.Command("sh", "-c", r.shellCmd)
			r.cmd.Stderr = os.Stderr
			r.cmd.Stdout = os.Stdout
			r.cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
			err := r.cmd.Run()
			var result interface{}
			if err != nil {
				result = err
			} else {
				result = r.cmd.ProcessState
			}
			log.Printf("[runner] finished: %s", result)

			time.Sleep(*delay)
			if !r.restarting && !r.autoRestart {
				r.started = false
				break
			}

			r.restarting = false
		}
	}()

	return nil
}

func (r *Runner) Kill() error {
	pgid, err := syscall.Getpgid(r.cmd.Process.Pid)
	if err == nil {
		syscall.Kill(-pgid, syscall.SIGTERM)
	}
	return err
}

func (r *Runner) Restart() error {
	r.restarting = true
	if r.started {
		return r.Kill()
	} else {
		return r.Start()
	}
}

func (r *Runner) Stop() {
	r.autoRestart = false
	r.Kill()
}

func isFile(file string) bool {
	info, err := os.Stat(file)
	return err == nil && !info.IsDir()
}
<commit_msg>disable autorestart by default.<commit_after>// qst - run things quickly
//
// Given a file or directory, guesses the project type and runs
// it for you. Restarts on changes. Intended for small experiments
// and working with unfamilar build systems.
package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/heyLu/qst/detect"
	"github.com/heyLu/qst/fileutil"
)

var delay = flag.Duration("delay", 1*time.Second, "time to wait until restart")
var autoRestart = flag.Bool("autorestart", false, "automatically restart after command exists")
var command = flag.String("command", "", "command to run ({file} will be substituted)")
var projectType = flag.String("type", "", "project type to use (autodetected if not present)")
var step = flag.String("step", "run", "which step to run (build, run or test)")
var justDetect = flag.Bool("detect", false, "detect the project type and exit")
var remote = flag.Bool("remote", false, "fetch and run a remote project")

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s <file>\n\n", os.Args[0])
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nSupported project types: \n")
		for _, project := range detect.ProjectTypes {
			paddedId := fmt.Sprintf("%s%s", project.Id, strings.Repeat(" ", 30-len(project.Id)))
			fmt.Fprintf(os.Stderr, "\t%s- %v\n", paddedId, project.Commands[*step])
		}
	}

	flag.Parse()
	args := flag.Args()

	if len(args) < 1 {
		flag.Usage()
		os.Exit(1)
	}

	file := args[0]

	if *remote {
		f, err := fetchFromGit(file)
		if err != nil {
			log.Fatal(err)
		}
		file = *f
	}

	if *justDetect {
		projects := detect.DetectAll(file)
		if len(projects) == 0 {
			log.Fatal("unknown project type")
		}
		for _, project := range projects {
			fmt.Println(project.Id)
		}
		os.Exit(0)
	}

	var cmd string
	if !flagEmpty(command) {
		cmd = *command
	} else {
		project, err := detect.Detect(file)
		if !flagEmpty(projectType) {
			project = detect.GetById(*projectType)
			if project == nil {
				log.Fatalf("unknown type: `%s'", *projectType)
			}
		}
		if err != nil {
			log.Fatal("error: ", err)
		}
		log.Printf("detected a %s project", project.Id)
		projectCmd, found := project.Commands[*step]
		if !found {
			log.Fatalf("%s doesn't support `%s'", project.Id, *step)
		}
		cmd = projectCmd
	}
	file, _ = filepath.Abs(file)
	cmd = strings.Replace(cmd, "{file}", file, -1)
	if err := os.Chdir(fileutil.Dir(file)); err != nil {
		log.Fatal(err)
	}
	log.Printf("command to run: `%s'", cmd)

	runner := MakeRunner(cmd, *autoRestart)
	go runCmd(file, runner)

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM)
	s := <-c
	log.Printf("got signal: %s, exiting...", s)
	runner.Stop()
}

func flagEmpty(stringFlag *string) bool {
	return stringFlag == nil || strings.TrimSpace(*stringFlag) == ""
}

func fetchFromGit(file string) (*string, error) {
	if !strings.HasPrefix(file, "github.com") {
		return nil, errors.New("only github repos supported for now, sorry")
	}
	repo, dir, err := splitGithubUrl(file)
	if err != nil {
		return nil, err
	}
	log.Printf("cloning %s", *repo)
	cloneCmd := exec.Command("git", "clone", *repo)
	cloneCmd.Stderr = os.Stderr
	cloneCmd.Stdout = os.Stdout
	err = cloneCmd.Run()
	if err != nil {
		return nil, err
	}
	return dir, nil
}

func splitGithubUrl(repo string) (*string, *string, error) {
	repoWithPath := strings.TrimPrefix(strings.TrimPrefix(repo, "git://"), "github.com/")
	parts := strings.SplitN(repoWithPath, "/", 3)
	if len(parts) < 2 || parts[0] == "" || parts[1] == "" {
		return nil, nil, errors.New(fmt.Sprint("error: invalid repo: ", repo))
	}
	repoUrl := fmt.Sprintf("git://github.com/%s/%s", parts[0], parts[1])
	dir := parts[1]
	if len(parts) == 3 {
		dir = path.Join(dir, parts[2])
	}
	return &repoUrl, &dir, nil
}

func runCmd(file string, runner *Runner) {
	runner.Start()
	lastMtime := time.Now()
	for {
		info, err := os.Stat(file)
		if os.IsNotExist(err) {
			log.Fatalf("`%s' disappeared, exiting", file)
		}

		mtime := info.ModTime()
		if mtime.After(lastMtime) {
			log.Printf("`%s' changed, trying to restart", file)
			runner.Restart()
		}

		lastMtime = mtime
		time.Sleep(1 * time.Second)
	}
}

type Runner struct {
	cmd         *exec.Cmd
	shellCmd    string
	started     bool
	autoRestart bool
	restarting  bool
}

func MakeRunner(shellCmd string, autoRestart bool) *Runner {
	return &Runner{nil, shellCmd, false, autoRestart, false}
}

func (r *Runner) Start() error {
	if r.started {
		return errors.New("already started, use Restart()")
	}

	r.started = true
	go func() {
		for {
			log.Printf("[runner] starting command")
			r.cmd = exec.Command("sh", "-c", r.shellCmd)
			r.cmd.Stderr = os.Stderr
			r.cmd.Stdout = os.Stdout
			r.cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
			err := r.cmd.Run()
			var result interface{}
			if err != nil {
				result = err
			} else {
				result = r.cmd.ProcessState
			}
			log.Printf("[runner] finished: %s", result)

			time.Sleep(*delay)
			if !r.restarting && !r.autoRestart {
				r.started = false
				break
			}

			r.restarting = false
		}
	}()

	return nil
}

func (r *Runner) Kill() error {
	pgid, err := syscall.Getpgid(r.cmd.Process.Pid)
	if err == nil {
		syscall.Kill(-pgid, syscall.SIGTERM)
	}
	return err
}

func (r *Runner) Restart() error {
	r.restarting = true
	if r.started {
		return r.Kill()
	} else {
		return r.Start()
	}
}

func (r *Runner) Stop() {
	r.autoRestart = false
	r.Kill()
}

func isFile(file string) bool {
	info, err := os.Stat(file)
	return err == nil && !info.IsDir()
}
<|endoftext|>