<commit_before>// Package frame provides plan9-like editable text images on a raster display. This implementation
// preserves NUL bytes, and uses a set of replacement characters for undenderable text glyphs generated
// with a smaller sized font (hexidecimal or ascii representation).
//
// A frame's text is not addressable
package frame

import (
	"github.com/as/frame/box"
	"github.com/as/text"
	"image"
	"image/draw"
)

const (
	TickWidth = 3
	tickOff   = 0
	tickOn    = 1
)

func (f *Frame) RGBA() *image.RGBA {
	return f.b
}
func (f *Frame) Size() image.Point {
	r := f.RGBA().Bounds()
	return image.Pt(r.Dx(), r.Dy())
}

type Frame struct {
	box.Run
	Color
	Font         Font
	b            *image.RGBA
	r, entire    image.Rectangle
	maxtab       int
	lastlinefull int

	p0 int64
	p1 int64

	tick      draw.Image
	tickback  draw.Image
	Ticked    bool
	tickscale int
	tickoff   bool
	maxlines  int
	modified  bool
	noredraw  bool
	op        draw.Op

	text.Drawer

	//	npts int
	pts [][2]image.Point

	Scroll func(int)
	fr     *Frame

	hexFont *Font
	hex     []draw.Image
}

// New creates a new frame on b with bounds r. The image b is used
// as the frame's internal bitmap cache.
func New(r image.Rectangle, ft Font, b *image.RGBA, cols Color) *Frame {
	f := &Frame{
		Font:   ft,
		maxtab: 4 * ft.Measure(' '),
		Color:  cols,
		Run:    box.Run{Measure: ft.MeasureBytes},
		op:     draw.Src,
	}
	f.setrects(r, b)
	f.inittick()
	f.fr = new(Frame)
	f.renderHex()
	f.Drawer = text.NewCached()
	return f
}

// Dirty returns true if the contents of the frame have changes since the last redraw
func (f *Frame) Dirty() bool {
	return f.modified
}

// SetDirty alters the frame's internal state
func (f *Frame) SetDirty(dirty bool) {
	f.modified = dirty
}

// Reset resets the frame to display on image b with bounds r and font ft.
func (f *Frame) Reset(r image.Rectangle, b *image.RGBA, ft Font) {
	f.r = r
	f.b = b
	f.Font = ft
	f.Run.Reset(f.Font.MeasureBytes)
}

// Dx returns the width of s in pixels
func (f *Frame) Dx(s string) int {
	return f.Font.Dx(s)
}

// Dy returns the height of a glyphs bounding box
func (f *Frame) Dy() int {
	return f.Font.Dy()
}

// Bounds returns the frame's clipping rectangle
func (f *Frame) Bounds() image.Rectangle {
	return f.r.Bounds()
}

func (f *Frame) SetTick(style int) {
	f.tickoff = style == tickOff
}

// Full returns true if the last line in the frame is full
func (f *Frame) Full() bool {
	return f.lastlinefull == 1
}

// Maxline returns the max number of wrapped lines fitting on the frame
func (f *Frame) MaxLine() int {
	return f.maxlines
}

// Line returns the number of wrapped lines currently in the frame
func (f *Frame) Line() int {
	return f.Nlines
}

// Dot returns the range of the selected text
func (f *Frame) Dot() (p0, p1 int64) {
	return f.p0, f.p1
}

// Select sets the range of the selected text
func (f *Frame) Select(p0, p1 int64) {
	f.modified = true
	f.p0, f.p1 = p0, p1
}

func (f *Frame) inittick() {
	h := f.Font.height + (f.Font.height / 5)
	r := image.Rect(0, 0, TickWidth, h)
	f.tickscale = 1 // TODO implement scalesize
	f.tick = image.NewRGBA(r)
	f.tickback = image.NewRGBA(r)
	drawtick := func(x0, y0, x1, y1 int) {
		draw.Draw(f.tick, image.Rect(x0, y0, x1, y1), f.Color.Text, image.ZP, draw.Src)
	}
	drawtick(TickWidth/2, 0, TickWidth/2+1, h)
	drawtick(0, 0, TickWidth, h/5)
	drawtick(0, h-h/5, TickWidth, h)
}

func (f *Frame) setrects(r image.Rectangle, b *image.RGBA) {
	f.b = b
	f.entire = r
	f.r = r
	f.r.Max.Y -= f.r.Dy() % f.Font.height
	f.maxlines = f.r.Dy() / f.Font.height
}

func (f *Frame) clear(freeall bool) {
	if f.Nbox != 0 {
		f.Run.Delete(0, f.Nbox-1)
	}
	if f.Box != nil {
		free(f.Box)
	}
	if freeall {
		// TODO: unnecessary
		freeimage(f.tick)
		freeimage(f.tickback)
		f.tick = nil
		f.tickback = nil
	}
	f.Box = nil
	f.Ticked = false
}

func free(i interface{}) {
}
func freeimage(i image.Image) {
}
<commit_msg>SetFont<commit_after>// Package frame provides plan9-like editable text images on a raster display. This implementation
// preserves NUL bytes, and uses a set of replacement characters for undenderable text glyphs generated
// with a smaller sized font (hexidecimal or ascii representation).
//
// A frame's text is not addressable
package frame

import (
	"github.com/as/frame/box"
	"github.com/as/text"
	"image"
	"image/draw"
)

const (
	TickWidth = 3
	tickOff   = 0
	tickOn    = 1
)

func (f *Frame) RGBA() *image.RGBA {
	return f.b
}
func (f *Frame) Size() image.Point {
	r := f.RGBA().Bounds()
	return image.Pt(r.Dx(), r.Dy())
}

type Frame struct {
	box.Run
	Color
	Font         Font
	b            *image.RGBA
	r, entire    image.Rectangle
	maxtab       int
	lastlinefull int

	p0 int64
	p1 int64

	tick      draw.Image
	tickback  draw.Image
	Ticked    bool
	tickscale int
	tickoff   bool
	maxlines  int
	modified  bool
	noredraw  bool
	op        draw.Op

	text.Drawer

	//	npts int
	pts [][2]image.Point

	Scroll func(int)
	fr     *Frame

	hexFont *Font
	hex     []draw.Image
}

// New creates a new frame on b with bounds r. The image b is used
// as the frame's internal bitmap cache.
func New(r image.Rectangle, ft Font, b *image.RGBA, cols Color) *Frame {
	f := &Frame{
		Font:   ft,
		maxtab: 4 * ft.Measure(' '),
		Color:  cols,
		Run:    box.Run{Measure: ft.MeasureBytes},
		op:     draw.Src,
	}
	f.setrects(r, b)
	f.inittick()
	f.fr = new(Frame)
	f.renderHex()
	f.Drawer = text.NewCached()
	return f
}

// Dirty returns true if the contents of the frame have changes since the last redraw
func (f *Frame) Dirty() bool {
	return f.modified
}

// SetDirty alters the frame's internal state
func (f *Frame) SetDirty(dirty bool) {
	f.modified = dirty
}

// Reset resets the frame to display on image b with bounds r and font ft.
func (f *Frame) Reset(r image.Rectangle, b *image.RGBA, ft Font) {
	f.r = r
	f.b = b
}

func (f *Frame) SetFont(ft Font){
	f.Font = ft
	f.Run.Reset(f.Font.MeasureBytes)
	f.Refresh()
}

// Dx returns the width of s in pixels
func (f *Frame) Dx(s string) int {
	return f.Font.Dx(s)
}

// Dy returns the height of a glyphs bounding box
func (f *Frame) Dy() int {
	return f.Font.Dy()
}

// Bounds returns the frame's clipping rectangle
func (f *Frame) Bounds() image.Rectangle {
	return f.r.Bounds()
}

func (f *Frame) SetTick(style int) {
	f.tickoff = style == tickOff
}

// Full returns true if the last line in the frame is full
func (f *Frame) Full() bool {
	return f.lastlinefull == 1
}

// Maxline returns the max number of wrapped lines fitting on the frame
func (f *Frame) MaxLine() int {
	return f.maxlines
}

// Line returns the number of wrapped lines currently in the frame
func (f *Frame) Line() int {
	return f.Nlines
}

// Dot returns the range of the selected text
func (f *Frame) Dot() (p0, p1 int64) {
	return f.p0, f.p1
}

// Select sets the range of the selected text
func (f *Frame) Select(p0, p1 int64) {
	f.modified = true
	f.p0, f.p1 = p0, p1
}

func (f *Frame) inittick() {
	h := f.Font.height + (f.Font.height / 5)
	r := image.Rect(0, 0, TickWidth, h)
	f.tickscale = 1 // TODO implement scalesize
	f.tick = image.NewRGBA(r)
	f.tickback = image.NewRGBA(r)
	drawtick := func(x0, y0, x1, y1 int) {
		draw.Draw(f.tick, image.Rect(x0, y0, x1, y1), f.Color.Text, image.ZP, draw.Src)
	}
	drawtick(TickWidth/2, 0, TickWidth/2+1, h)
	drawtick(0, 0, TickWidth, h/5)
	drawtick(0, h-h/5, TickWidth, h)
}

func (f *Frame) setrects(r image.Rectangle, b *image.RGBA) {
	f.b = b
	f.entire = r
	f.r = r
	f.r.Max.Y -= f.r.Dy() % f.Font.height
	f.maxlines = f.r.Dy() / f.Font.height
}

func (f *Frame) clear(freeall bool) {
	if f.Nbox != 0 {
		f.Run.Delete(0, f.Nbox-1)
	}
	if f.Box != nil {
		free(f.Box)
	}
	if freeall {
		// TODO: unnecessary
		freeimage(f.tick)
		freeimage(f.tickback)
		f.tick = nil
		f.tickback = nil
	}
	f.Box = nil
	f.Ticked = false
}

func free(i interface{}) {
}
func freeimage(i image.Image) {
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/breunigs/frank/frank"
	irc "github.com/fluffle/goirc/client"
	"log"
	"strings"
)

//~ const instaJoin = "#chaos-hd"
const instaJoin = "#test"

const nickServPass = "k6CA9b9cfrLAPKmjlJGTIDO2bRyTN6"

const ircServer = "irc.twice-irc.de"

func main() {
	flag.Parse() // parses the logging flags. TODO

	c := irc.SimpleClient("frank", "frank", "Frank Böterrich der Zweite")
	c.SSL = true

	// connect
	c.AddHandler(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			log.Printf("Connected as: %s\n", conn.Me.Nick)
			conn.Privmsg("nickserv", "identify "+nickServPass)
			for _, cn := range strings.Split(instaJoin, " ") {
				if cn != "" {
					conn.Join(cn)
				}
			}
		})

	// react
	c.AddHandler("PRIVMSG",
		func(conn *irc.Conn, line *irc.Line) {
			//~ tgt := line.Args[0]
			//~ msg := line.Args[1]

			go func() { frank.RaumBang(conn, line) }()
			go func() { frank.UriFind(conn, line) }()

			//~ log.Printf("      Debug: tgt: %s, msg: %s\n", tgt, msg)
		})

	// auto follow invites
	c.AddHandler("INVITE",
		func(conn *irc.Conn, line *irc.Line) {
			tgt := line.Args[0]
			cnnl := line.Args[1]
			if conn.Me.Nick != tgt {
				log.Printf("WTF: received invite for %s but target was %s\n")
				return
			}

			log.Printf("Following invite for channel: %s\n", cnnl)
			conn.Join(cnnl)
		})

	// auto deop frank
	c.AddHandler("MODE",
		func(conn *irc.Conn, line *irc.Line) {
			if len(line.Args) != 3 {
				// mode statement cannot be not in a channel, so ignore
				return
			}

			if line.Args[2] != conn.Me.Nick {
				// not referring to us
				return
			}

			if line.Args[1] != "+o" {
				// not relevant
				return
			}

			cn := line.Args[0]
			conn.Mode(cn, "+v", conn.Me.Nick)
			conn.Mode(cn, "-o", conn.Me.Nick)
			conn.Privmsg(cn, line.Nick+": SKYNET® Protection activated")
		})

	// disconnect
	quit := make(chan bool)
	c.AddHandler(irc.DISCONNECTED,
		func(conn *irc.Conn, line *irc.Line) { quit <- true })

	// go go GO!
	if err := c.Connect(ircServer); err != nil {
		log.Printf("Connection error: %s\n", err)
	}

	log.Printf("Frank has booted\n")

	// Wait for disconnect
	<-quit
}
<commit_msg>implement help in private query.<commit_after>package main

