<commit_before>// Package c2go contains the main function for running the executable.
//
// Installation
//
//     go get -u github.com/elliotchance/c2go
//
// Usage
//
//     c2go myfile.c
//
package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime"
	"strings"

	"errors"

	"github.com/elliotchance/c2go/ast"
	"github.com/elliotchance/c2go/preprocessor"
	"github.com/elliotchance/c2go/program"
	"github.com/elliotchance/c2go/transpiler"
)

var stderr io.Writer = os.Stderr

// ProgramArgs defines the options available when processing the program. There
// is no constructor since the zeroed out values are the appropriate defaults -
// you need only set the options you need.
//
// TODO: Better separation on CLI modes
// https://github.com/elliotchance/c2go/issues/134
//
// Do not instantiate this directly. Instead use DefaultProgramArgs(); then
// modify any specific attributes.
type ProgramArgs struct {
	verbose     bool
	ast         bool
	inputFiles  []string
	clangFlags  []string
	outputFile  string
	packageName string

	// A private option to output the Go as a *_test.go file.
	outputAsTest bool
}

// DefaultProgramArgs default value of ProgramArgs
func DefaultProgramArgs() ProgramArgs {
	return ProgramArgs{
		verbose:      false,
		ast:          false,
		packageName:  "main",
		clangFlags:   []string{},
		outputAsTest: false,
	}
}

func readAST(data []byte) []string {
	return strings.Split(string(data), "\n")
}

type treeNode struct {
	indent int
	node   ast.Node
}

func convertLinesToNodes(lines []string) []treeNode {
	nodes := make([]treeNode, len(lines))
	var counter int
	for _, line := range lines {
		if strings.TrimSpace(line) == "" {
			continue
		}

		// It is tempting to discard null AST nodes, but these may
		// have semantic importance: for example, they represent omitted
		// for-loop conditions, as in for(;;).
		line = strings.Replace(line, "<<<NULL>>>", "NullStmt", 1)
		trimmed := strings.TrimLeft(line, "|\\- `")
		node := ast.Parse(trimmed)
		indentLevel := (len(line) - len(trimmed)) / 2
		nodes[counter] = treeNode{indentLevel, node}
		counter++
	}
	nodes = nodes[0:counter]

	return nodes
}

func convertLinesToNodesParallel(lines []string) []treeNode {
	// function f separate full list on 2 parts and
	// then each part can recursive run function f
	var f func([]string, int) []treeNode

	f = func(lines []string, deep int) []treeNode {
		deep = deep - 2
		part := len(lines) / 2

		var tr1 = make(chan []treeNode)
		var tr2 = make(chan []treeNode)

		go func(lines []string, deep int) {
			if deep <= 0 || len(lines) < deep {
				tr1 <- convertLinesToNodes(lines)
				return
			}
			tr1 <- f(lines, deep)
		}(lines[0:part], deep)

		go func(lines []string, deep int) {
			if deep <= 0 || len(lines) < deep {
				tr2 <- convertLinesToNodes(lines)
				return
			}
			tr2 <- f(lines, deep)
		}(lines[part:], deep)

		defer close(tr1)
		defer close(tr2)

		return append(<-tr1, <-tr2...)
	}

	// Parameter of deep - can be any, but effective to use
	// same amount of CPU
	return f(lines, runtime.NumCPU())
}

// buildTree converts an array of nodes, each prefixed with a depth into a tree.
func buildTree(nodes []treeNode, depth int) []ast.Node {
	if len(nodes) == 0 {
		return []ast.Node{}
	}

	// Split the list into sections, treat each section as a tree with its own
	// root.
	sections := [][]treeNode{}
	for _, node := range nodes {
		if node.indent == depth {
			sections = append(sections, []treeNode{node})
		} else {
			sections[len(sections)-1] = append(sections[len(sections)-1], node)
		}
	}

	results := []ast.Node{}
	for _, section := range sections {
		slice := []treeNode{}
		for _, n := range section {
			if n.indent > depth {
				slice = append(slice, n)
			}
		}

		children := buildTree(slice, depth+1)
		for _, child := range children {
			section[0].node.AddChild(child)
		}
		results = append(results, section[0].node)
	}

	return results
}

// Start begins transpiling an input file.
func Start(args ProgramArgs) (err error) {
	if args.verbose {
		fmt.Println("Start tanspiling ...")
	}

	if os.Getenv("GOPATH") == "" {
		return fmt.Errorf("The $GOPATH must be set")
	}

	// 1. Compile it first (checking for errors)
	for _, in := range args.inputFiles {
		_, err := os.Stat(in)
		if err != nil {
			return fmt.Errorf("Input file %s is not found", in)
		}
	}

	// 2. Preprocess
	if args.verbose {
		fmt.Println("Running clang preprocessor...")
	}

	pp, comments, includes, err := preprocessor.Analyze(args.inputFiles, args.clangFlags)
	if err != nil {
		return err
	}

	if args.verbose {
		fmt.Println("Writing preprocessor ...")
	}
	dir, err := ioutil.TempDir("", "c2go")
	if err != nil {
		return fmt.Errorf("Cannot create temp folder: %v", err)
	}
	defer os.RemoveAll(dir) // clean up

	ppFilePath := path.Join(dir, "pp.c")
	err = ioutil.WriteFile(ppFilePath, pp, 0644)
	if err != nil {
		return fmt.Errorf("writing to %s failed: %v", ppFilePath, err)
	}

	// 3. Generate JSON from AST
	if args.verbose {
		fmt.Println("Running clang for AST tree...")
	}
	astPP, err := exec.Command("clang", "-Xclang", "-ast-dump",
		"-fsyntax-only", "-fno-color-diagnostics", ppFilePath).Output()
	if err != nil {
		// If clang fails it still prints out the AST, so we have to run it
		// again to get the real error.
		errBody, _ := exec.Command("clang", ppFilePath).CombinedOutput()

		panic("clang failed: " + err.Error() + ":\n\n" + string(errBody))
	}

	if args.verbose {
		fmt.Println("Reading clang AST tree...")
	}
	lines := readAST(astPP)
	if args.ast {
		for _, l := range lines {
			fmt.Println(l)
		}
		fmt.Println()

		return nil
	}

	p := program.NewProgram()
	p.Verbose = args.verbose
	p.OutputAsTest = args.outputAsTest
	p.Comments = comments
	p.IncludeHeaders = includes

	// Converting to nodes
	if args.verbose {
		fmt.Println("Converting to nodes...")
	}
	nodes := convertLinesToNodesParallel(lines)

	// build tree
	if args.verbose {
		fmt.Println("Building tree...")
	}
	tree := buildTree(nodes, 0)
	ast.FixPositions(tree)
	p.SetNodes(tree)

	// Repair the character literals. See RepairCharacterLiteralsFromSource for
	// more information.
	characterErrors := ast.RepairCharacterLiteralsFromSource(tree[0], ppFilePath)

	for _, cErr := range characterErrors {
		message := fmt.Sprintf("could not read exact character literal: %s",
			cErr.Err.Error())
		p.AddMessage(p.GenerateWarningMessage(errors.New(message), cErr.Node))
	}

	// Repair the floating literals. See RepairFloatingLiteralsFromSource for
	// more information.
	floatingErrors := ast.RepairFloatingLiteralsFromSource(tree[0], ppFilePath)

	for _, fErr := range floatingErrors {
		message := fmt.Sprintf("could not read exact floating literal: %s",
			fErr.Err.Error())
		p.AddMessage(p.GenerateWarningMessage(errors.New(message), fErr.Node))
	}

	outputFilePath := args.outputFile

	if outputFilePath == "" {
		// Choose inputFile for creating name of output file
		input := args.inputFiles[0]
		// We choose name for output Go code at the base
		// on filename for choosed input file
		cleanFileName := filepath.Clean(filepath.Base(input))
		extension := filepath.Ext(input)
		outputFilePath = cleanFileName[0:len(cleanFileName)-len(extension)] + ".go"
	}

	// transpile ast tree
	if args.verbose {
		fmt.Println("Transpiling tree...")
	}

	err = transpiler.TranspileAST(args.outputFile, args.packageName,
		p, tree[0].(ast.Node))
	if err != nil {
		return fmt.Errorf("cannot transpile AST : %v", err)
	}

	// write the output Go code
	if args.verbose {
		fmt.Println("Writing the output Go code...")
	}
	err = ioutil.WriteFile(outputFilePath, []byte(p.String()), 0644)
	if err != nil {
		return fmt.Errorf("writing Go output file failed: %v", err)
	}

	// simplify Go code by `gofmt`
	// error ignored, because it is not change the workflow
	_, _ = exec.Command("gofmt", "-w", outputFilePath).Output()

	return nil
}

type inputDataFlags []string

func (i *inputDataFlags) String() (s string) {
	for pos, item := range *i {
		s += fmt.Sprintf("Flag %d. %s\n", pos, item)
	}
	return
}

func (i *inputDataFlags) Set(value string) error {
	*i = append(*i, value)
	return nil
}

var clangFlags inputDataFlags

func init() {
	transpileCommand.Var(&clangFlags, "clang-flag", "Pass arguments to clang. You may provide multiple -clang-flag items.")
	astCommand.Var(&clangFlags, "clang-flag", "Pass arguments to clang. You may provide multiple -clang-flag items.")
}

var (
	versionFlag       = flag.Bool("v", false, "print the version and exit")
	transpileCommand  = flag.NewFlagSet("transpile", flag.ContinueOnError)
	verboseFlag       = transpileCommand.Bool("V", false, "print progress as comments")
	outputFlag        = transpileCommand.String("o", "", "output Go generated code to the specified file")
	packageFlag       = transpileCommand.String("p", "main", "set the name of the generated package")
	transpileHelpFlag = transpileCommand.Bool("h", false, "print help information")
	astCommand        = flag.NewFlagSet("ast", flag.ContinueOnError)
	astHelpFlag       = astCommand.Bool("h", false, "print help information")
)

func main() {
	code := runCommand()
	if code != 0 {
		os.Exit(code)
	}
}

func runCommand() int {

	flag.Usage = func() {
		usage := "Usage: %s [-v] [<command>] [<flags>] file1.c ...\n\n"
		usage += "Commands:\n"
		usage += "  transpile\ttranspile an input C source file or files to Go\n"
		usage += "  ast\t\tprint AST before translated Go code\n\n"

		usage += "Flags:\n"
		fmt.Fprintf(stderr, usage, os.Args[0])
		flag.PrintDefaults()
	}

	transpileCommand.SetOutput(stderr)
	astCommand.SetOutput(stderr)

	flag.Parse()

	if *versionFlag {
		// Simply print out the version and exit.
		fmt.Println(program.Version)
		return 0
	}

	if flag.NArg() < 1 {
		flag.Usage()
		return 1
	}

	args := DefaultProgramArgs()

	switch os.Args[1] {
	case "ast":
		err := astCommand.Parse(os.Args[2:])
		if err != nil {
			fmt.Printf("ast command cannot parse: %v", err)
			return 1
		}

		if *astHelpFlag || astCommand.NArg() == 0 {
			fmt.Fprintf(stderr, "Usage: %s ast file.c\n", os.Args[0])
			astCommand.PrintDefaults()
			return 1
		}

		args.ast = true
		args.inputFiles = astCommand.Args()
		args.clangFlags = clangFlags
	case "transpile":
		err := transpileCommand.Parse(os.Args[2:])
		if err != nil {
			fmt.Printf("transpile command cannot parse: %v", err)
			return 1
		}

		if *transpileHelpFlag || transpileCommand.NArg() == 0 {
			fmt.Fprintf(stderr, "Usage: %s transpile [-V] [-o file.go] [-p package] file1.c ...\n", os.Args[0])
			transpileCommand.PrintDefaults()
			return 1
		}

		args.inputFiles = transpileCommand.Args()
		args.outputFile = *outputFlag
		args.packageName = *packageFlag
		args.verbose = *verboseFlag
		args.clangFlags = clangFlags
	default:
		flag.Usage()
		return 1
	}

	if err := Start(args); err != nil {
		fmt.Printf("Error: %v\n", err)
		return 1
	}

	return 0
}
<commit_msg>Update GoDoc usage. Fixes #710 (#711)<commit_after>// Package c2go contains the main function for running the executable.
//
// Installation
//
//     go get -u github.com/elliotchance/c2go
//
// Usage
//
//     c2go transpile myfile.c
//
package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime"
	"strings"

	"errors"

	"github.com/elliotchance/c2go/ast"
	"github.com/elliotchance/c2go/preprocessor"
	"github.com/elliotchance/c2go/program"
	"github.com/elliotchance/c2go/transpiler"
)

var stderr io.Writer = os.Stderr

// ProgramArgs defines the options available when processing the program. There
// is no constructor since the zeroed out values are the appropriate defaults -
// you need only set the options you need.
//
// TODO: Better separation on CLI modes
// https://github.com/elliotchance/c2go/issues/134
//
// Do not instantiate this directly. Instead use DefaultProgramArgs(); then
// modify any specific attributes.
type ProgramArgs struct {
	verbose     bool
	ast         bool
	inputFiles  []string
	clangFlags  []string
	outputFile  string
	packageName string

	// A private option to output the Go as a *_test.go file.
	outputAsTest bool
}

