<commit_before>// gogetdoc gets documentation for Go objects given their locations in the source code
package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"go/types"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"runtime/debug"
	"runtime/pprof"
	"strconv"
	"strings"

	"golang.org/x/tools/go/buildutil"
	"golang.org/x/tools/go/loader"
)

var (
	cpuprofile           = flag.String("cpuprofile", "", "write cpu profile to file")
	pos                  = flag.String("pos", "", "Filename and byte offset of item to document, e.g. foo.go:#123")
	modified             = flag.Bool("modified", false, "read an archive of modified files from standard input")
	linelength           = flag.Int("linelength", 80, "maximum length of a line in the output (in Unicode code points)")
	jsonOutput           = flag.Bool("json", false, "enable extended JSON output")
	showUnexportedFields = flag.Bool("u", false, "show unexported fields")
)

const modifiedUsage = `
The archive format for the -modified flag consists of the file name, followed
by a newline, the decimal file size, another newline, and the contents of the file.

This allows editors to supply gogetdoc with the contents of their unsaved buffers.
`

func main() {
	// disable GC as gogetdoc is a short-lived program
	debug.SetGCPercent(-1)

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s\n", os.Args[0])
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, modifiedUsage)
	}
	flag.Parse()
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		if err := pprof.StartCPUProfile(f); err != nil {
			log.Fatal(err)
		}
		defer pprof.StopCPUProfile()
	}
	filename, offset, err := parsePos(*pos)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	ctx := &build.Default
	ctx.CgoEnabled = false
	if *modified {
		overlay, err := buildutil.ParseOverlayArchive(os.Stdin)
		if err != nil {
			log.Fatalln("invalid archive:", err)
		}
		ctx = buildutil.OverlayContext(ctx, overlay)
	}

	d, err := Run(ctx, filename, offset)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	if *jsonOutput {
		json.NewEncoder(os.Stdout).Encode(d)
	} else {
		fmt.Println(d.String())
	}
}

// Run is a wrapper for the gogetdoc command.  It is broken out of main for easier testing.
func Run(ctx *build.Context, filename string, offset int64) (*Doc, error) {
	wd, err := os.Getwd()
	if err != nil {
		return nil, errors.New("gogetdoc: couldn't get working directory")
	}
	bp, err := buildutil.ContainingPackage(ctx, wd, filename)
	if err != nil {
		return nil, fmt.Errorf("gogetdoc: couldn't get package for %s: %s", filename, err.Error())
	}
	var parseError error
	conf := &loader.Config{
		Build:      ctx,
		ParserMode: parser.ParseComments,
		TypeCheckFuncBodies: func(pkg string) bool {
			match := pkg == bp.ImportPath
			if strings.HasSuffix(filename, "_test.go") {
				return pkg == bp.ImportPath+"_test" || match
			}
			return match
		},
		AllowErrors: true,
		TypeChecker: types.Config{
			DisableUnusedImportCheck: true,
			Error: func(err error) {
				if parseError != nil {
					return
				}
				parseError = err
			},
		},
	}

	if isTestFile := strings.HasSuffix(filename, "_test.go"); isTestFile {
		conf.ImportWithTests(bp.ImportPath)
	} else {
		conf.Import(bp.ImportPath)
	}

	lprog, err := conf.Load()
	if err != nil {
		return nil, fmt.Errorf("gogetdoc: error loading program: %s", err.Error())
	}
	doc, err := DocForPos(ctx, lprog, filename, offset)
	if err != nil && parseError != nil {
		fmt.Fprintln(os.Stderr, parseError)
	}
	return doc, err
}

// DocForPos attempts to get the documentation for an item given a filename and byte offset.
func DocForPos(ctxt *build.Context, lprog *loader.Program, filename string, offset int64) (*Doc, error) {
	tokFile := FileFromProgram(lprog, filename)
	if tokFile == nil {
		return nil, fmt.Errorf("gogetdoc: couldn't find %s in program", filename)
	}
	offPos := tokFile.Pos(int(offset))

	pkgInfo, nodes, _ := lprog.PathEnclosingInterval(offPos, offPos)
	for _, node := range nodes {
		switch i := node.(type) {
		case *ast.ImportSpec:
			abs, err := filepath.Abs(filename)
			if err != nil {
				return nil, err
			}
			return PackageDoc(ctxt, lprog.Fset, filepath.Dir(abs), ImportPath(i))
		case *ast.Ident:
			// if we can't find the object denoted by the identifier, keep searching)
			if obj := pkgInfo.ObjectOf(i); obj == nil {
				continue
			}
			return IdentDoc(ctxt, i, pkgInfo, lprog)
		default:
			break
		}
	}
	return nil, errors.New("gogetdoc: no documentation found")
}

// FileFromProgram attempts to locate a token.File from a loaded program.
func FileFromProgram(prog *loader.Program, name string) *token.File {
	for _, info := range prog.AllPackages {
		for _, astFile := range info.Files {
			tokFile := prog.Fset.File(astFile.Pos())
			if tokFile == nil {
				continue
			}
			tokName := tokFile.Name()
			if runtime.GOOS == "windows" {
				tokName = filepath.ToSlash(tokName)
				name = filepath.ToSlash(name)
			}
			if tokName == name {
				return tokFile
			}
			if sameFile(tokName, name) {
				return tokFile
			}
		}
	}
	return nil
}

func parsePos(p string) (filename string, offset int64, err error) {
	// foo.go:#123
	if p == "" {
		err = errors.New("missing required -pos flag")
		return
	}
	sep := strings.LastIndex(p, ":")
	// need at least 2 characters after the ':'
	// (the # sign and the offset)
	if sep == -1 || sep > len(p)-2 || p[sep+1] != '#' {
		err = fmt.Errorf("invalid option: -pos=%s", p)
		return
	}
	filename = p[:sep]
	offset, err = strconv.ParseInt(p[sep+2:], 10, 32)
	return
}

func sameFile(a, b string) bool {
	if filepath.Base(a) != filepath.Base(b) {
		// We only care about symlinks for the GOPATH itself. File
		// names need to match.
		return false
	}
	if ai, err := os.Stat(a); err == nil {
		if bi, err := os.Stat(b); err == nil {
			return os.SameFile(ai, bi)
		}
	}
	return false
}
<commit_msg>Add a flag for specifying build tags<commit_after>// gogetdoc gets documentation for Go objects given their locations in the source code
package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"go/types"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"runtime/debug"
	"runtime/pprof"
	"strconv"
	"strings"

	"golang.org/x/tools/go/buildutil"
	"golang.org/x/tools/go/loader"
)

var (
	cpuprofile           = flag.String("cpuprofile", "", "write cpu profile to file")
	pos                  = flag.String("pos", "", "Filename and byte offset of item to document, e.g. foo.go:#123")
	modified             = flag.Bool("modified", false, "read an archive of modified files from standard input")
	linelength           = flag.Int("linelength", 80, "maximum length of a line in the output (in Unicode code points)")
	jsonOutput           = flag.Bool("json", false, "enable extended JSON output")
	showUnexportedFields = flag.Bool("u", false, "show unexported fields")
)

const modifiedUsage = `
The archive format for the -modified flag consists of the file name, followed
by a newline, the decimal file size, another newline, and the contents of the file.

This allows editors to supply gogetdoc with the contents of their unsaved buffers.
`

func main() {
	// disable GC as gogetdoc is a short-lived program
	debug.SetGCPercent(-1)

	flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s\n", os.Args[0])
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, modifiedUsage)
	}
	flag.Parse()
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		if err := pprof.StartCPUProfile(f); err != nil {
			log.Fatal(err)
		}
		defer pprof.StopCPUProfile()
	}
	filename, offset, err := parsePos(*pos)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	ctx := &build.Default
	ctx.CgoEnabled = false
	if *modified {
		var overlay map[string][]byte
		overlay, err = buildutil.ParseOverlayArchive(os.Stdin)
		if err != nil {
			log.Fatalln("invalid archive:", err)
		}
		ctx = buildutil.OverlayContext(ctx, overlay)
	}

	d, err := Run(ctx, filename, offset)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	if *jsonOutput {
		json.NewEncoder(os.Stdout).Encode(d)
	} else {
		fmt.Println(d.String())
	}
}

// Run is a wrapper for the gogetdoc command.  It is broken out of main for easier testing.
func Run(ctx *build.Context, filename string, offset int64) (*Doc, error) {
	wd, err := os.Getwd()
	if err != nil {
		return nil, errors.New("gogetdoc: couldn't get working directory")
	}
	bp, err := buildutil.ContainingPackage(ctx, wd, filename)
	if err != nil {
		return nil, fmt.Errorf("gogetdoc: couldn't get package for %s: %s", filename, err.Error())
	}
	var parseError error
	conf := &loader.Config{
		Build:      ctx,
		ParserMode: parser.ParseComments,
		TypeCheckFuncBodies: func(pkg string) bool {
			match := pkg == bp.ImportPath
			if strings.HasSuffix(filename, "_test.go") {
				return pkg == bp.ImportPath+"_test" || match
			}
			return match
		},
		AllowErrors: true,
		TypeChecker: types.Config{
			DisableUnusedImportCheck: true,
			Error: func(err error) {
				if parseError != nil {
					return
				}
				parseError = err
			},
		},
	}

	if isTestFile := strings.HasSuffix(filename, "_test.go"); isTestFile {
		conf.ImportWithTests(bp.ImportPath)
	} else {
		conf.Import(bp.ImportPath)
	}

	lprog, err := conf.Load()
	if err != nil {
		return nil, fmt.Errorf("gogetdoc: error loading program: %s", err.Error())
	}
	doc, err := DocForPos(ctx, lprog, filename, offset)
	if err != nil && parseError != nil {
		fmt.Fprintln(os.Stderr, parseError)
	}
	return doc, err
}

// DocForPos attempts to get the documentation for an item given a filename and byte offset.
func DocForPos(ctxt *build.Context, lprog *loader.Program, filename string, offset int64) (*Doc, error) {
	tokFile := FileFromProgram(lprog, filename)
	if tokFile == nil {
		return nil, fmt.Errorf("gogetdoc: couldn't find %s in program", filename)
	}
	offPos := tokFile.Pos(int(offset))

	pkgInfo, nodes, _ := lprog.PathEnclosingInterval(offPos, offPos)
	for _, node := range nodes {
		switch i := node.(type) {
		case *ast.ImportSpec:
			abs, err := filepath.Abs(filename)
			if err != nil {
				return nil, err
			}
			return PackageDoc(ctxt, lprog.Fset, filepath.Dir(abs), ImportPath(i))
		case *ast.Ident:
			// if we can't find the object denoted by the identifier, keep searching)
			if obj := pkgInfo.ObjectOf(i); obj == nil {
				continue
			}
			return IdentDoc(ctxt, i, pkgInfo, lprog)
		default:
			break
		}
	}
	return nil, errors.New("gogetdoc: no documentation found")
}

// FileFromProgram attempts to locate a token.File from a loaded program.
func FileFromProgram(prog *loader.Program, name string) *token.File {
	for _, info := range prog.AllPackages {
		for _, astFile := range info.Files {
			tokFile := prog.Fset.File(astFile.Pos())
			if tokFile == nil {
				continue
			}
			tokName := tokFile.Name()
			if runtime.GOOS == "windows" {
				tokName = filepath.ToSlash(tokName)
				name = filepath.ToSlash(name)
			}
			if tokName == name {
				return tokFile
			}
			if sameFile(tokName, name) {
				return tokFile
			}
		}
	}
	return nil
}

func parsePos(p string) (filename string, offset int64, err error) {
	// foo.go:#123
	if p == "" {
		err = errors.New("missing required -pos flag")
		return
	}
	sep := strings.LastIndex(p, ":")
	// need at least 2 characters after the ':'
	// (the # sign and the offset)
	if sep == -1 || sep > len(p)-2 || p[sep+1] != '#' {
		err = fmt.Errorf("invalid option: -pos=%s", p)
		return
	}
	filename = p[:sep]
	offset, err = strconv.ParseInt(p[sep+2:], 10, 32)
	return
}