import (
	"flag"
	"github.com/breunigs/frank/frank"
	irc "github.com/fluffle/goirc/client"
	"log"
	"strings"
)

//~ const instaJoin = "#chaos-hd"
const instaJoin = "#test"

const nickServPass = ""

const ircServer = "irc.twice-irc.de"

func main() {
	flag.Parse() // parses the logging flags. TODO

	c := irc.SimpleClient("frank", "frank", "Frank Böterrich der Zweite")
	c.SSL = true

	// connect
	c.AddHandler(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			log.Printf("Connected as: %s\n", conn.Me.Nick)
			conn.Privmsg("nickserv", "identify "+nickServPass)
			for _, cn := range strings.Split(instaJoin, " ") {
				if cn != "" {
					conn.Join(cn)
				}
			}
		})

	// react
	c.AddHandler("PRIVMSG",
		func(conn *irc.Conn, line *irc.Line) {
			//~ tgt := line.Args[0]
			//~ msg := line.Args[1]

			go func() { frank.RaumBang(conn, line) }()
			go func() { frank.UriFind(conn, line) }()

			if line.Args[0] == conn.Me.nick && line.Args[1] == "help" {
				conn.Privmsg(line.Nick, "It’s a game to find out what " + conn.Me.Nick + "can do.");
				conn.Privmsg(line.Nick, "1. Most likely I can find out the <title> of an URL, if:");
				conn.Privmsg(line.Nick, "  – I am in the channel where it is posted");
				conn.Privmsg(line.Nick, "  – you sent it in a query to me");
				conn.Privmsg(line.Nick, "  I’m going to cache that URL for a certain amount of time.");
				conn.Privmsg(line.Nick, "2. I’ll answer to !raum in certain channels.");
				conn.Privmsg(line.Nick, "If you need more details, please look at my source:");
				conn.Privmsg(line.Nick, "https://github.com/breunigs/frank");
			}

			//~ log.Printf("      Debug: tgt: %s, msg: %s\n", tgt, msg)
		})

	// auto follow invites
	c.AddHandler("INVITE",
		func(conn *irc.Conn, line *irc.Line) {
			tgt := line.Args[0]
			cnnl := line.Args[1]
			if conn.Me.Nick != tgt {
				log.Printf("WTF: received invite for %s but target was %s\n")
				return
			}

			log.Printf("Following invite for channel: %s\n", cnnl)
			conn.Join(cnnl)
		})

	// auto deop frank
	c.AddHandler("MODE",
		func(conn *irc.Conn, line *irc.Line) {
			if len(line.Args) != 3 {
				// mode statement cannot be not in a channel, so ignore
				return
			}

			if line.Args[2] != conn.Me.Nick {
				// not referring to us
				return
			}

			if line.Args[1] != "+o" {
				// not relevant
				return
			}

			cn := line.Args[0]
			conn.Mode(cn, "+v", conn.Me.Nick)
			conn.Mode(cn, "-o", conn.Me.Nick)
			conn.Privmsg(cn, line.Nick+": SKYNET® Protection activated")
		})

	// disconnect
	quit := make(chan bool)
	c.AddHandler(irc.DISCONNECTED,
		func(conn *irc.Conn, line *irc.Line) { quit <- true })

	// go go GO!
	if err := c.Connect(ircServer); err != nil {
		log.Printf("Connection error: %s\n", err)
	}

	log.Printf("Frank has booted\n")

	// Wait for disconnect
	<-quit
}
<|endoftext|>


<commit_before>// Copyright (C) 2012 Mostafa Hajizadeh
// Copyright (C) 2014-5 Steve Francia

// package fsync keeps two files or directories in sync.
//
//         err := fsync.Sync("~/dst", ".")
//
// After the above code, if err is nil, every file and directory in the current
// directory is copied to ~/dst and has the same permissions. Consequent calls
// will only copy changed or new files.
//
// SyncTo is a helper function which helps you sync a groups of files or
// directories into a single destination. For instance, calling
//
//     SyncTo("public", "build/app.js", "build/app.css", "images", "fonts")
//
// is equivalent to calling
//
//     Sync("public/app.js", "build/app.js")
//     Sync("public/app.css", "build/app.css")
//     Sync("public/images", "images")
//     Sync("public/fonts", "fonts")
//
// Actually, this is how SyncTo is implemented: consequent calls to Sync.
//
// By default, sync code ignores extra files in the destination that don’t have
// identicals in the source. Setting Delete field of a Syncer to true changes
// this behavior and deletes these extra files.

package fsync

import (
	"bytes"
	"errors"
	"io"
	"os"
	"path"
	"path/filepath"
	"runtime"

	"github.com/spf13/afero"
)

var (
	ErrFileOverDir = errors.New(
		"fsync: trying to overwrite a non-empty directory with a file")
)

// Sync copies files and directories inside src into dst.
func Sync(dst, src string) error {
	return NewSyncer().Sync(dst, src)
}

// SyncTo syncs srcs files and directories into to directory.
func SyncTo(to string, srcs ...string) error {
	return NewSyncer().SyncTo(to, srcs...)
}

// Type Syncer provides functions for syncing files.
type Syncer struct {
	// Set this to true to delete files in the destination that don't exist
	// in the source.
	Delete bool
	// By default, modification times are synced. This can be turned off by
	// setting this to true.
	NoTimes bool
	// TODO add options for not checking content for equality

	SrcFs  afero.Fs
	DestFs afero.Fs
}

// NewSyncer creates a new instance of Syncer with default options.
func NewSyncer() *Syncer {
	return &Syncer{SrcFs: new(afero.OsFs), DestFs: new(afero.OsFs)}
}

// Sync copies files and directories inside src into dst.
func (s *Syncer) Sync(dst, src string) error {
	// make sure src exists
	if _, err := s.SrcFs.Stat(src); err != nil {
		return err
	}
	// return error instead of replacing a non-empty directory with a file
	if b, err := s.checkDir(dst, src); err != nil {
		return err
	} else if b {
		return ErrFileOverDir
	}

	return s.syncRecover(dst, src)
}

// SyncTo syncs srcs files or directories into to directory.
func (s *Syncer) SyncTo(to string, srcs ...string) error {
	for _, src := range srcs {
		dst := filepath.Join(to, path.Base(src))
		if err := s.Sync(dst, src); err != nil {
			return err
		}
	}
	return nil
}

// syncRecover handles errors and calls sync
func (s *Syncer) syncRecover(dst, src string) (err error) {
	defer func() {
		if r := recover(); r != nil {
			if _, ok := r.(runtime.Error); ok {
				panic(r)
			}
			err = r.(error)
		}
	}()

	s.sync(dst, src)
	return nil
}

// sync updates dst to match with src, handling both files and directories.
func (s *Syncer) sync(dst, src string) {
	// sync permissions and modification times after handling content
	defer s.syncstats(dst, src)

	// read files info
	dstat, err := s.SrcFs.Stat(dst)
	if err != nil && !os.IsNotExist(err) {
		panic(err)
	}
	sstat, err := s.SrcFs.Stat(src)
	if err != nil && os.IsNotExist(err) {
		return // src was deleted before we could copy it
	}
	check(err)

	if !sstat.IsDir() {
		// src is a file
		// delete dst if its a directory
		if dstat != nil && dstat.IsDir() {
			check(s.DestFs.RemoveAll(dst))
		}
		if !s.equal(dst, src) {
			// perform copy
			df, err := s.DestFs.Create(dst)
			check(err)
			defer df.Close()
			sf, err := s.SrcFs.Open(src)
			if os.IsNotExist(err) {
				return
			}
			check(err)
			defer sf.Close()
			_, err = io.Copy(df, sf)
			if os.IsNotExist(err) {
				return
			}
			check(err)
		}
		return
	}

	// src is a directory
	// make dst if necessary
	if dstat == nil {
		// dst does not exist; create directory
		check(s.DestFs.MkdirAll(dst, 0755)) // permissions will be synced later
	} else if !dstat.IsDir() {
		// dst is a file; remove and create directory
		check(s.DestFs.Remove(dst))
		check(s.DestFs.MkdirAll(dst, 0755)) // permissions will be synced later
	}

	// go through sf files and sync them
	files, err := afero.ReadDir(s.SrcFs, src)
	if os.IsNotExist(err) {
		return
	}
	check(err)
	// make a map of filenames for quick lookup; used in deletion
	// deletion below
	m := make(map[string]bool, len(files))
	for _, file := range files {
		dst2 := filepath.Join(dst, file.Name())
		src2 := filepath.Join(src, file.Name())
		s.sync(dst2, src2)
		m[file.Name()] = true
	}

	// delete files from dst that does not exist in src
	if s.Delete {
		files, err = afero.ReadDir(s.DestFs, dst)
		check(err)
		for _, file := range files {
			if !m[file.Name()] {
				check(s.DestFs.RemoveAll(filepath.Join(dst, file.Name())))
			}
		}
	}
}

// syncstats makes sure dst has the same pemissions and modification time as src
func (s *Syncer) syncstats(dst, src string) {
	// get file infos; return if not exist and panic if error
	dstat, err1 := s.DestFs.Stat(dst)
	sstat, err2 := s.SrcFs.Stat(src)
	if os.IsNotExist(err1) || os.IsNotExist(err2) {
		return
	}
	check(err1)
	check(err2)

	// update dst's permission bits
	if dstat.Mode().Perm() != sstat.Mode().Perm() {
		check(s.DestFs.Chmod(dst, sstat.Mode().Perm()))
	}

	// update dst's modification time
	if !s.NoTimes {
		if !dstat.ModTime().Equal(sstat.ModTime()) {
			err := s.DestFs.Chtimes(dst, sstat.ModTime(), sstat.ModTime())
			check(err)
		}
	}
}

// equal returns true if both files are equal
func (s *Syncer) equal(a, b string) bool {
	// get file infos
	info1, err1 := os.Stat(a)
	info2, err2 := os.Stat(b)
	if os.IsNotExist(err1) || os.IsNotExist(err2) {
		return false
	}
	check(err1)
	check(err2)

	// check sizes
	if info1.Size() != info2.Size() {
		return false
	}

	// both have the same size, check the contents
	f1, err := os.Open(a)
	check(err)
	defer f1.Close()
	f2, err := os.Open(b)
	check(err)
	defer f2.Close()
	buf1 := make([]byte, 1000)
	buf2 := make([]byte, 1000)
	for {
		// read from both
		n1, err := f1.Read(buf1)
		if err != nil && err != io.EOF {
			panic(err)
		}
		n2, err := f2.Read(buf2)
		if err != nil && err != io.EOF {
			panic(err)
		}

		// compare read bytes
		if !bytes.Equal(buf1[:n1], buf2[:n2]) {
			return false
		}

		// end of both files
		if n1 == 0 && n2 == 0 {
			break
		}
	}

	return true
}

// checkDir returns true if dst is a non-empty directory and src is a file
func (s *Syncer) checkDir(dst, src string) (b bool, err error) {
	// read file info
	dstat, err := os.Stat(dst)
	if os.IsNotExist(err) {
		return false, nil
	} else if err != nil {
		return false, err
	}
	sstat, err := os.Stat(src)
	if err != nil {
		return false, err
	}

	// return false is dst is not a directory or src is a directory
	if !dstat.IsDir() || sstat.IsDir() {
		return false, nil
	}

	// dst is a directory and src is a file
	// check if dst is non-empty
	// read dst directory

	files, err := afero.ReadDir(s.DestFs, dst)
	if err != nil {
		return false, err
	}
	if len(files) > 0 {
		return true, nil
	}
	return false, nil
}

func check(err error) {
	if err != nil {
		panic(err)
	}
}
<commit_msg>fix spf13/hugo#1800, typo found by @Skrylar<commit_after>// Copyright (C) 2012 Mostafa Hajizadeh
// Copyright (C) 2014-5 Steve Francia

// package fsync keeps two files or directories in sync.
//
//         err := fsync.Sync("~/dst", ".")
//
// After the above code, if err is nil, every file and directory in the current
// directory is copied to ~/dst and has the same permissions. Consequent calls
// will only copy changed or new files.
//
// SyncTo is a helper function which helps you sync a groups of files or
// directories into a single destination. For instance, calling
//
//     SyncTo("public", "build/app.js", "build/app.css", "images", "fonts")
//
// is equivalent to calling
//
//     Sync("public/app.js", "build/app.js")
//     Sync("public/app.css", "build/app.css")
//     Sync("public/images", "images")
//     Sync("public/fonts", "fonts")
//
// Actually, this is how SyncTo is implemented: consequent calls to Sync.
//
// By default, sync code ignores extra files in the destination that don’t have
// identicals in the source. Setting Delete field of a Syncer to true changes
// this behavior and deletes these extra files.