// DefaultProgramArgs default value of ProgramArgs
func DefaultProgramArgs() ProgramArgs {
	return ProgramArgs{
		verbose:      false,
		ast:          false,
		packageName:  "main",
		clangFlags:   []string{},
		outputAsTest: false,
	}
}

func readAST(data []byte) []string {
	return strings.Split(string(data), "\n")
}

type treeNode struct {
	indent int
	node   ast.Node
}

func convertLinesToNodes(lines []string) []treeNode {
	nodes := make([]treeNode, len(lines))
	var counter int
	for _, line := range lines {
		if strings.TrimSpace(line) == "" {
			continue
		}

		// It is tempting to discard null AST nodes, but these may
		// have semantic importance: for example, they represent omitted
		// for-loop conditions, as in for(;;).
		line = strings.Replace(line, "<<<NULL>>>", "NullStmt", 1)
		trimmed := strings.TrimLeft(line, "|\\- `")
		node := ast.Parse(trimmed)
		indentLevel := (len(line) - len(trimmed)) / 2
		nodes[counter] = treeNode{indentLevel, node}
		counter++
	}
	nodes = nodes[0:counter]

	return nodes
}

func convertLinesToNodesParallel(lines []string) []treeNode {
	// function f separate full list on 2 parts and
	// then each part can recursive run function f
	var f func([]string, int) []treeNode

	f = func(lines []string, deep int) []treeNode {
		deep = deep - 2
		part := len(lines) / 2

		var tr1 = make(chan []treeNode)
		var tr2 = make(chan []treeNode)

		go func(lines []string, deep int) {
			if deep <= 0 || len(lines) < deep {
				tr1 <- convertLinesToNodes(lines)
				return
			}
			tr1 <- f(lines, deep)
		}(lines[0:part], deep)

		go func(lines []string, deep int) {
			if deep <= 0 || len(lines) < deep {
				tr2 <- convertLinesToNodes(lines)
				return
			}
			tr2 <- f(lines, deep)
		}(lines[part:], deep)

		defer close(tr1)
		defer close(tr2)

		return append(<-tr1, <-tr2...)
	}

	// Parameter of deep - can be any, but effective to use
	// same amount of CPU
	return f(lines, runtime.NumCPU())
}

// buildTree converts an array of nodes, each prefixed with a depth into a tree.
func buildTree(nodes []treeNode, depth int) []ast.Node {
	if len(nodes) == 0 {
		return []ast.Node{}
	}

	// Split the list into sections, treat each section as a tree with its own
	// root.
	sections := [][]treeNode{}
	for _, node := range nodes {
		if node.indent == depth {
			sections = append(sections, []treeNode{node})
		} else {
			sections[len(sections)-1] = append(sections[len(sections)-1], node)
		}
	}

	results := []ast.Node{}
	for _, section := range sections {
		slice := []treeNode{}
		for _, n := range section {
			if n.indent > depth {
				slice = append(slice, n)
			}
		}

		children := buildTree(slice, depth+1)
		for _, child := range children {
			section[0].node.AddChild(child)
		}
		results = append(results, section[0].node)
	}

	return results
}

// Start begins transpiling an input file.
func Start(args ProgramArgs) (err error) {
	if args.verbose {
		fmt.Println("Start tanspiling ...")
	}

	if os.Getenv("GOPATH") == "" {
		return fmt.Errorf("The $GOPATH must be set")
	}

	// 1. Compile it first (checking for errors)
	for _, in := range args.inputFiles {
		_, err := os.Stat(in)
		if err != nil {
			return fmt.Errorf("Input file %s is not found", in)
		}
	}

	// 2. Preprocess
	if args.verbose {
		fmt.Println("Running clang preprocessor...")
	}

	pp, comments, includes, err := preprocessor.Analyze(args.inputFiles, args.clangFlags)
	if err != nil {
		return err
	}

	if args.verbose {
		fmt.Println("Writing preprocessor ...")
	}
	dir, err := ioutil.TempDir("", "c2go")
	if err != nil {
		return fmt.Errorf("Cannot create temp folder: %v", err)
	}
	defer os.RemoveAll(dir) // clean up

	ppFilePath := path.Join(dir, "pp.c")
	err = ioutil.WriteFile(ppFilePath, pp, 0644)
	if err != nil {
		return fmt.Errorf("writing to %s failed: %v", ppFilePath, err)
	}

	// 3. Generate JSON from AST
	if args.verbose {
		fmt.Println("Running clang for AST tree...")
	}
	astPP, err := exec.Command("clang", "-Xclang", "-ast-dump",
		"-fsyntax-only", "-fno-color-diagnostics", ppFilePath).Output()
	if err != nil {
		// If clang fails it still prints out the AST, so we have to run it
		// again to get the real error.
		errBody, _ := exec.Command("clang", ppFilePath).CombinedOutput()

		panic("clang failed: " + err.Error() + ":\n\n" + string(errBody))
	}

	if args.verbose {
		fmt.Println("Reading clang AST tree...")
	}
	lines := readAST(astPP)
	if args.ast {
		for _, l := range lines {
			fmt.Println(l)
		}
		fmt.Println()

		return nil
	}

	p := program.NewProgram()
	p.Verbose = args.verbose
	p.OutputAsTest = args.outputAsTest
	p.Comments = comments
	p.IncludeHeaders = includes

	// Converting to nodes
	if args.verbose {
		fmt.Println("Converting to nodes...")
	}
	nodes := convertLinesToNodesParallel(lines)

	// build tree
	if args.verbose {
		fmt.Println("Building tree...")
	}
	tree := buildTree(nodes, 0)
	ast.FixPositions(tree)
	p.SetNodes(tree)

	// Repair the character literals. See RepairCharacterLiteralsFromSource for
	// more information.
	characterErrors := ast.RepairCharacterLiteralsFromSource(tree[0], ppFilePath)

	for _, cErr := range characterErrors {
		message := fmt.Sprintf("could not read exact character literal: %s",
			cErr.Err.Error())
		p.AddMessage(p.GenerateWarningMessage(errors.New(message), cErr.Node))
	}

	// Repair the floating literals. See RepairFloatingLiteralsFromSource for
	// more information.
	floatingErrors := ast.RepairFloatingLiteralsFromSource(tree[0], ppFilePath)

	for _, fErr := range floatingErrors {
		message := fmt.Sprintf("could not read exact floating literal: %s",
			fErr.Err.Error())
		p.AddMessage(p.GenerateWarningMessage(errors.New(message), fErr.Node))
	}

	outputFilePath := args.outputFile

	if outputFilePath == "" {
		// Choose inputFile for creating name of output file
		input := args.inputFiles[0]
		// We choose name for output Go code at the base
		// on filename for choosed input file
		cleanFileName := filepath.Clean(filepath.Base(input))
		extension := filepath.Ext(input)
		outputFilePath = cleanFileName[0:len(cleanFileName)-len(extension)] + ".go"
	}

	// transpile ast tree
	if args.verbose {
		fmt.Println("Transpiling tree...")
	}

	err = transpiler.TranspileAST(args.outputFile, args.packageName,
		p, tree[0].(ast.Node))
	if err != nil {
		return fmt.Errorf("cannot transpile AST : %v", err)
	}

	// write the output Go code
	if args.verbose {
		fmt.Println("Writing the output Go code...")
	}
	err = ioutil.WriteFile(outputFilePath, []byte(p.String()), 0644)
	if err != nil {
		return fmt.Errorf("writing Go output file failed: %v", err)
	}

	// simplify Go code by `gofmt`
	// error ignored, because it is not change the workflow
	_, _ = exec.Command("gofmt", "-w", outputFilePath).Output()

	return nil
}

type inputDataFlags []string

func (i *inputDataFlags) String() (s string) {
	for pos, item := range *i {
		s += fmt.Sprintf("Flag %d. %s\n", pos, item)
	}
	return
}

func (i *inputDataFlags) Set(value string) error {
	*i = append(*i, value)
	return nil
}

var clangFlags inputDataFlags

func init() {
	transpileCommand.Var(&clangFlags, "clang-flag", "Pass arguments to clang. You may provide multiple -clang-flag items.")
	astCommand.Var(&clangFlags, "clang-flag", "Pass arguments to clang. You may provide multiple -clang-flag items.")
}

var (
	versionFlag       = flag.Bool("v", false, "print the version and exit")
	transpileCommand  = flag.NewFlagSet("transpile", flag.ContinueOnError)
	verboseFlag       = transpileCommand.Bool("V", false, "print progress as comments")
	outputFlag        = transpileCommand.String("o", "", "output Go generated code to the specified file")
	packageFlag       = transpileCommand.String("p", "main", "set the name of the generated package")
	transpileHelpFlag = transpileCommand.Bool("h", false, "print help information")
	astCommand        = flag.NewFlagSet("ast", flag.ContinueOnError)
	astHelpFlag       = astCommand.Bool("h", false, "print help information")
)

func main() {
	code := runCommand()
	if code != 0 {
		os.Exit(code)
	}
}

func runCommand() int {

	flag.Usage = func() {
		usage := "Usage: %s [-v] [<command>] [<flags>] file1.c ...\n\n"
		usage += "Commands:\n"
		usage += "  transpile\ttranspile an input C source file or files to Go\n"
		usage += "  ast\t\tprint AST before translated Go code\n\n"

		usage += "Flags:\n"
		fmt.Fprintf(stderr, usage, os.Args[0])
		flag.PrintDefaults()
	}

	transpileCommand.SetOutput(stderr)
	astCommand.SetOutput(stderr)

	flag.Parse()

	if *versionFlag {
		// Simply print out the version and exit.
		fmt.Println(program.Version)
		return 0
	}

	if flag.NArg() < 1 {
		flag.Usage()
		return 1
	}

	args := DefaultProgramArgs()

	switch os.Args[1] {
	case "ast":
		err := astCommand.Parse(os.Args[2:])
		if err != nil {
			fmt.Printf("ast command cannot parse: %v", err)
			return 1
		}

		if *astHelpFlag || astCommand.NArg() == 0 {
			fmt.Fprintf(stderr, "Usage: %s ast file.c\n", os.Args[0])
			astCommand.PrintDefaults()
			return 1
		}

		args.ast = true
		args.inputFiles = astCommand.Args()
		args.clangFlags = clangFlags
	case "transpile":
		err := transpileCommand.Parse(os.Args[2:])
		if err != nil {
			fmt.Printf("transpile command cannot parse: %v", err)
			return 1
		}

		if *transpileHelpFlag || transpileCommand.NArg() == 0 {
			fmt.Fprintf(stderr, "Usage: %s transpile [-V] [-o file.go] [-p package] file1.c ...\n", os.Args[0])
			transpileCommand.PrintDefaults()
			return 1
		}

		args.inputFiles = transpileCommand.Args()
		args.outputFile = *outputFlag
		args.packageName = *packageFlag
		args.verbose = *verboseFlag
		args.clangFlags = clangFlags
	default:
		flag.Usage()
		return 1
	}

	if err := Start(args); err != nil {
		fmt.Printf("Error: %v\n", err)
		return 1
	}

	return 0
}
<|endoftext|>


<commit_before>package main

import (
	//	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/lucagrulla/cw/cloudwatch"
	"github.com/lucagrulla/cw/timeutil"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	lsCommand = kingpin.Command("ls", "Show all log groups.")
	//logGroupPattern = lsCommand.Arg("group", "The log group name.").String()

	tailCommand  = kingpin.Command("tail", "Tail a log group")
	follow       = tailCommand.Flag("follow", "Don't stop when the end of stream is reached.").Short('f').Default("false").Bool()
	grep         = tailCommand.Flag("grep", "Pattern to filter logs by. See http://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html for syntax.").Short('g').Default("").String()
	logGroupName = tailCommand.Arg("group", "The log group name.").Required().String()
	startTime    = tailCommand.Arg("start", "The tailing start time. If a timestamp is passed(format: hh[:mm]) it's expanded to today at the given time. Full format: 2017-02-27[T09:00[:00]].").Default(time.Now().Add(-30 * time.Second).Format(timeutil.TimeFormat)).String()
	endTime      = tailCommand.Arg("end", "The tailing end time. If a timestamp is passed(format: hh[:mm]) it's expanded to today at the given time. Full format: 2017-02-27[T09:00[:00]].").String()
	streamName   = tailCommand.Arg("stream", "An optional stream name.").String()
)