func sameFile(a, b string) bool {
	if filepath.Base(a) != filepath.Base(b) {
		// We only care about symlinks for the GOPATH itself. File
		// names need to match.
		return false
	}
	if ai, err := os.Stat(a); err == nil {
		if bi, err := os.Stat(b); err == nil {
			return os.SameFile(ai, bi)
		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"strconv"
	"time"

	"github.com/codegangsta/negroni"
	"github.com/xyproto/onthefly"
)

const (
	bannerW = 20
	bannerH = 40

	zoomFactor = 8 // 8x zoom

	fullW  = bannerW
	fullH  = bannerH
	halfW  = bannerW / 2
	halfH  = bannerH / 2
	thirdW = bannerW / 3
	thirdH = bannerH / 3

	maxX = fullW - 1
	maxY = fullH - 1

	maxPatterns = 99 // 6

	// Same order as minecraft.gamepedia.com/Banner
	patternLowerThird         = iota // Base Fess Banner
	patternUpperThird                // Chief Fess Banner
	patternLeftThird                 // Pale Dexter Banner
	patternRightThird                // Pale Sinister Banner
	patternCenterThird               // Pale Banner
	patternHorizontalLine            // Fess Banner
	patternDiagonal1                 // Bend Banner
	patternDiagonal2                 // Bend Sinister Banner
	patterStripes                    // Paly Banner
	patternDiaCross                  // Saltire Banner
	patternCross                     // Cross Banner
	patternUpperLeftTriangle         // Per Bend Sinister Banner
	patternUpperRightTriangle        // Per Bend Banner
	patternLowerLeftTriangle         // Per Bend Inverted Banner
	patternLowerRightTriangle        // Per Bend Sinister Inverted Banner
	patternLeftHalf                  // Per Pale Banner
	patternRightHalf                 // Per Pale Inverted Banner
	patternUpperHalf                 // Per Fess Banner
	patternLowerHalf                 // Per Fess Inverted Banner
	patternLowerLeftSquare           // Base Dexter Canton Banner
	patternLowerRightSquare          // Base Sinister Canton Banner
	patternUpperLeftSquare           // Chief Dexter Canton Banner
	patternUpperRightSqaure          // Chief Sinister Canton Banner
	patternLowerTriangle             // Chevron Banner
	patternUpperTriangle             // Inverted Chevron Banner
	patternLowerWaves                // Base Indented Banner
	patternUpperWaves                // Chief Indented Banner
	patternCircle                    // Roundel Banner
	patternDiamond                   // Lozenge Banner
	patternBorder                    // Bordure Banner
	patternWaveBorder                // Black/Dyed Borduer Indented Banner
	patternBricks                    // Black/Dyed Field Masoned Banner
	patternGradientDown              // Gradient Banner
	patternGradientUp                // Base Gradient Banner
	patternCreeper                   // Black/Dyed Creeper Charge Banner
	patternSkull                     // Black/Dyed Skull Charge Banner
	patternFlower                    // Black/Dyed Flower Charge Banner
	patternLogo                      // Black/Dyed Mojang Charge Banner

	// Custom patterns
	patternFull // Background

	colorWhite = iota
	colorOrange
	colorMagenta
	colorLightBlue
	colorYellow
	colorLime
	colorPink
	colorGray
	colorLightGray
	colorCyan
	colorPurple
	colorBlue
	colorBrown
	colorGreen
	colorRed
	colorBlack

	// Custom colors
	colorBrightWhite
)

var (
	colors = map[int]string{
		colorWhite:       "#989898",
		colorOrange:      "#804d23",
		colorMagenta:     "#6b3181",
		colorLightBlue:   "#3e5b7f",
		colorYellow:      "#898923",
		colorLime:        "#4c7815",
		colorPink:        "#8f4c62",
		colorGray:        "#313131",
		colorLightGray:   "#5b5b5b",
		colorCyan:        "#314c5b",
		colorPurple:      "#4d2b6a",
		colorBlue:        "#23316b",
		colorBrown:       "#3e3123",
		colorGreen:       "#3e4c23",
		colorRed:         "#5c2323",
		colorBlack:       "#151515",
		colorBrightWhite: "#ffffff",
	}
)

type Pattern struct {
	pattern int
	color   int
}

type Banner struct {
	patterns []*Pattern
	curpat   int
}

func (b *Banner) Draw(svg *onthefly.Tag) {
	// generate the picture, then draw
	for _, p := range b.patterns {
		color, ok := colors[p.color]
		if !ok {
			log.Fatalln("Invalid color ID: ", p.color)
		}
		//log.Println("color:", color)
		switch p.pattern {
		case patternLowerThird: // Base Fess Banner
			svg.Box(0, fullH-thirdH, fullW, fullH-thirdH, color)
		case patternUpperThird: // Chief Fess Banner
			svg.Box(0, 0, fullW, thirdH, color)
		case patternLeftThird: // Pale Dexter Banner
		case patternRightThird: // Pale Sinister Banner
		case patternCenterThird: // Pale Banner
			svg.Box(halfW-(thirdW/2), 0, thirdW, fullH, color)
		case patternHorizontalLine: // Fess Banner
			svg.Box(0, halfH-1, fullW, 2, color)
		case patternDiagonal1: // Bend Banner
		case patternDiagonal2: // Bend Sinister Banner
		case patterStripes: // Paly Banner
		case patternDiaCross: // Saltire Banner
		case patternCross: // Cross Banner
		case patternUpperLeftTriangle: // Per Bend Sinister Banner
		case patternUpperRightTriangle: // Per Bend Banner
		case patternLowerLeftTriangle: // Per Bend Inverted Banner
		case patternLowerRightTriangle: // Per Bend Sinister Inverted Banner
		case patternLeftHalf: // Per Pale Banner
		case patternRightHalf: // Per Pale Inverted Banner
		case patternUpperHalf: // Per Fess Banner
			svg.Box(0, 0, fullW, halfH, color)
		case patternLowerHalf: // Per Fess Inverted Banner
			svg.Box(0, halfH, fullW, halfH, color)
		case patternLowerLeftSquare: // Base Dexter Canton Banner
		case patternLowerRightSquare: // Base Sinister Canton Banner
		case patternUpperLeftSquare: // Chief Dexter Canton Banner
		case patternUpperRightSqaure: // Chief Sinister Canton Banner
		case patternLowerTriangle: // Chevron Banner
			svg.Triangle(0, fullH, fullW, fullH, halfW, halfH, color)
		case patternUpperTriangle: // Inverted Chevron Banner
			svg.Triangle(0, 0, fullW, 0, halfW, halfH, color)
		case patternLowerWaves: // Base Indented Banner
		case patternUpperWaves: // Chief Indented Banner
		case patternCircle: // Roundel Banner
			svg.Circle(halfW, halfH, thirdW, color)
		case patternDiamond: // Lozenge Banner
		case patternBorder: // Bordure Banner
		case patternWaveBorder: // Black/Dyed Borduer Indented Banner
		case patternBricks: // Black/Dyed Field Masoned Banner
		case patternGradientDown: // Gradient Banner
		case patternGradientUp: // Base Gradient Banner
		case patternCreeper: // Black/Dyed Creeper Charge Banner
		case patternSkull: // Black/Dyed Skull Charge Banner
		case patternFlower: // Black/Dyed Flower Charge Banner
		case patternLogo: // Black/Dyed Mojang Charge Banner
		case patternFull:
			svg.Box(0, 0, fullW, fullH, color)
		}
	}
	// For debugging
	//svg.Pixel(0, 0, 0, 255, 0)
	//svg.Pixel(maxX, 0, 0, 0, 255)
	//svg.Pixel(0, maxY, 255, 255, 0)
	//svg.Pixel(maxX, maxY, 0, 255, 255)
}

// Generate a new SVG Page for a banner
func (b *Banner) SVGpage() *onthefly.Page {
	page, svg := onthefly.NewTinySVG(0, 0, bannerW, bannerH)
	desc := svg.AddNewTag("desc")
	desc.AddContent("A banner")
	b.Draw(svg)
	return page
}

func NewBanner() *Banner {
	return &Banner{}
}

func NewPattern(pattern, color int) *Pattern {
	if (pattern < patternLowerThird) || (pattern > patternFull) {
		log.Fatalln("Invalid pattern ID: ", pattern)
	}
	return &Pattern{pattern, color}
}

func (b *Banner) AddPattern(p *Pattern) {
	if len(b.patterns) > maxPatterns {
		log.Fatalln("Too many patters for banner, max", maxPatterns)
	}
	b.patterns = append(b.patterns, p)
}

// Generate a new onthefly Page (HTML5 and CSS combined)
func mainPage(svgurls []string) *onthefly.Page {

	title := "Banners"

	// Create a new HTML5 page, with CSS included
	page := onthefly.NewHTML5Page(title)
	page.AddContent(title)

	// Change the margin (em is default)
	page.SetMargin(4)

	// Change the font family
	page.SetFontFamily("sans-serif")

	// Change the color scheme
	page.SetColor("black", "#a0a0a0")

	// Include the generated SVG image on the page
	body, _ := page.GetTag("body")

	// CSS attributes for the body tag
	body.AddStyle("font-size", "2em")

	// Paragraph
	p := body.AddNewTag("p")

	// CSS style
	p.AddStyle("margin-top", "2em")

	// Add images
	var (
		tag          *onthefly.Tag
		useObjectTag = false
	)
	for _, svgurl := range svgurls {
		if useObjectTag {
			// object tag
			tag = p.AddNewTag("object")
			// HTML attributes
			tag.AddAttrib("data", svgurl)
			tag.AddAttrib("type", "image/svg+xml")
		} else {
			// img tag
			tag = p.AddNewTag("img")
			// HTML attributes
			tag.AddAttrib("src", svgurl)
			tag.AddAttrib("alt", "Banner")
		}
	}

	// CSS style
	w := strconv.Itoa(bannerW * zoomFactor)
	h := strconv.Itoa(bannerH * zoomFactor)
	tag.AddStyle("width", w+"px")
	tag.AddStyle("height", h+"px")
	tag.AddStyle("border", "8px solid black")

	return page
}

func randomColor() int {
	return colorWhite + rand.Intn((colorBlack-colorWhite)+1)
}

func seed() {
	rand.Seed(time.Now().UnixNano())
}

// Set up the paths and handlers then start serving.
func main() {
	seed()

	// Create a Negroni instance and a ServeMux instance
	n := negroni.Classic()
	mux := http.NewServeMux()

	var (
		svgurls []string
		b       *Banner
	)
	for i := patternLowerThird; i < patternLogo; i++ {

		b = NewBanner()
		b.AddPattern(NewPattern(patternFull, colorBrightWhite))
		b.AddPattern(NewPattern(i, colorRed))

		svgString := b.SVGpage().String()

		// Publish the generated SVG as "/img/banner_NNN.svg"
		svgurl := fmt.Sprintf("/img/banner_%d.svg", i)
		mux.HandleFunc(svgurl, func(w http.ResponseWriter, req *http.Request) {
			w.Header().Add("Content-Type", "image/svg+xml")
			fmt.Fprint(w, svgString)
		})

		svgurls = append(svgurls, svgurl)
	}

	// Generate a Page that includes the svg images
	page := mainPage(svgurls)
	// Publish the generated Page in a way that connects the HTML and CSS
	page.Publish(mux, "/", "/css/banner.css", false)

	// Handler goes last
	n.UseHandler(mux)

	// Listen for requests at port 3000
	n.Run(":3000")
}
<commit_msg>Generating almost all patterns now<commit_after>package main

import (
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"strconv"
	"time"

	"github.com/codegangsta/negroni"
	"github.com/xyproto/onthefly"
)

const (
	bannerW = 20
	bannerH = 40

	zoomFactor = 8 // 8x zoom

	fullW  = bannerW
	fullH  = bannerH
	halfW  = bannerW / 2
	halfH  = bannerH / 2
	thirdW = bannerW / 3
	thirdH = bannerH / 3
	fifthW = bannerW / 5
	fifthH = bannerH / 5
	sixthW = bannerW / 6
	sixthH = bannerH / 6
	tenthW = bannerW / 10
	tenthH = bannerH / 10

	maxX = fullW - 1
	maxY = fullH - 1

	maxPatterns = 99 // 6

	// Same order as minecraft.gamepedia.com/Banner
	patternLowerThird         = iota // Base Fess Banner
	patternUpperThird                // Chief Fess Banner
	patternLeftThird                 // Pale Dexter Banner
	patternRightThird                // Pale Sinister Banner
	patternCenterThird               // Pale Banner
	patternHorizontalLine            // Fess Banner
	patternDiagonal1                 // Bend Banner
	patternDiagonal2                 // Bend Sinister Banner
	patterStripes                    // Paly Banner
	patternDiaCross                  // Saltire Banner
	patternCross                     // Cross Banner
	patternUpperLeftTriangle         // Per Bend Sinister Banner
	patternUpperRightTriangle        // Per Bend Banner
	patternLowerLeftTriangle         // Per Bend Inverted Banner
	patternLowerRightTriangle        // Per Bend Sinister Inverted Banner
	patternLeftHalf                  // Per Pale Banner
	patternRightHalf                 // Per Pale Inverted Banner
	patternUpperHalf                 // Per Fess Banner
	patternLowerHalf                 // Per Fess Inverted Banner
	patternLowerLeftSquare           // Base Dexter Canton Banner
	patternLowerRightSquare          // Base Sinister Canton Banner
	patternUpperLeftSquare           // Chief Dexter Canton Banner
	patternUpperRightSqaure          // Chief Sinister Canton Banner
	patternLowerTriangle             // Chevron Banner
	patternUpperTriangle             // Inverted Chevron Banner
	patternLowerWaves                // Base Indented Banner
	patternUpperWaves                // Chief Indented Banner
	patternCircle                    // Roundel Banner
	patternDiamond                   // Lozenge Banner
	patternBorder                    // Bordure Banner
	patternWaveBorder                // Black/Dyed Borduer Indented Banner
	patternBricks                    // Black/Dyed Field Masoned Banner
	patternGradientDown              // Gradient Banner
	patternGradientUp                // Base Gradient Banner
	patternCreeper                   // Black/Dyed Creeper Charge Banner
	patternSkull                     // Black/Dyed Skull Charge Banner
	patternFlower                    // Black/Dyed Flower Charge Banner
	patternLogo                      // Black/Dyed Mojang Charge Banner

	// Custom patterns
	patternFull // Background

	colorWhite = iota
	colorOrange
	colorMagenta
	colorLightBlue
	colorYellow
	colorLime
	colorPink
	colorGray
	colorLightGray
	colorCyan
	colorPurple
	colorBlue
	colorBrown
	colorGreen
	colorRed
	colorBlack

	// Custom colors
	colorBrightWhite
)

var (
	colors = map[int]string{
		colorWhite:       "#989898",
		colorOrange:      "#804d23",
		colorMagenta:     "#6b3181",
		colorLightBlue:   "#3e5b7f",
		colorYellow:      "#898923",
		colorLime:        "#4c7815",
		colorPink:        "#8f4c62",
		colorGray:        "#313131",
		colorLightGray:   "#5b5b5b",
		colorCyan:        "#314c5b",
		colorPurple:      "#4d2b6a",
		colorBlue:        "#23316b",
		colorBrown:       "#3e3123",
		colorGreen:       "#3e4c23",
		colorRed:         "#5c2323",
		colorBlack:       "#151515",
		colorBrightWhite: "#ffffff",
	}
)

type Pattern struct {
	pattern int
	color   int
}

type Banner struct {
	patterns []*Pattern
	curpat   int
}

func (b *Banner) Draw(svg *onthefly.Tag) {
	// generate the picture, then draw
	for _, p := range b.patterns {
		color, ok := colors[p.color]
		if !ok {
			log.Fatalln("Invalid color ID: ", p.color)
		}
		//log.Println("color:", color)
		switch p.pattern {
		case patternLowerThird: // Base Fess Banner
			svg.Box(0, fullH-thirdH, fullW, fullH-thirdH, color)
		case patternUpperThird: // Chief Fess Banner
			svg.Box(0, 0, fullW, thirdH, color)
		case patternLeftThird: // Pale Dexter Banner
			svg.Box(0, 0, thirdW, fullH, color)
		case patternRightThird: // Pale Sinister Banner
			svg.Box(fullW-thirdW, 0, thirdW, fullH, color)
		case patternCenterThird: // Pale Banner
			svg.Box(halfW-(thirdW/2), 0, thirdW, fullH, color)
		case patternHorizontalLine: // Fess Banner
			svg.Box(0, halfH-1, fullW, thirdW/2, color)
		case patternDiagonal1: // Bend Banner
			svg.Line(0, 0, fullW, fullH, thirdW, color)
		case patternDiagonal2: // Bend Sinister Banner
			svg.Line(fullW, 0, 0, fullH, thirdW, color)
		case patterStripes: // Paly Banner
			for i := 0.15; i < 1.0; i += 0.25 {
				x := int(float64(fullW) * i)
				svg.Line(x, 0, x, fullH, sixthW, color)
			}
		case patternDiaCross: // Saltire Banner
			svg.Line(0, 0, fullW, fullH, thirdW, color)
			svg.Line(fullW, 0, 0, fullH, thirdW, color)
		case patternCross: // Cross Banner
			svg.Line(0, halfH, fullW, halfH, sixthW, color)
			svg.Line(halfW, 0, halfW, fullH, sixthW, color)
		case patternUpperLeftTriangle: // Per Bend Sinister Banner
			svg.Triangle(0, 0, fullW, 0, 0, fullH, color)
		case patternUpperRightTriangle: // Per Bend Banner
			svg.Triangle(0, 0, fullW, 0, fullW, fullH, color)
		case patternLowerLeftTriangle: // Per Bend Inverted Banner
			svg.Triangle(0, fullH, 0, 0, fullW, fullH, color)
		case patternLowerRightTriangle: // Per Bend Sinister Inverted Banner
			svg.Triangle(0, fullH, fullW, 0, fullW, fullH, color)
		case patternLeftHalf: // Per Pale Banner
			svg.Box(0, 0, halfW, fullH, color)
		case patternRightHalf: // Per Pale Inverted Banner
			svg.Box(halfW, 0, halfW, fullH, color)
		case patternUpperHalf: // Per Fess Banner
			svg.Box(0, 0, fullW, halfH, color)
		case patternLowerHalf: // Per Fess Inverted Banner
			svg.Box(0, halfH, fullW, halfH, color)
		case patternLowerLeftSquare: // Base Dexter Canton Banner
			svg.Box(0, fullH-thirdH, halfW, thirdH, color)
		case patternLowerRightSquare: // Base Sinister Canton Banner
			svg.Box(halfW, fullH-thirdH, halfW, thirdH, color)
		case patternUpperLeftSquare: // Chief Dexter Canton Banner
			svg.Box(0, 0, halfW, thirdH, color)
		case patternUpperRightSqaure: // Chief Sinister Canton Banner
			svg.Box(halfW, 0, halfW, thirdH, color)
		case patternLowerTriangle: // Chevron Banner
			svg.Triangle(0, fullH, fullW, fullH, halfW, halfH, color)
		case patternUpperTriangle: // Inverted Chevron Banner
			svg.Triangle(0, 0, fullW, 0, halfW, halfH, color)
		case patternLowerWaves: // Base Indented Banner
			svg.Ellipse(sixthW, fullH, fifthW, thirdW, color)
			svg.Ellipse(halfW, fullH, fifthW, thirdW, color)
			svg.Ellipse(fullW-sixthW, fullH, fifthW, thirdW, color)
		case patternUpperWaves: // Chief Indented Banner
			svg.Ellipse(sixthW, 0, fifthW, thirdW, color)
			svg.Ellipse(halfW, 0, fifthW, thirdW, color)
			svg.Ellipse(fullW-sixthW, 0, fifthW, thirdW, color)
		case patternCircle: // Roundel Banner
			svg.Circle(halfW, halfH, thirdW, color)
		case patternDiamond: // Lozenge Banner
			svg.Poly4(halfW, fifthH,
				fullW-fifthW, halfH,
				halfW, fullH-fifthH,
				fifthW, halfH,
				color)
		case patternBorder: // Bordure Banner
			svg.Line(0, 0, fullW, 0, sixthW, color)
			svg.Line(fullW, 0, fullW, fullH, sixthW, color)
			svg.Line(0, fullH, fullW, fullH, sixthW, color)
			svg.Line(0, 0, 0, fullH, sixthW, color)
		case patternWaveBorder: // Black/Dyed Borduer Indented Banner
			br := halfH             // radius of circles in corners
			ofs1 := fifthH          // offset away from the center, corner circles
			brx := sixthH           // radius x of circles along the sides
			bry := fullH / 7        // radius y of circles along the sides
			ofs2 := sixthH / 3      // offset away from the center, side circles
			bh := fullH/4 + fullH/8 // placement of circles along the sides
			// corners
			svg.Ellipse(0-ofs1, 0-ofs1, br, br, color)
			svg.Ellipse(0-ofs1, fullH+ofs1, br, br, color)
			svg.Ellipse(fullW+ofs1, 0-ofs1, br, br, color)
			svg.Ellipse(fullW+ofs1, fullH+ofs1, br, br, color)
			// sides
			svg.Ellipse(0-ofs2, bh, brx, bry, color)
			svg.Ellipse(0-ofs2, fullH-bh, brx, bry, color)
			svg.Ellipse(fullW+ofs2, bh, brx, bry, color)
			svg.Ellipse(fullW+ofs2, fullH-bh, brx, bry, color)
		case patternBricks: // Black/Dyed Field Masoned Banner
			brickW := fullW / 4
			brickH := fullH / 13
			ofs := 0
			for y := 0; y < fullH; y += brickH + 1 {
				for x := ofs; x < fullW; x += brickW + 1 {
					svg.Box(x, y, brickW, brickH, color)
				}
				// Alternate the x offset for every row of bricks
				if ofs == 0 {
					ofs = -brickH
				} else {
					ofs = 0
				}
			}
		case patternGradientDown: // Gradient Banner
			ur, _ := strconv.ParseUint(color[1:3], 16, 0)
			ug, _ := strconv.ParseUint(color[3:5], 16, 0)
			ub, _ := strconv.ParseUint(color[5:7], 16, 0)
			r := int(ur)
			g := int(ug)
			b := int(ub)
			a := 1.0
			for y := 0; y < fullH; y++ {
				a = 1.0 - (float64(y) / float64(fullH))
				svg.Box(0, y, fullW, 1, onthefly.ColorStringAlpha(r, g, b, a))
			}
		case patternGradientUp: // Base Gradient Banner
			ur, _ := strconv.ParseUint(color[1:3], 16, 0)
			ug, _ := strconv.ParseUint(color[3:5], 16, 0)
			ub, _ := strconv.ParseUint(color[5:7], 16, 0)
			r := int(ur)
			g := int(ug)
			b := int(ub)
			a := 1.0
			for y := 0; y < fullH; y++ {
				a = float64(y) / float64(fullH)
				svg.Box(0, y, fullW, 1, onthefly.ColorStringAlpha(r, g, b, a))
			}
		case patternCreeper: // Black/Dyed Creeper Charge Banner
			// eyes
			svg.Box(sixthW-tenthW/2, thirdH-tenthH/2, halfW/2, halfW/2, color)
			svg.Box(fullW-(sixthW+halfW/2)+tenthW/2, thirdH-tenthH/2, halfW/2, halfW/2, color)
			// nose
			svg.Line(halfW, thirdH+tenthH, halfW, halfH+(halfW/3), halfW/2, color)
			// whiskers
			svg.Box(sixthW+sixthW, thirdH+fifthH, tenthW, halfW/2, color)
			svg.Box(fullW-(sixthW+sixthW)-tenthW, thirdH+fifthH, tenthW, halfW/2, color)
		case patternSkull: // Black/Dyed Skull Charge Banner
		case patternFlower: // Black/Dyed Flower Charge Banner
		case patternLogo: // Black/Dyed Mojang Charge Banner
		case patternFull:
			svg.Box(0, 0, fullW, fullH, color)
		}
	}
	// For debugging
	//svg.Pixel(0, 0, 0, 255, 0)
	//svg.Pixel(maxX, 0, 0, 0, 255)
	//svg.Pixel(0, maxY, 255, 255, 0)
	//svg.Pixel(maxX, maxY, 0, 255, 255)
}

// Generate a new SVG Page for a banner
func (b *Banner) SVGpage() *onthefly.Page {
	page, svg := onthefly.NewTinySVG(0, 0, bannerW, bannerH)
	desc := svg.AddNewTag("desc")
	desc.AddContent("A banner")
	b.Draw(svg)
	return page
}

func NewBanner() *Banner {
	return &Banner{}
}

func NewPattern(pattern, color int) *Pattern {
	if (pattern < patternLowerThird) || (pattern > patternFull) {
		log.Fatalln("Invalid pattern ID: ", pattern)
	}
	return &Pattern{pattern, color}
}

func (b *Banner) AddPattern(p *Pattern) {
	if len(b.patterns) > maxPatterns {
		log.Fatalln("Too many patters for banner, max", maxPatterns)
	}
	b.patterns = append(b.patterns, p)
}

// Generate a new onthefly Page (HTML5 and CSS combined)
func mainPage(svgurls []string) *onthefly.Page {

	title := "Banners"

	// Create a new HTML5 page, with CSS included
	page := onthefly.NewHTML5Page(title)
	page.AddContent(title)

	// Change the margin (em is default)
	page.SetMargin(4)

	// Change the font family
	page.SetFontFamily("sans-serif")

	// Change the color scheme
	page.SetColor("black", "#a0a0a0")

	// Include the generated SVG image on the page
	body, _ := page.GetTag("body")

	// CSS attributes for the body tag
	body.AddStyle("font-size", "2em")

	// Paragraph
	p := body.AddNewTag("p")

	// CSS style
	p.AddStyle("margin-top", "2em")

	// Add images
	var (
		tag          *onthefly.Tag
		useObjectTag = false
	)
	for _, svgurl := range svgurls {
		if useObjectTag {
			// object tag
			tag = p.AddNewTag("object")
			// HTML attributes
			tag.AddAttrib("data", svgurl)
			tag.AddAttrib("type", "image/svg+xml")
		} else {
			// img tag
			tag = p.AddNewTag("img")
			// HTML attributes
			tag.AddAttrib("src", svgurl)
			tag.AddAttrib("alt", "Banner")
		}
	}

	// CSS style
	w := strconv.Itoa(bannerW * zoomFactor)
	h := strconv.Itoa(bannerH * zoomFactor)
	tag.AddStyle("width", w+"px")
	tag.AddStyle("height", h+"px")
	tag.AddStyle("border", "8px solid black")

	return page
}

func randomColor() int {
	return colorWhite + rand.Intn((colorBlack-colorWhite)+1)
}

func seed() {
	rand.Seed(time.Now().UnixNano())
}

// Set up the paths and handlers then start serving.
func main() {
	seed()

	// Create a Negroni instance and a ServeMux instance
	n := negroni.Classic()
	mux := http.NewServeMux()

	var (
		svgurls []string
		b       *Banner
	)
	for i := patternLowerThird; i < patternLogo; i++ {

		b = NewBanner()
		b.AddPattern(NewPattern(patternFull, colorBrightWhite))
		b.AddPattern(NewPattern(i, colorRed))

		svgString := b.SVGpage().String()

		// Publish the generated SVG as "/img/banner_NNN.svg"
		svgurl := fmt.Sprintf("/img/banner_%d.svg", i)
		mux.HandleFunc(svgurl, func(w http.ResponseWriter, req *http.Request) {
			w.Header().Add("Content-Type", "image/svg+xml")
			fmt.Fprint(w, svgString)
		})

		svgurls = append(svgurls, svgurl)
	}

	// Generate a Page that includes the svg images
	page := mainPage(svgurls)
	// Publish the generated Page in a way that connects the HTML and CSS
	page.Publish(mux, "/", "/css/banner.css", false)

	// Handler goes last
	n.UseHandler(mux)

	// Listen for requests at port 3000
	n.Run(":3000")
}
<|endoftext|>


<commit_before>package main

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

var (
	name    = "vub"
	version = "0.3.1"

	flagset    = flag.NewFlagSet(name, flag.ContinueOnError)
	filetype   = flagset.String("filetype", "", "")
	listMode   = flagset.Bool("list", false, "")
	removeMode = flagset.Bool("remove", false, "")
	updateMode = flagset.Bool("update", false, "")
	isHelp     = flagset.Bool("help", false, "")
	isVersion  = flagset.Bool("version", false, "")
)

func init() {
	flagset.SetOutput(ioutil.Discard)
	flagset.StringVar(filetype, "f", "", "")
	flagset.BoolVar(listMode, "l", false, "")
	flagset.BoolVar(removeMode, "r", false, "")
	flagset.BoolVar(updateMode, "u", false, "")
	flagset.BoolVar(isHelp, "h", false, "")
}

func countTrue(bls ...bool) int {
	cnt := 0
	for _, b := range bls {
		if b {
			cnt++
		}
	}
	return cnt
}

func printShortUsage() {
	fmt.Fprintf(os.Stderr, `
Usage: %[1]s [OPTION]... URI...
Try '%[1]s --help' for more information.
`[1:], name)
}

func printUsage() {
	fmt.Fprintf(os.Stderr, `
Usage: %s [OPTION]... URI...
Install Vim plugin to under the management of vim-unbundle.

URI:
  sunaku/vim-unbundle                    # short URI
  https://github.com/sunaku/vim-unbundle # full URI

Options:
  -f, --filetype=TYPE       installing under the ftbundle/TYPE
  -l, --list                change the behavior to list packages
  -r, --remove              change the behavior to remove
  -u, --update              change the behavior to update
      --help                show this help message and exit
      --version             output version information and exit
`[1:], name)
}

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

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

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

	if !*listMode && flagset.NArg() < 1 {
		printShortUsage()
		return 2
	}
	if countTrue(*listMode, *removeMode, *updateMode) > 1 {
		printErr("cannot specify multiple mode")
		return 2
	}

	if *listMode {
		ListPackages(*filetype)
		return 0
	} else {
		var err error
		for _, uri := range flagset.Args() {
			p := NewPackage(uri, *filetype)
			switch {
			case *removeMode:
				err = p.Remove()
			case *updateMode:
				err = p.Update()
			default:
				err = p.Install()
			}
			if err != nil {
				printErr(err)
				return 1
			}
		}
		return 0
	}
}

func main() {
	e := _main()
	os.Exit(e)
}
<commit_msg>s/&main.name/&main.cmdName/<commit_after>package main

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

var (
	cmdName = "vub"
	version = "0.3.1"

	flagset    = flag.NewFlagSet(cmdName, flag.ContinueOnError)
	filetype   = flagset.String("filetype", "", "")
	listMode   = flagset.Bool("list", false, "")
	removeMode = flagset.Bool("remove", false, "")
	updateMode = flagset.Bool("update", false, "")
	isHelp     = flagset.Bool("help", false, "")
	isVersion  = flagset.Bool("version", false, "")
)

func init() {
	flagset.SetOutput(ioutil.Discard)
	flagset.StringVar(filetype, "f", "", "")
	flagset.BoolVar(listMode, "l", false, "")
	flagset.BoolVar(removeMode, "r", false, "")
	flagset.BoolVar(updateMode, "u", false, "")
	flagset.BoolVar(isHelp, "h", false, "")
}

func countTrue(bls ...bool) int {
	cnt := 0
	for _, b := range bls {
		if b {
			cnt++
		}
	}
	return cnt
}

func printShortUsage() {
	fmt.Fprintf(os.Stderr, `
Usage: %[1]s [OPTION]... URI...
Try '%[1]s --help' for more information.
`[1:], cmdName)
}

func printUsage() {
	fmt.Fprintf(os.Stderr, `
Usage: %s [OPTION]... URI...
Install Vim plugin to under the management of vim-unbundle.

URI:
  sunaku/vim-unbundle                    # short URI
  https://github.com/sunaku/vim-unbundle # full URI

Options:
  -f, --filetype=TYPE       installing under the ftbundle/TYPE
  -l, --list                change the behavior to list packages
  -r, --remove              change the behavior to remove
  -u, --update              change the behavior to update
      --help                show this help message and exit
      --version             output version information and exit
`[1:], cmdName)
}

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

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

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

	if !*listMode && flagset.NArg() < 1 {
		printShortUsage()
		return 2
	}
	if countTrue(*listMode, *removeMode, *updateMode) > 1 {
		printErr("cannot specify multiple mode")
		return 2
	}

	if *listMode {
		ListPackages(*filetype)
		return 0
	} else {
		var err error
		for _, uri := range flagset.Args() {
			p := NewPackage(uri, *filetype)
			switch {
			case *removeMode:
				err = p.Remove()
			case *updateMode:
				err = p.Update()
			default:
				err = p.Install()
			}
			if err != nil {
				printErr(err)
				return 1
			}
		}
		return 0
	}
}

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