package fsync

import (
	"bytes"
	"errors"
	"io"
	"os"
	"path"
	"path/filepath"
	"runtime"

	"github.com/spf13/afero"
)

var (
	ErrFileOverDir = errors.New(
		"fsync: trying to overwrite a non-empty directory with a file")
)

// Sync copies files and directories inside src into dst.
func Sync(dst, src string) error {
	return NewSyncer().Sync(dst, src)
}

// SyncTo syncs srcs files and directories into to directory.
func SyncTo(to string, srcs ...string) error {
	return NewSyncer().SyncTo(to, srcs...)
}

// Type Syncer provides functions for syncing files.
type Syncer struct {
	// Set this to true to delete files in the destination that don't exist
	// in the source.
	Delete bool
	// By default, modification times are synced. This can be turned off by
	// setting this to true.
	NoTimes bool
	// TODO add options for not checking content for equality

	SrcFs  afero.Fs
	DestFs afero.Fs
}

// NewSyncer creates a new instance of Syncer with default options.
func NewSyncer() *Syncer {
	return &Syncer{SrcFs: new(afero.OsFs), DestFs: new(afero.OsFs)}
}

// Sync copies files and directories inside src into dst.
func (s *Syncer) Sync(dst, src string) error {
	// make sure src exists
	if _, err := s.SrcFs.Stat(src); err != nil {
		return err
	}
	// return error instead of replacing a non-empty directory with a file
	if b, err := s.checkDir(dst, src); err != nil {
		return err
	} else if b {
		return ErrFileOverDir
	}

	return s.syncRecover(dst, src)
}

// SyncTo syncs srcs files or directories into to directory.
func (s *Syncer) SyncTo(to string, srcs ...string) error {
	for _, src := range srcs {
		dst := filepath.Join(to, path.Base(src))
		if err := s.Sync(dst, src); err != nil {
			return err
		}
	}
	return nil
}

// syncRecover handles errors and calls sync
func (s *Syncer) syncRecover(dst, src string) (err error) {
	defer func() {
		if r := recover(); r != nil {
			if _, ok := r.(runtime.Error); ok {
				panic(r)
			}
			err = r.(error)
		}
	}()

	s.sync(dst, src)
	return nil
}

// sync updates dst to match with src, handling both files and directories.
func (s *Syncer) sync(dst, src string) {
	// sync permissions and modification times after handling content
	defer s.syncstats(dst, src)

	// read files info
	dstat, err := s.DestFs.Stat(dst)
	if err != nil && !os.IsNotExist(err) {
		panic(err)
	}
	sstat, err := s.SrcFs.Stat(src)
	if err != nil && os.IsNotExist(err) {
		return // src was deleted before we could copy it
	}
	check(err)

	if !sstat.IsDir() {
		// src is a file
		// delete dst if its a directory
		if dstat != nil && dstat.IsDir() {
			check(s.DestFs.RemoveAll(dst))
		}
		if !s.equal(dst, src) {
			// perform copy
			df, err := s.DestFs.Create(dst)
			check(err)
			defer df.Close()
			sf, err := s.SrcFs.Open(src)
			if os.IsNotExist(err) {
				return
			}
			check(err)
			defer sf.Close()
			_, err = io.Copy(df, sf)
			if os.IsNotExist(err) {
				return
			}
			check(err)
		}
		return
	}

	// src is a directory
	// make dst if necessary
	if dstat == nil {
		// dst does not exist; create directory
		check(s.DestFs.MkdirAll(dst, 0755)) // permissions will be synced later
	} else if !dstat.IsDir() {
		// dst is a file; remove and create directory
		check(s.DestFs.Remove(dst))
		check(s.DestFs.MkdirAll(dst, 0755)) // permissions will be synced later
	}

	// go through sf files and sync them
	files, err := afero.ReadDir(s.SrcFs, src)
	if os.IsNotExist(err) {
		return
	}
	check(err)
	// make a map of filenames for quick lookup; used in deletion
	// deletion below
	m := make(map[string]bool, len(files))
	for _, file := range files {
		dst2 := filepath.Join(dst, file.Name())
		src2 := filepath.Join(src, file.Name())
		s.sync(dst2, src2)
		m[file.Name()] = true
	}

	// delete files from dst that does not exist in src
	if s.Delete {
		files, err = afero.ReadDir(s.DestFs, dst)
		check(err)
		for _, file := range files {
			if !m[file.Name()] {
				check(s.DestFs.RemoveAll(filepath.Join(dst, file.Name())))
			}
		}
	}
}

// syncstats makes sure dst has the same pemissions and modification time as src
func (s *Syncer) syncstats(dst, src string) {
	// get file infos; return if not exist and panic if error
	dstat, err1 := s.DestFs.Stat(dst)
	sstat, err2 := s.SrcFs.Stat(src)
	if os.IsNotExist(err1) || os.IsNotExist(err2) {
		return
	}
	check(err1)
	check(err2)

	// update dst's permission bits
	if dstat.Mode().Perm() != sstat.Mode().Perm() {
		check(s.DestFs.Chmod(dst, sstat.Mode().Perm()))
	}

	// update dst's modification time
	if !s.NoTimes {
		if !dstat.ModTime().Equal(sstat.ModTime()) {
			err := s.DestFs.Chtimes(dst, sstat.ModTime(), sstat.ModTime())
			check(err)
		}
	}
}

// equal returns true if both files are equal
func (s *Syncer) equal(a, b string) bool {
	// get file infos
	info1, err1 := os.Stat(a)
	info2, err2 := os.Stat(b)
	if os.IsNotExist(err1) || os.IsNotExist(err2) {
		return false
	}
	check(err1)
	check(err2)

	// check sizes
	if info1.Size() != info2.Size() {
		return false
	}

	// both have the same size, check the contents
	f1, err := os.Open(a)
	check(err)
	defer f1.Close()
	f2, err := os.Open(b)
	check(err)
	defer f2.Close()
	buf1 := make([]byte, 1000)
	buf2 := make([]byte, 1000)
	for {
		// read from both
		n1, err := f1.Read(buf1)
		if err != nil && err != io.EOF {
			panic(err)
		}
		n2, err := f2.Read(buf2)
		if err != nil && err != io.EOF {
			panic(err)
		}

		// compare read bytes
		if !bytes.Equal(buf1[:n1], buf2[:n2]) {
			return false
		}

		// end of both files
		if n1 == 0 && n2 == 0 {
			break
		}
	}

	return true
}

// checkDir returns true if dst is a non-empty directory and src is a file
func (s *Syncer) checkDir(dst, src string) (b bool, err error) {
	// read file info
	dstat, err := os.Stat(dst)
	if os.IsNotExist(err) {
		return false, nil
	} else if err != nil {
		return false, err
	}
	sstat, err := os.Stat(src)
	if err != nil {
		return false, err
	}

	// return false is dst is not a directory or src is a directory
	if !dstat.IsDir() || sstat.IsDir() {
		return false, nil
	}

	// dst is a directory and src is a file
	// check if dst is non-empty
	// read dst directory

	files, err := afero.ReadDir(s.DestFs, dst)
	if err != nil {
		return false, err
	}
	if len(files) > 0 {
		return true, nil
	}
	return false, nil
}

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


<commit_before>//file is borrowed from github.com/kelseyhightower/confd

package main

import (
	"encoding/json"
	"errors"
	"os"
	"path"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/fatih/structs"
)

////////////////////////////////////////////////////////////////////////////////
func Inspect(args ...interface{}) {
	spew.Dump(args)
}

func newFuncMap() map[string]interface{} {
	m := make(map[string]interface{})
	m["base"] = path.Base
	m["split"] = strings.Split
	m["json"] = UnmarshalJsonObject
	m["jsonArray"] = UnmarshalJsonArray
	m["dir"] = path.Dir
	m["getenv"] = os.Getenv
	m["join"] = strings.Join
	m["atoi"] = strconv.Atoi
	m["where"] = where
	m["datetime"] = time.Now
	m["toUpper"] = strings.ToUpper
	m["toLower"] = strings.ToLower
	m["contains"] = strings.Contains
	m["replace"] = strings.Replace
	return m
}

////////////////////////////////////////////////////////////////////////////////
func where(in interface{}, sliceKey string, sliceVal interface{}) ([]interface{}, error) {
	ret := make([]interface{}, 0)
	if in == nil {
		return ret, errors.New("where: source is nil")
	}
	if sliceKey == "" {
		return ret, errors.New("where: key is empty")
	}
	if sliceVal == nil {
		return ret, errors.New("where: value is nil")
	}

	if reflect.TypeOf(in).Kind() != reflect.Slice {
		return ret, errors.New("where: source is no slice value")
	}

	s := reflect.ValueOf(in)
	for i := 0; i < s.Len(); i++ {
		val := s.Index(i)

		Inspect(val)
		st := structs.New(val)
		field, ok := st.FieldOk(sliceKey)
		if !ok {
			return ret, errors.New("where: invalid input type")
		}
		if field.Value() == sliceVal {
			ret = append(ret, val)
		}
	}

	return ret, nil
}

func addFuncs(out, in map[string]interface{}) {
	for name, fn := range in {
		out[name] = fn
	}
}

func UnmarshalJsonObject(data string) (map[string]interface{}, error) {
	var ret map[string]interface{}
	err := json.Unmarshal([]byte(data), &ret)
	return ret, err
}

func UnmarshalJsonArray(data string) ([]interface{}, error) {
	var ret []interface{}
	err := json.Unmarshal([]byte(data), &ret)
	return ret, err
}
<commit_msg>autopush@1446222954<commit_after>//file is borrowed from github.com/kelseyhightower/confd

package main

import (
	"encoding/json"
	"errors"
	"os"
	"path"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/fatih/structs"
)

////////////////////////////////////////////////////////////////////////////////
func Inspect(args ...interface{}) {
	spew.Dump(args)
}

func newFuncMap() map[string]interface{} {
	m := make(map[string]interface{})
	m["base"] = path.Base
	m["split"] = strings.Split
	m["json"] = UnmarshalJsonObject
	m["jsonArray"] = UnmarshalJsonArray
	m["dir"] = path.Dir
	m["getenv"] = os.Getenv
	m["join"] = strings.Join
	m["atoi"] = strconv.Atoi
	m["where"] = where
	m["datetime"] = time.Now
	m["toUpper"] = strings.ToUpper
	m["toLower"] = strings.ToLower
	m["contains"] = strings.Contains
	m["replace"] = strings.Replace
	return m
}

////////////////////////////////////////////////////////////////////////////////
func where(in interface{}, sliceKey string, sliceVal interface{}) ([]interface{}, error) {
	ret := make([]interface{}, 0)
	if in == nil {
		return ret, errors.New("where: source is nil")
	}
	if sliceKey == "" {
		return ret, errors.New("where: key is empty")
	}
	if sliceVal == nil {
		return ret, errors.New("where: value is nil")
	}

	if reflect.TypeOf(in).Kind() != reflect.Slice {
		return ret, errors.New("where: source is no slice value")
	}

	s := reflect.ValueOf(in)
	for i := 0; i < s.Len(); i++ {
		val := s.Index(i)

		Inspect(val)
		st := structs.New(val.Interface())
		field, ok := st.FieldOk(sliceKey)
		if !ok {
			return ret, errors.New("where: invalid input type")
		}
		if field.Value() == sliceVal {
			ret = append(ret, val)
		}
	}

	return ret, nil
}

func addFuncs(out, in map[string]interface{}) {
	for name, fn := range in {
		out[name] = fn
	}
}

func UnmarshalJsonObject(data string) (map[string]interface{}, error) {
	var ret map[string]interface{}
	err := json.Unmarshal([]byte(data), &ret)
	return ret, err
}

func UnmarshalJsonArray(data string) ([]interface{}, error) {
	var ret []interface{}
	err := json.Unmarshal([]byte(data), &ret)
	return ret, err
}
<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"fmt"
	"os"
	"time"

	"github.com/getgauge/gauge/api"
	"github.com/getgauge/gauge/config"
	"github.com/getgauge/gauge/env"
	"github.com/getgauge/gauge/execution"
	"github.com/getgauge/gauge/filter"
	"github.com/getgauge/gauge/formatter"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/plugin"
	"github.com/getgauge/gauge/reporter"
	"github.com/getgauge/gauge/version"

	"github.com/getgauge/gauge/plugin/install"
	"github.com/getgauge/gauge/projectInit"
	"github.com/getgauge/gauge/refactor"
	"github.com/getgauge/gauge/util"
	flag "github.com/getgauge/mflag"
)