func timestampShortcut(timeStamp *string) string {
	if regexp.MustCompile("^\\d{4}-\\d{2}-\\d{2}$").MatchString(*timeStamp) {
		return strings.Join([]string{*timeStamp, "00:00:00"}, "T")
	}
	if regexp.MustCompile("^\\d{4}-\\d{2}-\\d{2}T\\d{2}$").MatchString(*timeStamp) {
		return strings.Join([]string{*timeStamp, "00:00"}, ":")
	}
	if regexp.MustCompile("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$").MatchString(*timeStamp) {
		return strings.Join([]string{*timeStamp, "00"}, ":")
	}
	if regexp.MustCompile("^\\d{1,2}$").MatchString(*timeStamp) {
		y, m, d := time.Now().Date()
		t, _ := strconv.Atoi(*timeStamp)
		c := time.Date(y, m, d, t, 0, 0, 0, time.UTC)

		return c.Format(timeutil.TimeFormat)
	}

	res := regexp.MustCompile(`(?P<Hour>\d{1,2}):(?P<Minute>\d{2})`).FindStringSubmatch(*timeStamp)
	if res != nil {
		y, m, d := time.Now().Date()

		t, _ := strconv.Atoi(res[1])
		mm, _ := strconv.Atoi(res[2])

		c := time.Date(y, m, d, t, mm, 0, 0, time.UTC)

		return c.Format(timeutil.TimeFormat)
	}

	return *timeStamp
}

func main() {
	kingpin.Version("0.2.0")
	command := kingpin.Parse()

	switch command {
	case "ls":
		cloudwatch.Ls()
	case "tail":
		st := timestampShortcut(startTime)
		et := timestampShortcut(endTime)
		//		fmt.Println(st, et)
		cloudwatch.Tail(logGroupName, follow, &st, &et, streamName, grep)
	}
}
<commit_msg>0.2.1<commit_after>package main

import (
	//	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/lucagrulla/cw/cloudwatch"
	"github.com/lucagrulla/cw/timeutil"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	lsCommand = kingpin.Command("ls", "Show all log groups.")
	//logGroupPattern = lsCommand.Arg("group", "The log group name.").String()

	tailCommand  = kingpin.Command("tail", "Tail a log group")
	follow       = tailCommand.Flag("follow", "Don't stop when the end of stream is reached.").Short('f').Default("false").Bool()
	grep         = tailCommand.Flag("grep", "Pattern to filter logs by. See http://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html for syntax.").Short('g').Default("").String()
	logGroupName = tailCommand.Arg("group", "The log group name.").Required().String()
	startTime    = tailCommand.Arg("start", "The tailing start time. If a timestamp is passed(format: hh[:mm]) it's expanded to today at the given time. Full format: 2017-02-27[T09:00[:00]].").Default(time.Now().Add(-30 * time.Second).Format(timeutil.TimeFormat)).String()
	endTime      = tailCommand.Arg("end", "The tailing end time. If a timestamp is passed(format: hh[:mm]) it's expanded to today at the given time. Full format: 2017-02-27[T09:00[:00]].").String()
	streamName   = tailCommand.Arg("stream", "An optional stream name.").String()
)

func timestampShortcut(timeStamp *string) string {
	if regexp.MustCompile("^\\d{4}-\\d{2}-\\d{2}$").MatchString(*timeStamp) {
		return strings.Join([]string{*timeStamp, "00:00:00"}, "T")
	}
	if regexp.MustCompile("^\\d{4}-\\d{2}-\\d{2}T\\d{2}$").MatchString(*timeStamp) {
		return strings.Join([]string{*timeStamp, "00:00"}, ":")
	}
	if regexp.MustCompile("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$").MatchString(*timeStamp) {
		return strings.Join([]string{*timeStamp, "00"}, ":")
	}
	if regexp.MustCompile("^\\d{1,2}$").MatchString(*timeStamp) {
		y, m, d := time.Now().Date()
		t, _ := strconv.Atoi(*timeStamp)
		c := time.Date(y, m, d, t, 0, 0, 0, time.UTC)

		return c.Format(timeutil.TimeFormat)
	}

	res := regexp.MustCompile(`(?P<Hour>\d{1,2}):(?P<Minute>\d{2})`).FindStringSubmatch(*timeStamp)
	if res != nil {
		y, m, d := time.Now().Date()

		t, _ := strconv.Atoi(res[1])
		mm, _ := strconv.Atoi(res[2])

		c := time.Date(y, m, d, t, mm, 0, 0, time.UTC)

		return c.Format(timeutil.TimeFormat)
	}

	return *timeStamp
}

func main() {
	kingpin.Version("0.2.1")
	command := kingpin.Parse()

	switch command {
	case "ls":
		cloudwatch.Ls()
	case "tail":
		st := timestampShortcut(startTime)
		et := timestampShortcut(endTime)
		//		fmt.Println(st, et)
		cloudwatch.Tail(logGroupName, follow, &st, &et, streamName, grep)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 tsuru-autoscale authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/tsuru/tsuru-autoscale/alarm"
	"github.com/tsuru/tsuru-autoscale/api"
)

func port() string {
	var p string
	if p = os.Getenv("PORT"); p != "" {
		return p
	}
	return "8080"
}

func runServer() {
	r := api.Router()
	http.Handle("/", r)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port()), nil))
}

func main() {
	if os.Args[1] == "agent" {
		alarm.StartAutoScale()
	} else {
		runServer()
	}
}
<commit_msg>main: remove var<commit_after>// Copyright 2015 tsuru-autoscale authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/tsuru/tsuru-autoscale/alarm"
	"github.com/tsuru/tsuru-autoscale/api"
)

func port() string {
	var p string
	if p = os.Getenv("PORT"); p != "" {
		return p
	}
	return "8080"
}

func runServer() {
	http.Handle("/", api.Router())
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port()), nil))
}

func main() {
	if os.Args[1] == "agent" {
		alarm.StartAutoScale()
	} else {
		runServer()
	}
}
<|endoftext|>


<commit_before>// See LICENSE.txt for licensing information.

package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"time"
)

const (
	VERSION = `0.9.1`
	timeFmt = `2006-01-02 15:04:05`
	fileFmt = `2006-01-02_150405`
)

var build = `UNKNOWN` // injected in Makefile

var (
	flagConfig  string
	flagVerbose bool
	cfg         *Config
	idRange     = regexp.MustCompile(`(\d+)-(\d+)`)
	spinner     = Spinner{}
)

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `Usage: %s (options...) <command>
saver v%s by Piotr S. Staszewski, see LICENSE.txt
binary build by %s

Options:
`, os.Args[0], VERSION, build)
		flag.PrintDefaults()
		fmt.Fprintln(os.Stderr, `
Commands:
  [list]                       - list games
  <name> [add] <path>          - add new game
  <name> [b]ackup (note)       - backup current save
  <name> [l]ist                - list saves
  <name> [r]estore <id|ref>    - restore given save
  <name> [del]ete <id|from-to> - delete given save(s)
  <name> [i]nfo                - game info, mostly paths to stuff
  <name> [kill]                - delete game and all saves
  [migrate]                    - migrate config, if needed

Where:
  name     - arbitrary name used to identify a game/character/world etc.
  path     - absolute path to save file/directory
  note     - optional quoted note, e.g. "haven't died yet"
  id       - particular save id
  from-to  - inclusive range of save ids
  ref      - non-positive offset from the latest save, e.g. -1 is the save before the latest`)
	}
	flag.StringVar(&flagConfig, "c", "saver.json", "path to config file")
	flag.BoolVar(&flagVerbose, "v", false, "be very verbose")
}

func main() {
	flag.Parse()
	checkArgs(false, 1)

	if flagConfig == "" {
		fmt.Fprintln(os.Stderr, "Config file path can't be empty")
		os.Exit(2)
	}
	flagConfig, err := filepath.Abs(flagConfig)
	dieOnErr("Can't resolve config path", err)

	save := false
	if _, err := os.Stat(flagConfig); err != nil {
		r, err := os.Getwd()
		dieOnErr("Can't get current path", err)
		cfg = &Config{
			Root:  r,
			Games: make([]*Game, 0),
		}
		fmt.Println("Will save fresh config to", flagConfig)
		save = true
	} else {
		c, err := loadConfig(flagConfig)
		dieOnErr("Config load error", err)
		cfg = c
	}

	switch flag.Arg(0) {
	case "list":
		// list games
		cfg.PrintWhole()
	case "migrate":
		// migrate config to latest version
		err := cfg.Migrate()
		dieOnErr("ERROR", err)
		save = true
	default:
		// per-game commands
		checkArgs(false, 2)
		game := flag.Arg(0)
		if flag.Arg(1) == "add" {
			// add a new game
			checkArgs(true, 3)
			if g := cfg.GetGame(game); g != nil {
				fmt.Fprintf(os.Stderr, "Game \"%s\" already exist\n", game)
				os.Exit(1)
			}
			p, err := filepath.Abs(flag.Arg(2))
			dieOnErr("Can't resolve path", err)
			gm, err := cfg.AddGame(game, p)
			dieOnErr("ERROR", err)
			gm.PrintHeader()
			gm.Print()
			save = true
		} else {
			g := cfg.GetGame(game)
			if g == nil {
				fmt.Fprintf(os.Stderr, "Game \"%s\" not found\n", game)
				os.Exit(1)
			}
			switch flag.Arg(1) {
			case "l", "list":
				// list game saves
				g.PrintWhole()
			case "i", "info":
				// print game paths etc.
				g.PrintInfo()
			case "kill":
				// remove game and all saves
				err := cfg.DelGame(game)
				dieOnErr("ERROR", err)
				fmt.Printf("Deleted game \"%s\" and all backed up saves\n", game)
				save = true
			case "b", "backup":
				// backup current save
				sv, err := g.Backup()
				dieOnErr("ERROR", err)
				g.Stamp = time.Now()
				if flag.NArg() > 2 {
					sv.Note = flag.Arg(2)
				}
				fmt.Printf("%3d ", len(g.Saves))
				sv.Print()
				save = true
			case "r", "restore":
				// restore selected save
				checkArgs(true, 3)
				if len(g.Saves) < 1 {
					fmt.Fprintf(os.Stderr, "Game \"%s\" has no saves backed up\n", g.Name)
					os.Exit(1)
				}
				i, err := strconv.Atoi(flag.Arg(2))
				dieOnErr(fmt.Sprintf("Malformed index \"%s\"", flag.Arg(2)), err)
				sv, err := g.Restore(i)
				dieOnErr("ERROR", err)
				g.Stamp = time.Now()
				fmt.Println("Restored save from", sv.Stamp.Format(timeFmt))
				if sv.Note != "" {
					fmt.Println("Save note:", sv.Note)
				}
				save = true
			case "del", "delete":
				// delete save(s)
				checkArgs(true, 3)
				if len(g.Saves) < 1 {
					fmt.Fprintf(os.Stderr, "Game \"%s\" has no saves backed up\n", g.Name)
					os.Exit(1)
				}
				var err error
				var f, t int
				f, err = strconv.Atoi(flag.Arg(2))
				t = f
				if err != nil {
					m := idRange.FindStringSubmatch(flag.Arg(2))
					f, err = strconv.Atoi(m[1])
					dieOnErr(fmt.Sprintf("Malformed index/range \"%s\"", flag.Arg(2)), err)
					t, err = strconv.Atoi(m[2])
					dieOnErr(fmt.Sprintf("Malformed index/range \"%s\"", flag.Arg(2)), err)
				}
				n, err := g.Delete(f, t)
				if err != nil {
					fmt.Fprintln(os.Stderr, "ERROR:", err)
				}
				fmt.Printf("Deleted %d save(s)\n", n)
				save = true
			default:
				flag.Usage()
				os.Exit(1)
			}
		}
	}

	if save {
		err := cfg.Save(flagConfig)
		dieOnErr("Can't save config", err)
	}
}

func dieOnErr(msg string, err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, msg+":", err)
		os.Exit(3)
	}
}

func checkArgs(exact bool, num int) {
	if (exact && flag.NArg() != num) ||
		(!exact && flag.NArg() < num) {
		flag.Usage()
		os.Exit(1)
	}
}
<commit_msg>Add the notion of short outputs<commit_after>// See LICENSE.txt for licensing information.

package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"time"
)

const (
	VERSION = `0.9.1`
	timeFmt = `2006-01-02 15:04:05`
	fileFmt = `2006-01-02_150405`
)

var build = `UNKNOWN` // injected in Makefile

var (
	flagConfig  string
	flagShort   bool
	flagVerbose bool
	cfg         *Config
	idRange     = regexp.MustCompile(`(\d+)-(\d+)`)
	spinner     = Spinner{}
)

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `Usage: %s (options...) <command>
saver v%s by Piotr S. Staszewski, see LICENSE.txt
binary build by %s

Options:
`, os.Args[0], VERSION, build)
		flag.PrintDefaults()
		fmt.Fprintln(os.Stderr, `
Commands:
  [list]                       - list games
  <name> [add] <path>          - add new game
  <name> [b]ackup (note)       - backup current save
  <name> [l]ist                - list saves
  <name> [r]estore <id|ref>    - restore given save
  <name> [del]ete <id|from-to> - delete given save(s)
  <name> [i]nfo                - game info, mostly paths to stuff
  <name> [kill]                - delete game and all saves
  [migrate]                    - migrate config, if needed

Where:
  name     - arbitrary name used to identify a game/character/world etc.
  path     - absolute path to save file/directory
  note     - optional quoted note, e.g. "haven't died yet"
  id       - particular save id
  from-to  - inclusive range of save ids
  ref      - non-positive offset from the latest save, e.g. -1 is the save before the latest`)
	}
	flag.StringVar(&flagConfig, "c", "saver.json", "path to config file")
	flag.BoolVar(&flagShort, "s", false, "be short, be scriptful")
	flag.BoolVar(&flagVerbose, "v", false, "be very verbose")
}