<commit_before>package main

import (
	"os"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/docker/docker/cli/config"
)

// will be filled in at compile time
var gitCommit = ""

const (
	version = "0.8.0"
	usage   = "inspect and push manifest list images to a registry"
)

func main() {
	app := cli.NewApp()
	app.Name = os.Args[0]
	app.Version = version + " (commit: " + gitCommit + ")"
	app.Usage = usage
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "debug",
			Usage: "enable debug output",
		},
		cli.StringFlag{
			Name:  "username",
			Value: "",
			Usage: "registry username",
		},
		cli.StringFlag{
			Name:  "password",
			Value: "",
			Usage: "registry password",
		},
		cli.StringFlag{
			Name:  "docker-cfg",
			Value: config.Dir(),
			Usage: "Docker's cli config for auth",
		},
	}
	app.Before = func(c *cli.Context) error {
		if c.GlobalBool("debug") {
			logrus.SetLevel(logrus.DebugLevel)
		} else {
			logrus.SetLevel(logrus.WarnLevel)
		}
		return nil
	}
	// currently support inspect and pushml
	app.Commands = []cli.Command{
		inspectCmd,
		pushCmd,
	}

	if err := app.Run(os.Args); err != nil {
		logrus.Fatal(err)
	}
}
<commit_msg>Update version post-v0.8.0 release for dev<commit_after>package main