// Command line flags
var daemonize = flag.Bool([]string{"-daemonize"}, false, "Run as a daemon")
var gaugeVersion = flag.Bool([]string{"v", "-version", "version"}, false, "Print the current version and exit. Eg: gauge --version")
var verbosity = flag.Bool([]string{"-verbose"}, false, "Enable verbose logging for debugging")
var logLevel = flag.String([]string{"-log-level"}, "", "Set level of logging to debug, info, warning, error or critical")
var simpleConsoleOutput = flag.Bool([]string{"-simple-console"}, false, "Removes colouring and simplifies from the console output")
var initialize = flag.String([]string{"-init"}, "", "Initializes project structure in the current directory. Eg: gauge --init java")
var installPlugin = flag.String([]string{"-install"}, "", "Downloads and installs a plugin. Eg: gauge --install java")
var uninstallPlugin = flag.String([]string{"-uninstall"}, "", "Uninstalls a plugin. Eg: gauge --uninstall java")
var installAll = flag.Bool([]string{"-install-all"}, false, "Installs all the plugins specified in project manifest, if not installed. Eg: gauge --install-all")
var update = flag.String([]string{"-update"}, "", "Updates a plugin. Eg: gauge --update java")
var pluginVersion = flag.String([]string{"-plugin-version"}, "", "Version of plugin to be installed. This is used with --install")
var installZip = flag.String([]string{"-file", "f"}, "", "Installs the plugin from zip file. This is used with --install. Eg: gauge --install java -f ZIP_FILE")
var currentEnv = flag.String([]string{"-env"}, "default", "Specifies the environment. If not specified, default will be used")
var addPlugin = flag.String([]string{"-add-plugin"}, "", "Adds the specified non-language plugin to the current project")
var pluginArgs = flag.String([]string{"-plugin-args"}, "", "Specified additional arguments to the plugin. This is used together with --add-plugin")
var specFilesToFormat = flag.String([]string{"-format"}, "", "Formats the specified spec files")
var executeTags = flag.String([]string{"-tags"}, "", "Executes the specs and scenarios tagged with given tags. Eg: gauge --tags tag1,tag2 specs")
var tableRows = flag.String([]string{"-table-rows"}, "", "Executes the specs and scenarios only for the selected rows. Eg: gauge --table-rows \"1-3\" specs/hello.spec")
var apiPort = flag.String([]string{"-api-port"}, "", "Specifies the api port to be used. Eg: gauge --daemonize --api-port 7777")
var refactorSteps = flag.String([]string{"-refactor"}, "", "Refactor steps")
var parallel = flag.Bool([]string{"-parallel", "p"}, false, "Execute specs in parallel")
var numberOfExecutionStreams = flag.Int([]string{"n"}, util.NumberOfCores(), "Specify number of parallel execution streams")
var distribute = flag.Int([]string{"g", "-group"}, -1, "Specify which group of specification to execute based on -n flag")
var workingDir = flag.String([]string{"-dir"}, ".", "Set the working directory for the current command, accepts a path relative to current directory.")
var strategy = flag.String([]string{"-strategy"}, "lazy", "Set the parallelization strategy for execution. Possible options are: `eager`, `lazy`. Ex: gauge -p --strategy=\"eager\"")
var doNotRandomize = flag.Bool([]string{"-sort", "s"}, false, "Run specs in Alphabetical Order. Eg: gauge -s specs")
var validate = flag.Bool([]string{"-validate", "#-check"}, false, "Check for validation and parse errors. Eg: gauge --validate specs")
var updateAll = flag.Bool([]string{"-update-all"}, false, "Updates all the installed Gauge plugins. Eg: gauge --update-all")
var checkUpdates = flag.Bool([]string{"#-check-updates"}, false, "Checks for Gauge and plugins updates. Eg: gauge --check-updates")
var listTemplates = flag.Bool([]string{"-list-templates"}, false, "Lists all the Gauge templates available. Eg: gauge --list-templates")

func main() {
	flag.Parse()
	projectInit.SetWorkingDir(*workingDir)
	initPackageFlags()
	validGaugeProject := true
	err := config.SetProjectRoot(flag.Args())
	if err != nil {
		validGaugeProject = false
	}
	env.LoadEnv(true)
	logger.Initialize(*logLevel)
	if *gaugeVersion {
		printVersion()
	} else if *initialize != "" {
		projectInit.InitializeProject(*initialize)
	} else if *installZip != "" && *installPlugin != "" {
		install.InstallPluginZip(*installZip, *installPlugin)
	} else if *installPlugin != "" {
		install.DownloadAndInstallPlugin(*installPlugin, *pluginVersion, "Successfully installed plugin => %s")
	} else if *uninstallPlugin != "" {
		install.UninstallPlugin(*uninstallPlugin, *pluginVersion)
	} else if *installAll {
		install.InstallAllPlugins()
	} else if *update != "" {
		install.DownloadAndInstallPlugin(*update, *pluginVersion, "Successfully updated plugin => %s")
	} else if *updateAll {
		install.UpdatePlugins()
	} else if *checkUpdates {
		install.PrintUpdateInfoWithDetails()
	} else if *addPlugin != "" {
		install.AddPluginToProject(*addPlugin, *pluginArgs)
	} else if *listTemplates {
		projectInit.ListTemplates()
	} else if flag.NFlag() == 0 && len(flag.Args()) == 0 {
		printUsage()
		os.Exit(0)
	} else if validGaugeProject {
		if *refactorSteps != "" {
			startChan := api.StartAPI()
			if len(flag.Args()) != 1 {
				logger.Error("flag needs two arguments: --refactor\n.Usage : gauge --refactor {old step} {new step}")
				os.Exit(1)
			}
			refactor.RefactorSteps(*refactorSteps, flag.Args()[0], startChan)
		} else if *daemonize {
			api.RunInBackground(*apiPort)
		} else if *specFilesToFormat != "" {
			formatter.FormatSpecFilesIn(*specFilesToFormat)
		} else if *validate {
			execution.ParseAndValidateSpecs(flag.Args())
		} else {
			exitCode := execution.ExecuteSpecs(*parallel, flag.Args())
			os.Exit(exitCode)
		}
	} else {
		logger.Error(err.Error())
		os.Exit(1)
	}
}

func printVersion() {
	fmt.Printf("Gauge version: %s\n\n", version.CurrentGaugeVersion.String())
	fmt.Println("Plugins\n-------")
	allPluginsWithVersion, err := plugin.GetAllInstalledPluginsWithVersion()
	if err != nil {
		fmt.Println("No plugins found")
		fmt.Println("Plugins can be installed with `gauge --install {plugin-name}`")
		os.Exit(0)
	}
	for _, pluginInfo := range allPluginsWithVersion {
		fmt.Printf("%s (%s)\n", pluginInfo.Name, pluginInfo.Version.String())
	}
}

func printUsage() {
	fmt.Printf("gauge -version %s\n", version.CurrentGaugeVersion.String())
	fmt.Printf("Copyright %d Thoughtworks\n\n", time.Now().Year())
	fmt.Println("Usage:")
	fmt.Println("\tgauge specs/")
	fmt.Println("\tgauge specs/spec_name.spec")
	fmt.Println("\nOptions:")
	flag.PrintDefaults()
}

func initPackageFlags() {
	if *parallel {
		*simpleConsoleOutput = true
	}
	reporter.SimpleConsoleOutput = *simpleConsoleOutput
	reporter.Verbose = *verbosity
	env.ProjectEnv = *currentEnv
	execution.ExecuteTags = *executeTags
	execution.TableRows = *tableRows
	execution.NumberOfExecutionStreams = *numberOfExecutionStreams
	filter.ExecuteTags = *executeTags
	filter.DoNotRandomize = *doNotRandomize
	filter.Distribute = *distribute
	filter.NumberOfExecutionStreams = *numberOfExecutionStreams
	execution.Strategy = *strategy
	if *distribute != -1 {
		execution.Strategy = execution.EAGER
	}
}
<commit_msg>updating verbose flag def<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"fmt"
	"os"
	"time"

	"github.com/getgauge/gauge/api"
	"github.com/getgauge/gauge/config"
	"github.com/getgauge/gauge/env"
	"github.com/getgauge/gauge/execution"
	"github.com/getgauge/gauge/filter"
	"github.com/getgauge/gauge/formatter"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/plugin"
	"github.com/getgauge/gauge/reporter"
	"github.com/getgauge/gauge/version"

	"github.com/getgauge/gauge/plugin/install"
	"github.com/getgauge/gauge/projectInit"
	"github.com/getgauge/gauge/refactor"
	"github.com/getgauge/gauge/util"
	flag "github.com/getgauge/mflag"
)

// Command line flags
var daemonize = flag.Bool([]string{"-daemonize"}, false, "Run as a daemon")
var gaugeVersion = flag.Bool([]string{"v", "-version", "version"}, false, "Print the current version and exit. Eg: gauge --version")
var verbosity = flag.Bool([]string{"-verbose"}, false, "Enable step level reporting on console, default being scenario level. Eg: gauge --verbose specs")
var logLevel = flag.String([]string{"-log-level"}, "", "Set level of logging to debug, info, warning, error or critical")
var simpleConsoleOutput = flag.Bool([]string{"-simple-console"}, false, "Removes colouring and simplifies from the console output")
var initialize = flag.String([]string{"-init"}, "", "Initializes project structure in the current directory. Eg: gauge --init java")
var installPlugin = flag.String([]string{"-install"}, "", "Downloads and installs a plugin. Eg: gauge --install java")
var uninstallPlugin = flag.String([]string{"-uninstall"}, "", "Uninstalls a plugin. Eg: gauge --uninstall java")
var installAll = flag.Bool([]string{"-install-all"}, false, "Installs all the plugins specified in project manifest, if not installed. Eg: gauge --install-all")
var update = flag.String([]string{"-update"}, "", "Updates a plugin. Eg: gauge --update java")
var pluginVersion = flag.String([]string{"-plugin-version"}, "", "Version of plugin to be installed. This is used with --install")
var installZip = flag.String([]string{"-file", "f"}, "", "Installs the plugin from zip file. This is used with --install. Eg: gauge --install java -f ZIP_FILE")
var currentEnv = flag.String([]string{"-env"}, "default", "Specifies the environment. If not specified, default will be used")
var addPlugin = flag.String([]string{"-add-plugin"}, "", "Adds the specified non-language plugin to the current project")
var pluginArgs = flag.String([]string{"-plugin-args"}, "", "Specified additional arguments to the plugin. This is used together with --add-plugin")
var specFilesToFormat = flag.String([]string{"-format"}, "", "Formats the specified spec files")
var executeTags = flag.String([]string{"-tags"}, "", "Executes the specs and scenarios tagged with given tags. Eg: gauge --tags tag1,tag2 specs")
var tableRows = flag.String([]string{"-table-rows"}, "", "Executes the specs and scenarios only for the selected rows. Eg: gauge --table-rows \"1-3\" specs/hello.spec")
var apiPort = flag.String([]string{"-api-port"}, "", "Specifies the api port to be used. Eg: gauge --daemonize --api-port 7777")
var refactorSteps = flag.String([]string{"-refactor"}, "", "Refactor steps")
var parallel = flag.Bool([]string{"-parallel", "p"}, false, "Execute specs in parallel")
var numberOfExecutionStreams = flag.Int([]string{"n"}, util.NumberOfCores(), "Specify number of parallel execution streams")
var distribute = flag.Int([]string{"g", "-group"}, -1, "Specify which group of specification to execute based on -n flag")
var workingDir = flag.String([]string{"-dir"}, ".", "Set the working directory for the current command, accepts a path relative to current directory.")
var strategy = flag.String([]string{"-strategy"}, "lazy", "Set the parallelization strategy for execution. Possible options are: `eager`, `lazy`. Ex: gauge -p --strategy=\"eager\"")
var doNotRandomize = flag.Bool([]string{"-sort", "s"}, false, "Run specs in Alphabetical Order. Eg: gauge -s specs")
var validate = flag.Bool([]string{"-validate", "#-check"}, false, "Check for validation and parse errors. Eg: gauge --validate specs")
var updateAll = flag.Bool([]string{"-update-all"}, false, "Updates all the installed Gauge plugins. Eg: gauge --update-all")
var checkUpdates = flag.Bool([]string{"#-check-updates"}, false, "Checks for Gauge and plugins updates. Eg: gauge --check-updates")
var listTemplates = flag.Bool([]string{"-list-templates"}, false, "Lists all the Gauge templates available. Eg: gauge --list-templates")