func main() {
	flag.Parse()
	checkArgs(false, 1)

	if flagConfig == "" {
		fmt.Fprintln(os.Stderr, "Config file path can't be empty")
		os.Exit(2)
	}
	flagConfig, err := filepath.Abs(flagConfig)
	dieOnErr("Can't resolve config path", err)

	save := false
	if _, err := os.Stat(flagConfig); err != nil {
		r, err := os.Getwd()
		dieOnErr("Can't get current path", err)
		cfg = &Config{
			Root:  r,
			Games: make([]*Game, 0),
		}
		fmt.Println("Will save fresh config to", flagConfig)
		save = true
	} else {
		c, err := loadConfig(flagConfig)
		dieOnErr("Config load error", err)
		cfg = c
	}

	switch flag.Arg(0) {
	case "list":
		// list games
		cfg.PrintWhole()
	case "migrate":
		// migrate config to latest version
		err := cfg.Migrate()
		dieOnErr("ERROR", err)
		save = true
	default:
		// per-game commands
		checkArgs(false, 2)
		game := flag.Arg(0)
		if flag.Arg(1) == "add" {
			// add a new game
			checkArgs(true, 3)
			if g := cfg.GetGame(game); g != nil {
				fmt.Fprintf(os.Stderr, "Game \"%s\" already exist\n", game)
				os.Exit(1)
			}
			p, err := filepath.Abs(flag.Arg(2))
			dieOnErr("Can't resolve path", err)
			gm, err := cfg.AddGame(game, p)
			dieOnErr("ERROR", err)
			gm.PrintHeader()
			gm.Print()
			save = true
		} else {
			g := cfg.GetGame(game)
			if g == nil {
				fmt.Fprintf(os.Stderr, "Game \"%s\" not found\n", game)
				os.Exit(1)
			}
			switch flag.Arg(1) {
			case "l", "list":
				// list game saves
				g.PrintWhole()
			case "i", "info":
				// print game paths etc.
				g.PrintInfo()
			case "kill":
				// remove game and all saves
				err := cfg.DelGame(game)
				dieOnErr("ERROR", err)
				fmt.Printf("Deleted game \"%s\" and all backed up saves\n", game)
				save = true
			case "b", "backup":
				// backup current save
				sv, err := g.Backup()
				dieOnErr("ERROR", err)
				g.Stamp = time.Now()
				if flag.NArg() > 2 {
					sv.Note = flag.Arg(2)
				}
				fmt.Printf("%3d ", len(g.Saves))
				sv.Print()
				save = true
			case "r", "restore":
				// restore selected save
				checkArgs(true, 3)
				if len(g.Saves) < 1 {
					fmt.Fprintf(os.Stderr, "Game \"%s\" has no saves backed up\n", g.Name)
					os.Exit(1)
				}
				i, err := strconv.Atoi(flag.Arg(2))
				dieOnErr(fmt.Sprintf("Malformed index \"%s\"", flag.Arg(2)), err)
				sv, err := g.Restore(i)
				dieOnErr("ERROR", err)
				g.Stamp = time.Now()
				fmt.Println("Restored save from", sv.Stamp.Format(timeFmt))
				if sv.Note != "" {
					fmt.Println("Save note:", sv.Note)
				}
				save = true
			case "del", "delete":
				// delete save(s)
				checkArgs(true, 3)
				if len(g.Saves) < 1 {
					fmt.Fprintf(os.Stderr, "Game \"%s\" has no saves backed up\n", g.Name)
					os.Exit(1)
				}
				var err error
				var f, t int
				f, err = strconv.Atoi(flag.Arg(2))
				t = f
				if err != nil {
					m := idRange.FindStringSubmatch(flag.Arg(2))
					f, err = strconv.Atoi(m[1])
					dieOnErr(fmt.Sprintf("Malformed index/range \"%s\"", flag.Arg(2)), err)
					t, err = strconv.Atoi(m[2])
					dieOnErr(fmt.Sprintf("Malformed index/range \"%s\"", flag.Arg(2)), err)
				}
				n, err := g.Delete(f, t)
				if err != nil {
					fmt.Fprintln(os.Stderr, "ERROR:", err)
				}
				fmt.Printf("Deleted %d save(s)\n", n)
				save = true
			default:
				flag.Usage()
				os.Exit(1)
			}
		}
	}

	if save {
		err := cfg.Save(flagConfig)
		dieOnErr("Can't save config", err)
	}
}

func dieOnErr(msg string, err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, msg+":", err)
		os.Exit(3)
	}
}

func checkArgs(exact bool, num int) {
	if (exact && flag.NArg() != num) ||
		(!exact && flag.NArg() < num) {
		flag.Usage()
		os.Exit(1)
	}
}
<|endoftext|>


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

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/naoina/toml"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path"
	"path/filepath"
)

const (
	optClobber = 1 << 0
	optForce   = 1 << 1
	optVerbose = 1 << 2
)