import (
	"os"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/docker/docker/cli/config"
)

// will be filled in at compile time
var gitCommit = ""

const (
	version = "0.9.0-dev"
	usage   = "inspect and push manifest list images to a registry"
)

func main() {
	app := cli.NewApp()
	app.Name = os.Args[0]
	app.Version = version + " (commit: " + gitCommit + ")"
	app.Usage = usage
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "debug",
			Usage: "enable debug output",
		},
		cli.StringFlag{
			Name:  "username",
			Value: "",
			Usage: "registry username",
		},
		cli.StringFlag{
			Name:  "password",
			Value: "",
			Usage: "registry password",
		},
		cli.StringFlag{
			Name:  "docker-cfg",
			Value: config.Dir(),
			Usage: "Docker's cli config for auth",
		},
	}
	app.Before = func(c *cli.Context) error {
		if c.GlobalBool("debug") {
			logrus.SetLevel(logrus.DebugLevel)
		} else {
			logrus.SetLevel(logrus.WarnLevel)
		}
		return nil
	}
	// currently support inspect and pushml
	app.Commands = []cli.Command{
		inspectCmd,
		pushCmd,
	}

	if err := app.Run(os.Args); err != nil {
		logrus.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	"./cmd"
)

func main() {
	rootCmd := cmd.GetRootCmd()

	if err := rootCmd.Execute(); err != nil {
		os.Exit(-1)
	}
}
<commit_msg>add version<commit_after>package main

import (
	"./cmd"
	"os"
)

var GitVersion string

func main() {

	rootCmd := cmd.GetRootCmd()
	rootCmd.Version = "0.2, Git: " + GitVersion

	if err := rootCmd.Execute(); err != nil {
		os.Exit(-1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"os"

	"github.com/spf13/cobra"
)

var (
	failOnFinding  bool
	analyzePackage string
)

var rootCmd = &cobra.Command{
	Use:   "tarp",
	Short: "tarp is a coverage helper tool",
	Long:  `tarp is a tool which aims to help ensure you have direct unit tests for all your declared functions for a particular Go package.`,
}

var analyzeCmd = &cobra.Command{
	Use:   "analyze",
	Short: "Analyze a given package",
	Long:  "Analyze takes a given package's code and determines which functions lack direct temp coverage.",
	PreRunE: func(cmd *cobra.Command, args []string) error {
		if analyzePackage == "" {
			return errors.New("the required flag `-p, --package` was not specified")
		}
		return nil
	},
	Run: func(cmd *cobra.Command, args []string) {
		analyze(analyzePackage, failOnFinding)
	},
}

func init() {
	rootCmd.AddCommand(analyzeCmd)
	analyzeCmd.Flags().BoolVarP(&failOnFinding, "fail-on-found", "f", false, "Call os.Exit(1) when functions without direct tests are found")
	analyzeCmd.Flags().StringVarP(&analyzePackage, "package", "p", "", "Package to run analyze on")
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
<commit_msg>Better analyze description<commit_after>package main

import (
	"errors"
	"fmt"
	"os"

	"github.com/spf13/cobra"
)

var (
	failOnFinding  bool
	analyzePackage string
)

var rootCmd = &cobra.Command{
	Use:   "tarp",
	Short: "tarp is a coverage helper tool",
	Long:  `tarp is a tool which aims to help ensure you have direct unit tests for all your declared functions for a particular Go package.`,
}

var analyzeCmd = &cobra.Command{
	Use:   "analyze",
	Short: "Analyze a given package",
	Long:  "Analyze takes a given package and determines which functions lack direct unit tests.",
	PreRunE: func(cmd *cobra.Command, args []string) error {
		if analyzePackage == "" {
			return errors.New("the required flag `-p, --package` was not specified")
		}
		return nil
	},
	Run: func(cmd *cobra.Command, args []string) {
		analyze(analyzePackage, failOnFinding)
	},
}

func init() {
	rootCmd.AddCommand(analyzeCmd)
	analyzeCmd.Flags().BoolVarP(&failOnFinding, "fail-on-found", "f", false, "Call os.Exit(1) when functions without direct tests are found")
	analyzeCmd.Flags().StringVarP(&analyzePackage, "package", "p", "", "Package to run analyze on")
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/blakesmith/ar"
)

type Conf struct {
	ListenPort   string   `json:"listenPort"`
	RootRepoPath string   `json:"rootRepoPath"`
	SupportArch  []string `json:"supportedArch"`
	DistroNames  []string `json:"distroNames"`
	EnableSSL    bool     `json:"enableSSL"`
	SSLCert      string   `json:"SSLcert"`
	SSLKey       string   `json:"SSLkey"`
}

func (c Conf) ArchPath(distro, arch string) string {
	return filepath.Join(c.RootRepoPath, "dists", distro, "main/binary-"+arch)
}

type DeleteObj struct {
	Filename   string
	DistroName string
	Arch       string
}

var (
	mutex        sync.Mutex
	configFile   = flag.String("c", "conf.json", "config file location")
	parsedConfig = Conf{}
)

func main() {
	flag.Parse()
	file, err := ioutil.ReadFile(*configFile)
	if err != nil {
		log.Fatal("unable to read config file, exiting...")
	}
	if err := json.Unmarshal(file, &parsedConfig); err != nil {
		log.Fatal("unable to marshal config file, exiting...")
	}

	if err := createDirs(parsedConfig); err != nil {
		log.Println(err)
		log.Fatalf("error creating directory structure, exiting")
	}

	http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(parsedConfig.RootRepoPath))))
	http.Handle("/upload", uploadHandler(parsedConfig))
	http.Handle("/delete", deleteHandler(parsedConfig))

	if parsedConfig.EnableSSL {
		log.Println("running with SSL enabled")
		log.Fatal(http.ListenAndServeTLS(":"+parsedConfig.ListenPort, parsedConfig.SSLCert, parsedConfig.SSLKey, nil))
	} else {
		log.Println("running without SSL enabled")
		log.Fatal(http.ListenAndServe(":"+parsedConfig.ListenPort, nil))
	}
}

func createDirs(config Conf) error {
	for _, distro := range config.DistroNames {
		for _, arch := range config.SupportArch {
			if _, err := os.Stat(config.ArchPath(distro, arch)); err != nil {
				if os.IsNotExist(err) {
					log.Printf("Directory for %s (%s) does not exist, creating", distro, arch)
					if err := os.MkdirAll(config.ArchPath(distro, arch), 0755); err != nil {
						return fmt.Errorf("error creating directory for %s (%s): %s", distro, arch, err)
					}
				} else {
					return fmt.Errorf("error inspecting %s (%s): %s", distro, arch, err)
				}
			}
		}
	}
	return nil
}