func main() {
	flag.Parse()
	projectInit.SetWorkingDir(*workingDir)
	initPackageFlags()
	validGaugeProject := true
	err := config.SetProjectRoot(flag.Args())
	if err != nil {
		validGaugeProject = false
	}
	env.LoadEnv(true)
	logger.Initialize(*logLevel)
	if *gaugeVersion {
		printVersion()
	} else if *initialize != "" {
		projectInit.InitializeProject(*initialize)
	} else if *installZip != "" && *installPlugin != "" {
		install.InstallPluginZip(*installZip, *installPlugin)
	} else if *installPlugin != "" {
		install.DownloadAndInstallPlugin(*installPlugin, *pluginVersion, "Successfully installed plugin => %s")
	} else if *uninstallPlugin != "" {
		install.UninstallPlugin(*uninstallPlugin, *pluginVersion)
	} else if *installAll {
		install.InstallAllPlugins()
	} else if *update != "" {
		install.DownloadAndInstallPlugin(*update, *pluginVersion, "Successfully updated plugin => %s")
	} else if *updateAll {
		install.UpdatePlugins()
	} else if *checkUpdates {
		install.PrintUpdateInfoWithDetails()
	} else if *addPlugin != "" {
		install.AddPluginToProject(*addPlugin, *pluginArgs)
	} else if *listTemplates {
		projectInit.ListTemplates()
	} else if flag.NFlag() == 0 && len(flag.Args()) == 0 {
		printUsage()
		os.Exit(0)
	} else if validGaugeProject {
		if *refactorSteps != "" {
			startChan := api.StartAPI()
			if len(flag.Args()) != 1 {
				logger.Error("flag needs two arguments: --refactor\n.Usage : gauge --refactor {old step} {new step}")
				os.Exit(1)
			}
			refactor.RefactorSteps(*refactorSteps, flag.Args()[0], startChan)
		} else if *daemonize {
			api.RunInBackground(*apiPort)
		} else if *specFilesToFormat != "" {
			formatter.FormatSpecFilesIn(*specFilesToFormat)
		} else if *validate {
			execution.ParseAndValidateSpecs(flag.Args())
		} else {
			exitCode := execution.ExecuteSpecs(*parallel, flag.Args())
			os.Exit(exitCode)
		}
	} else {
		logger.Error(err.Error())
		os.Exit(1)
	}
}

func printVersion() {
	fmt.Printf("Gauge version: %s\n\n", version.CurrentGaugeVersion.String())
	fmt.Println("Plugins\n-------")
	allPluginsWithVersion, err := plugin.GetAllInstalledPluginsWithVersion()
	if err != nil {
		fmt.Println("No plugins found")
		fmt.Println("Plugins can be installed with `gauge --install {plugin-name}`")
		os.Exit(0)
	}
	for _, pluginInfo := range allPluginsWithVersion {
		fmt.Printf("%s (%s)\n", pluginInfo.Name, pluginInfo.Version.String())
	}
}

func printUsage() {
	fmt.Printf("gauge -version %s\n", version.CurrentGaugeVersion.String())
	fmt.Printf("Copyright %d Thoughtworks\n\n", time.Now().Year())
	fmt.Println("Usage:")
	fmt.Println("\tgauge specs/")
	fmt.Println("\tgauge specs/spec_name.spec")
	fmt.Println("\nOptions:")
	flag.PrintDefaults()
}

func initPackageFlags() {
	if *parallel {
		*simpleConsoleOutput = true
	}
	reporter.SimpleConsoleOutput = *simpleConsoleOutput
	reporter.Verbose = *verbosity
	env.ProjectEnv = *currentEnv
	execution.ExecuteTags = *executeTags
	execution.TableRows = *tableRows
	execution.NumberOfExecutionStreams = *numberOfExecutionStreams
	filter.ExecuteTags = *executeTags
	filter.DoNotRandomize = *doNotRandomize
	filter.Distribute = *distribute
	filter.NumberOfExecutionStreams = *numberOfExecutionStreams
	execution.Strategy = *strategy
	if *distribute != -1 {
		execution.Strategy = execution.EAGER
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/pachyderm/pachyderm/src/cmd/pachctl/cmd"
	"go.pedge.io/env"
)

type appEnv struct {
	Address string `env:"PACH_ADDRESS,default=0.0.0.0:30650"`
}

func main() {
	env.Main(do, &appEnv{})
}

func do(appEnvObj interface{}) error {
	appEnv := appEnvObj.(*appEnv)
	rootCmd, err := cmd.PachctlCmd(appEnv.Address)
	if err != nil {
		return err
	}
	return rootCmd.Execute()
}
<commit_msg>Pachctl looks for ADDRESS not PACH_ADDRESS.<commit_after>package main

import (
	"github.com/pachyderm/pachyderm/src/cmd/pachctl/cmd"
	"go.pedge.io/env"
)

type appEnv struct {
	Address string `env:"ADDRESS,default=0.0.0.0:30650"`
}

func main() {
	env.Main(do, &appEnv{})
}

func do(appEnvObj interface{}) error {
	appEnv := appEnvObj.(*appEnv)
	rootCmd, err := cmd.PachctlCmd(appEnv.Address)
	if err != nil {
		return err
	}
	return rootCmd.Execute()
}
<|endoftext|>


<commit_before>// Copyright 2017 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package helpers

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"github.com/cilium/cilium/test/config"
	ginkgoext "github.com/cilium/cilium/test/ginkgo-ext"

	"github.com/sirupsen/logrus"
	"golang.org/x/crypto/ssh"
)

var (
	//SSHMetaLogs is a buffer where all commands sent over ssh are saved.
	SSHMetaLogs = ginkgoext.NewWriter(new(bytes.Buffer))
)

// SSHMeta contains metadata to SSH into a remote location to run tests
type SSHMeta struct {
	sshClient *SSHClient
	env       []string
	rawConfig []byte
	nodeName  string
	logger    *logrus.Entry
}

// CreateSSHMeta returns an SSHMeta with the specified host, port, and user, as
// well as an according SSHClient.
func CreateSSHMeta(host string, port int, user string) *SSHMeta {
	return &SSHMeta{
		sshClient: GetSSHClient(host, port, user),
	}
}

func (s *SSHMeta) String() string {
	return fmt.Sprintf("environment: %s, SSHClient: %s", s.env, s.sshClient.String())

}

// GetVagrantSSHMeta returns a SSHMeta initialized based on the provided
// SSH-config target.
func GetVagrantSSHMeta(vmName string) *SSHMeta {
	config, err := GetVagrantSSHMetadata(vmName)
	if err != nil {
		return nil
	}

	log.Debugf("generated SSHConfig for node %s", vmName)
	nodes, err := ImportSSHconfig(config)
	if err != nil {
		log.WithError(err).Error("Error importing ssh config")
		return nil
	}
	var node *SSHConfig
	log.Debugf("done importing ssh config")
	for name := range nodes {
		if strings.HasPrefix(name, vmName) {
			node = nodes[name]
			break
		}
	}
	if node == nil {
		log.Errorf("Node %s not found in ssh config", vmName)
		return nil
	}
	sshMeta := &SSHMeta{
		sshClient: node.GetSSHClient(),
		rawConfig: config,
		nodeName:  vmName,
	}

	sshMeta.setBasePath()
	return sshMeta
}

// setBasePath if the SSHConfig is defined we set the BasePath to the GOPATH,
// from golang 1.8 GOPATH is by default $HOME/go so we also check that.
func (s *SSHMeta) setBasePath() {
	if config.CiliumTestConfig.SSHConfig == "" {
		return
	}

	gopath := s.Exec("echo $GOPATH").SingleOut()
	if gopath != "" {
		BasePath = filepath.Join(gopath, CiliumPath)
		return
	}

	home := s.Exec("echo $HOME").SingleOut()
	if home == "" {
		return
	}

	BasePath = filepath.Join(home, "go", CiliumPath)
	return
}

// Execute executes cmd on the provided node and stores the stdout / stderr of
// the command in the provided buffers. Returns false if the command failed
// during its execution.
func (s *SSHMeta) Execute(cmd string, stdout io.Writer, stderr io.Writer) error {
	if stdout == nil {
		stdout = os.Stdout
	}

	if stderr == nil {
		stderr = os.Stderr
	}
	fmt.Fprintln(SSHMetaLogs, cmd)
	command := &SSHCommand{
		Path:   cmd,
		Stdin:  os.Stdin,
		Stdout: stdout,
		Stderr: stderr,
	}
	err := s.sshClient.RunCommand(command)
	return err
}

// ExecWithSudo returns the result of executing the provided cmd via SSH using
// sudo.
func (s *SSHMeta) ExecWithSudo(cmd string, options ...ExecOptions) *CmdRes {
	command := fmt.Sprintf("sudo %s", cmd)
	return s.Exec(command, options...)
}

// ExecOptions options to execute Exec and ExecWithContext
type ExecOptions struct {
	SkipLog bool
}

// Exec returns the results of executing the provided cmd via SSH.
func (s *SSHMeta) Exec(cmd string, options ...ExecOptions) *CmdRes {
	var ops ExecOptions
	if len(options) > 0 {
		ops = options[0]
	}

	log.Debugf("running command: %s", cmd)
	stdout := new(bytes.Buffer)
	stderr := new(bytes.Buffer)
	exit := true
	err := s.Execute(cmd, stdout, stderr)
	if err != nil {
		exit = false
	}
	res := CmdRes{
		cmd:     cmd,
		stdout:  stdout,
		stderr:  stderr,
		success: exit,
	}
	// Set the exitCode if the error is an ExitError
	if exiterr, ok := err.(*ssh.ExitError); ok {
		res.exitcode = exiterr.Waitmsg.ExitStatus()
	}
	if !ops.SkipLog {
		res.SendToLog()
	}
	return &res
}

// GetCopy returns a copy of SSHMeta, useful for parallel requests
func (s *SSHMeta) GetCopy() *SSHMeta {
	nodes, err := ImportSSHconfig(s.rawConfig)
	if err != nil {
		log.WithError(err).Error("while importing ssh config for meta copy")
		return nil
	}

	config := nodes[s.nodeName]
	if config == nil {
		log.Errorf("no node %s in imported config", s.nodeName)
		return nil
	}

	copy := &SSHMeta{
		sshClient: config.GetSSHClient(),
		rawConfig: s.rawConfig,
		nodeName:  s.nodeName,
	}

	return copy
}

// ExecContext returns the results of running cmd via SSH in the specified
// context.
func (s *SSHMeta) ExecContext(ctx context.Context, cmd string, options ...ExecOptions) *CmdRes {
	if ctx == nil {
		panic("no context provided")
	}

	var ops ExecOptions
	if len(options) > 0 {
		ops = options[0]
	}

	fmt.Fprintln(SSHMetaLogs, cmd)
	stdout := new(bytes.Buffer)
	stderr := new(bytes.Buffer)

	command := &SSHCommand{
		Path:   cmd,
		Stdin:  os.Stdin,
		Stdout: stdout,
		Stderr: stderr,
	}

	res := CmdRes{
		cmd:     cmd,
		stdout:  stdout,
		stderr:  stderr,
		success: false,
	}

	go func() {
		s.sshClient.RunCommandContext(ctx, command)
		if !ops.SkipLog {
			res.SendToLog()
		}
	}()

	return &res
}
<commit_msg>test: report collection prints error from ssh.RunCommand<commit_after>// Copyright 2017 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package helpers

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"github.com/cilium/cilium/test/config"
	ginkgoext "github.com/cilium/cilium/test/ginkgo-ext"

	"github.com/sirupsen/logrus"
	"golang.org/x/crypto/ssh"
)

var (
	//SSHMetaLogs is a buffer where all commands sent over ssh are saved.
	SSHMetaLogs = ginkgoext.NewWriter(new(bytes.Buffer))
)

// SSHMeta contains metadata to SSH into a remote location to run tests
type SSHMeta struct {
	sshClient *SSHClient
	env       []string
	rawConfig []byte
	nodeName  string
	logger    *logrus.Entry
}