func parse(filename string) (*config, error) {
	bytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	conf := &config{}
	switch path.Ext(filename) {
	case "json":
		if err := json.Unmarshal(bytes, &conf); err != nil {
			return nil, err
		}
	case "toml":
		if err := toml.Unmarshal(bytes, &conf); err != nil {
			return nil, err
		}
	case "yaml":
		if err := yaml.Unmarshal(bytes, &conf); err != nil {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("Unsupported configuration file format")
	}

	return conf, nil
}

func fatalUsage() {
	_, executable := path.Split(os.Args[0])
	fmt.Printf("Usage: %s [options] config_file [source_dir]\n", executable)
	flag.PrintDefaults()
	os.Exit(1)
}

func absPath(path string) string {
	path, err := filepath.Abs(path)
	if err != nil {
		log.Fatal(err)
	}

	return path
}

func main() {
	currUsr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}

	taskName := flag.String("task", "default", "name of task to execute")
	action := flag.String("action", "install", "'install' or 'uninstall' symlinks")
	dstDir := flag.String("dest", currUsr.HomeDir, "target directory for symlinks")
	force := flag.Bool("force", true, "create parent directories to target")
	clobber := flag.Bool("clobber", false, "delete files and directories at target")
	verbose := flag.Bool("verbose", false, "verbose output")

	flag.Parse()

	flags := 0
	if *clobber {
		flags |= optClobber
	}
	if *force {
		flags |= optForce
	}
	if *verbose {
		flags |= optVerbose
	}

	if flag.NArg() == 0 {
		fatalUsage()
	}

	conf, err := parse(flag.Arg(0))
	if err != nil {
		log.Fatal(err)
	}

	switch *action {
	case "install":
		if flag.NArg() >= 2 {
			if err := conf.install(absPath(flag.Arg(1)), absPath(*dstDir), *taskName, flags); err != nil {
				log.Fatal(err)
			}
		} else {
			fatalUsage()
		}
	case "uninstall":
		if err := conf.uninstall(absPath(*dstDir), *taskName, flags); err != nil {
			log.Fatal(err)
		}
	default:
		fatalUsage()
	}
}
<commit_msg>Fixing extensions<commit_after>/*
 * Copyright (c) 2015 Alex Yatskov <alex@foosoft.net>
 * Author: Alex Yatskov <alex@foosoft.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/naoina/toml"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path"
	"path/filepath"
)

const (
	optClobber = 1 << 0
	optForce   = 1 << 1
	optVerbose = 1 << 2
)

func parse(filename string) (*config, error) {
	bytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	conf := &config{}
	switch path.Ext(filename) {
	case ".json":
		if err := json.Unmarshal(bytes, &conf); err != nil {
			return nil, err
		}
	case ".toml":
		if err := toml.Unmarshal(bytes, &conf); err != nil {
			return nil, err
		}
	case ".yaml":
		if err := yaml.Unmarshal(bytes, &conf); err != nil {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("Unsupported configuration file format")
	}

	return conf, nil
}

func fatalUsage() {
	_, executable := path.Split(os.Args[0])
	fmt.Printf("Usage: %s [options] config_file [source_dir]\n", executable)
	flag.PrintDefaults()
	os.Exit(1)
}

func absPath(path string) string {
	path, err := filepath.Abs(path)
	if err != nil {
		log.Fatal(err)
	}

	return path
}

func main() {
	currUsr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}

	taskName := flag.String("task", "default", "name of task to execute")
	action := flag.String("action", "install", "'install' or 'uninstall' symlinks")
	dstDir := flag.String("dest", currUsr.HomeDir, "target directory for symlinks")
	force := flag.Bool("force", true, "create parent directories to target")
	clobber := flag.Bool("clobber", false, "delete files and directories at target")
	verbose := flag.Bool("verbose", false, "verbose output")

	flag.Parse()

	flags := 0
	if *clobber {
		flags |= optClobber
	}
	if *force {
		flags |= optForce
	}
	if *verbose {
		flags |= optVerbose
	}

	if flag.NArg() == 0 {
		fatalUsage()
	}

	conf, err := parse(flag.Arg(0))
	if err != nil {
		log.Fatal(err)
	}

	switch *action {
	case "install":
		if flag.NArg() >= 2 {
			if err := conf.install(absPath(flag.Arg(1)), absPath(*dstDir), *taskName, flags); err != nil {
				log.Fatal(err)
			}
		} else {
			fatalUsage()
		}
	case "uninstall":
		if err := conf.uninstall(absPath(*dstDir), *taskName, flags); err != nil {
			log.Fatal(err)
		}
	default:
		fatalUsage()
	}
}
<|endoftext|>


<commit_before>// Eac2json parses Schwab's Employee Awards Center transaction
// history page from HTML into JSON. The output is a sequence of
// entries, each of which is a key-value bag. They are in the same
// order as they appear in the input document. Eac2json coalesces
// "more details" entries into its parent entry; their key-values
// appear together as one.
//
// In order to use eac2json, you must first save the HTML from the
// Schwab Employee Awards Center history page. Go to  the "My Equity
// Awards" tab, then to the "History & Statements" sub-tab. Set the
// date range to "All", and save the page to a file. Chrome doesn't
// seem to do very well; Safari works fine.
//
// NB! Eac2json is intended to assist in computing wash sales only.
// It ignores certain entries that are not relevant for these purposes.
package main

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"os"
	"strings"

	"golang.org/x/net/html"
)

type Node struct {
	*html.Node
	next *html.Node
	err  error

	stack *Node
}

func (n *Node) Sibling(tag string) {
	if n.err != nil {
		return
	}

	for c := n.next; c != nil; c = c.NextSibling {
		if c.Type == html.ElementNode && c.Data == tag {
			n.Node = c
			n.next = c.NextSibling
			return
		}
	}

	n.err = errors.New(fmt.Sprintf("no sibling tag %s", tag))
}

func (n *Node) Child(tag string) {
	if n.err != nil {
		return
	}

	n.Node = n.FirstChild
	n.next = n.Node
	n.Sibling(tag)
}

// Root out the next (child) text node without advancing the cursor.
func (n *Node) Text() string {
	if n.err != nil {
		return ""
	}

	for c := n.Node; c != nil; c = c.FirstChild {
		if c.Type == html.TextNode {
			return c.Data
		}
	}

	return ""
}

func (n *Node) Push() {
	m := new(Node)
	*m = *n
	n.stack = m
}

func (n *Node) Pop() {
	*n = *n.stack
}

func (n *Node) Error() error {
	return n.err
}

func (n *Node) Ok() bool {
	return n.err == nil
}

type Ledger struct {
	entries []map[string]string
	e       map[string]string
}

func (l *Ledger) Next() {
	if len(l.e) > 0 {
		l.entries = append(l.entries, l.e)
	}
	l.e = make(map[string]string)
}

func (l *Ledger) Write(k, v string) {
	l.e[k] = v
}

func findHistory(n *html.Node) *html.Node {
	if n.Type == html.ElementNode && n.Data == "a" {
		for _, a := range n.Attr {
			if a.Key == "name" && a.Val == "History" {
				return n
			}
		}
	}

	for c := n.FirstChild; c != nil; c = c.NextSibling {
		n := findHistory(c)
		if n != nil {
			return n
		}
	}

	return nil
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: eac2json eac.html\n")
	os.Exit(2)
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("eac2json: ")

	if len(os.Args) != 2 {
		usage()
	}

	file, err := os.Open(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}

	doc, err := html.Parse(file)
	if err != nil {
		log.Fatal(err)
	}

	// First find the transaction history table.
	root := findHistory(doc)
	if root == nil {
		log.Fatalf("no history")
	}

	// Now grub out the actual table body.
	n := &Node{root, root, nil, nil}
	n.Child("table")
	n.Child("tbody")
	n.Child("tr")
	n.Sibling("tr")
	n.Child("td")
	n.Child("table")
	n.Child("tbody")

	if !n.Ok() {
		log.Fatal(n.Error())
	}

	if n.Type != html.ElementNode || n.Data != "tbody" {
		log.Fatalf("bad table node %v type %d data %s", n, n.Type, n.Data)
	}

	n.Child("tr")

	// The first row is the header
	ok, header := row(n)
	if !ok {
		log.Fatal("Found no header")
	}

	action := -1
	for i := range header {
		if header[i] == "Action" {
			action = i
		}
	}
	if action < 0 {
		log.Fatal("\"Action\" header not found")
	}

	l := &Ledger{}

	for n.Sibling("tr"); n.Ok(); n.Sibling("tr") {
		// First try to extract a regular data row.
		ok, values := row(n)
		if !ok {
			log.Fatal("Unrecognized row")
		}

		// Schwab's transaction history generates duplicate entries
		// for many actions, so it's important to be careful here
		// to account for each action only once. Further, we only
		// extract acquisitions (purchases) and sales (e.g., which may
		// be done automatically for tax purposes).
		//
		// - "Lapse" entries are summary entries; they are covered by
		// "Deposit" and "Forced Quick Sell" entries.
		switch values[action] {
		case "Deposit", "Forced Quick Sell", "Exer and Hold":
			// - Deposits: deposits RSUs into your EAC account;
			// - Forced Quick Sell: immediate sale of RSUs for taxes;
			// - Exer and Hold: exercise and hold.

			// Incorporate data into the row.
			l.Next()

			for i := range header {
				l.Write(header[i], values[i])
			}

			n.Sibling("tr")
			if err := more(n, l); err != nil {
				log.Fatal(err)
			}

		case "Journal":
			// The next row holds more details, but it's not useful to us.
			n.Sibling("tr")

		case "Lapse":
			// These are summary entries for RSU lapses.
			// They are covered by deposits & forced quick sells.
			n.Sibling("tr")

		case "Sale":
			// NB!! We ignore options and ESPP sales as these should never be
			// at a loss, and thus unimportant to computing wash sales.
			// (This is unlike Forced Quick Sells, which may be at a loss.)
			n.Sibling("tr")

		case "Forced Disbursement":
			// Not relevant.

		default:
			log.Fatalf(fmt.Sprintf("unknown row type \"%s\"", action))
		}
	}

	w := bufio.NewWriter(os.Stdout)
	enc := json.NewEncoder(w)
	if err := enc.Encode(l.entries); err != nil {
		log.Fatal(err)
	}
	if err := w.Flush(); err != nil {
		log.Fatal(err)
	}
}

// Extract a regular data row.
func row(n *Node) (bool, []string) {
	n.Push()
	defer n.Pop()

	var values []string
	for n.Child("td"); n.Ok(); n.Sibling("td") {
		n.Push()
		n.Child("label")
		val := strings.TrimSpace(n.Text())
		if !n.Ok() {
			n.Pop()
			return false, nil
		}

		n.Pop()
		values = append(values, val)
	}

	return true, values
}

// Extract a standard "more details" row.
func more(n *Node, l *Ledger) error {
	n.Push()
	defer n.Pop()

	n.Child("td")
	n.Child("div")
	n.Child("div")
	n.Child("table")
	n.Sibling("table")
	n.Child("tbody")
	if n.Error() != nil {
		return n.Error()
	}

	// First row is headers.
	n.Child("tr")

	var headers []string

	n.Push()
	for n.Child("td"); n.Ok(); n.Sibling("td") {
		n.Push()
		n.Child("b")
		headers = append(headers, strings.TrimSpace(n.Text()))
		n.Pop()
	}
	n.Pop()

	i := 0
	n.Sibling("tr")
	for n.Child("td"); n.Ok(); n.Sibling("td") {
		l.Write(headers[i], strings.TrimSpace(n.Text()))
		i++
	}

	return nil
}
<commit_msg>idiom<commit_after>// Eac2json parses Schwab's Employee Awards Center transaction
// history page from HTML into JSON. The output is a sequence of
// entries, each of which is a key-value bag. They are in the same
// order as they appear in the input document. Eac2json coalesces
// "more details" entries into its parent entry; their key-values
// appear together as one.
//
// In order to use eac2json, you must first save the HTML from the
// Schwab Employee Awards Center history page. Go to  the "My Equity
// Awards" tab, then to the "History & Statements" sub-tab. Set the
// date range to "All", and save the page to a file. Chrome doesn't
// seem to do very well; Safari works fine.
//
// NB! Eac2json is intended to assist in computing wash sales only.
// It ignores certain entries that are not relevant for these purposes.
package main

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"os"
	"strings"

	"golang.org/x/net/html"
)

type Node struct {
	*html.Node
	next *html.Node
	err  error

	stack *Node
}

func (n *Node) Sibling(tag string) {
	if n.err != nil {
		return
	}

	for c := n.next; c != nil; c = c.NextSibling {
		if c.Type == html.ElementNode && c.Data == tag {
			n.Node = c
			n.next = c.NextSibling
			return
		}
	}

	n.err = errors.New(fmt.Sprintf("no sibling tag %s", tag))
}

func (n *Node) Child(tag string) {
	if n.err != nil {
		return
	}

	n.Node = n.FirstChild
	n.next = n.Node
	n.Sibling(tag)
}

// Root out the next (child) text node without advancing the cursor.
func (n *Node) Text() string {
	if n.err != nil {
		return ""
	}

	for c := n.Node; c != nil; c = c.FirstChild {
		if c.Type == html.TextNode {
			return c.Data
		}
	}

	return ""
}

func (n *Node) Push() {
	m := new(Node)
	*m = *n
	n.stack = m
}

func (n *Node) Pop() {
	*n = *n.stack
}

func (n *Node) Error() error {
	return n.err
}

func (n *Node) Ok() bool {
	return n.err == nil
}

type Ledger struct {
	entries []map[string]string
	e       map[string]string
}

func (l *Ledger) Next() {
	if len(l.e) > 0 {
		l.entries = append(l.entries, l.e)
	}
	l.e = make(map[string]string)
}

func (l *Ledger) Write(k, v string) {
	l.e[k] = v
}

func findHistory(n *html.Node) *html.Node {
	if n.Type == html.ElementNode && n.Data == "a" {
		for _, a := range n.Attr {
			if a.Key == "name" && a.Val == "History" {
				return n
			}
		}
	}

	for c := n.FirstChild; c != nil; c = c.NextSibling {
		n := findHistory(c)
		if n != nil {
			return n
		}
	}

	return nil
}

// Extract a regular data row.
func row(n *Node) (bool, []string) {
	n.Push()
	defer n.Pop()

	var values []string
	for n.Child("td"); n.Ok(); n.Sibling("td") {
		n.Push()
		n.Child("label")
		val := strings.TrimSpace(n.Text())
		if !n.Ok() {
			n.Pop()
			return false, nil
		}

		n.Pop()
		values = append(values, val)
	}

	return true, values
}

// Extract a standard "more details" row.
func more(n *Node, l *Ledger) error {
	n.Push()
	defer n.Pop()

	n.Child("td")
	n.Child("div")
	n.Child("div")
	n.Child("table")
	n.Sibling("table")
	n.Child("tbody")
	if n.Error() != nil {
		return n.Error()
	}

	// First row is headers.
	n.Child("tr")

	var headers []string

	n.Push()
	for n.Child("td"); n.Ok(); n.Sibling("td") {
		n.Push()
		n.Child("b")
		headers = append(headers, strings.TrimSpace(n.Text()))
		n.Pop()
	}
	n.Pop()

	i := 0
	n.Sibling("tr")
	for n.Child("td"); n.Ok(); n.Sibling("td") {
		l.Write(headers[i], strings.TrimSpace(n.Text()))
		i++
	}

	return nil
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: eac2json eac.html\n")
	os.Exit(2)
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("eac2json: ")

	if len(os.Args) != 2 {
		usage()
	}

	file, err := os.Open(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}

	doc, err := html.Parse(file)
	if err != nil {
		log.Fatal(err)
	}

	// First find the transaction history table.
	root := findHistory(doc)
	if root == nil {
		log.Fatalf("no history")
	}

	// Now grub out the actual table body.
	n := &Node{root, root, nil, nil}
	n.Child("table")
	n.Child("tbody")
	n.Child("tr")
	n.Sibling("tr")
	n.Child("td")
	n.Child("table")
	n.Child("tbody")

	if !n.Ok() {
		log.Fatal(n.Error())
	}

	if n.Type != html.ElementNode || n.Data != "tbody" {
		log.Fatalf("bad table node %v type %d data %s", n, n.Type, n.Data)
	}

	n.Child("tr")

	// The first row is the header
	ok, header := row(n)
	if !ok {
		log.Fatal("Found no header")
	}

	action := -1
	for i := range header {
		if header[i] == "Action" {
			action = i
		}
	}
	if action < 0 {
		log.Fatal("\"Action\" header not found")
	}

	l := &Ledger{}

	for n.Sibling("tr"); n.Ok(); n.Sibling("tr") {
		// First try to extract a regular data row.
		ok, values := row(n)
		if !ok {
			log.Fatal("Unrecognized row")
		}

		// Schwab's transaction history generates duplicate entries
		// for many actions, so it's important to be careful here
		// to account for each action only once. Further, we only
		// extract acquisitions (purchases) and sales (e.g., which may
		// be done automatically for tax purposes).
		//
		// - "Lapse" entries are summary entries; they are covered by
		// "Deposit" and "Forced Quick Sell" entries.
		switch values[action] {
		case "Deposit", "Forced Quick Sell", "Exer and Hold":
			// - Deposits: deposits RSUs into your EAC account;
			// - Forced Quick Sell: immediate sale of RSUs for taxes;
			// - Exer and Hold: exercise and hold.

			// Incorporate data into the row.
			l.Next()

			for i := range header {
				l.Write(header[i], values[i])
			}

			n.Sibling("tr")
			if err := more(n, l); err != nil {
				log.Fatal(err)
			}

		case "Journal":
			// The next row holds more details, but it's not useful to us.
			n.Sibling("tr")

		case "Lapse":
			// These are summary entries for RSU lapses.
			// They are covered by deposits & forced quick sells.
			n.Sibling("tr")

		case "Sale":
			// NB!! We ignore options and ESPP sales as these should never be
			// at a loss, and thus unimportant to computing wash sales.
			// (This is unlike Forced Quick Sells, which may be at a loss.)
			n.Sibling("tr")

		case "Forced Disbursement":
			// Not relevant.

		default:
			log.Fatalf(fmt.Sprintf("unknown row type \"%s\"", action))
		}
	}

	w := bufio.NewWriter(os.Stdout)
	defer w.Flush()
	enc := json.NewEncoder(w)
	if err := enc.Encode(l.entries); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Package gorequest inspired by Nodejs SuperAgent provides easy-way to write http client
package gorequest

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

type Request *http.Request
type Response *http.Response

// A SuperAgent is a object storing all request data for client.
type SuperAgent struct {
	Url        string
	Method     string
	Header     map[string]string
	TargetType string
	ForceType  string
	Data       map[string]interface{}
	FormData   url.Values
	QueryData  url.Values
	Client     *http.Client
	Transport  *http.Transport
	Errors     []error
}

// Used to create a new SuperAgent object.
func New() *SuperAgent {
	s := &SuperAgent{
		TargetType: "json",
		Data:       make(map[string]interface{}),
		Header:     make(map[string]string),
		FormData:   url.Values{},
		QueryData:  url.Values{},
		Client:     &http.Client{},
		Transport:  &http.Transport{},
		Errors:     nil,
	}
	return s
}

// Clear SuperAgent data for another new request.
func (s *SuperAgent) ClearSuperAgent() {
	s.Url = ""
	s.Method = ""
	s.Header = make(map[string]string)
	s.Data = make(map[string]interface{})
	s.FormData = url.Values{}
	s.QueryData = url.Values{}
	s.ForceType = ""
	s.TargetType = "json"
	s.Errors = nil
}

func (s *SuperAgent) Get(targetUrl string) *SuperAgent {
	s.ClearSuperAgent()
	s.Method = "GET"
	s.Url = targetUrl
	s.Errors = nil
	return s
}

func (s *SuperAgent) Post(targetUrl string) *SuperAgent {
	s.ClearSuperAgent()
	s.Method = "POST"
	s.Url = targetUrl
	s.Errors = nil
	return s
}

// TODO: testing for Head func
func (s *SuperAgent) Head(targetUrl string) *SuperAgent {
	s.ClearSuperAgent()
	s.Method = "HEAD"
	s.Url = targetUrl
	s.Errors = nil
	return s
}

// Set is used for setting header fields.
// Example. To set `Accept` as `application/json`
//
//    gorequest.New().
//      Post("/gamelist").
//      Set("Accept", "application/json").
//      End()
// TODO: make Set be able to get multiple fields
func (s *SuperAgent) Set(param string, value string) *SuperAgent {
	s.Header[param] = value
	return s
}

var Types = map[string]string{
	"html":       "text/html",
	"json":       "application/json",
	"xml":        "application/xml",
	"urlencoded": "application/x-www-form-urlencoded",
	"form":       "application/x-www-form-urlencoded",
	"form-data":  "application/x-www-form-urlencoded",
}

// Type is a convenience function to specify the data type to send.
// For example, to send data as `application/x-www-form-urlencoded` :
//
//    gorequest.New().
//      Post("/recipe").
//      Type("form").
//      Send(`{ name: "egg benedict", category: "brunch" }`).
//      End()
//
// This will POST the body "name=egg benedict&category=brunch" to url /recipe
//
// GoRequest supports
//
//    "text/html" uses "html"
//    "application/json" uses "json"
//    "application/xml" uses "xml"
//    "application/x-www-form-urlencoded" uses "urlencoded", "form" or "form-data"
//
func (s *SuperAgent) Type(typeStr string) *SuperAgent {
	if _, ok := Types[typeStr]; ok {
		s.ForceType = typeStr
	} else {
		s.Errors = append(s.Errors, errors.New("Type func: incorrect type \""+typeStr+"\""))
	}
	return s
}

// Query function accepts either json string or strings which will form a query-string in url of GET method or body of POST method.
// For example, making "/search?query=bicycle&size=50x50&weight=20kg" using GET method:
//
//      gorequest.New().
//        Get("/search").
//        Query(`{ query: 'bicycle' }`).
//        Query(`{ size: '50x50' }`).
//        Query(`{ weight: '20kg' }`).
//        End()
//
// Or you can put multiple json values:
//
//      gorequest.New().
//        Get("/search").
//        Query(`{ query: 'bicycle', size: '50x50', weight: '20kg' }`).
//        End()
//
// Strings are also acceptable:
//
//      gorequest.New().
//        Get("/search").
//        Query("query=bicycle&size=50x50").
//        Query("weight=20kg").
//        End()
//
// Or even Mixed! :)
//
//      gorequest.New().
//        Get("/search").
//        Query("query=bicycle").
//        Query(`{ size: '50x50', weight:'20kg' }`).
//        End()
//
// TODO: check error
func (s *SuperAgent) Query(content string) *SuperAgent {
	var val map[string]string
	if err := json.Unmarshal([]byte(content), &val); err == nil {
		for k, v := range val {
			s.QueryData.Add(k, v)
		}
	} else {
		if queryVal, err := url.ParseQuery(content); err == nil {
			for k, _ := range queryVal {
				s.QueryData.Add(k, queryVal.Get(k))
			}
		} else {
			s.Errors = append(s.Errors, err)
		}
		// TODO: need to check correct format of 'field=val&field=val&...'
	}
	return s
}

// TODO-1: Add docs for Timeout
// TODO-2: a test for Timeout, check time and slow server
func (s *SuperAgent) Timeout(timeout time.Duration) *SuperAgent {
	s.Transport.Dial = func(network, addr string) (net.Conn, error) {
		return net.DialTimeout(network, addr, timeout)
	}
	return s
}

// Proxy function accepts a proxy url string to setup proxy url for any request.
// It provides a convenience way to setup proxy which have advantages over usual old ways.
// One example is you might try to set `http_proxy` environment. This means you are setting proxy up for all the requests.
// You will not be able to send different request with different proxy unless you change your `http_proxy` environment again.
// Another example is using Golang proxy setting. This is normal prefer way to do but too verbase compared to GoRequest's Proxy:
//
//      gorequest.New().Proxy("http://myproxy:9999").
//        Post("http://www.google.com").
//        End()
//
// To set no_proxy, just put empty string to Proxy func:
//
//      gorequest.New().Proxy("").
//        Post("http://www.google.com").
//        End()
//
func (s *SuperAgent) Proxy(proxyUrl string) *SuperAgent {
	parsedProxyUrl, err := url.Parse(proxyUrl)
	if err != nil {
		s.Errors = append(s.Errors, err)
	} else if proxyUrl == "" {
		s.Transport.Proxy = nil
	} else {
		s.Transport.Proxy = http.ProxyURL(parsedProxyUrl)
	}
	return s
}

func (s *SuperAgent) RedirectPolicy(policy func(req Request, via []Request) error) *SuperAgent {
	s.Client.CheckRedirect = func(r *http.Request, v []*http.Request) error {
		vv := make([]Request, len(v))
		for i, r := range v {
			vv[i] = Request(r)
		}
		return policy(Request(r), vv)
	}
	return s
}

// Send function accepts either json string or query strings which is usually used to assign data to POST or PUT method.
// Without specifying any type, if you give Send with json data, you are doing requesting in json format:
//
//      gorequest.New().
//        Post("/search").
//        Send(`{ query: 'sushi' }`).
//        End()
//
// While if you use at least one of querystring, GoRequest understands and automatically set the Content-Type to `application/x-www-form-urlencoded`
//
//      gorequest.New().
//        Post("/search").
//        Send("query=tonkatsu").
//        End()
//
// So, if you want to strictly send json format, you need to use Type func to set it as `json` (Please see more details in Type function).
// You can also do multiple chain of Send:
//
//      gorequest.New().
//        Post("/search").
//        Send("query=bicycle&size=50x50").
//        Send(`{ wheel: '4'}`).
//        End()
//
// TODO: check error
func (s *SuperAgent) Send(content string) *SuperAgent {
	var val map[string]interface{}
	// check if it is json format
	if err := json.Unmarshal([]byte(content), &val); err == nil {
		for k, v := range val {
			s.Data[k] = v
		}
	} else {
		formVal, _ := url.ParseQuery(content)
		for k, _ := range formVal {
			// make it array if already have key
			if val, ok := s.Data[k]; ok {
				var strArray []string
				strArray = append(strArray, formVal.Get(k))
				// check if previous data is one string or array
				switch oldValue := val.(type) {
				case []string:
					strArray = append(strArray, oldValue...)
				case string:
					strArray = append(strArray, oldValue)
				}
				s.Data[k] = strArray
			} else {
				// make it just string if does not already have same key
				s.Data[k] = formVal.Get(k)
			}
		}
		s.TargetType = "form"
	}
	return s
}

func changeMapToURLValues(data map[string]interface{}) url.Values {
	var newUrlValues = url.Values{}
	for k, v := range data {
		switch val := v.(type) {
		case string:
			newUrlValues.Add(k, string(val))
		case []string:
			for _, element := range val {
				newUrlValues.Add(k, element)
			}
		}
	}
	return newUrlValues
}

// End is the most important function that you need to call when ending the chain. The request won't proceed without calling it.
// End function returns Response which matchs the structure of Response type in Golang's http package (but without Body data). The body data itself returns as a string in a 2nd return value.
// Lastly but worht noticing, error array (NOTE: not just single error value) is returned as a 3rd value and nil otherwise.
//
// For example:
//
//    resp, body, errs := gorequest.New().Get("http://www.google.com").End()
//    if( errs != nil){
//      fmt.Pritnln(errs)
//    }
//    fmt.Println(resp, body)
//
// Moreover, End function also supports callback which you can put as a parameter.
// This extends the flexibility and makes GoRequest fun and clean! You can use GoRequest in whatever style you love!
//
// For example:
//
//    func printBody(resp gorequest.Response, body string, errs []error){
//      fmt.Println(resp.Status)
//    }
//    gorequest.New().Get("http://www..google.com").End(printBody)
//
func (s *SuperAgent) End(callback ...func(response Response, body string, errs []error)) (Response, string, []error) {
	var (
		req  *http.Request
		err  error
		resp Response
	)
	// check whether there is an error. if yes, return all errors
	if len(s.Errors) != 0 {
		return nil, "", s.Errors
	}
	// check if there is forced type
	if s.ForceType == "json" {
		s.TargetType = "json"
	} else if s.ForceType == "form" {
		s.TargetType = "form"
	}
	if s.Method == "POST" {
		if s.TargetType == "json" {
			contentJson, _ := json.Marshal(s.Data)
			contentReader := bytes.NewReader(contentJson)
			req, err = http.NewRequest(s.Method, s.Url, contentReader)
			req.Header.Set("Content-Type", "application/json")
		} else if s.TargetType == "form" {
			formData := changeMapToURLValues(s.Data)
			req, err = http.NewRequest(s.Method, s.Url, strings.NewReader(formData.Encode()))
			req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		}
	} else if s.Method == "GET" {
		req, err = http.NewRequest(s.Method, s.Url, nil)
	} else if s.Method == "HEAD" {
		req, err = http.NewRequest(s.Method, s.Url, nil)
	}
	for k, v := range s.Header {
		req.Header.Set(k, v)
	}
	// Add all querystring from Query func
	q := req.URL.Query()
	for k, v := range s.QueryData {
		for _, vv := range v {
			q.Add(k, vv)
		}
	}
	req.URL.RawQuery = q.Encode()

	// Set Transport
	s.Client.Transport = s.Transport
	// Send request
	resp, err = s.Client.Do(req)
	if err != nil {
		s.Errors = append(s.Errors, err)
		return nil, "", s.Errors
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	bodyCallback := body
	// deep copy response to give it to both return and callback func
	respCallback := *resp
	if len(callback) != 0 {
		callback[0](&respCallback, string(bodyCallback), s.Errors)
	}
	return resp, string(body), nil
}
<commit_msg>Fix #3 Added 'PUT' method as a function 'Put'<commit_after>// Package gorequest inspired by Nodejs SuperAgent provides easy-way to write http client
package gorequest

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

type Request *http.Request
type Response *http.Response

// A SuperAgent is a object storing all request data for client.
type SuperAgent struct {
	Url        string
	Method     string
	Header     map[string]string
	TargetType string
	ForceType  string
	Data       map[string]interface{}
	FormData   url.Values
	QueryData  url.Values
	Client     *http.Client
	Transport  *http.Transport
	Errors     []error
}

// Used to create a new SuperAgent object.
func New() *SuperAgent {
	s := &SuperAgent{
		TargetType: "json",
		Data:       make(map[string]interface{}),
		Header:     make(map[string]string),
		FormData:   url.Values{},
		QueryData:  url.Values{},
		Client:     &http.Client{},
		Transport:  &http.Transport{},
		Errors:     nil,
	}
	return s
}

// Clear SuperAgent data for another new request.
func (s *SuperAgent) ClearSuperAgent() {
	s.Url = ""
	s.Method = ""
	s.Header = make(map[string]string)
	s.Data = make(map[string]interface{})
	s.FormData = url.Values{}
	s.QueryData = url.Values{}
	s.ForceType = ""
	s.TargetType = "json"
	s.Errors = nil
}

func (s *SuperAgent) Get(targetUrl string) *SuperAgent {
	s.ClearSuperAgent()
	s.Method = "GET"
	s.Url = targetUrl
	s.Errors = nil
	return s
}

// TODO: add test for Post
func (s *SuperAgent) Post(targetUrl string) *SuperAgent {
	s.ClearSuperAgent()
	s.Method = "POST"
	s.Url = targetUrl
	s.Errors = nil
	return s
}

// TODO: testing for Head func
func (s *SuperAgent) Head(targetUrl string) *SuperAgent {
	s.ClearSuperAgent()
	s.Method = "HEAD"
	s.Url = targetUrl
	s.Errors = nil
	return s
}

func (s *SuperAgent) Put(targetUrl string) *SuperAgent {
	s.ClearSuperAgent()
	s.Method = "PUT"
	s.Url = targetUrl
	s.Errors = nil
	return s
}

// Set is used for setting header fields.
// Example. To set `Accept` as `application/json`
//
//    gorequest.New().
//      Post("/gamelist").
//      Set("Accept", "application/json").
//      End()
// TODO: make Set be able to get multiple fields
func (s *SuperAgent) Set(param string, value string) *SuperAgent {
	s.Header[param] = value
	return s
}

var Types = map[string]string{
	"html":       "text/html",
	"json":       "application/json",
	"xml":        "application/xml",
	"urlencoded": "application/x-www-form-urlencoded",
	"form":       "application/x-www-form-urlencoded",
	"form-data":  "application/x-www-form-urlencoded",
}

// Type is a convenience function to specify the data type to send.
// For example, to send data as `application/x-www-form-urlencoded` :
//
//    gorequest.New().
//      Post("/recipe").
//      Type("form").
//      Send(`{ name: "egg benedict", category: "brunch" }`).
//      End()
//
// This will POST the body "name=egg benedict&category=brunch" to url /recipe
//
// GoRequest supports
//
//    "text/html" uses "html"
//    "application/json" uses "json"
//    "application/xml" uses "xml"
//    "application/x-www-form-urlencoded" uses "urlencoded", "form" or "form-data"
//
func (s *SuperAgent) Type(typeStr string) *SuperAgent {
	if _, ok := Types[typeStr]; ok {
		s.ForceType = typeStr
	} else {
		s.Errors = append(s.Errors, errors.New("Type func: incorrect type \""+typeStr+"\""))
	}
	return s
}

// Query function accepts either json string or strings which will form a query-string in url of GET method or body of POST method.
// For example, making "/search?query=bicycle&size=50x50&weight=20kg" using GET method:
//
//      gorequest.New().
//        Get("/search").
//        Query(`{ query: 'bicycle' }`).
//        Query(`{ size: '50x50' }`).
//        Query(`{ weight: '20kg' }`).
//        End()
//
// Or you can put multiple json values:
//
//      gorequest.New().
//        Get("/search").
//        Query(`{ query: 'bicycle', size: '50x50', weight: '20kg' }`).
//        End()
//
// Strings are also acceptable:
//
//      gorequest.New().
//        Get("/search").
//        Query("query=bicycle&size=50x50").
//        Query("weight=20kg").
//        End()
//
// Or even Mixed! :)
//
//      gorequest.New().
//        Get("/search").
//        Query("query=bicycle").
//        Query(`{ size: '50x50', weight:'20kg' }`).
//        End()
//
// TODO: check error
func (s *SuperAgent) Query(content string) *SuperAgent {
	var val map[string]string
	if err := json.Unmarshal([]byte(content), &val); err == nil {
		for k, v := range val {
			s.QueryData.Add(k, v)
		}
	} else {
		if queryVal, err := url.ParseQuery(content); err == nil {
			for k, _ := range queryVal {
				s.QueryData.Add(k, queryVal.Get(k))
			}
		} else {
			s.Errors = append(s.Errors, err)
		}
		// TODO: need to check correct format of 'field=val&field=val&...'
	}
	return s
}

// TODO-1: Add docs for Timeout
// TODO-2: a test for Timeout, check time and slow server
func (s *SuperAgent) Timeout(timeout time.Duration) *SuperAgent {
	s.Transport.Dial = func(network, addr string) (net.Conn, error) {
		return net.DialTimeout(network, addr, timeout)
	}
	return s
}

// Proxy function accepts a proxy url string to setup proxy url for any request.
// It provides a convenience way to setup proxy which have advantages over usual old ways.
// One example is you might try to set `http_proxy` environment. This means you are setting proxy up for all the requests.
// You will not be able to send different request with different proxy unless you change your `http_proxy` environment again.
// Another example is using Golang proxy setting. This is normal prefer way to do but too verbase compared to GoRequest's Proxy:
//
//      gorequest.New().Proxy("http://myproxy:9999").
//        Post("http://www.google.com").
//        End()
//
// To set no_proxy, just put empty string to Proxy func:
//
//      gorequest.New().Proxy("").
//        Post("http://www.google.com").
//        End()
//
func (s *SuperAgent) Proxy(proxyUrl string) *SuperAgent {
	parsedProxyUrl, err := url.Parse(proxyUrl)
	if err != nil {
		s.Errors = append(s.Errors, err)
	} else if proxyUrl == "" {
		s.Transport.Proxy = nil
	} else {
		s.Transport.Proxy = http.ProxyURL(parsedProxyUrl)
	}
	return s
}

func (s *SuperAgent) RedirectPolicy(policy func(req Request, via []Request) error) *SuperAgent {
	s.Client.CheckRedirect = func(r *http.Request, v []*http.Request) error {
		vv := make([]Request, len(v))
		for i, r := range v {
			vv[i] = Request(r)
		}
		return policy(Request(r), vv)
	}
	return s
}

// Send function accepts either json string or query strings which is usually used to assign data to POST or PUT method.
// Without specifying any type, if you give Send with json data, you are doing requesting in json format:
//
//      gorequest.New().
//        Post("/search").
//        Send(`{ query: 'sushi' }`).
//        End()
//
// While if you use at least one of querystring, GoRequest understands and automatically set the Content-Type to `application/x-www-form-urlencoded`
//
//      gorequest.New().
//        Post("/search").
//        Send("query=tonkatsu").
//        End()
//
// So, if you want to strictly send json format, you need to use Type func to set it as `json` (Please see more details in Type function).
// You can also do multiple chain of Send:
//
//      gorequest.New().
//        Post("/search").
//        Send("query=bicycle&size=50x50").
//        Send(`{ wheel: '4'}`).
//        End()
//
// TODO: check error
func (s *SuperAgent) Send(content string) *SuperAgent {
	var val map[string]interface{}
	// check if it is json format
	if err := json.Unmarshal([]byte(content), &val); err == nil {
		for k, v := range val {
			s.Data[k] = v
		}
	} else {
		formVal, _ := url.ParseQuery(content)
		for k, _ := range formVal {
			// make it array if already have key
			if val, ok := s.Data[k]; ok {
				var strArray []string
				strArray = append(strArray, formVal.Get(k))
				// check if previous data is one string or array
				switch oldValue := val.(type) {
				case []string:
					strArray = append(strArray, oldValue...)
				case string:
					strArray = append(strArray, oldValue)
				}
				s.Data[k] = strArray
			} else {
				// make it just string if does not already have same key
				s.Data[k] = formVal.Get(k)
			}
		}
		s.TargetType = "form"
	}
	return s
}

func changeMapToURLValues(data map[string]interface{}) url.Values {
	var newUrlValues = url.Values{}
	for k, v := range data {
		switch val := v.(type) {
		case string:
			newUrlValues.Add(k, string(val))
		case []string:
			for _, element := range val {
				newUrlValues.Add(k, element)
			}
		}
	}
	return newUrlValues
}

// End is the most important function that you need to call when ending the chain. The request won't proceed without calling it.
// End function returns Response which matchs the structure of Response type in Golang's http package (but without Body data). The body data itself returns as a string in a 2nd return value.
// Lastly but worht noticing, error array (NOTE: not just single error value) is returned as a 3rd value and nil otherwise.
//
// For example:
//
//    resp, body, errs := gorequest.New().Get("http://www.google.com").End()
//    if( errs != nil){
//      fmt.Pritnln(errs)
//    }
//    fmt.Println(resp, body)
//
// Moreover, End function also supports callback which you can put as a parameter.
// This extends the flexibility and makes GoRequest fun and clean! You can use GoRequest in whatever style you love!
//
// For example:
//
//    func printBody(resp gorequest.Response, body string, errs []error){
//      fmt.Println(resp.Status)
//    }
//    gorequest.New().Get("http://www..google.com").End(printBody)
//
func (s *SuperAgent) End(callback ...func(response Response, body string, errs []error)) (Response, string, []error) {
	var (
		req  *http.Request
		err  error
		resp Response
	)
	// check whether there is an error. if yes, return all errors
	if len(s.Errors) != 0 {
		return nil, "", s.Errors
	}
	// check if there is forced type
	if s.ForceType == "json" {
		s.TargetType = "json"
	} else if s.ForceType == "form" {
		s.TargetType = "form"
	}
	if s.Method == "POST" || s.Method == "PUT" {
		if s.TargetType == "json" {
			contentJson, _ := json.Marshal(s.Data)
			contentReader := bytes.NewReader(contentJson)
			req, err = http.NewRequest(s.Method, s.Url, contentReader)
			req.Header.Set("Content-Type", "application/json")
		} else if s.TargetType == "form" {
			formData := changeMapToURLValues(s.Data)
			req, err = http.NewRequest(s.Method, s.Url, strings.NewReader(formData.Encode()))
			req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		}
	} else if s.Method == "GET" {
		req, err = http.NewRequest(s.Method, s.Url, nil)
	} else if s.Method == "HEAD" {
		req, err = http.NewRequest(s.Method, s.Url, nil)
	}
	for k, v := range s.Header {
		req.Header.Set(k, v)
	}
	// Add all querystring from Query func
	q := req.URL.Query()
	for k, v := range s.QueryData {
		for _, vv := range v {
			q.Add(k, vv)
		}
	}
	req.URL.RawQuery = q.Encode()

	// Set Transport
	s.Client.Transport = s.Transport
	// Send request
	resp, err = s.Client.Do(req)
	if err != nil {
		s.Errors = append(s.Errors, err)
		return nil, "", s.Errors
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	bodyCallback := body
	// deep copy response to give it to both return and callback func
	respCallback := *resp
	if len(callback) != 0 {
		callback[0](&respCallback, string(bodyCallback), s.Errors)
	}
	return resp, string(body), nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

var (
	version = "0.2.1"

	flagset     = flag.NewFlagSet("msub", flag.ContinueOnError)
	useBoundary = flagset.Bool("boundary", false, "")
	isHelp      = flagset.Bool("help", false, "")
	isVersion   = flagset.Bool("version", false, "")
)

func init() {
	flagset.SetOutput(ioutil.Discard)
	flagset.BoolVar(useBoundary, "b", false, "")
	flagset.BoolVar(isHelp, "h", false, "")
	flagset.BoolVar(isVersion, "v", false, "")
}

func usage() {
	os.Stderr.WriteString(`
Usage: msub [OPTION]... FROM TO [FILE]...
Substitute multiple words at once
by FROM and TO patterns.

Options:
  -b, --boundary    use word boundary in matcher
  -h, --help        show this help message
  -v, --version     output version information and exit

Syntax:
  pattern = group {"/" group}
  group   = branch {"," branch}
  branch  = {letter | "\/" | "\,"}

Examples:
  msub true,false false,true ./file
  msub dog,cat/s cat,dog/s ~/Document/questionnaire
`[1:])
}

func printVersion() {
	fmt.Fprintln(os.Stderr, version)
}

func printError(err interface{}) {
	fmt.Fprintln(os.Stderr, "msub:", err)
}

func do(r *Replacer, src io.Reader) error {
	b := bufio.NewScanner(src)
	for b.Scan() {
		fmt.Println(r.ReplaceAll(b.Text()))
	}
	return b.Err()
}

func _main() int {
	if err := flagset.Parse(os.Args[1:]); err != nil {
		printError(err)
		return 2
	}
	switch {
	case *isHelp:
		usage()
		return 0
	case *isVersion:
		printVersion()
		return 0
	}

	switch flagset.NArg() {
	case 0:
		printError("no specify FROM and TO")
		return 2
	case 1:
		printError("no specify TO")
		return 2
	}
	from, to := flagset.Arg(0), flagset.Arg(1)

	r, err := NewReplacer(from, to, *useBoundary)
	if err != nil {
		printError(err)
		return 2
	}

	if flagset.NArg() < 3 {
		if err = do(r, os.Stdin); err != nil {
			printError(err)
			return 1
		}
		return 0
	}

	var srcls []io.Reader
	for _, name := range flagset.Args()[2:] {
		f, err := os.Open(name)
		if err != nil {
			printError(err)
			return 1
		}
		defer f.Close()
		srcls = append(srcls, f)
	}
	if err = do(r, io.MultiReader(srcls...)); err != nil {
		printError(err)
		return 1
	}
	return 0
}

func main() {
	e := _main()
	os.Exit(e)
}
<commit_msg>Extract name<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

var (
	name    = "msub"
	version = "0.2.1"

	flagset     = flag.NewFlagSet(name, flag.ContinueOnError)
	useBoundary = flagset.Bool("boundary", false, "")
	isHelp      = flagset.Bool("help", false, "")
	isVersion   = flagset.Bool("version", false, "")
)

func init() {
	flagset.SetOutput(ioutil.Discard)
	flagset.BoolVar(useBoundary, "b", false, "")
	flagset.BoolVar(isHelp, "h", false, "")
	flagset.BoolVar(isVersion, "v", false, "")
}

func usage() {
	fmt.Fprintf(os.Stderr, `
Usage: %[1]s [OPTION]... FROM TO [FILE]...
Substitute multiple words at once
by FROM and TO patterns.

Options:
  -b, --boundary    use word boundary in matcher
  -h, --help        show this help message
  -v, --version     output version information and exit

Syntax:
  pattern = group {"/" group}
  group   = branch {"," branch}
  branch  = {letter | "\/" | "\,"}

Examples:
  %[1]s true,false false,true ./file
  %[1]s dog,cat/s cat,dog/s ~/Document/questionnaire
`[1:], name)
}

func printVersion() {
	fmt.Fprintln(os.Stderr, version)
}

func printError(err interface{}) {
	fmt.Fprintf(os.Stderr, "%s: %s\n", name, err)
}

func do(r *Replacer, src io.Reader) error {
	b := bufio.NewScanner(src)
	for b.Scan() {
		fmt.Println(r.ReplaceAll(b.Text()))
	}
	return b.Err()
}

func _main() int {
	if err := flagset.Parse(os.Args[1:]); err != nil {
		printError(err)
		return 2
	}
	switch {
	case *isHelp:
		usage()
		return 0
	case *isVersion:
		printVersion()
		return 0
	}

	switch flagset.NArg() {
	case 0:
		printError("no specify FROM and TO")
		return 2
	case 1:
		printError("no specify TO")
		return 2
	}
	from, to := flagset.Arg(0), flagset.Arg(1)

	r, err := NewReplacer(from, to, *useBoundary)
	if err != nil {
		printError(err)
		return 2
	}

	if flagset.NArg() < 3 {
		if err = do(r, os.Stdin); err != nil {
			printError(err)
			return 1
		}
		return 0
	}

	var srcls []io.Reader
	for _, file := range flagset.Args()[2:] {
		src, err := os.Open(file)
		if err != nil {
			printError(err)
			return 1
		}
		defer src.Close()
		srcls = append(srcls, src)
	}
	if err = do(r, io.MultiReader(srcls...)); err != nil {
		printError(err)
		return 1
	}
	return 0
}

func main() {
	e := _main()
	os.Exit(e)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/jrperritt/gophercloud/rackspace"
	rsV1Servers "github.com/jrperritt/gophercloud/rackspace/compute/v1/servers"
	"github.com/rackspace/gophercloud"
	osV2Servers "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	"github.com/rackspace/gophercloud/pagination"
	rsV2Servers "github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
	"github.com/rackspace/gophercloud/rackspace/identity/v2/tokens"
)

const (
	metadataKey     = "rax:reboot_window"
	metadataTimeFmt = "2006-01-02T15:04:05Z"
)

func main() {
	outputToCSV := *flag.Bool("csv", false, "Output a CSV file")
	flag.Parse()

	if flag.NArg() != 2 {
		fmt.Println("You must supply a username and API key after all other args.")
		os.Exit(1)
	}

	username, apiKey := flag.Arg(0), flag.Arg(1)

	opts := gophercloud.AuthOptions{
		Username: username,
		APIKey:   apiKey,
	}

	provider, err := rackspace.AuthenticatedClient(opts)
	if err != nil {
		fmt.Printf("Unable to authenticate: %v\n", err)
		os.Exit(1)
	}

	regions, fg := Regions(provider, opts)

	fmt.Printf("Regions with a compute endpoint: %s\n", strings.Join(regions, ", "))
	if fg {
		fmt.Println("You do have a first-gen endpoint, too.")
	}

	var entries []entry

	// Iterate through regions with an NG compute endpoint. Collect data about each server.
	for _, region := range regions {
		compute, err := rackspace.NewComputeV2(provider, gophercloud.EndpointOpts{
			Region: region,
		})
		if err != nil {
			fmt.Printf("Unable to locate a v2 compute endpoint in region %s: %v\n", region, err)
			continue
		}

		err = rsV2Servers.List(compute, nil).EachPage(func(page pagination.Page) (bool, error) {
			s, err := osV2Servers.ExtractServers(page)
			if err != nil {
				return false, err
			}

			for _, server := range s {
				md, err := osV2Servers.Metadatum(compute, server.ID, "rax:reboot_window").Extract()
				if err != nil {
					fmt.Printf("Unable to retrieve rax:reboot_window metadatum for server %s: %v\n", server.ID, err)
					continue
				}

				windowString, ok := md["rax:reboot_window"]
				if !ok {
					fmt.Printf("Metadatum rax:reboot_window was not present in the result for server %s.\n", server.ID)
					continue
				}

				// Expected format: 2014-01-28T00:00:00Z;2014-01-28T03:00:00Z

				parts := strings.Split(windowString, ";")
				if len(parts) != 2 {
					fmt.Printf("Unexpected metadatum format for server %s: %s\n", server.ID, windowString)
					continue
				}

				start, err := time.Parse(metadataTimeFmt, parts[0])
				if err != nil {
					fmt.Printf("Unable to parse window start time for server %s: %s\n", server.ID, parts[0])
					continue
				}

				end, err := time.Parse(metadataTimeFmt, parts[1])
				if err != nil {
					fmt.Printf("Unable to parse window end time for server %s: %s\n", server.ID, parts[1])
					continue
				}

				entry := entry{
					Server:      server,
					Region:      region,
					GenType:     "Next Gen",
					WindowStart: start,
					WindowEnd:   end,
				}
				entries = append(entries, entry)
			}

			return true, nil
		})
	}

	// Iterate through regions with an FG compute endpoint. Collect data about each server.
	compute, err := rackspace.NewComputeV1(provider, gophercloud.EndpointOpts{
		Availability: gophercloud.AvailabilityPublic,
	})
	if err != nil {
		fmt.Printf("Unable to locate a v1 compute endpoint: %v\n", err)
	}

	err = rsV1Servers.List(compute, rsV1Servers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
		s, err := osV2Servers.ExtractServers(page)
		if err != nil {
			return false, err
		}

		for _, server := range s {
			windowString, ok := server.Metadata["rax:reboot_window"]
			if !ok {
				fmt.Printf("Metadatum rax:reboot_window was not present in the result for server %s.\n", server.ID)
				continue
			}

			// Expected format: 2014-01-28T00:00:00Z;2014-01-28T03:00:00Z

			parts := strings.Split(windowString.(string), ";")
			if len(parts) != 2 {
				fmt.Printf("Unexpected metadatum format for server %s: %s\n", server.ID, windowString)
				continue
			}

			start, err := time.Parse(metadataTimeFmt, parts[0])
			if err != nil {
				fmt.Printf("Unable to parse window start time for server %s: %s\n", server.ID, parts[0])
				continue
			}

			end, err := time.Parse(metadataTimeFmt, parts[1])
			if err != nil {
				fmt.Printf("Unable to parse window end time for server %s: %s\n", server.ID, parts[1])
				continue
			}

			entry := entry{
				Server:      server,
				Region:      "DFW",
				GenType:     "First Gen",
				WindowStart: start,
				WindowEnd:   end,
			}
			entries = append(entries, entry)
		}

		return true, nil
	})
	if err != nil {
		fmt.Printf("Error listing servers: %+v\n", err)
	}

	// Pull the metadata key

	if outputToCSV {
		outputCSV(entries)
	} else {
		outputTabular(entries)
	}
}

// Regions acquires the service catalog and returns a slice of every region that contains a next-gen
// server endpoint, and a boolean indicating whether or not this customer has access to FG servers.
func Regions(provider *gophercloud.ProviderClient, opts gophercloud.AuthOptions) ([]string, bool) {
	service := rackspace.NewIdentityV2(provider)

	result := tokens.Create(service, tokens.WrapOptions(opts))
	catalog, err := result.ExtractServiceCatalog()
	if err != nil {
		fmt.Printf("Unable to retrieve the service catalog: %v\n", err)
		os.Exit(1)
	}

	var regions []string
	var fg bool
	for _, entry := range catalog.Entries {
		if entry.Type == "compute" {
			for _, endpoint := range entry.Endpoints {
				if endpoint.Region == "" {
					fg = true
				} else {
					regions = append(regions, endpoint.Region)
				}
			}
		}
	}
	return regions, fg
}

// ConstructEntry extracts the metadata key and builds an entry for a server.
func ConstructEntry(server osV2Servers.Server, genType, region string) (*entry, error) {
	window, ok := server.Metadata[metadataKey]
	if !ok {
		return nil, fmt.Errorf("Metadatum %s was not present in the result for server %s", metadataKey, server.ID)
	}

	windowString, ok := window.(string)
	if !ok {
		return nil, fmt.Errorf("Metadatum %s for server %s was not a string: %#v", metadataKey, server.ID, window)
	}

	// Expected format: 2014-01-28T00:00:00Z;2014-01-28T03:00:00Z

	parts := strings.Split(windowString, ";")
	if len(parts) != 2 {
		return nil, fmt.Errorf("Unexpected metadatum format for server %s: %s", server.ID, windowString)
	}

	start, err := time.Parse(metadataTimeFmt, parts[0])
	if err != nil {
		return nil, fmt.Errorf("Unable to parse window start time for server %s: %s", server.ID, parts[0])
	}

	end, err := time.Parse(metadataTimeFmt, parts[1])
	if err != nil {
		return nil, fmt.Errorf("Unable to parse window end time for server %s: %s", server.ID, parts[1])
	}

	e := &entry{
		Server:      server,
		Region:      region,
		GenType:     genType,
		WindowStart: start,
		WindowEnd:   end,
	}
	return e, nil
}
<commit_msg>Use ConstructEntry for next-gen servers.<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/jrperritt/gophercloud/rackspace"
	rsV1Servers "github.com/jrperritt/gophercloud/rackspace/compute/v1/servers"
	"github.com/rackspace/gophercloud"
	osV2Servers "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	"github.com/rackspace/gophercloud/pagination"
	rsV2Servers "github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
	"github.com/rackspace/gophercloud/rackspace/identity/v2/tokens"
)

const (
	metadataKey     = "rax:reboot_window"
	metadataTimeFmt = "2006-01-02T15:04:05Z"
)

func main() {
	outputToCSV := *flag.Bool("csv", false, "Output a CSV file")
	flag.Parse()

	if flag.NArg() != 2 {
		fmt.Println("You must supply a username and API key after all other args.")
		os.Exit(1)
	}

	username, apiKey := flag.Arg(0), flag.Arg(1)

	opts := gophercloud.AuthOptions{
		Username: username,
		APIKey:   apiKey,
	}

	provider, err := rackspace.AuthenticatedClient(opts)
	if err != nil {
		fmt.Printf("Unable to authenticate: %v\n", err)
		os.Exit(1)
	}

	regions, fg := Regions(provider, opts)

	fmt.Printf("Regions with a compute endpoint: %s\n", strings.Join(regions, ", "))
	if fg {
		fmt.Println("You do have a first-gen endpoint, too.")
	}

	var entries []entry

	// Iterate through regions with an NG compute endpoint. Collect data about each server.
	for _, region := range regions {
		compute, err := rackspace.NewComputeV2(provider, gophercloud.EndpointOpts{
			Region: region,
		})
		if err != nil {
			fmt.Printf("Unable to locate a v2 compute endpoint in region %s: %v\n", region, err)
			continue
		}

		err = rsV2Servers.List(compute, nil).EachPage(func(page pagination.Page) (bool, error) {
			s, err := osV2Servers.ExtractServers(page)
			if err != nil {
				return false, err
			}

			for _, server := range s {
				entry, err := ConstructEntry(server, "Next Gen", region)
				if err != nil {
					fmt.Printf("%s\n", err)
					continue
				}

				entries = append(entries, *entry)
			}

			return true, nil
		})
	}

	// Iterate through regions with an FG compute endpoint. Collect data about each server.
	compute, err := rackspace.NewComputeV1(provider, gophercloud.EndpointOpts{
		Availability: gophercloud.AvailabilityPublic,
	})
	if err != nil {
		fmt.Printf("Unable to locate a v1 compute endpoint: %v\n", err)
	}

	err = rsV1Servers.List(compute, rsV1Servers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
		s, err := osV2Servers.ExtractServers(page)
		if err != nil {
			return false, err
		}

		for _, server := range s {
			windowString, ok := server.Metadata["rax:reboot_window"]
			if !ok {
				fmt.Printf("Metadatum rax:reboot_window was not present in the result for server %s.\n", server.ID)
				continue
			}

			// Expected format: 2014-01-28T00:00:00Z;2014-01-28T03:00:00Z

			parts := strings.Split(windowString.(string), ";")
			if len(parts) != 2 {
				fmt.Printf("Unexpected metadatum format for server %s: %s\n", server.ID, windowString)
				continue
			}

			start, err := time.Parse(metadataTimeFmt, parts[0])
			if err != nil {
				fmt.Printf("Unable to parse window start time for server %s: %s\n", server.ID, parts[0])
				continue
			}

			end, err := time.Parse(metadataTimeFmt, parts[1])
			if err != nil {
				fmt.Printf("Unable to parse window end time for server %s: %s\n", server.ID, parts[1])
				continue
			}

			entry := entry{
				Server:      server,
				Region:      "DFW",
				GenType:     "First Gen",
				WindowStart: start,
				WindowEnd:   end,
			}
			entries = append(entries, entry)
		}

		return true, nil
	})
	if err != nil {
		fmt.Printf("Error listing servers: %+v\n", err)
	}

	// Pull the metadata key

	if outputToCSV {
		outputCSV(entries)
	} else {
		outputTabular(entries)
	}
}

// Regions acquires the service catalog and returns a slice of every region that contains a next-gen
// server endpoint, and a boolean indicating whether or not this customer has access to FG servers.
func Regions(provider *gophercloud.ProviderClient, opts gophercloud.AuthOptions) ([]string, bool) {
	service := rackspace.NewIdentityV2(provider)

	result := tokens.Create(service, tokens.WrapOptions(opts))
	catalog, err := result.ExtractServiceCatalog()
	if err != nil {
		fmt.Printf("Unable to retrieve the service catalog: %v\n", err)
		os.Exit(1)
	}

	var regions []string
	var fg bool
	for _, entry := range catalog.Entries {
		if entry.Type == "compute" {
			for _, endpoint := range entry.Endpoints {
				if endpoint.Region == "" {
					fg = true
				} else {
					regions = append(regions, endpoint.Region)
				}
			}
		}
	}
	return regions, fg
}

// ConstructEntry extracts the metadata key and builds an entry for a server.
func ConstructEntry(server osV2Servers.Server, genType, region string) (*entry, error) {
	window, ok := server.Metadata[metadataKey]
	if !ok {
		return nil, fmt.Errorf("Metadatum %s was not present in the result for server %s", metadataKey, server.ID)
	}

	windowString, ok := window.(string)
	if !ok {
		return nil, fmt.Errorf("Metadatum %s for server %s was not a string: %#v", metadataKey, server.ID, window)
	}

	// Expected format: 2014-01-28T00:00:00Z;2014-01-28T03:00:00Z

	parts := strings.Split(windowString, ";")
	if len(parts) != 2 {
		return nil, fmt.Errorf("Unexpected metadatum format for server %s: %s", server.ID, windowString)
	}

	start, err := time.Parse(metadataTimeFmt, parts[0])
	if err != nil {
		return nil, fmt.Errorf("Unable to parse window start time for server %s: %s", server.ID, parts[0])
	}

	end, err := time.Parse(metadataTimeFmt, parts[1])
	if err != nil {
		return nil, fmt.Errorf("Unable to parse window end time for server %s: %s", server.ID, parts[1])
	}

	e := &entry{
		Server:      server,
		Region:      region,
		GenType:     genType,
		WindowStart: start,
		WindowEnd:   end,
	}
	return e, nil
}
<|endoftext|>