func inspectPackage(filename string) (string, error) {
	f, err := os.Open(filename)
	if err != nil {
		return "", fmt.Errorf("error opening package file %s: %s", filename, err)
	}

	arReader := ar.NewReader(f)
	defer f.Close()
	var controlBuf bytes.Buffer

	for {
		header, err := arReader.Next()

		if err == io.EOF {
			break
		}

		if err != nil {
			return "", fmt.Errorf("error in inspectPackage loop: %s", err)
		}

		if header.Name == "control.tar.gz" {
			io.Copy(&controlBuf, arReader)
			return inspectPackageControl(controlBuf)
		}

	}
	return "", nil
}

func inspectPackageControl(filename bytes.Buffer) (string, error) {
	gzf, err := gzip.NewReader(bytes.NewReader(filename.Bytes()))
	if err != nil {
		return "", fmt.Errorf("error creating gzip reader: %s", err)
	}

	tarReader := tar.NewReader(gzf)
	var controlBuf bytes.Buffer
	for {
		header, err := tarReader.Next()

		if err == io.EOF {
			break
		}

		if err != nil {
			return "", fmt.Errorf("failed to inspect package: %s", err)
		}

		name := header.Name

		switch header.Typeflag {
		case tar.TypeDir:
			continue
		case tar.TypeReg:
			if name == "./control" {
				io.Copy(&controlBuf, tarReader)
				return controlBuf.String(), nil
			}
		default:
			log.Printf(
				"Unable to figure out type : %c in file %s\n",
				header.Typeflag, name,
			)
		}
	}
	return "", nil
}

func createPackagesGz(config Conf, distro string, arch string) error {
	outfile, err := os.Create(filepath.Join(config.ArchPath(distro, arch), "Packages.gz"))
	if err != nil {
		return fmt.Errorf("failed to create packages.gz: %s", err)
	}
	defer outfile.Close()
	gzOut := gzip.NewWriter(outfile)
	defer gzOut.Close()
	// loop through each directory
	// run inspectPackage
	dirList, err := ioutil.ReadDir(config.ArchPath(distro, arch))
	if err != nil {
		return fmt.Errorf("scanning: %s: %s", config.ArchPath(distro, arch), err)
	}
	for _, debFile := range dirList {
		if strings.HasSuffix(debFile.Name(), "deb") {
			var packBuf bytes.Buffer
			debPath := filepath.Join(config.ArchPath(distro, arch), debFile.Name())
			tempCtlData, err := inspectPackage(debPath)
			if err != nil {
				return err
			}
			packBuf.WriteString(tempCtlData)
			dir := filepath.Join("dists", distro, "main/binary-"+arch, debFile.Name())
			fmt.Fprintf(&packBuf, "Filename: %s\n", dir)
			fmt.Fprintf(&packBuf, "Size: %d\n", debFile.Size())
			f, err := os.Open(debPath)
			if err != nil {
				log.Println("error opening deb file: ", err)
			}
			defer f.Close()

			var (
				md5hash    = md5.New()
				sha1hash   = sha1.New()
				sha256hash = sha256.New()
			)
			_, err = io.Copy(io.MultiWriter(md5hash, sha1hash, sha256hash), f)
			if err != nil {
				log.Println("error with the md5 hash: ", err)
			}
			fmt.Fprintf(&packBuf, "MD5sum: %s\n",
				hex.EncodeToString(md5hash.Sum(nil)))
			if _, err = io.Copy(sha1hash, f); err != nil {
				log.Println("error with the sha1 hash: ", err)
			}
			fmt.Fprintf(&packBuf, "SHA1: %s\n",
				hex.EncodeToString(sha1hash.Sum(nil)))
			if _, err = io.Copy(sha256hash, f); err != nil {
				log.Println("error with the sha256 hash: ", err)
			}
			fmt.Fprintf(&packBuf, "SHA256: %s\n",
				hex.EncodeToString(sha256hash.Sum(nil)))
			packBuf.WriteString("\n\n")
			gzOut.Write(packBuf.Bytes())
			f = nil
		}
	}

	gzOut.Flush()
	return nil
}

func uploadHandler(config Conf) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			http.Error(w, "method not supported", http.StatusMethodNotAllowed)
			return
		}
		archType := r.URL.Query().Get("arch")
		if archType == "" {
			archType = "all"
		}
		distroName := r.URL.Query().Get("distro")
		if distroName == "" {
			distroName = "stable"
		}
		reader, err := r.MultipartReader()
		if err != nil {
			httpErrorf(w, "error creating multipart reader: %s", err)
			return
		}
		for {
			part, err := reader.NextPart()
			if err == io.EOF {
				break
			}
			if part.FileName() == "" {
				continue
			}
			dst, err := os.Create(filepath.Join(config.ArchPath(distroName, archType), part.FileName()))
			if err != nil {
				httpErrorf(w, "error creating deb file: %s", err)
				return
			}
			defer dst.Close()
			if _, err := io.Copy(dst, part); err != nil {
				httpErrorf(w, "error writing deb file: %s", err)
				return
			}
		}
		mutex.Lock()
		defer mutex.Unlock()

		log.Println("got lock, updating package list...")
		if err := createPackagesGz(config, distroName, archType); err != nil {
			httpErrorf(w, "error creating package: %s", err)
			return
		}
		w.WriteHeader(http.StatusOK)
	})
}

func deleteHandler(config Conf) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "DELETE" {
			http.Error(w, "method not supported", http.StatusMethodNotAllowed)
			return
		}
		var toDelete DeleteObj
		if err := json.NewDecoder(r.Body).Decode(&toDelete); err != nil {
			httpErrorf(w, "failed to decode json: %s", err)
			return
		}
		debPath := filepath.Join(config.ArchPath(toDelete.DistroName, toDelete.Arch), toDelete.Filename)
		if err := os.Remove(debPath); err != nil {
			httpErrorf(w, "failed to delete: %s", err)
			return
		}
		mutex.Lock()
		defer mutex.Unlock()

		log.Println("got lock, updating package list...")
		if err := createPackagesGz(config, toDelete.DistroName, toDelete.Arch); err != nil {
			httpErrorf(w, "failed to create package: %s", err)
			return
		}
	})
}

func httpErrorf(w http.ResponseWriter, format string, a ...interface{}) {
	err := fmt.Errorf(format, a...)
	log.Println(err)
	http.Error(w, err.Error(), http.StatusInternalServerError)
}
<commit_msg>Revert extra type on distro<commit_after>package main

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/blakesmith/ar"
)

type Conf struct {
	ListenPort   string   `json:"listenPort"`
	RootRepoPath string   `json:"rootRepoPath"`
	SupportArch  []string `json:"supportedArch"`
	DistroNames  []string `json:"distroNames"`
	EnableSSL    bool     `json:"enableSSL"`
	SSLCert      string   `json:"SSLcert"`
	SSLKey       string   `json:"SSLkey"`
}

func (c Conf) ArchPath(distro, arch string) string {
	return filepath.Join(c.RootRepoPath, "dists", distro, "main/binary-"+arch)
}

type DeleteObj struct {
	Filename   string
	DistroName string
	Arch       string
}

var (
	mutex        sync.Mutex
	configFile   = flag.String("c", "conf.json", "config file location")
	parsedConfig = Conf{}
)

func main() {
	flag.Parse()
	file, err := ioutil.ReadFile(*configFile)
	if err != nil {
		log.Fatal("unable to read config file, exiting...")
	}
	if err := json.Unmarshal(file, &parsedConfig); err != nil {
		log.Fatal("unable to marshal config file, exiting...")
	}

	if err := createDirs(parsedConfig); err != nil {
		log.Println(err)
		log.Fatalf("error creating directory structure, exiting")
	}

	http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(parsedConfig.RootRepoPath))))
	http.Handle("/upload", uploadHandler(parsedConfig))
	http.Handle("/delete", deleteHandler(parsedConfig))

	if parsedConfig.EnableSSL {
		log.Println("running with SSL enabled")
		log.Fatal(http.ListenAndServeTLS(":"+parsedConfig.ListenPort, parsedConfig.SSLCert, parsedConfig.SSLKey, nil))
	} else {
		log.Println("running without SSL enabled")
		log.Fatal(http.ListenAndServe(":"+parsedConfig.ListenPort, nil))
	}
}

func createDirs(config Conf) error {
	for _, distro := range config.DistroNames {
		for _, arch := range config.SupportArch {
			if _, err := os.Stat(config.ArchPath(distro, arch)); err != nil {
				if os.IsNotExist(err) {
					log.Printf("Directory for %s (%s) does not exist, creating", distro, arch)
					if err := os.MkdirAll(config.ArchPath(distro, arch), 0755); err != nil {
						return fmt.Errorf("error creating directory for %s (%s): %s", distro, arch, err)
					}
				} else {
					return fmt.Errorf("error inspecting %s (%s): %s", distro, arch, err)
				}
			}
		}
	}
	return nil
}

func inspectPackage(filename string) (string, error) {
	f, err := os.Open(filename)
	if err != nil {
		return "", fmt.Errorf("error opening package file %s: %s", filename, err)
	}

	arReader := ar.NewReader(f)
	defer f.Close()
	var controlBuf bytes.Buffer

	for {
		header, err := arReader.Next()

		if err == io.EOF {
			break
		}

		if err != nil {
			return "", fmt.Errorf("error in inspectPackage loop: %s", err)
		}

		if header.Name == "control.tar.gz" {
			io.Copy(&controlBuf, arReader)
			return inspectPackageControl(controlBuf)
		}

	}
	return "", nil
}

func inspectPackageControl(filename bytes.Buffer) (string, error) {
	gzf, err := gzip.NewReader(bytes.NewReader(filename.Bytes()))
	if err != nil {
		return "", fmt.Errorf("error creating gzip reader: %s", err)
	}

	tarReader := tar.NewReader(gzf)
	var controlBuf bytes.Buffer
	for {
		header, err := tarReader.Next()

		if err == io.EOF {
			break
		}

		if err != nil {
			return "", fmt.Errorf("failed to inspect package: %s", err)
		}

		name := header.Name

		switch header.Typeflag {
		case tar.TypeDir:
			continue
		case tar.TypeReg:
			if name == "./control" {
				io.Copy(&controlBuf, tarReader)
				return controlBuf.String(), nil
			}
		default:
			log.Printf(
				"Unable to figure out type : %c in file %s\n",
				header.Typeflag, name,
			)
		}
	}
	return "", nil
}

func createPackagesGz(config Conf, distro, arch string) error {
	outfile, err := os.Create(filepath.Join(config.ArchPath(distro, arch), "Packages.gz"))
	if err != nil {
		return fmt.Errorf("failed to create packages.gz: %s", err)
	}
	defer outfile.Close()
	gzOut := gzip.NewWriter(outfile)
	defer gzOut.Close()
	// loop through each directory
	// run inspectPackage
	dirList, err := ioutil.ReadDir(config.ArchPath(distro, arch))
	if err != nil {
		return fmt.Errorf("scanning: %s: %s", config.ArchPath(distro, arch), err)
	}
	for _, debFile := range dirList {
		if strings.HasSuffix(debFile.Name(), "deb") {
			var packBuf bytes.Buffer
			debPath := filepath.Join(config.ArchPath(distro, arch), debFile.Name())
			tempCtlData, err := inspectPackage(debPath)
			if err != nil {
				return err
			}
			packBuf.WriteString(tempCtlData)
			dir := filepath.Join("dists", distro, "main/binary-"+arch, debFile.Name())
			fmt.Fprintf(&packBuf, "Filename: %s\n", dir)
			fmt.Fprintf(&packBuf, "Size: %d\n", debFile.Size())
			f, err := os.Open(debPath)
			if err != nil {
				log.Println("error opening deb file: ", err)
			}
			defer f.Close()

			var (
				md5hash    = md5.New()
				sha1hash   = sha1.New()
				sha256hash = sha256.New()
			)
			_, err = io.Copy(io.MultiWriter(md5hash, sha1hash, sha256hash), f)
			if err != nil {
				log.Println("error with the md5 hash: ", err)
			}
			fmt.Fprintf(&packBuf, "MD5sum: %s\n",
				hex.EncodeToString(md5hash.Sum(nil)))
			if _, err = io.Copy(sha1hash, f); err != nil {
				log.Println("error with the sha1 hash: ", err)
			}
			fmt.Fprintf(&packBuf, "SHA1: %s\n",
				hex.EncodeToString(sha1hash.Sum(nil)))
			if _, err = io.Copy(sha256hash, f); err != nil {
				log.Println("error with the sha256 hash: ", err)
			}
			fmt.Fprintf(&packBuf, "SHA256: %s\n",
				hex.EncodeToString(sha256hash.Sum(nil)))
			packBuf.WriteString("\n\n")
			gzOut.Write(packBuf.Bytes())
			f = nil
		}
	}

	gzOut.Flush()
	return nil
}

func uploadHandler(config Conf) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			http.Error(w, "method not supported", http.StatusMethodNotAllowed)
			return
		}
		archType := r.URL.Query().Get("arch")
		if archType == "" {
			archType = "all"
		}
		distroName := r.URL.Query().Get("distro")
		if distroName == "" {
			distroName = "stable"
		}
		reader, err := r.MultipartReader()
		if err != nil {
			httpErrorf(w, "error creating multipart reader: %s", err)
			return
		}
		for {
			part, err := reader.NextPart()
			if err == io.EOF {
				break
			}
			if part.FileName() == "" {
				continue
			}
			dst, err := os.Create(filepath.Join(config.ArchPath(distroName, archType), part.FileName()))
			if err != nil {
				httpErrorf(w, "error creating deb file: %s", err)
				return
			}
			defer dst.Close()
			if _, err := io.Copy(dst, part); err != nil {
				httpErrorf(w, "error writing deb file: %s", err)
				return
			}
		}
		mutex.Lock()
		defer mutex.Unlock()

		log.Println("got lock, updating package list...")
		if err := createPackagesGz(config, distroName, archType); err != nil {
			httpErrorf(w, "error creating package: %s", err)
			return
		}
		w.WriteHeader(http.StatusOK)
	})
}