// CreateSSHMeta returns an SSHMeta with the specified host, port, and user, as
// well as an according SSHClient.
func CreateSSHMeta(host string, port int, user string) *SSHMeta {
	return &SSHMeta{
		sshClient: GetSSHClient(host, port, user),
	}
}

func (s *SSHMeta) String() string {
	return fmt.Sprintf("environment: %s, SSHClient: %s", s.env, s.sshClient.String())

}

// GetVagrantSSHMeta returns a SSHMeta initialized based on the provided
// SSH-config target.
func GetVagrantSSHMeta(vmName string) *SSHMeta {
	config, err := GetVagrantSSHMetadata(vmName)
	if err != nil {
		return nil
	}

	log.Debugf("generated SSHConfig for node %s", vmName)
	nodes, err := ImportSSHconfig(config)
	if err != nil {
		log.WithError(err).Error("Error importing ssh config")
		return nil
	}
	var node *SSHConfig
	log.Debugf("done importing ssh config")
	for name := range nodes {
		if strings.HasPrefix(name, vmName) {
			node = nodes[name]
			break
		}
	}
	if node == nil {
		log.Errorf("Node %s not found in ssh config", vmName)
		return nil
	}
	sshMeta := &SSHMeta{
		sshClient: node.GetSSHClient(),
		rawConfig: config,
		nodeName:  vmName,
	}

	sshMeta.setBasePath()
	return sshMeta
}

// setBasePath if the SSHConfig is defined we set the BasePath to the GOPATH,
// from golang 1.8 GOPATH is by default $HOME/go so we also check that.
func (s *SSHMeta) setBasePath() {
	if config.CiliumTestConfig.SSHConfig == "" {
		return
	}

	gopath := s.Exec("echo $GOPATH").SingleOut()
	if gopath != "" {
		BasePath = filepath.Join(gopath, CiliumPath)
		return
	}

	home := s.Exec("echo $HOME").SingleOut()
	if home == "" {
		return
	}

	BasePath = filepath.Join(home, "go", CiliumPath)
	return
}

// Execute executes cmd on the provided node and stores the stdout / stderr of
// the command in the provided buffers. Returns false if the command failed
// during its execution.
func (s *SSHMeta) Execute(cmd string, stdout io.Writer, stderr io.Writer) error {
	if stdout == nil {
		stdout = os.Stdout
	}

	if stderr == nil {
		stderr = os.Stderr
	}
	fmt.Fprintln(SSHMetaLogs, cmd)
	command := &SSHCommand{
		Path:   cmd,
		Stdin:  os.Stdin,
		Stdout: stdout,
		Stderr: stderr,
	}
	err := s.sshClient.RunCommand(command)
	return err
}

// ExecWithSudo returns the result of executing the provided cmd via SSH using
// sudo.
func (s *SSHMeta) ExecWithSudo(cmd string, options ...ExecOptions) *CmdRes {
	command := fmt.Sprintf("sudo %s", cmd)
	return s.Exec(command, options...)
}

// ExecOptions options to execute Exec and ExecWithContext
type ExecOptions struct {
	SkipLog bool
}

// Exec returns the results of executing the provided cmd via SSH.
func (s *SSHMeta) Exec(cmd string, options ...ExecOptions) *CmdRes {
	var ops ExecOptions
	if len(options) > 0 {
		ops = options[0]
	}

	log.Debugf("running command: %s", cmd)
	stdout := new(bytes.Buffer)
	stderr := new(bytes.Buffer)
	exit := true
	err := s.Execute(cmd, stdout, stderr)
	if err != nil {
		exit = false
	}

	res := CmdRes{
		cmd:     cmd,
		stdout:  stdout,
		stderr:  stderr,
		success: exit,
	}
	// Set res's exitcode if the error is an ExitError
	if exiterr, ok := err.(*ssh.ExitError); ok {
		res.exitcode = exiterr.Waitmsg.ExitStatus()
	} else {
		// Log other error types. They are likely from SSH or the network
		log.WithError(err).Errorf("Error executing command '%s'", cmd)
	}

	if !ops.SkipLog {
		res.SendToLog()
	}
	return &res
}

// GetCopy returns a copy of SSHMeta, useful for parallel requests
func (s *SSHMeta) GetCopy() *SSHMeta {
	nodes, err := ImportSSHconfig(s.rawConfig)
	if err != nil {
		log.WithError(err).Error("while importing ssh config for meta copy")
		return nil
	}

	config := nodes[s.nodeName]
	if config == nil {
		log.Errorf("no node %s in imported config", s.nodeName)
		return nil
	}

	copy := &SSHMeta{
		sshClient: config.GetSSHClient(),
		rawConfig: s.rawConfig,
		nodeName:  s.nodeName,
	}

	return copy
}

// ExecContext returns the results of running cmd via SSH in the specified
// context.
func (s *SSHMeta) ExecContext(ctx context.Context, cmd string, options ...ExecOptions) *CmdRes {
	if ctx == nil {
		panic("no context provided")
	}

	var ops ExecOptions
	if len(options) > 0 {
		ops = options[0]
	}

	fmt.Fprintln(SSHMetaLogs, cmd)
	stdout := new(bytes.Buffer)
	stderr := new(bytes.Buffer)

	command := &SSHCommand{
		Path:   cmd,
		Stdin:  os.Stdin,
		Stdout: stdout,
		Stderr: stderr,
	}

	res := CmdRes{
		cmd:     cmd,
		stdout:  stdout,
		stderr:  stderr,
		success: false,
	}

	go func() {
		s.sshClient.RunCommandContext(ctx, command)
		if !ops.SkipLog {
			res.SendToLog()
		}
	}()

	return &res
}
<|endoftext|>


<commit_before>package main

import (
     "http"
     "json"
     "flag"
     "exec"
     "os"
     "io/ioutil"
     "strings"
	//iface "flunky/interfaces"
	daemon "flunky/daemon"
)

type outletNode struct{
     Address   string
     Outlet    string
}

var resources       map[string]outletNode
var powerDaemon     *daemon.Daemon
var fileDir         string
//var resourcesLock   sync.Mutex   shouldn't need a lock, never changing data.

func init() {
     flag.StringVar(&fileDir, "F", "../../../etc/Power/", "Directory where client files can be found.")

     flag.Parse()

     powerDaemon = daemon.New("Power", fileDir)

     powerDaemon.DaemonLog.Log("Initializting data for daemon setup.")

     powerDBFile, error := os.Open(fileDir + "power.db")
     powerDaemon.DaemonLog.LogError("ERROR: Unable to open power.db for reading.", error)

     someBytes, error := ioutil.ReadAll(powerDBFile)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to read from file power.db.", error)

     error = powerDBFile.Close()
     powerDaemon.DaemonLog.LogError("ERROR: Failed to close power.db.", error)

     error = json.Unmarshal(someBytes, &resources)
     powerDaemon.DaemonLog.LogError("ERROR: Failed to unmarshal data read from power.db file.", error)
}

func DumpCall(w http.ResponseWriter, req *http.Request) {
        powerDaemon.DaemonLog.LogHttp(req)
        req.ProtoMinor = 0
       /* username, authed, _ := powerDaemon.AuthN.HTTPAuthenticate(req)
        if !authed {
                powerDaemon.DaemonLog.LogError(fmt.Sprintf("User Authentications for %s failed", username), os.NewError("Access Denied"))
                return
        }*/
        tmp, err := json.Marshal(resources)
        powerDaemon.DaemonLog.LogError("Cannot Marshal power resources", err)
        _, err = w.Write(tmp)
        if err != nil {
                http.Error(w, "Cannot write to socket", 500)
        }
}

func rebootList(writer http.ResponseWriter, request *http.Request) {
     //This will free a requested node if the user is the owner of the node.  It removes
     //the node from current resources if it exists and also resets it in resources map.
     powerDaemon.DaemonLog.Log("Rebooting list given by client.")
     var nodes []string
     request.ProtoMinor = 0

     _, authed, admin := powerDaemon.AuthN.HTTPAuthenticate(request)

     if !authed {
          powerDaemon.DaemonLog.LogError("ERROR: Username password combo invalid.", os.NewError("Access Denied"))
          return
     }

     if !admin {
          powerDaemon.DaemonLog.LogError("ERROR: No access to admin command.", os.NewError("Access Denied"))
          return
     }

     someBytes, error := ioutil.ReadAll(request.Body)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to read all from reboot POST.", error)

     error = request.Body.Close()
     powerDaemon.DaemonLog.LogError("ERROR: Failed to close reboot request body.", error)

     error = json.Unmarshal(someBytes, &nodes)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to unmarshal nodes to be rebooted.", error)

     for _, value := range nodes {
          go func(value string) {
               error = exec.Command("./powerCont.sh", resources[value].Address, "admn", "admn", "reboot", resources[value].Outlet).Run()
               powerDaemon.DaemonLog.LogError("ERROR: Failed to run powerCont.sh in rebootList.", error)
          }(value)
     }
}

func offList(writer http.ResponseWriter, request *http.Request) {
     //This will free a requested node if the user is the owner of the node.  It removes
     //the node from current resources if it exists and also resets it in resources map.
     powerDaemon.DaemonLog.Log("Turning off list of nodes given by client.")
     var nodes []string
     request.ProtoMinor = 0

     _, authed, admin := powerDaemon.AuthN.HTTPAuthenticate(request)

     if !authed {
          powerDaemon.DaemonLog.LogError("ERROR: Username password combo invalid.", os.NewError("Access Denied"))
          return
     }

     if !admin {
          powerDaemon.DaemonLog.LogError("ERROR: No access to admin command.", os.NewError("Access Denied"))
          return
     }

     someBytes, error := ioutil.ReadAll(request.Body)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to read all from off POST.", error)

     error = request.Body.Close()
     powerDaemon.DaemonLog.LogError("ERROR: Failed to close off request body.", error)

     error = json.Unmarshal(someBytes, &nodes)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to unmarshal nodes to be turned off.", error)

     for _, value := range nodes {
          go func(value string) {
               error = exec.Command("./powerCont.sh", resources[value].Address, "admn", "admn", "off", resources[value].Outlet).Run()
               powerDaemon.DaemonLog.LogError("ERROR: Failed to run powerCont.sh in offList.", error)
          }(value)
     }
}

func statusList(writer http.ResponseWriter, request *http.Request) {
     //This will free a requested node if the user is the owner of the node.  It removes
     //the node from current resources if it exists and also resets it in resources map.
     powerDaemon.DaemonLog.Log("Retreiving status for list given by client.")
     var nodes []string
     outletStatus := make(map[string]string)
     request.ProtoMinor = 0

     _, authed, admin := powerDaemon.AuthN.HTTPAuthenticate(request)

     if !authed {
          powerDaemon.DaemonLog.LogError("ERROR: Username password combo invalid.", os.NewError("Access Denied"))
          return
     }

     if !admin {
          powerDaemon.DaemonLog.LogError("ERROR: No access to admin command.", os.NewError("Access Denied"))
          return
     }

     someBytes, error := ioutil.ReadAll(request.Body)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to read all from off POST.", error)

     error = request.Body.Close()
     powerDaemon.DaemonLog.LogError("ERROR: Failed to close off request body.", error)

     error = json.Unmarshal(someBytes, &nodes)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to unmarshal nodes to be turned off.", error)

     for _, value := range nodes {
          _, ok := outletStatus[value]

          if !ok {
               someBytes, error = exec.Command("./powerCont.sh", resources[value].Address, "admn", "admn", "status").Output()
               powerDaemon.DaemonLog.LogError("ERROR: Failed to execute powerCont.sh and get out put in power status request.", error)

               tmpStatusLines := strings.Split(string(someBytes), "\n")

               for i := 18 ; i < 42 ; i++ {
                    tmpStatusFields := strings.Split(tmpStatusLines[i], " ")

                    for _, value2 := range nodes {
                         if resources[value2].Address == resources[value].Address && resources[value2].Outlet == tmpStatusFields[3] {
                              outletStatus[value2] = tmpStatusFields[13]
                         }
                    }
               }
          }
     }

     jsonStat, error := json.Marshal(outletStatus)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to marshal outlet status response.", error)

     _, error = writer.Write(jsonStat)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to write outlet status response.", error)
}

func main() {
     http.HandleFunc("/dump", DumpCall)
     http.HandleFunc("/reboot", rebootList)
     http.HandleFunc("/off", offList)
     http.HandleFunc("/status", statusList)

     error := http.ListenAndServe(":" + powerDaemon.Cfg.Data["powerPort"], nil)
     powerDaemon.DaemonLog.LogError("ERROR: Failed to listen on http socket.", error)
}<commit_msg>added map security to power daemon.<commit_after>package main

import (
     "http"
     "json"
     "flag"
     "exec"
     "os"
     "io/ioutil"
     "strings"
	//iface "flunky/interfaces"
	daemon "flunky/daemon"
)

type outletNode struct{
     Address   string
     Outlet    string
}

var resources       map[string]outletNode
var powerDaemon     *daemon.Daemon
var fileDir         string
//var resourcesLock   sync.Mutex   shouldn't need a lock, never changing data.

func init() {
     flag.StringVar(&fileDir, "F", "../../../etc/Power/", "Directory where client files can be found.")

     flag.Parse()

     powerDaemon = daemon.New("Power", fileDir)

     powerDaemon.DaemonLog.Log("Initializting data for daemon setup.")

     powerDBFile, error := os.Open(fileDir + "power.db")
     powerDaemon.DaemonLog.LogError("ERROR: Unable to open power.db for reading.", error)

     someBytes, error := ioutil.ReadAll(powerDBFile)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to read from file power.db.", error)

     error = powerDBFile.Close()
     powerDaemon.DaemonLog.LogError("ERROR: Failed to close power.db.", error)

     error = json.Unmarshal(someBytes, &resources)
     powerDaemon.DaemonLog.LogError("ERROR: Failed to unmarshal data read from power.db file.", error)
}

func DumpCall(w http.ResponseWriter, req *http.Request) {
        powerDaemon.DaemonLog.LogHttp(req)
        req.ProtoMinor = 0
       /* username, authed, _ := powerDaemon.AuthN.HTTPAuthenticate(req)
        if !authed {
                powerDaemon.DaemonLog.LogError(fmt.Sprintf("User Authentications for %s failed", username), os.NewError("Access Denied"))
                return
        }*/
        tmp, err := json.Marshal(resources)
        powerDaemon.DaemonLog.LogError("Cannot Marshal power resources", err)
        _, err = w.Write(tmp)
        if err != nil {
                http.Error(w, "Cannot write to socket", 500)
        }
}

func rebootList(writer http.ResponseWriter, request *http.Request) {
     //This will free a requested node if the user is the owner of the node.  It removes
     //the node from current resources if it exists and also resets it in resources map.
     powerDaemon.DaemonLog.Log("Rebooting list given by client.")
     var nodes []string
     request.ProtoMinor = 0

     _, authed, admin := powerDaemon.AuthN.HTTPAuthenticate(request)

     if !authed {
          powerDaemon.DaemonLog.LogError("ERROR: Username password combo invalid.", os.NewError("Access Denied"))
          return
     }

     if !admin {
          powerDaemon.DaemonLog.LogError("ERROR: No access to admin command.", os.NewError("Access Denied"))
          return
     }

     someBytes, error := ioutil.ReadAll(request.Body)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to read all from reboot POST.", error)

     error = request.Body.Close()
     powerDaemon.DaemonLog.LogError("ERROR: Failed to close reboot request body.", error)

     error = json.Unmarshal(someBytes, &nodes)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to unmarshal nodes to be rebooted.", error)

     for _, value := range nodes {
          if _, ok := resources[value] ; ok {
               go func(value string) {
                    error = exec.Command("./powerCont.sh", resources[value].Address, "admn", "admn", "reboot", resources[value].Outlet).Run()
                    powerDaemon.DaemonLog.LogError("ERROR: Failed to run powerCont.sh in rebootList.", error)
               }(value)
          }
     }
}

func offList(writer http.ResponseWriter, request *http.Request) {
     //This will free a requested node if the user is the owner of the node.  It removes
     //the node from current resources if it exists and also resets it in resources map.
     powerDaemon.DaemonLog.Log("Turning off list of nodes given by client.")
     var nodes []string
     request.ProtoMinor = 0

     _, authed, admin := powerDaemon.AuthN.HTTPAuthenticate(request)

     if !authed {
          powerDaemon.DaemonLog.LogError("ERROR: Username password combo invalid.", os.NewError("Access Denied"))
          return
     }

     if !admin {
          powerDaemon.DaemonLog.LogError("ERROR: No access to admin command.", os.NewError("Access Denied"))
          return
     }

     someBytes, error := ioutil.ReadAll(request.Body)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to read all from off POST.", error)

     error = request.Body.Close()
     powerDaemon.DaemonLog.LogError("ERROR: Failed to close off request body.", error)

     error = json.Unmarshal(someBytes, &nodes)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to unmarshal nodes to be turned off.", error)

     for _, value := range nodes {
          if _, ok := resources[value] ; ok {
               go func(value string) {
                    error = exec.Command("./powerCont.sh", resources[value].Address, "admn", "admn", "off", resources[value].Outlet).Run()
                    powerDaemon.DaemonLog.LogError("ERROR: Failed to run powerCont.sh in offList.", error)
               }(value)
          }
     }
}

func statusList(writer http.ResponseWriter, request *http.Request) {
     //This will free a requested node if the user is the owner of the node.  It removes
     //the node from current resources if it exists and also resets it in resources map.
     powerDaemon.DaemonLog.Log("Retreiving status for list given by client.")
     var nodes []string
     outletStatus := make(map[string]string)
     request.ProtoMinor = 0

     _, authed, admin := powerDaemon.AuthN.HTTPAuthenticate(request)

     if !authed {
          powerDaemon.DaemonLog.LogError("ERROR: Username password combo invalid.", os.NewError("Access Denied"))
          return
     }

     if !admin {
          powerDaemon.DaemonLog.LogError("ERROR: No access to admin command.", os.NewError("Access Denied"))
          return
     }

     someBytes, error := ioutil.ReadAll(request.Body)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to read all from off POST.", error)

     error = request.Body.Close()
     powerDaemon.DaemonLog.LogError("ERROR: Failed to close off request body.", error)

     error = json.Unmarshal(someBytes, &nodes)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to unmarshal nodes to be turned off.", error)

     for _, value := range nodes {
          _, ok := outletStatus[value]
          _, ok2 := resources[value]

          if !ok && ok2 {
               someBytes, error = exec.Command("./powerCont.sh", resources[value].Address, "admn", "admn", "status").Output()
               powerDaemon.DaemonLog.LogError("ERROR: Failed to execute powerCont.sh and get out put in power status request.", error)

               tmpStatusLines := strings.Split(string(someBytes), "\n")

               for i := 18 ; i < 42 ; i++ {
                    tmpStatusFields := strings.Split(tmpStatusLines[i], " ")

                    for _, value2 := range nodes {
                         if _, ok3 := resources[value2] ; ok3 && ok2 {
                              if resources[value2].Address == resources[value].Address && resources[value2].Outlet == tmpStatusFields[3] {
                                   outletStatus[value2] = tmpStatusFields[13]
                              }
                         }
                    }
               }
          }
     }

     jsonStat, error := json.Marshal(outletStatus)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to marshal outlet status response.", error)

     _, error = writer.Write(jsonStat)
     powerDaemon.DaemonLog.LogError("ERROR: Unable to write outlet status response.", error)
}

func main() {
     http.HandleFunc("/dump", DumpCall)
     http.HandleFunc("/reboot", rebootList)
     http.HandleFunc("/off", offList)
     http.HandleFunc("/status", statusList)

     error := http.ListenAndServe(":" + powerDaemon.Cfg.Data["powerPort"], nil)
     powerDaemon.DaemonLog.LogError("ERROR: Failed to listen on http socket.", error)
}<|endoftext|>


<commit_before>package main

type nullFile struct {
	data    []byte
	content string
}

// NotFoundFiles to return HTTP 404, not found
var NotFoundFiles = map[string]bool{
	"7z":   true,
	"avi":  true,
	"bin":  true,
	"bz2":  true,
	"doc":  true,
	"docx": true,
	"exe":  true,
	"gz":   true,
	"iso":  true,
	"java": true,
	"mov":  true,
	"mp4":  true,
	"pdf":  true,
	"ppt":  true,
	"pptx": true,
	"lz":   true,
	"lzma": true,
	"sh":   true,
	"swf":  true,
	"rar":  true,
	"tar":  true,
	"tb2":  true,
	"tbz":  true,
	"tbz2": true,
	"tgz":  true,
	"txz":  true,
	"webp": true,
	"webm": true,
	"xls":  true,
	"xlsx": true,
	"xz":   true,
	"zip":  true,
}

// AltSuffix maps alternative extensions to other valid nullFiles
var AltSuffix = map[string]string{
	"asp":  "html",
	"aspx": "html",
	"cgi":  "html",
	"dll":  "html",
	"do":   "html",
	"htm":  "html",
	"jpeg": "jpg",
	"m4a":  "mp4",
	"m4b":  "mp4",
	"m4p":  "mp4",
	"m4r":  "mp4",
	"m4v":  "mp4",
	"tif":  "tiff",
	"xht":  "xhtml",
}