func deleteHandler(config Conf) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "DELETE" {
			http.Error(w, "method not supported", http.StatusMethodNotAllowed)
			return
		}
		var toDelete DeleteObj
		if err := json.NewDecoder(r.Body).Decode(&toDelete); err != nil {
			httpErrorf(w, "failed to decode json: %s", err)
			return
		}
		debPath := filepath.Join(config.ArchPath(toDelete.DistroName, toDelete.Arch), toDelete.Filename)
		if err := os.Remove(debPath); err != nil {
			httpErrorf(w, "failed to delete: %s", err)
			return
		}
		mutex.Lock()
		defer mutex.Unlock()

		log.Println("got lock, updating package list...")
		if err := createPackagesGz(config, toDelete.DistroName, toDelete.Arch); err != nil {
			httpErrorf(w, "failed to create package: %s", err)
			return
		}
	})
}

func httpErrorf(w http.ResponseWriter, format string, a ...interface{}) {
	err := fmt.Errorf(format, a...)
	log.Println(err)
	http.Error(w, err.Error(), http.StatusInternalServerError)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/FarmRadioHangar/fessboxconfig/parser"
	"github.com/gernest/hot"
	"github.com/gorilla/mux"
)

//Config holds configuration values for this application.
type Config struct {
	Port           int64  `json:"port"`
	Host           string `json:"host"`
	StaticDir      string `json:"static_dir"`
	TemplatesDir   string `json:"templates_dir"`
	AsteriskConfig string `json:"asterisk_config_dir"`
}

func defaultConfig() *Config {
	return &Config{
		Port:           8080,
		Host:           "",
		StaticDir:      "static",
		TemplatesDir:   "templates",
		AsteriskConfig: "/etc/asterisk",
	}
}

func main() {
	c := flag.String("c", "conf/config.json", "path to the configuration file")
	dev := flag.Bool("dev", false, "set true if running in dev mode")
	flag.Parse()
	b, err := ioutil.ReadFile(*c)
	if err != nil {
		log.Fatal(err)
	}
	cfg := &Config{}
	err = json.Unmarshal(b, cfg)
	if err != nil {
		log.Fatal(err)
	}
	if *dev {
		cfg.AsteriskConfig = "sample"
		tmp, err := ioutil.TempDir("", "fconf")
		if err != nil {
			log.Fatal(err)
		}
		c := make(chan os.Signal, 1)
		signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)
		go func(dir string) {
		END:
			for {
				select {
				case <-c:
					log.Println("removing ", dir)
					_ = os.RemoveAll(dir)
					break END
				}
			}
		}(tmp)
		cfg.AsteriskConfig = tmp
		err = copyFiles(tmp, "sample")
		if err != nil {
			log.Fatal(err)
		}
	}
	s := newServer(cfg)
	log.Printf(" starting server on  localhost:%d\n", cfg.Port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", cfg.Port), s))
}

//copyFiles copies files from src to dst, directories are ignored
func copyFiles(dst, src string) error {
	files, err := ioutil.ReadDir(src)
	if err != nil {
		return err
	}
	for _, file := range files {
		if file.IsDir() {
			continue
		}
		fname := filepath.Join(src, file.Name())
		f, err := ioutil.ReadFile(fname)
		if err != nil {
			return err
		}
		err = ioutil.WriteFile(filepath.Join(dst, file.Name()), f, 0600)
		if err != nil {
			return err
		}
	}
	return nil
}

//newServer returns a http.Handler with all the routes for configuring supported
//devices registered.
func newServer(c *Config) http.Handler {
	s := mux.NewRouter()
	w := newWeb(c)
	s.HandleFunc("/device/dongle", w.Dongle).Methods("GET")
	s.HandleFunc("/device/dongle", w.UpdateDongle).Methods("POST")
	s.PathPrefix("/static/").
		Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(c.StaticDir))))
	s.HandleFunc("/", w.Home)
	return s
}

// web is the application struct, it defines all the handlers for this
// application as its methods.
//
// It is safe to use this in multiple goroutines
//
// This contains a a loaded hot template, so when run in development mode the
// templates will automatially be reloaded without the need to re run the
// application process. The auto reloading of templates is disabled in
// production.
type web struct {
	cfg *Config
	tpl *hot.Template
}

//newWeb intialises and returns a new instance of *web, the templates are loaded
//and if dev mode is set to true then auto reload is enabled.
func newWeb(cfg *Config) *web {
	w := &web{cfg: cfg}
	config := &hot.Config{
		Watch:          true,
		BaseName:       "fconf",
		Dir:            cfg.TemplatesDir,
		FilesExtension: []string{".tpl", ".html", ".tmpl"},
	}

	tpl, err := hot.New(config)
	if err != nil {
		panic(err)
	}
	w.tpl = tpl
	return w
}

//Home serves the home page
func (ww *web) Home(w http.ResponseWriter, r *http.Request) {
	err := ww.tpl.Execute(w, "index.html", nil)
	if err != nil {
		log.Println(err)
	}
}

type errMSG struct {
	Message string `json:"error"`
}

// Dongle implements http.HandleFunc for serving the dongle configuration values
// as a json object
//
// This only returns the current values of the dongle configuration file, so it
// is good for GET requests only.
func (ww *web) Dongle(w http.ResponseWriter, r *http.Request) {
	fName := filepath.Join(ww.cfg.AsteriskConfig, "dongle.conf")
	enc := json.NewEncoder(w)
	w.Header().Set("Content-Type", "application/json")

	f, err := os.Open(fName)
	if err != nil {
		log.Println(err)
		_ = enc.Encode(&errMSG{"trouble opening dongle configuration"})
		return
	}
	defer func() { _ = f.Close() }()
	p, err := parser.NewParser(f)
	if err != nil {
		log.Println(err)
		_ = enc.Encode(&errMSG{"trouble scanning dongle configuration"})
		return
	}
	ast, err := p.Parse()
	if err != nil {
		log.Println(err)
		_ = enc.Encode(&errMSG{"trouble parsing dongle configuration"})
		return
	}
	err = ast.ToJSON(w)
	if err != nil {
		log.Println(err)
	}
}

//UpdateDongle updates the dongle documentation file, via a json object. This
//doesnot do verification of the object sent with the request.
//
// The received json is loaded into ast and writen to the dongle configuration
// file directly.
//
// TODO(gernest) do some kind of verification?
func (ww *web) UpdateDongle(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	ast := &parser.Ast{}
	src := &bytes.Buffer{}
	enc := json.NewEncoder(w)
	_, err := io.Copy(src, r.Body)
	if err != nil {
		_ = enc.Encode(&errMSG{Message: "trouble reading request body"})
		return
	}
	err = ast.LoadJSON(src.Bytes())
	if err != nil {
		_ = enc.Encode(&errMSG{Message: "trouble loading request body"})
		return
	}
	fName := filepath.Join(ww.cfg.AsteriskConfig, "dongle.conf")
	info, err := os.Stat(fName)
	if err != nil {
		log.Println(err)
		_ = enc.Encode(&errMSG{"trouble opening dongle configuration"})
		return
	}
	dst := &bytes.Buffer{}
	parser.PrintAst(dst, ast)
	_ = ioutil.WriteFile(fName, dst.Bytes(), info.Mode())
	_, _ = io.Copy(w, src)
}
<commit_msg>Change configuration endpoints<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/FarmRadioHangar/fessboxconfig/parser"
	"github.com/gernest/hot"
	"github.com/gorilla/mux"
)

//Config holds configuration values for this application.
type Config struct {
	Port           int64  `json:"port"`
	Host           string `json:"host"`
	StaticDir      string `json:"static_dir"`
	TemplatesDir   string `json:"templates_dir"`
	AsteriskConfig string `json:"asterisk_config_dir"`
}

func defaultConfig() *Config {
	return &Config{
		Port:           8080,
		Host:           "",
		StaticDir:      "static",
		TemplatesDir:   "templates",
		AsteriskConfig: "/etc/asterisk",
	}
}

func main() {
	c := flag.String("c", "conf/config.json", "path to the configuration file")
	dev := flag.Bool("dev", false, "set true if running in dev mode")
	flag.Parse()
	b, err := ioutil.ReadFile(*c)
	if err != nil {
		log.Fatal(err)
	}
	cfg := &Config{}
	err = json.Unmarshal(b, cfg)
	if err != nil {
		log.Fatal(err)
	}
	if *dev {
		cfg.AsteriskConfig = "sample"
		tmp, err := ioutil.TempDir("", "fconf")
		if err != nil {
			log.Fatal(err)
		}
		c := make(chan os.Signal, 1)
		signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)
		go func(dir string) {
		END:
			for {
				select {
				case <-c:
					log.Println("removing ", dir)
					_ = os.RemoveAll(dir)
					break END
				}
			}
		}(tmp)
		cfg.AsteriskConfig = tmp
		err = copyFiles(tmp, "sample")
		if err != nil {
			log.Fatal(err)
		}
	}
	s := newServer(cfg)
	log.Printf(" starting server on  localhost:%d\n", cfg.Port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", cfg.Port), s))
}

//copyFiles copies files from src to dst, directories are ignored
func copyFiles(dst, src string) error {
	files, err := ioutil.ReadDir(src)
	if err != nil {
		return err
	}
	for _, file := range files {
		if file.IsDir() {
			continue
		}
		fname := filepath.Join(src, file.Name())
		f, err := ioutil.ReadFile(fname)
		if err != nil {
			return err
		}
		err = ioutil.WriteFile(filepath.Join(dst, file.Name()), f, 0600)
		if err != nil {
			return err
		}
	}
	return nil
}

//newServer returns a http.Handler with all the routes for configuring supported
//devices registered.
func newServer(c *Config) http.Handler {
	s := mux.NewRouter()
	w := newWeb(c)
	s.HandleFunc("/config/{filename}", w.Dongle).Methods("GET")
	s.HandleFunc("/config/{filename}", w.UpdateDongle).Methods("POST")
	s.PathPrefix("/static/").
		Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(c.StaticDir))))
	s.HandleFunc("/", w.Home)
	return s
}

// web is the application struct, it defines all the handlers for this
// application as its methods.
//
// It is safe to use this in multiple goroutines
//
// This contains a a loaded hot template, so when run in development mode the
// templates will automatially be reloaded without the need to re run the
// application process. The auto reloading of templates is disabled in
// production.
type web struct {
	cfg *Config
	tpl *hot.Template
}

//newWeb intialises and returns a new instance of *web, the templates are loaded
//and if dev mode is set to true then auto reload is enabled.
func newWeb(cfg *Config) *web {
	w := &web{cfg: cfg}
	config := &hot.Config{
		Watch:          true,
		BaseName:       "fconf",
		Dir:            cfg.TemplatesDir,
		FilesExtension: []string{".tpl", ".html", ".tmpl"},
	}

	tpl, err := hot.New(config)
	if err != nil {
		panic(err)
	}
	w.tpl = tpl
	return w
}

//Home serves the home page
func (ww *web) Home(w http.ResponseWriter, r *http.Request) {
	err := ww.tpl.Execute(w, "index.html", nil)
	if err != nil {
		log.Println(err)
	}
}

type errMSG struct {
	Message string `json:"error"`
}

// Dongle implements http.HandleFunc for serving the dongle configuration values
// as a json object
//
// This only returns the current values of the dongle configuration file, so it
// is good for GET requests only.
func (ww *web) Dongle(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	file := vars["filename"] + ".conf"
	fName := filepath.Join(ww.cfg.AsteriskConfig, file)
	enc := json.NewEncoder(w)
	w.Header().Set("Content-Type", "application/json")

	f, err := os.Open(fName)
	if err != nil {
		log.Println(err)
		_ = enc.Encode(&errMSG{"trouble opening dongle configuration"})
		return
	}
	defer func() { _ = f.Close() }()
	p, err := parser.NewParser(f)
	if err != nil {
		log.Println(err)
		_ = enc.Encode(&errMSG{"trouble scanning dongle configuration"})
		return
	}
	ast, err := p.Parse()
	if err != nil {
		log.Println(err)
		_ = enc.Encode(&errMSG{"trouble parsing dongle configuration"})
		return
	}
	err = ast.ToJSON(w)
	if err != nil {
		log.Println(err)
	}
}