// All known nullfiles
var nullFiles = map[string]nullFile{
	"": nullFile{nil, "text/plain"}, // Catch-all empty suffix
	"bmp": nullFile{[]byte{
		'\x42', '\x4d', '\x1e', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x1a', '\x00', '\x00', '\x00', '\x0c', '\x00',
		'\x00', '\x00', '\x01', '\x00', '\x01', '\x00', '\x01', '\x00',
		'\x18', '\x00', '\x00', '\x00', '\xff', '\x00',
	}, "image/bmp"},
	"css": nullFile{nil, "text/css"},
	"csv": nullFile{nil, "text/csv"},
	"gif": nullFile{[]byte{
		'\x47', '\x49', '\x46', '\x38', '\x39', '\x61', '\x01', '\x00',
		'\x01', '\x00', '\x80', '\x00', '\x00', '\xff', '\xff', '\xff',
		'\x00', '\x00', '\x00', '\x21', '\xf9', '\x04', '\x01', '\x00',
		'\x00', '\x00', '\x00', '\x2c', '\x00', '\x00', '\x00', '\x00',
		'\x01', '\x00', '\x01', '\x00', '\x00', '\x02', '\x02', '\x44',
		'\x01', '\x00', '\x3b',
	}, "image/gif"},
	"html": nullFile{[]byte("<!DOCTYPE html><title>x</title>"),
		"text/html"},
	"ico": nullFile{[]byte{
		'\x00', '\x00', '\x01', '\x00', '\x01', '\x00', '\x01', '\x01',
		'\x00', '\x00', '\x01', '\x00', '\x18', '\x00', '\x30', '\x00',
		'\x00', '\x00', '\x16', '\x00', '\x00', '\x00', '\x28', '\x00',
		'\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x02', '\x00',
		'\x00', '\x00', '\x01', '\x00', '\x18', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\xff', '\x00', '\x00', '\x00', '\x00', '\x00',
	}, "image/x-icon"},
	"jpg": nullFile{[]byte{
		'\xff', '\xd8', '\xff', '\xe0', '\x00', '\x10', '\x4a', '\x46',
		'\x49', '\x46', '\x00', '\x01', '\x01', '\x01', '\x00', '\x01',
		'\x00', '\x01', '\x00', '\x00', '\xff', '\xdb', '\x00', '\x43',
		'\x00', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xc0', '\x00', '\x0b', '\x08', '\x00', '\x01',
		'\x00', '\x01', '\x01', '\x01', '\x11', '\x00', '\xff', '\xc4',
		'\x00', '\x14', '\x00', '\x01', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x03', '\xff', '\xc4', '\x00', '\x14',
		'\x10', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\xff', '\xda', '\x00', '\x08', '\x01', '\x01',
		'\x00', '\x00', '\x3f', '\x00', '\x47', '\xff', '\xd9',
	}, "image/jpg"},
	"js":   nullFile{nil, "application/javascript"},
	"json": nullFile{[]byte("{}"), "application/json"},
	"png": nullFile{[]byte{
		'\x89', '\x50', '\x4e', '\x47', '\x0d', '\x0a', '\x1a', '\x0a',
		'\x00', '\x00', '\x00', '\x0d', '\x49', '\x48', '\x44', '\x52',
		'\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01',
		'\x08', '\x06', '\x00', '\x00', '\x00', '\x1f', '\x15', '\xc4',
		'\x89', '\x00', '\x00', '\x00', '\x0a', '\x49', '\x44', '\x41',
		'\x54', '\x78', '\x9c', '\x63', '\x00', '\x01', '\x00', '\x00',
		'\x05', '\x00', '\x01', '\x0d', '\x0a', '\x2d', '\xb4', '\x00',
		'\x00', '\x00', '\x00', '\x49', '\x45', '\x4e', '\x44', '\xae',
		'\x42', '\x60', '\x82',
	}, "image/png"},
	"tiff": nullFile{[]byte{
		'\x4d', '\x4d', '\x00', '\x2a', '\x00', '\x00', '\x00', '\x08',
		'\x00', '\x07', '\x01', '\x00', '\x00', '\x03', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x01', '\x00', '\x00', '\x01', '\x01',
		'\x00', '\x03', '\x00', '\x00', '\x00', '\x01', '\x00', '\x01',
		'\x00', '\x00', '\x01', '\x06', '\x00', '\x03', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x00', '\x00', '\x00', '\x01', '\x11',
		'\x00', '\x03', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00',
		'\x00', '\x00', '\x01', '\x17', '\x00', '\x03', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x01', '\x00', '\x00', '\x01', '\x1a',
		'\x00', '\x05', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00',
		'\x00', '\x64', '\x01', '\x1b', '\x00', '\x05', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x00', '\x00', '\x64', '\x00', '\x00',
		'\x00', '\x00', '\x20', '\x20', '\x20', '\x62', '\x79', '\x20',
		'\x61', '\x6c', '\x6f', '\x6b',
	}, "image/tiff"},
	"svg": nullFile{[]byte{
		'\x3c', '\x73', '\x76', '\x67', '\x20', '\x78', '\x6d', '\x6c',
		'\x6e', '\x73', '\x3d', '\x22', '\x68', '\x74', '\x74', '\x70',
		'\x3a', '\x2f', '\x2f', '\x77', '\x77', '\x77', '\x2e', '\x77',
		'\x33', '\x2e', '\x6f', '\x72', '\x67', '\x2f', '\x32', '\x30',
		'\x30', '\x30', '\x2f', '\x73', '\x76', '\x67', '\x22', '\x2f',
		'\x3e',
	}, "image/svg+xml"},
	"wasm": nullFile{[]byte{
		'\x00', '\x61', '\x73', '\x6d', '\x01', '\x00', '\x00', '\x00',
	}, "application/wasm"},
	"xhtml": nullFile{[]byte{
		'\x3c', '\x68', '\x74', '\x6d', '\x6c', '\x20', '\x78', '\x6d',
		'\x6c', '\x6e', '\x73', '\x3d', '\x22', '\x68', '\x74', '\x74',
		'\x70', '\x3a', '\x2f', '\x2f', '\x77', '\x77', '\x77', '\x2e',
		'\x77', '\x33', '\x2e', '\x6f', '\x72', '\x67', '\x2f', '\x31',
		'\x39', '\x39', '\x39', '\x2f', '\x78', '\x68', '\x74', '\x6d',
		'\x6c', '\x22', '\x2f', '\x3e',
	}, "application/xhtml+xml"},
	"xml": nullFile{[]byte{
		'\x3c', '\x3f', '\x78', '\x6d', '\x6c', '\x20', '\x76', '\x65',
		'\x72', '\x73', '\x69', '\x6f', '\x6e', '\x3d', '\x22', '\x31',
		'\x2e', '\x31', '\x22', '\x3f', '\x3e', '\x3c', '\x21', '\x44',
		'\x4f', '\x43', '\x54', '\x59', '\x50', '\x45', '\x20', '\x5f',
		'\x5b', '\x3c', '\x21', '\x45', '\x4c', '\x45', '\x4d', '\x45',
		'\x4e', '\x54', '\x20', '\x5f', '\x20', '\x45', '\x4d', '\x50',
		'\x54', '\x59', '\x3e', '\x5d', '\x3e', '\x3c', '\x5f', '\x2f',
		'\x3e',
	}, "application/xml"},
}
<commit_msg>files: .mp3 returns 404<commit_after>package main

type nullFile struct {
	data    []byte
	content string
}

// NotFoundFiles to return HTTP 404, not found
var NotFoundFiles = map[string]bool{
	"7z":   true,
	"avi":  true,
	"bin":  true,
	"bz2":  true,
	"doc":  true,
	"docx": true,
	"exe":  true,
	"gz":   true,
	"iso":  true,
	"java": true,
	"mov":  true,
	"mp3":  true,
	"mp4":  true,
	"pdf":  true,
	"ppt":  true,
	"pptx": true,
	"lz":   true,
	"lzma": true,
	"sh":   true,
	"swf":  true,
	"rar":  true,
	"tar":  true,
	"tb2":  true,
	"tbz":  true,
	"tbz2": true,
	"tgz":  true,
	"txz":  true,
	"webp": true,
	"webm": true,
	"xls":  true,
	"xlsx": true,
	"xz":   true,
	"zip":  true,
}

// AltSuffix maps alternative extensions to other valid nullFiles
var AltSuffix = map[string]string{
	"asp":  "html",
	"aspx": "html",
	"cgi":  "html",
	"dll":  "html",
	"do":   "html",
	"htm":  "html",
	"jpeg": "jpg",
	"m4a":  "mp4",
	"m4b":  "mp4",
	"m4p":  "mp4",
	"m4r":  "mp4",
	"m4v":  "mp4",
	"tif":  "tiff",
	"xht":  "xhtml",
}

// All known nullfiles
var nullFiles = map[string]nullFile{
	"": nullFile{nil, "text/plain"}, // Catch-all empty suffix
	"bmp": nullFile{[]byte{
		'\x42', '\x4d', '\x1e', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x1a', '\x00', '\x00', '\x00', '\x0c', '\x00',
		'\x00', '\x00', '\x01', '\x00', '\x01', '\x00', '\x01', '\x00',
		'\x18', '\x00', '\x00', '\x00', '\xff', '\x00',
	}, "image/bmp"},
	"css": nullFile{nil, "text/css"},
	"csv": nullFile{nil, "text/csv"},
	"gif": nullFile{[]byte{
		'\x47', '\x49', '\x46', '\x38', '\x39', '\x61', '\x01', '\x00',
		'\x01', '\x00', '\x80', '\x00', '\x00', '\xff', '\xff', '\xff',
		'\x00', '\x00', '\x00', '\x21', '\xf9', '\x04', '\x01', '\x00',
		'\x00', '\x00', '\x00', '\x2c', '\x00', '\x00', '\x00', '\x00',
		'\x01', '\x00', '\x01', '\x00', '\x00', '\x02', '\x02', '\x44',
		'\x01', '\x00', '\x3b',
	}, "image/gif"},
	"html": nullFile{[]byte("<!DOCTYPE html><title>x</title>"),
		"text/html"},
	"ico": nullFile{[]byte{
		'\x00', '\x00', '\x01', '\x00', '\x01', '\x00', '\x01', '\x01',
		'\x00', '\x00', '\x01', '\x00', '\x18', '\x00', '\x30', '\x00',
		'\x00', '\x00', '\x16', '\x00', '\x00', '\x00', '\x28', '\x00',
		'\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x02', '\x00',
		'\x00', '\x00', '\x01', '\x00', '\x18', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\xff', '\x00', '\x00', '\x00', '\x00', '\x00',
	}, "image/x-icon"},
	"jpg": nullFile{[]byte{
		'\xff', '\xd8', '\xff', '\xe0', '\x00', '\x10', '\x4a', '\x46',
		'\x49', '\x46', '\x00', '\x01', '\x01', '\x01', '\x00', '\x01',
		'\x00', '\x01', '\x00', '\x00', '\xff', '\xdb', '\x00', '\x43',
		'\x00', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xc0', '\x00', '\x0b', '\x08', '\x00', '\x01',
		'\x00', '\x01', '\x01', '\x01', '\x11', '\x00', '\xff', '\xc4',
		'\x00', '\x14', '\x00', '\x01', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x03', '\xff', '\xc4', '\x00', '\x14',
		'\x10', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\xff', '\xda', '\x00', '\x08', '\x01', '\x01',
		'\x00', '\x00', '\x3f', '\x00', '\x47', '\xff', '\xd9',
	}, "image/jpg"},
	"js":   nullFile{nil, "application/javascript"},
	"json": nullFile{[]byte("{}"), "application/json"},
	"png": nullFile{[]byte{
		'\x89', '\x50', '\x4e', '\x47', '\x0d', '\x0a', '\x1a', '\x0a',
		'\x00', '\x00', '\x00', '\x0d', '\x49', '\x48', '\x44', '\x52',
		'\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01',
		'\x08', '\x06', '\x00', '\x00', '\x00', '\x1f', '\x15', '\xc4',
		'\x89', '\x00', '\x00', '\x00', '\x0a', '\x49', '\x44', '\x41',
		'\x54', '\x78', '\x9c', '\x63', '\x00', '\x01', '\x00', '\x00',
		'\x05', '\x00', '\x01', '\x0d', '\x0a', '\x2d', '\xb4', '\x00',
		'\x00', '\x00', '\x00', '\x49', '\x45', '\x4e', '\x44', '\xae',
		'\x42', '\x60', '\x82',
	}, "image/png"},
	"tiff": nullFile{[]byte{
		'\x4d', '\x4d', '\x00', '\x2a', '\x00', '\x00', '\x00', '\x08',
		'\x00', '\x07', '\x01', '\x00', '\x00', '\x03', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x01', '\x00', '\x00', '\x01', '\x01',
		'\x00', '\x03', '\x00', '\x00', '\x00', '\x01', '\x00', '\x01',
		'\x00', '\x00', '\x01', '\x06', '\x00', '\x03', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x00', '\x00', '\x00', '\x01', '\x11',
		'\x00', '\x03', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00',
		'\x00', '\x00', '\x01', '\x17', '\x00', '\x03', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x01', '\x00', '\x00', '\x01', '\x1a',
		'\x00', '\x05', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00',
		'\x00', '\x64', '\x01', '\x1b', '\x00', '\x05', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x00', '\x00', '\x64', '\x00', '\x00',
		'\x00', '\x00', '\x20', '\x20', '\x20', '\x62', '\x79', '\x20',
		'\x61', '\x6c', '\x6f', '\x6b',
	}, "image/tiff"},
	"svg": nullFile{[]byte{
		'\x3c', '\x73', '\x76', '\x67', '\x20', '\x78', '\x6d', '\x6c',
		'\x6e', '\x73', '\x3d', '\x22', '\x68', '\x74', '\x74', '\x70',
		'\x3a', '\x2f', '\x2f', '\x77', '\x77', '\x77', '\x2e', '\x77',
		'\x33', '\x2e', '\x6f', '\x72', '\x67', '\x2f', '\x32', '\x30',
		'\x30', '\x30', '\x2f', '\x73', '\x76', '\x67', '\x22', '\x2f',
		'\x3e',
	}, "image/svg+xml"},
	"wasm": nullFile{[]byte{
		'\x00', '\x61', '\x73', '\x6d', '\x01', '\x00', '\x00', '\x00',
	}, "application/wasm"},
	"xhtml": nullFile{[]byte{
		'\x3c', '\x68', '\x74', '\x6d', '\x6c', '\x20', '\x78', '\x6d',
		'\x6c', '\x6e', '\x73', '\x3d', '\x22', '\x68', '\x74', '\x74',
		'\x70', '\x3a', '\x2f', '\x2f', '\x77', '\x77', '\x77', '\x2e',
		'\x77', '\x33', '\x2e', '\x6f', '\x72', '\x67', '\x2f', '\x31',
		'\x39', '\x39', '\x39', '\x2f', '\x78', '\x68', '\x74', '\x6d',
		'\x6c', '\x22', '\x2f', '\x3e',
	}, "application/xhtml+xml"},
	"xml": nullFile{[]byte{
		'\x3c', '\x3f', '\x78', '\x6d', '\x6c', '\x20', '\x76', '\x65',
		'\x72', '\x73', '\x69', '\x6f', '\x6e', '\x3d', '\x22', '\x31',
		'\x2e', '\x31', '\x22', '\x3f', '\x3e', '\x3c', '\x21', '\x44',
		'\x4f', '\x43', '\x54', '\x59', '\x50', '\x45', '\x20', '\x5f',
		'\x5b', '\x3c', '\x21', '\x45', '\x4c', '\x45', '\x4d', '\x45',
		'\x4e', '\x54', '\x20', '\x5f', '\x20', '\x45', '\x4d', '\x50',
		'\x54', '\x59', '\x3e', '\x5d', '\x3e', '\x3c', '\x5f', '\x2f',
		'\x3e',
	}, "application/xml"},
}
<|endoftext|>