//UpdateDongle updates the dongle documentation file, via a json object. This
//doesnot do verification of the object sent with the request.
//
// The received json is loaded into ast and writen to the dongle configuration
// file directly.
//
// TODO(gernest) do some kind of verification?
func (ww *web) UpdateDongle(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	ast := &parser.Ast{}
	src := &bytes.Buffer{}
	enc := json.NewEncoder(w)
	_, err := io.Copy(src, r.Body)
	if err != nil {
		_ = enc.Encode(&errMSG{Message: "trouble reading request body"})
		return
	}
	err = ast.LoadJSON(src.Bytes())
	if err != nil {
		_ = enc.Encode(&errMSG{Message: "trouble loading request body"})
		return
	}
	vars := mux.Vars(r)
	file := vars["filename"] + ".conf"
	fName := filepath.Join(ww.cfg.AsteriskConfig, file)
	info, err := os.Stat(fName)
	if err != nil {
		log.Println(err)
		_ = enc.Encode(&errMSG{"trouble opening dongle configuration"})
		return
	}
	dst := &bytes.Buffer{}
	parser.PrintAst(dst, ast)
	_ = ioutil.WriteFile(fName, dst.Bytes(), info.Mode())
	_, _ = io.Copy(w, src)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
)

var term *Terminal
var game *Game
var shouldReset = false

func main() {
	var size = flag.Int("size", 4, "Width/height of the grid.")
	var goal = flag.Int("goal", 2048, "Target score.")

	flag.Parse()

	if *size < 2 {
		fmt.Fprintln(os.Stderr, "Error: size must be at least 2.")
		os.Exit(1)
	}
	if *goal < 4 {
		fmt.Fprintln(os.Stderr, "Error: goal must be at least 4.")
		os.Exit(1)
	}
	SetGoal(*goal)

	var err error
	term, err = NewTerminal(os.Stdin, os.Stdout, "")
	handle(err)
	defer term.Reset()
	term.Println("Controls: wasd or arrow keys to play, enter to exit.")

	game = NewGame(*size)
	update()

	term.KeyCallback = doKeypress

	go func() {
		term.ReadLine()
		game.Finished.Done()
	}()

	game.Wait()

	if game.Won {
		term.Println("\nCongratulations!")
	} else if game.Lost {
		term.Println("\nYou lose.")
	}
}

func doKeypress(key int) {
	var dir Direction
	switch key {
	case 'w', KeyUp:
		dir = Up
	case 'a', KeyLeft:
		dir = Left
	case 's', KeyDown:
		dir = Down
	case 'd', KeyRight:
		dir = Right
	default:
		return
	}

	// Try the move.
	if game.Play(dir) {
		// Move was successful.
		update()
	}
}

func update() {
	if shouldReset {
		handle(reset())
	}
	term.Println("Score:", game.Score)
	term.Print(game)
	shouldReset = true
}

func reset() error {
	_, err := term.Write([]byte{'\r'})
	if err != nil {
		return err
	}
	term.ClearLine()
	for i := 0; i < game.Size*2+1; i++ {
		_, err := term.Write([]byte{keyEscape, '[', 'A'})
		if err != nil {
			return err
		}
		term.ClearLine()
	}
	return nil
}

func complain(msg string) {
	fmt.Fprintln(os.Stderr, msg)
	os.Exit(1)
}

func handle(err error) {
	if err != nil {
		panic(err)
	}
}
<commit_msg>Minor fix<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
)

var term *Terminal
var game *Game
var shouldReset = false

func main() {
	var size = flag.Int("size", 4, "Width/height of the grid.")
	var goal = flag.Int("goal", 2048, "Target score.")

	flag.Parse()

	if *size < 2 {
		complain("Error: size must be at least 2.")
	}
	if *goal < 4 {
		complain("Error: goal must be at least 4.")
	}
	SetGoal(*goal)

	var err error
	term, err = NewTerminal(os.Stdin, os.Stdout, "")
	handle(err)
	defer term.Reset()
	term.Println("Controls: wasd or arrow keys to play, enter to exit.")

	game = NewGame(*size)
	update()

	term.KeyCallback = doKeypress

	go func() {
		term.ReadLine()
		game.Finished.Done()
	}()

	game.Wait()

	if game.Won {
		term.Println("\nCongratulations!")
	} else if game.Lost {
		term.Println("\nYou lose.")
	}
}

func doKeypress(key int) {
	var dir Direction
	switch key {
	case 'w', KeyUp:
		dir = Up
	case 'a', KeyLeft:
		dir = Left
	case 's', KeyDown:
		dir = Down
	case 'd', KeyRight:
		dir = Right
	default:
		return
	}

	// Try the move.
	if game.Play(dir) {
		// Move was successful.
		update()
	}
}

func update() {
	if shouldReset {
		handle(reset())
	}
	term.Println("Score:", game.Score)
	term.Print(game)
	shouldReset = true
}

func reset() error {
	_, err := term.Write([]byte{'\r'})
	if err != nil {
		return err
	}
	term.ClearLine()
	for i := 0; i < game.Size*2+1; i++ {
		_, err := term.Write([]byte{keyEscape, '[', 'A'})
		if err != nil {
			return err
		}
		term.ClearLine()
	}
	return nil
}

func complain(msg string) {
	fmt.Fprintln(os.Stderr, msg)
	os.Exit(1)
}

func handle(err error) {
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"errors"
	"github.com/codegangsta/cli"
	"github.com/gi4nks/quant"
	"os"
	"os/exec"
	"strconv"
	"time"
)

var parrot = quant.NewParrot("ambros")

var settings = Settings{}
var repository = Repository{}

func initDB() {
	repository.InitDB()
	repository.InitSchema()
}

func closeDB() {
	repository.CloseDB()
}

func readSettings() {
	settings.LoadSettings()

	if settings.DebugMode() {
		parrot = quant.NewVerboseParrot("ambros")
	}

	parrot.Debug("Parrot is set to talk so much!")

}

func main() {
	readSettings()
	initDB()

	// -------------------
	app := cli.NewApp()
	app.Name = "ambros"
	app.Usage = "the personal command butler!!!!"
	app.Version = "0.0.1-alpha"
	app.Copyright = "gi4nks - 2015"

	app.Commands = []cli.Command{
		{
			Name:    "run",
			Aliases: []string{"ru"},
			Usage:   "run a command, remember to run the command with -- before. (./ambros r -- ls -la)",
			Action:  CmdRun,
		},
		{
			Name:    "list",
			Aliases: []string{"li"},
			Usage:   "list current configuration settings",
			Action:  CmdListSettings,
		},
		{
			Name:    "revive",
			Aliases: []string{"re"},
			Usage:   "revive ambros",
			Action:  CmdRevive,
		},
		{
			Name:    "logs",
			Aliases: []string{"lo"},
			Usage:   "show me the logs of ambros",
			Subcommands: []cli.Command{
				{
					Name:   "id",
					Usage:  "Get the log of specific id",
					Action: CmdLogsById,
				},
				{
					Name:   "all",
					Usage:  "Get all the logs",
					Action: CmdLogs,
				},
			},
		},
		{
			Name:    "last",
			Aliases: []string{"ll"},
			Usage:   "show the last executed commands ",
			Action:  CmdLast,
		},
		{
			Name:    "output",
			Aliases: []string{"ou"},
			Usage:   "show me the output of a command executed with ambros",
			Action:  CmdOutput,
		},
		{
			Name:    "recall",
			Aliases: []string{"rc"},
			Usage:   "recall a command and execute again",
			Action:  CmdRecall,
		},
	}

	app.Run(os.Args)

	defer closeDB()
}

// List of functions
func CmdRevive(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		parrot.Info("==> Reviving ambros will reinitialize all the statistics.")

		repository.BackupSchema()
		repository.InitSchema()
	})
}

func CmdLogs(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		var commands = repository.GetAllCommands()

		for _, c := range commands {
			parrot.Info(c.String())
		}
	})
}

func CmdLogsById(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		id, err := stringFromArguments(ctx)
		if err != nil {
			parrot.Error("Error...", err)
			return
		}

		var command = repository.FindById(id)

		parrot.Info(command.String())
	})
}

func CmdLast(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		limit, err := intFromArguments(ctx)
		if (err!= nil) {
			limit = settings.LastLimitDefault()
		}

		var commands = repository.GetExecutedCommands(limit)

		for _, c := range commands {
			parrot.Info(c.AsFlatCommand())
		}
	})
}

func CmdRecall(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		id, err := stringFromArguments(ctx)
		if err != nil {
			parrot.Error("Error...", err)
			return
		}

		var stored = repository.FindById(id)

		var command = initializeCommand(stored.Name, stored.Arguments)

		executeCommand(&command)
		finalizeCommand(&command)
	})
}

func CmdOutput(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		parrot.Debug("Output command invoked")

		id, err := stringFromArguments(ctx)
		if err != nil {
			parrot.Error("Error...", err)
			return
		}

		parrot.Debug("==> id: " + id)

		var command = repository.FindById(id)

		if (command.Output != "") {
			parrot.Info("==> Output:")
			parrot.Info(command.Output)
		}

		if (command.Error != "") {
			parrot.Info("==> Error:")
			parrot.Info(command.Error)
		}
	})
}

func CmdRun(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		var command = initializeCommand(ctx.Args()[0], ctx.Args().Tail())
		executeCommand(&command)
		finalizeCommand(&command)
	})
}

func CmdListSettings(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		parrot.Info(settings.String())
	})
}

func CmdWrapper(ctx *cli.Context) {
}

// ----------------
// Arguments from command string
// ----------------
func stringFromArguments(ctx *cli.Context) (string, error) {
	if !ctx.Args().Present() {
		return "", errors.New("Value must be provided!")
	}

	str := ctx.Args()[0]

	return str, nil
}

func intFromArguments(ctx *cli.Context) (int, error) {
	if !ctx.Args().Present() {
		return -1, errors.New("Value must be provided!")
	}

	i, err := strconv.Atoi(ctx.Args()[0])
	if err != nil {
		return -1, err
	}

	return i, nil
}

// ----------------
// command management
// ----------------
func initializeCommand(name string, arguments []string) Command {
	var command = Command{}
	command.ID = Random()

	command.Name = name
	command.Arguments = arguments

	command.CreatedAt = time.Now()
	return command
}

func finalizeCommand(command *Command) {
	command.TerminatedAt = time.Now()
	repository.Put(*command)

	parrot.Info("[" + command.ID + "]")

}

func executeCommand(command *Command) {
	var bufferOutput bytes.Buffer
	var bufferError bytes.Buffer

	cmd := exec.Command(command.Name, command.Arguments...)

	parrot.Debug("--> CommandName " + command.Name)
	parrot.Debug("--> Command Arguments " + AsJson(command.Arguments))

	outputReader, err := cmd.StdoutPipe()
	if err != nil {
		parrot.Error("Error creating StdoutPipe for Cmd", err)
		command.Error = err.Error()
		command.Status = false
		return
	}

	errorReader, err := cmd.StderrPipe()
	if err != nil {
		parrot.Error("Error creating StderrPipe for Cmd", err)
		command.Error = err.Error()
		command.Status = false
		return
	}

	err = cmd.Start()
	if err != nil {
		parrot.Error("Error starting Cmd", err)
		command.Error = err.Error()
		command.Status = false
		return
	}

	scannerOutput := bufio.NewScanner(outputReader)
	go func() {
		for scannerOutput.Scan() {
			parrot.Info(scannerOutput.Text())
			bufferOutput.WriteString(scannerOutput.Text() + "\n")
		}
	}()

	scannerError := bufio.NewScanner(errorReader)
	go func() {
		for scannerError.Scan() {
			parrot.Info(scannerError.Text())
			bufferError.WriteString(scannerError.Text() + "\n")
		}
	}()

	err = cmd.Wait()
	if err != nil {
		parrot.Error("Error waiting for Cmd", err)
		command.Error = err.Error()
		command.Status = false
		return
	}

	command.Output = bufferOutput.String()
	command.Error = bufferError.String()

	command.Status = true
}

// -------------------------------
// Cli command wrapper
// -------------------------------
func commandWrapper(ctx *cli.Context, cmd quant.Action0) {
	CmdWrapper(ctx)

	cmd()
}<commit_msg>added<commit_after>package main

import (
	"bufio"
	"bytes"
	"errors"
	"github.com/codegangsta/cli"
	"github.com/gi4nks/quant"
	"os"
	"os/exec"
	"strconv"
	"time"
)

var parrot = quant.NewParrot("ambros")

var settings = Settings{}
var repository = Repository{}

func initDB() {
	repository.InitDB()
	repository.InitSchema()
}

func closeDB() {
	repository.CloseDB()
}

func readSettings() {
	settings.LoadSettings()

	if settings.DebugMode() {
		parrot = quant.NewVerboseParrot("ambros")
	}

	parrot.Debug("Parrot is set to talk so much!")

}

func main() {
	readSettings()
	initDB()

	// -------------------
	app := cli.NewApp()
	app.Name = "ambros"
	app.Usage = "the personal command butler!!!!"
	app.Version = "0.0.1"
	app.Copyright = "gi4nks - 2015"

	app.Commands = []cli.Command{
		{
			Name:    "run",
			Aliases: []string{"ru"},
			Usage:   "run a command, remember to run the command with -- before. (./ambros r -- ls -la)",
			Action:  CmdRun,
		},
		{
			Name:    "list",
			Aliases: []string{"li"},
			Usage:   "list current configuration settings",
			Action:  CmdListSettings,
		},
		{
			Name:    "revive",
			Aliases: []string{"re"},
			Usage:   "revive ambros",
			Action:  CmdRevive,
		},
		{
			Name:    "logs",
			Aliases: []string{"lo"},
			Usage:   "show me the logs of ambros",
			Subcommands: []cli.Command{
				{
					Name:   "id",
					Usage:  "Get the log of specific id",
					Action: CmdLogsById,
				},
				{
					Name:   "all",
					Usage:  "Get all the logs",
					Action: CmdLogs,
				},
			},
		},
		{
			Name:    "last",
			Aliases: []string{"ll"},
			Usage:   "show the last executed commands ",
			Action:  CmdLast,
		},
		{
			Name:    "output",
			Aliases: []string{"ou"},
			Usage:   "show me the output of a command executed with ambros",
			Action:  CmdOutput,
		},
		{
			Name:    "recall",
			Aliases: []string{"rc"},
			Usage:   "recall a command and execute again",
			Action:  CmdRecall,
		},
	}

	app.Run(os.Args)

	defer closeDB()
}

// List of functions
func CmdRevive(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		parrot.Info("==> Reviving ambros will reinitialize all the statistics.")

		repository.BackupSchema()
		repository.InitSchema()
	})
}

func CmdLogs(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		var commands = repository.GetAllCommands()

		for _, c := range commands {
			parrot.Info(c.String())
		}
	})
}

func CmdLogsById(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		id, err := stringFromArguments(ctx)
		if err != nil {
			parrot.Error("Error...", err)
			return
		}

		var command = repository.FindById(id)

		parrot.Info(command.String())
	})
}

func CmdLast(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		limit, err := intFromArguments(ctx)
		if (err!= nil) {
			limit = settings.LastLimitDefault()
		}

		var commands = repository.GetExecutedCommands(limit)

		for _, c := range commands {
			parrot.Info(c.AsFlatCommand())
		}
	})
}

func CmdRecall(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		id, err := stringFromArguments(ctx)
		if err != nil {
			parrot.Error("Error...", err)
			return
		}

		var stored = repository.FindById(id)

		var command = initializeCommand(stored.Name, stored.Arguments)

		executeCommand(&command)
		finalizeCommand(&command)
	})
}

func CmdOutput(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		parrot.Debug("Output command invoked")

		id, err := stringFromArguments(ctx)
		if err != nil {
			parrot.Error("Error...", err)
			return
		}

		parrot.Debug("==> id: " + id)

		var command = repository.FindById(id)

		if (command.Output != "") {
			parrot.Info("==> Output:")
			parrot.Info(command.Output)
		}

		if (command.Error != "") {
			parrot.Info("==> Error:")
			parrot.Info(command.Error)
		}
	})
}

func CmdRun(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		var command = initializeCommand(ctx.Args()[0], ctx.Args().Tail())
		executeCommand(&command)
		finalizeCommand(&command)
	})
}

func CmdListSettings(ctx *cli.Context) {
	commandWrapper(ctx, func() {
		parrot.Info(settings.String())
	})
}

func CmdWrapper(ctx *cli.Context) {
}

// ----------------
// Arguments from command string
// ----------------
func stringFromArguments(ctx *cli.Context) (string, error) {
	if !ctx.Args().Present() {
		return "", errors.New("Value must be provided!")
	}

	str := ctx.Args()[0]

	return str, nil
}

func intFromArguments(ctx *cli.Context) (int, error) {
	if !ctx.Args().Present() {
		return -1, errors.New("Value must be provided!")
	}

	i, err := strconv.Atoi(ctx.Args()[0])
	if err != nil {
		return -1, err
	}

	return i, nil
}

// ----------------
// command management
// ----------------
func initializeCommand(name string, arguments []string) Command {
	var command = Command{}
	command.ID = Random()

	command.Name = name
	command.Arguments = arguments

	command.CreatedAt = time.Now()
	return command
}

func finalizeCommand(command *Command) {
	command.TerminatedAt = time.Now()
	repository.Put(*command)

	parrot.Info("[" + command.ID + "]")

}

func executeCommand(command *Command) {
	var bufferOutput bytes.Buffer
	var bufferError bytes.Buffer

	cmd := exec.Command(command.Name, command.Arguments...)

	parrot.Debug("--> CommandName " + command.Name)
	parrot.Debug("--> Command Arguments " + AsJson(command.Arguments))

	outputReader, err := cmd.StdoutPipe()
	if err != nil {
		parrot.Error("Error creating StdoutPipe for Cmd", err)
		command.Error = err.Error()
		command.Status = false
		return
	}

	errorReader, err := cmd.StderrPipe()
	if err != nil {
		parrot.Error("Error creating StderrPipe for Cmd", err)
		command.Error = err.Error()
		command.Status = false
		return
	}

	err = cmd.Start()
	if err != nil {
		parrot.Error("Error starting Cmd", err)
		command.Error = err.Error()
		command.Status = false
		return
	}

	scannerOutput := bufio.NewScanner(outputReader)
	go func() {
		for scannerOutput.Scan() {
			parrot.Info(scannerOutput.Text())
			bufferOutput.WriteString(scannerOutput.Text() + "\n")
		}
	}()

	scannerError := bufio.NewScanner(errorReader)
	go func() {
		for scannerError.Scan() {
			parrot.Info(scannerError.Text())
			bufferError.WriteString(scannerError.Text() + "\n")
		}
	}()

	err = cmd.Wait()
	if err != nil {
		parrot.Error("Error waiting for Cmd", err)
		command.Error = err.Error()
		command.Status = false
		return
	}

	command.Output = bufferOutput.String()
	command.Error = bufferError.String()

	command.Status = true
}

// -------------------------------
// Cli command wrapper
// -------------------------------
func commandWrapper(ctx *cli.Context, cmd quant.Action0) {
	CmdWrapper(ctx)

	cmd()
}<|endoftext|>


<commit_before>/*

Command rmdupes removes duplicated files by content hash.

Usage:

	rmdupes             # get help
	rmdupes path/to/dir # remove duplicated files in path/to/dir

*/
package main

import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"flag"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

var flags struct {
	hash          string
	printOnly     bool
	processHidden bool
	recursive, r  bool
}

func init() {
	flag.StringVar(&flags.hash, "hash", "sha512", "Hash to detect duplicated files (md5, sha1, sha256, sha512)")
	flag.BoolVar(&flags.printOnly, "print", false, "Only print files instead of removing")
	flag.BoolVar(&flags.processHidden, "hidden", false, "Process hidden (starting with dot) files/directories")
	flag.BoolVar(&flags.recursive, "recursive", false, "Process files recursively (descend into inner dirs)")
	flag.BoolVar(&flags.r, "r", false, "Process files recursively (descend into inner dirs)")
}

func main() {
	flag.Parse()
	if err := run(); err != nil {
		os.Stderr.WriteString(err.Error() + "\n")
		os.Exit(1)
	}
}

func run() error {
	paths := flag.Args()
	if len(paths) == 0 {
		flag.PrintDefaults()
		return nil
	}

	hasher := hasherByName(flags.hash)
	if hasher == nil {
		return fmt.Errorf("unknown hash: " + flags.hash + "; try these: md5, sha1, sha256, sha512")
	}

	walker := walkFiles
	if flags.recursive || flags.r {
		walker = walkFilesRecursive
	}

	onFile := deduper(hasher, flags.printOnly)

	for _, path := range paths {
		path, err := filepath.Abs(path)
		if err != nil {
			return err
		}

		if err := walker(path, flags.processHidden, onFile); err != nil {
			return err
		}
	}
	return nil
}

func hasherByName(name string) hash.Hash {
	switch name {
	case "md5":
		return md5.New()
	case "sha1":
		return sha1.New()
	case "sha256":
		return sha256.New()
	case "sha512":
		return sha512.New()
	default:
		return nil
	}
}

func walkFilesRecursive(path string, processHidden bool, cb func(path string) error) error {
	return filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !processHidden && strings.HasPrefix(info.Name(), ".") {
			if info.IsDir() {
				return filepath.SkipDir
			}
			return nil
		}

		if info.Mode().IsRegular() {
			return cb(path)
		}
		return nil
	})
}

func walkFiles(path string, processHidden bool, cb func(path string) error) error {
	stats, err := os.Stat(path)
	if err != nil {
		return err
	}

	if stats.Mode().IsRegular() {
		return cb(path)
	}
	if !stats.IsDir() {
		return nil
	}

	fis, err := ioutil.ReadDir(path)
	for _, fi := range fis {
		if !fi.Mode().IsRegular() {
			continue
		}
		if !processHidden && strings.HasPrefix(fi.Name(), "") {
			continue
		}

		fip := filepath.Join(path, fi.Name())
		if err := cb(fip); err != nil {
			return err
		}
	}
	return nil
}

func deduper(hasher hash.Hash, printOnly bool) func(path string) error {
	seen := map[string]struct{}{}
	dupe := map[string]string{}

	hashBytes := make([]byte, 0, hasher.Size())

	return func(path string) error {
		hasher.Reset()
		hashBytes = hashBytes[:0]

		if _, ok := seen[path]; ok {
			return nil
		}
		seen[path] = struct{}{}

		f, err := os.Open(path)
		if err != nil {
			return err
		}
		defer f.Close()

		if _, err := io.Copy(hasher, f); err != nil {
			return err
		}

		hashBytes = hasher.Sum(hashBytes)
		original, isDupe := dupe[string(hashBytes)]

		if !isDupe {
			dupe[string(hashBytes)] = path
			return nil
		}

		remove := path
		if len(filepath.Base(original)) > len(filepath.Base(path)) {
			remove = original
			dupe[string(hashBytes)] = path
		}

		fmt.Printf("%s\n", remove)

		if printOnly {
			return nil
		}
		return os.Remove(remove)
	}
}
<commit_msg>Doing no tests is bad :facepalm:<commit_after>/*

Command rmdupes removes duplicated files by content hash.

Usage:

	rmdupes             # get help
	rmdupes path/to/dir # remove duplicated files in path/to/dir

*/
package main

import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"flag"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

var flags struct {
	hash          string
	printOnly     bool
	processHidden bool
	recursive, r  bool
}

func init() {
	flag.StringVar(&flags.hash, "hash", "sha512", "Hash to detect duplicated files (md5, sha1, sha256, sha512)")
	flag.BoolVar(&flags.printOnly, "print", false, "Only print files instead of removing")
	flag.BoolVar(&flags.processHidden, "hidden", false, "Process hidden (starting with dot) files/directories")
	flag.BoolVar(&flags.recursive, "recursive", false, "Process files recursively (descend into inner dirs)")
	flag.BoolVar(&flags.r, "r", false, "Process files recursively (descend into inner dirs)")
}

func main() {
	flag.Parse()
	if err := run(); err != nil {
		os.Stderr.WriteString(err.Error() + "\n")
		os.Exit(1)
	}
}

func run() error {
	paths := flag.Args()
	if len(paths) == 0 {
		flag.PrintDefaults()
		return nil
	}

	hasher := hasherByName(flags.hash)
	if hasher == nil {
		return fmt.Errorf("unknown hash: " + flags.hash + "; try these: md5, sha1, sha256, sha512")
	}

	walker := walkFiles
	if flags.recursive || flags.r {
		walker = walkFilesRecursive
	}

	onFile := deduper(hasher, flags.printOnly)

	for _, path := range paths {
		path, err := filepath.Abs(path)
		if err != nil {
			return err
		}

		if err := walker(path, flags.processHidden, onFile); err != nil {
			return err
		}
	}
	return nil
}

func hasherByName(name string) hash.Hash {
	switch name {
	case "md5":
		return md5.New()
	case "sha1":
		return sha1.New()
	case "sha256":
		return sha256.New()
	case "sha512":
		return sha512.New()
	default:
		return nil
	}
}

func walkFilesRecursive(path string, processHidden bool, cb func(path string) error) error {
	return filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !processHidden && strings.HasPrefix(info.Name(), ".") {
			if info.IsDir() {
				return filepath.SkipDir
			}
			return nil
		}

		if info.Mode().IsRegular() {
			return cb(path)
		}
		return nil
	})
}

func walkFiles(path string, processHidden bool, cb func(path string) error) error {
	stats, err := os.Stat(path)
	if err != nil {
		return err
	}

	if stats.Mode().IsRegular() {
		return cb(path)
	}
	if !stats.IsDir() {
		return nil
	}

	fis, err := ioutil.ReadDir(path)
	if err != nil {
		return err
	}
	for _, fi := range fis {
		if !fi.Mode().IsRegular() {
			continue
		}
		if !processHidden && strings.HasPrefix(fi.Name(), ".") {
			continue
		}

		fip := filepath.Join(path, fi.Name())
		if err := cb(fip); err != nil {
			return err
		}
	}
	return nil
}

func deduper(hasher hash.Hash, printOnly bool) func(path string) error {
	seen := map[string]struct{}{}
	dupe := map[string]string{}

	hashBytes := make([]byte, 0, hasher.Size())

	return func(path string) error {
		hasher.Reset()
		hashBytes = hashBytes[:0]

		if _, ok := seen[path]; ok {
			return nil
		}
		seen[path] = struct{}{}

		f, err := os.Open(path)
		if err != nil {
			return err
		}
		defer f.Close()

		if _, err := io.Copy(hasher, f); err != nil {
			return err
		}

		hashBytes = hasher.Sum(hashBytes)
		original, isDupe := dupe[string(hashBytes)]

		if !isDupe {
			dupe[string(hashBytes)] = path
			return nil
		}

		remove := path
		if len(filepath.Base(original)) > len(filepath.Base(path)) {
			remove = original
			dupe[string(hashBytes)] = path
		}

		fmt.Printf("%s\n", remove)

		if printOnly {
			return nil
		}
		return os.Remove(remove)
	}
}
<|endoftext|>