<commit_before>package integration_test

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/blang/semver"
	"github.com/cloudfoundry/libbuildpack/cutlass"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

var bpDir string
var buildpackVersion string
var packagedBuildpack cutlass.VersionedBuildpackPackage

func init() {
	flag.StringVar(&buildpackVersion, "version", "", "version to use (builds if empty)")
	flag.BoolVar(&cutlass.Cached, "cached", true, "cached buildpack")
	flag.StringVar(&cutlass.DefaultMemory, "memory", "128M", "default memory for pushed apps")
	flag.StringVar(&cutlass.DefaultDisk, "disk", "384M", "default disk for pushed apps")
	flag.Parse()
}

var _ = SynchronizedBeforeSuite(func() []byte {
	// Run once
	if buildpackVersion == "" {
		packagedBuildpack, err := cutlass.PackageUniquelyVersionedBuildpack()
		Expect(err).NotTo(HaveOccurred())

		data, err := json.Marshal(packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		return data
	}

	return []byte{}
}, func(data []byte) {
	// Run on all nodes
	var err error
	if len(data) > 0 {
		err = json.Unmarshal(data, &packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		buildpackVersion = packagedBuildpack.Version
	}

	bpDir, err = cutlass.FindRoot()
	Expect(err).NotTo(HaveOccurred())

	Expect(cutlass.CopyCfHome()).To(Succeed())
	cutlass.SeedRandom()
	cutlass.DefaultStdoutStderr = GinkgoWriter
})

var _ = SynchronizedAfterSuite(func() {
	// Run on all nodes
	Expect(os.RemoveAll("CF_HOME")).To(Succeed())
}, func() {
	// Run once
	// Expect(cutlass.RemovePackagedBuildpack(packagedBuildpack)).To(Succeed())
	Expect(cutlass.DeleteOrphanedRoutes()).To(Succeed())
})

func TestIntegration(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Integration Suite")
}

func PushAppAndConfirm(app *cutlass.App) {
	Expect(app.Push()).To(Succeed())
	Eventually(func() ([]string, error) { return app.InstanceStates() }, 20*time.Second).Should(Equal([]string{"RUNNING"}))
	Expect(app.ConfirmBuildpack(buildpackVersion)).To(Succeed())
}

func Restart(app *cutlass.App) {
	Expect(app.Restart()).To(Succeed())
	Eventually(func() ([]string, error) { return app.InstanceStates() }, 20*time.Second).Should(Equal([]string{"RUNNING"}))
}

func ApiHasTask() bool {
	apiVersionString, err := cutlass.ApiVersion()
	Expect(err).To(BeNil())
	apiVersion, err := semver.Make(apiVersionString)
	Expect(err).To(BeNil())
	apiHasTask, err := semver.ParseRange(">= 2.75.0")
	Expect(err).To(BeNil())
	return apiHasTask(apiVersion)
}

func AssertUsesProxyDuringStagingIfPresent(fixtureName string) {
	Context("with an uncached buildpack", func() {
		BeforeEach(func() {
			if cutlass.Cached {
				Skip("Running cached tests")
			}
		})

		It("uses a proxy during staging if present", func() {
			proxy, err := cutlass.NewProxy()
			Expect(err).To(BeNil())
			defer proxy.Close()

			bpFile := filepath.Join(bpDir, buildpackVersion+"tmp")
			cmd := exec.Command("cp", packagedBuildpack.File, bpFile)
			err = cmd.Run()
			Expect(err).To(BeNil())
			defer os.Remove(bpFile)

			traffic, err := cutlass.InternetTraffic(
				bpDir,
				filepath.Join("fixtures", fixtureName),
				bpFile,
				[]string{"HTTP_PROXY=" + proxy.URL, "HTTPS_PROXY=" + proxy.URL},
			)
			Expect(err).To(BeNil())

			destUrl, err := url.Parse(proxy.URL)
			Expect(err).To(BeNil())

			Expect(cutlass.UniqueDestination(
				traffic, fmt.Sprintf("%s.%s", destUrl.Hostname(), destUrl.Port()),
			)).To(BeNil())
		})
	})
}

func AssertNoInternetTraffic(fixtureName string) {
	It("has no traffic", func() {
		if !cutlass.Cached {
			Skip("Running uncached tests")
		}

		bpFile := filepath.Join(bpDir, buildpackVersion+"tmp")
		cmd := exec.Command("cp", packagedBuildpack.File, bpFile)
		err := cmd.Run()
		Expect(err).To(BeNil())
		defer os.Remove(bpFile)

		traffic, err := cutlass.InternetTraffic(
			bpDir,
			filepath.Join("fixtures", fixtureName),
			bpFile,
			[]string{},
		)
		Expect(err).To(BeNil())
		Expect(traffic).To(BeEmpty())
	})
}
<commit_msg>Remove os.RemoveAll CF_HOME<commit_after>package integration_test

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/blang/semver"
	"github.com/cloudfoundry/libbuildpack/cutlass"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

var bpDir string
var buildpackVersion string
var packagedBuildpack cutlass.VersionedBuildpackPackage

func init() {
	flag.StringVar(&buildpackVersion, "version", "", "version to use (builds if empty)")
	flag.BoolVar(&cutlass.Cached, "cached", true, "cached buildpack")
	flag.StringVar(&cutlass.DefaultMemory, "memory", "128M", "default memory for pushed apps")
	flag.StringVar(&cutlass.DefaultDisk, "disk", "384M", "default disk for pushed apps")
	flag.Parse()
}

var _ = SynchronizedBeforeSuite(func() []byte {
	// Run once
	if buildpackVersion == "" {
		packagedBuildpack, err := cutlass.PackageUniquelyVersionedBuildpack()
		Expect(err).NotTo(HaveOccurred())

		data, err := json.Marshal(packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		return data
	}

	return []byte{}
}, func(data []byte) {
	// Run on all nodes
	var err error
	if len(data) > 0 {
		err = json.Unmarshal(data, &packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		buildpackVersion = packagedBuildpack.Version
	}

	bpDir, err = cutlass.FindRoot()
	Expect(err).NotTo(HaveOccurred())

	Expect(cutlass.CopyCfHome()).To(Succeed())
	cutlass.SeedRandom()
	cutlass.DefaultStdoutStderr = GinkgoWriter
})

var _ = SynchronizedAfterSuite(func() {
	// Run on all nodes
}, func() {
	// Run once
	Expect(cutlass.RemovePackagedBuildpack(packagedBuildpack)).To(Succeed())
	Expect(cutlass.DeleteOrphanedRoutes()).To(Succeed())
})

func TestIntegration(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Integration Suite")
}

func PushAppAndConfirm(app *cutlass.App) {
	Expect(app.Push()).To(Succeed())
	Eventually(func() ([]string, error) { return app.InstanceStates() }, 20*time.Second).Should(Equal([]string{"RUNNING"}))
	Expect(app.ConfirmBuildpack(buildpackVersion)).To(Succeed())
}

func Restart(app *cutlass.App) {
	Expect(app.Restart()).To(Succeed())
	Eventually(func() ([]string, error) { return app.InstanceStates() }, 20*time.Second).Should(Equal([]string{"RUNNING"}))
}

func ApiHasTask() bool {
	apiVersionString, err := cutlass.ApiVersion()
	Expect(err).To(BeNil())
	apiVersion, err := semver.Make(apiVersionString)
	Expect(err).To(BeNil())
	apiHasTask, err := semver.ParseRange(">= 2.75.0")
	Expect(err).To(BeNil())
	return apiHasTask(apiVersion)
}

func AssertUsesProxyDuringStagingIfPresent(fixtureName string) {
	Context("with an uncached buildpack", func() {
		BeforeEach(func() {
			if cutlass.Cached {
				Skip("Running cached tests")
			}
		})

		It("uses a proxy during staging if present", func() {
			proxy, err := cutlass.NewProxy()
			Expect(err).To(BeNil())
			defer proxy.Close()

			bpFile := filepath.Join(bpDir, buildpackVersion+"tmp")
			cmd := exec.Command("cp", packagedBuildpack.File, bpFile)
			err = cmd.Run()
			Expect(err).To(BeNil())
			defer os.Remove(bpFile)

			traffic, err := cutlass.InternetTraffic(
				bpDir,
				filepath.Join("fixtures", fixtureName),
				bpFile,
				[]string{"HTTP_PROXY=" + proxy.URL, "HTTPS_PROXY=" + proxy.URL},
			)
			Expect(err).To(BeNil())

			destUrl, err := url.Parse(proxy.URL)
			Expect(err).To(BeNil())

			Expect(cutlass.UniqueDestination(
				traffic, fmt.Sprintf("%s.%s", destUrl.Hostname(), destUrl.Port()),
			)).To(BeNil())
		})
	})
}

func AssertNoInternetTraffic(fixtureName string) {
	It("has no traffic", func() {
		if !cutlass.Cached {
			Skip("Running uncached tests")
		}

		bpFile := filepath.Join(bpDir, buildpackVersion+"tmp")
		cmd := exec.Command("cp", packagedBuildpack.File, bpFile)
		err := cmd.Run()
		Expect(err).To(BeNil())
		defer os.Remove(bpFile)

		traffic, err := cutlass.InternetTraffic(
			bpDir,
			filepath.Join("fixtures", fixtureName),
			bpFile,
			[]string{},
		)
		Expect(err).To(BeNil())
		Expect(traffic).To(BeEmpty())
	})
}
<|endoftext|>


<commit_before>// Package sss implements the Shamir Secret Sharing algorithm over GF2(256).
package sss

import (
	"crypto/rand"
	"github.com/codahale/sss/gf256"
)

// Split the given secret into N shares of which K are required to recover the
// secret. Returns a map of share IDs (1-255) to shares.
func Split(n, k int, secret []byte) (map[int][]byte, error) {
	shares := make(map[int][]byte, n)

	for _, b := range secret {
		p, err := gf256.RandPoly(k-1, gf256.Element(b), rand.Reader)
		if err != nil {
			return nil, err
		}

		for x := 1; x <= n; x++ {
			y := p.Eval(gf256.Element(x))
			shares[x] = append(shares[x], byte(y))
		}
	}

	return shares, nil
}

// Combine the given shares into the original secret.
// N.B.: There is no way to know whether the returned value is, in fact, the
// original secret.
func Combine(shares map[int][]byte) []byte {
	var secret []byte
	for _, v := range shares {
		secret = make([]byte, len(v))
		break
	}

	points := make([][2]gf256.Element, len(shares))
	for i := range secret {
		p := 0
		for k, v := range shares {
			points[p][0] = gf256.Element(k)
			points[p][1] = gf256.Element(v[i])
			p++
		}

		s := gf256.Interpolate(points, 0)
		secret[i] = byte(s)
	}

	return secret
}
<commit_msg>Get a little scary in the docs.<commit_after>// Package sss implements the Shamir Secret Sharing algorithm over GF2(256).
// This package has not been audited by cryptography or security professionals.
package sss

import (
	"crypto/rand"
	"github.com/codahale/sss/gf256"
)

// Split the given secret into N shares of which K are required to recover the
// secret. Returns a map of share IDs (1-255) to shares.
func Split(n, k int, secret []byte) (map[int][]byte, error) {
	shares := make(map[int][]byte, n)

	for _, b := range secret {
		p, err := gf256.RandPoly(k-1, gf256.Element(b), rand.Reader)
		if err != nil {
			return nil, err
		}

		for x := 1; x <= n; x++ {
			y := p.Eval(gf256.Element(x))
			shares[x] = append(shares[x], byte(y))
		}
	}

	return shares, nil
}

// Combine the given shares into the original secret.
// N.B.: There is no way to know whether the returned value is, in fact, the
// original secret.
func Combine(shares map[int][]byte) []byte {
	var secret []byte
	for _, v := range shares {
		secret = make([]byte, len(v))
		break
	}

	points := make([][2]gf256.Element, len(shares))
	for i := range secret {
		p := 0
		for k, v := range shares {
			points[p][0] = gf256.Element(k)
			points[p][1] = gf256.Element(v[i])
			p++
		}

		s := gf256.Interpolate(points, 0)
		secret[i] = byte(s)
	}

	return secret
}
<|endoftext|>


<commit_before>package stripe

import "encoding/json"

// SubStatus is the list of allowed values for the subscription's status.
// Allowed values are "trialing", "active", "past_due", "canceled", "unpaid".
type SubStatus string

// SubParams is the set of parameters that can be used when creating or updating a subscription.
// For more details see https://stripe.com/docs/api#create_subscription and https://stripe.com/docs/api#update_subscription.
type SubParams struct {
	Params
	Customer, Plan                                  string
	Coupon, Token                                   string
	TrialEnd                                        int64
	Card                                            *CardParams
	Quantity                                        uint64
	FeePercent                                      float64
	NoProrate, EndCancel, QuantityZero, TrialEndNow bool
}

// SubListParams is the set of parameters that can be used when listing active subscriptions.
// For more details see https://stripe.com/docs/api#list_subscriptions.
type SubListParams struct {
	ListParams
	Customer string
}

// Sub is the resource representing a Stripe subscription.
// For more details see https://stripe.com/docs/api#subscriptions.
type Sub struct {
	ID          string            `json:"id"`
	EndCancel   bool              `json:"cancel_at_period_end"`
	Customer    *Customer         `json:"customer"`
	Plan        *Plan             `json:"plan"`
	Quantity    uint64            `json:"quantity"`
	Status      SubStatus         `json:"status"`
	FeePercent  float64           `json:"application_fee_percent"`
	Canceled    int64             `json:"canceled_at"`
	PeriodEnd   int64             `json:"current_period_end"`
	PeriodStart int64             `json:"current_period_start"`
	Discount    *Discount         `json:"discount"`
	Ended       int64             `json:"ended_at"`
	Meta        map[string]string `json:"metadata"`
	TrialEnd    int64             `json:"trial_end"`
	TrialStart  int64             `json:"trial_start"`
}

// SubList is a list object for subscriptions.
type SubList struct {
	ListMeta
	Values []*Sub `json:"data"`
}

// UnmarshalJSON handles deserialization of a Sub.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (s *Sub) UnmarshalJSON(data []byte) error {
	type sub Sub
	var ss sub
	err := json.Unmarshal(data, &ss)
	if err == nil {
		*s = Sub(ss)
	} else {
		// the id is surrounded by "\" characters, so strip them
		s.ID = string(data[1 : len(data)-1])
	}

	return nil
}
<commit_msg>add TaxPercent field to Sub<commit_after>package stripe

import "encoding/json"

// SubStatus is the list of allowed values for the subscription's status.
// Allowed values are "trialing", "active", "past_due", "canceled", "unpaid".
type SubStatus string

// SubParams is the set of parameters that can be used when creating or updating a subscription.
// For more details see https://stripe.com/docs/api#create_subscription and https://stripe.com/docs/api#update_subscription.
type SubParams struct {
	Params
	Customer, Plan                                  string
	Coupon, Token                                   string
	TrialEnd                                        int64
	Card                                            *CardParams
	Quantity                                        uint64
	FeePercent                                      float64
	NoProrate, EndCancel, QuantityZero, TrialEndNow bool
}

// SubListParams is the set of parameters that can be used when listing active subscriptions.
// For more details see https://stripe.com/docs/api#list_subscriptions.
type SubListParams struct {
	ListParams
	Customer string
}

// Sub is the resource representing a Stripe subscription.
// For more details see https://stripe.com/docs/api#subscriptions.
type Sub struct {
	ID          string            `json:"id"`
	EndCancel   bool              `json:"cancel_at_period_end"`
	Customer    *Customer         `json:"customer"`
	Plan        *Plan             `json:"plan"`
	Quantity    uint64            `json:"quantity"`
	Status      SubStatus         `json:"status"`
	FeePercent  float64           `json:"application_fee_percent"`
	Canceled    int64             `json:"canceled_at"`
	PeriodEnd   int64             `json:"current_period_end"`
	PeriodStart int64             `json:"current_period_start"`
	Discount    *Discount         `json:"discount"`
	Ended       int64             `json:"ended_at"`
	Meta        map[string]string `json:"metadata"`
	TaxPercent  int64             `json:"tax_percent"`
	TrialEnd    int64             `json:"trial_end"`
	TrialStart  int64             `json:"trial_start"`
}

// SubList is a list object for subscriptions.
type SubList struct {
	ListMeta
	Values []*Sub `json:"data"`
}

// UnmarshalJSON handles deserialization of a Sub.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (s *Sub) UnmarshalJSON(data []byte) error {
	type sub Sub
	var ss sub
	err := json.Unmarshal(data, &ss)
	if err == nil {
		*s = Sub(ss)
	} else {
		// the id is surrounded by "\" characters, so strip them
		s.ID = string(data[1 : len(data)-1])
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package id3v2

import (
	"bufio"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/bogem/id3v2/bwpool"
	"github.com/bogem/id3v2/util"
)

// Tag stores all frames of opened file.
type Tag struct {
	frames    map[string]Framer
	sequences map[string]sequencer

	file         *os.File
	originalSize int64
	version      byte
}

// AddFrame adds f to tag with appropriate id. If id is "" or f is nil,
// AddFrame will not add f to tag.
//
// If you want to add attached picture, comment or unsynchronised lyrics/text
// transcription frames, better use AddAttachedPicture, AddCommentFrame
// or AddUnsynchronisedLyricsFrame methods respectively.
func (t *Tag) AddFrame(id string, f Framer) {
	if id == "" || f == nil {
		return
	}

	if id == t.CommonID("Attached picture") || id == t.CommonID("Comments") ||
		id == t.CommonID("Unsynchronised lyrics/text transcription") {
		t.checkSequence(id)
		t.addFrameToSequence(id, f)
	} else {
		t.frames[id] = f
	}
}

func (t *Tag) checkSequence(id string) {
	if t.sequences[id] == nil {
		switch id {
		case t.CommonID("Attached picture"):
			t.sequences[id] = newPictureSequence()
		case t.CommonID("Comments"):
			t.sequences[id] = newCommentSequence()
		case t.CommonID("Unsynchronised lyrics/text transcription"):
			t.sequences[id] = newUSLFSequence()
		}
	}
}

func (t *Tag) addFrameToSequence(id string, f Framer) {
	t.sequences[id].AddFrame(f)
}

func (t *Tag) AddAttachedPicture(pf PictureFrame) {
	id := t.CommonID("Attached picture")
	t.AddFrame(id, pf)
}

func (t *Tag) AddCommentFrame(cf CommentFrame) {
	id := t.CommonID("Comments")
	t.AddFrame(id, cf)
}

func (t *Tag) AddUnsynchronisedLyricsFrame(uslf UnsynchronisedLyricsFrame) {
	id := t.CommonID("Unsynchronised lyrics/text transcription")
	t.AddFrame(id, uslf)
}

// CommonID returns ID3v2.3 or ID3v2.4 (in appropriate to version of Tag) frame ID
// from given description.
// For example, CommonID("Language") will return "TLAN".
// All descriptions you can find in file common_ids.go or in id3 documentation (for fourth version: http://id3.org/id3v2.4.0-frames; for third version: http://id3.org/id3v2.3.0#Declared_ID3v2_frames).
func (t Tag) CommonID(description string) string {
	if t.version == 3 {
		return V23CommonIDs[description]
	} else {
		return V24CommonIDs[description]
	}

	return ""
}

// AllFrames returns map, that contains all frames in tag, that could be parsed.
// The key of this map is an ID of frame and value is an array of frames.
func (t *Tag) AllFrames() map[string][]Framer {
	frames := make(map[string][]Framer)

	for id := range t.frames {
		frames[id] = t.GetFrames(id)
	}

	for id := range t.sequences {
		frames[id] = t.GetFrames(id)
	}

	return frames
}

// DeleteAllFrames deletes all frames in tag.
func (t *Tag) DeleteAllFrames() {
	t.frames = make(map[string]Framer)
	t.sequences = make(map[string]sequencer)
}

// DeleteFrames deletes frames in tag with given id.
func (t *Tag) DeleteFrames(id string) {
	delete(t.frames, id)
	delete(t.sequences, id)
}

// GetLastFrame returns last frame from slice, that is returned from GetFrames function.
// GetLastFrame is suitable for frames, that can be only one in whole tag.
// For example, for text frames.
//
// Example of usage:
//	bpmFramer := tag.GetLastFrame(tag.CommonID("BPM"))
//	if bpmFramer != nil {
//		bpm, ok := bpmFramer.(id3v2.TextFrame)
//		if !ok {
//			log.Fatal("Couldn't assert bpm frame")
//		}
//		fmt.Println(bpm.Text)
//	}
func (t *Tag) GetLastFrame(id string) Framer {
	// Avoid an allocation of slice in GetFrames,
	// if there is anyway one frame.
	if f, exists := t.frames[id]; exists {
		return f
	}

	fs := t.GetFrames(id)
	if len(fs) == 0 || fs == nil {
		return nil
	}
	return fs[len(fs)-1]
}

// GetFrames returns frames with corresponding id.
// It returns nil if there is no frames with given id.
//
// Example of usage:
//	pictures := tag.GetFrames(tag.CommonID("Attached picture"))
//	if pictures != nil {
//		for _, f := range pictures {
//			pic, ok := f.(id3v2.PictureFrame)
//			if !ok {
//				log.Fatal("Couldn't assert picture frame")
//			}
//
//			// Do some operations with picture frame:
//			fmt.Println(pic.Description) // For example, print description of picture frame
//		}
//	}
func (t *Tag) GetFrames(id string) []Framer {
	if f, exists := t.frames[id]; exists {
		return []Framer{f}
	} else if s, exists := t.sequences[id]; exists {
		return s.Frames()
	}
	return nil
}

// GetTextFrame returns text frame with corresponding id.
func (t Tag) GetTextFrame(id string) TextFrame {
	f := t.GetLastFrame(id)
	if f == nil {
		return TextFrame{}
	}
	tf := f.(TextFrame)
	return tf
}

// Count returns the number of frames in tag.
func (t Tag) Count() int {
	n := len(t.frames)
	for _, s := range t.sequences {
		n += s.Count()
	}
	return n
}

// HasAnyFrames checks if there is at least one frame in tag.
// It's much faster than tag.Count() > 0.
func (t Tag) HasAnyFrames() bool {
	return len(t.frames) > 0 || len(t.sequences) > 0
}

func (t Tag) Title() string {
	f := t.GetTextFrame(t.CommonID("Title/Songname/Content description"))
	return f.Text
}

func (t *Tag) SetTitle(title string) {
	t.AddFrame(t.CommonID("Title/Songname/Content description"), TextFrame{Encoding: ENUTF8, Text: title})
}

func (t Tag) Artist() string {
	f := t.GetTextFrame(t.CommonID("Lead artist/Lead performer/Soloist/Performing group"))
	return f.Text
}

func (t *Tag) SetArtist(artist string) {
	t.AddFrame(t.CommonID("Lead artist/Lead performer/Soloist/Performing group"), TextFrame{Encoding: ENUTF8, Text: artist})
}

func (t Tag) Album() string {
	f := t.GetTextFrame(t.CommonID("Album/Movie/Show title"))
	return f.Text
}

func (t *Tag) SetAlbum(album string) {
	t.AddFrame(t.CommonID("Album/Movie/Show title"), TextFrame{Encoding: ENUTF8, Text: album})
}

func (t Tag) Year() string {
	f := t.GetTextFrame(t.CommonID("Year"))
	return f.Text
}

func (t *Tag) SetYear(year string) {
	t.AddFrame(t.CommonID("Year"), TextFrame{Encoding: ENUTF8, Text: year})
}

func (t Tag) Genre() string {
	f := t.GetTextFrame(t.CommonID("Content type"))
	return f.Text
}

func (t *Tag) SetGenre(genre string) {
	t.AddFrame(t.CommonID("Content type"), TextFrame{Encoding: ENUTF8, Text: genre})
}

// Version returns current ID3v2 version of tag.
func (t Tag) Version() byte {
	return t.version
}

// SetVersion sets given ID3v2 version to tag.
// If version is less than 3 or more than 4, then this method will do nothing.
// If tag has some frames, which are deprecated or changed in given version,
// then to your notice you can delete, change or just stay them.
func (t *Tag) SetVersion(version byte) {
	if version < 3 || version > 4 {
		return
	}
	t.version = version
}

// Save writes tag to the file. If there are no frames in tag, Save will write
// only music part without any ID3v2 information.
func (t *Tag) Save() error {
	// Create a temp file for mp3 file, which will contain new tag
	newFile, err := ioutil.TempFile(filepath.Dir(t.file.Name()), "id3v2-")
	if err != nil {
		return err
	}

	// Make sure we clean up the temp file if it's still around
	defer os.Remove(newFile.Name())

	// If there is at least one frame, write it
	if t.HasAnyFrames() {
		// Form size of new frames
		framesSize, err := util.FormSize(t.allFramesSize())
		if err != nil {
			return err
		}

		// Write to new file new tag header
		if _, err = newFile.Write(formTagHeader(framesSize, t.version)); err != nil {
			return err
		}

		// Write to new file new frames
		if err = t.writeAllFrames(newFile); err != nil {
			return err
		}
	}

	// Seek to a music part of original file
	originalFile := t.file
	if _, err = originalFile.Seek(t.originalSize, os.SEEK_SET); err != nil {
		return err
	}

	// Write to new file the music part
	if _, err = io.Copy(newFile, originalFile); err != nil {
		return err
	}

	// Get original file mode
	originalFileStat, err := originalFile.Stat()
	if err != nil {
		return err
	}

	// Set original file mode to new file
	if err = os.Chmod(newFile.Name(), originalFileStat.Mode()); err != nil {
		return err
	}

	// Close files to allow replacing
	newFile.Close()
	originalFile.Close()

	// Replace original file with new file
	if err = os.Rename(newFile.Name(), originalFile.Name()); err != nil {
		return err
	}

	// Set t.file to new file with original name
	t.file, err = os.Open(originalFile.Name())
	if err != nil {
		return err
	}

	// Set t.originalSize to new size
	t.originalSize = int64(t.allFramesSize())

	return nil
}

// Close closes the tag's file, rendering it unusable for I/O.
// It returns an error, if any.
func (t *Tag) Close() error {
	return t.file.Close()
}

var errBlankID = errors.New("blank ID")

func (t Tag) allFramesSize() int {
	var n int

	n += t.Count() * frameHeaderSize

	for _, frames := range t.AllFrames() {
		for _, f := range frames {
			n += f.Size()
		}
	}

	return n
}

func (t Tag) writeAllFrames(w io.Writer) error {
	bw := bwpool.Get(w)
	defer bwpool.Put(bw)

	for id, frames := range t.AllFrames() {
		for _, f := range frames {
			err := writeFrame(bw, id, f)
			if err == errBlankID {
				continue
			}
			if err != nil {
				return err
			}
		}
	}

	return bw.Flush()
}

func writeFrame(bw *bufio.Writer, id string, frame Framer) error {
	if id == "" {
		return errBlankID
	}

	if err := writeFrameHeader(bw, id, frame.Size()); err != nil {
		return err
	}
	if _, err := frame.WriteTo(bw); err != nil {
		return err
	}

	return nil
}

func writeFrameHeader(bw *bufio.Writer, id string, frameSize int) error {
	size, err := util.FormSize(frameSize)
	if err != nil {
		return err
	}

	bw.WriteString(id)
	bw.Write(size)
	bw.Write([]byte{0, 0})
	return nil
}
<commit_msg>Add tag.Size() method<commit_after>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package id3v2

import (
	"bufio"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/bogem/id3v2/bwpool"
	"github.com/bogem/id3v2/util"
)

// Tag stores all frames of opened file.
type Tag struct {
	frames    map[string]Framer
	sequences map[string]sequencer

	file         *os.File
	originalSize int64
	version      byte
}

// AddFrame adds f to tag with appropriate id. If id is "" or f is nil,
// AddFrame will not add f to tag.
//
// If you want to add attached picture, comment or unsynchronised lyrics/text
// transcription frames, better use AddAttachedPicture, AddCommentFrame
// or AddUnsynchronisedLyricsFrame methods respectively.
func (t *Tag) AddFrame(id string, f Framer) {
	if id == "" || f == nil {
		return
	}

	if id == t.CommonID("Attached picture") || id == t.CommonID("Comments") ||
		id == t.CommonID("Unsynchronised lyrics/text transcription") {
		t.checkSequence(id)
		t.addFrameToSequence(id, f)
	} else {
		t.frames[id] = f
	}
}

func (t *Tag) checkSequence(id string) {
	if t.sequences[id] == nil {
		switch id {
		case t.CommonID("Attached picture"):
			t.sequences[id] = newPictureSequence()
		case t.CommonID("Comments"):
			t.sequences[id] = newCommentSequence()
		case t.CommonID("Unsynchronised lyrics/text transcription"):
			t.sequences[id] = newUSLFSequence()
		}
	}
}

func (t *Tag) addFrameToSequence(id string, f Framer) {
	t.sequences[id].AddFrame(f)
}

func (t *Tag) AddAttachedPicture(pf PictureFrame) {
	id := t.CommonID("Attached picture")
	t.AddFrame(id, pf)
}

func (t *Tag) AddCommentFrame(cf CommentFrame) {
	id := t.CommonID("Comments")
	t.AddFrame(id, cf)
}

func (t *Tag) AddUnsynchronisedLyricsFrame(uslf UnsynchronisedLyricsFrame) {
	id := t.CommonID("Unsynchronised lyrics/text transcription")
	t.AddFrame(id, uslf)
}

// CommonID returns ID3v2.3 or ID3v2.4 (in appropriate to version of Tag) frame ID
// from given description.
// For example, CommonID("Language") will return "TLAN".
// All descriptions you can find in file common_ids.go or in id3 documentation (for fourth version: http://id3.org/id3v2.4.0-frames; for third version: http://id3.org/id3v2.3.0#Declared_ID3v2_frames).
func (t Tag) CommonID(description string) string {
	if t.version == 3 {
		return V23CommonIDs[description]
	} else {
		return V24CommonIDs[description]
	}

	return ""
}

// AllFrames returns map, that contains all frames in tag, that could be parsed.
// The key of this map is an ID of frame and value is an array of frames.
func (t *Tag) AllFrames() map[string][]Framer {
	frames := make(map[string][]Framer)

	for id := range t.frames {
		frames[id] = t.GetFrames(id)
	}

	for id := range t.sequences {
		frames[id] = t.GetFrames(id)
	}

	return frames
}

// DeleteAllFrames deletes all frames in tag.
func (t *Tag) DeleteAllFrames() {
	t.frames = make(map[string]Framer)
	t.sequences = make(map[string]sequencer)
}

// DeleteFrames deletes frames in tag with given id.
func (t *Tag) DeleteFrames(id string) {
	delete(t.frames, id)
	delete(t.sequences, id)
}

// GetLastFrame returns last frame from slice, that is returned from GetFrames function.
// GetLastFrame is suitable for frames, that can be only one in whole tag.
// For example, for text frames.
//
// Example of usage:
//	bpmFramer := tag.GetLastFrame(tag.CommonID("BPM"))
//	if bpmFramer != nil {
//		bpm, ok := bpmFramer.(id3v2.TextFrame)
//		if !ok {
//			log.Fatal("Couldn't assert bpm frame")
//		}
//		fmt.Println(bpm.Text)
//	}
func (t *Tag) GetLastFrame(id string) Framer {
	// Avoid an allocation of slice in GetFrames,
	// if there is anyway one frame.
	if f, exists := t.frames[id]; exists {
		return f
	}

	fs := t.GetFrames(id)
	if len(fs) == 0 || fs == nil {
		return nil
	}
	return fs[len(fs)-1]
}

// GetFrames returns frames with corresponding id.
// It returns nil if there is no frames with given id.
//
// Example of usage:
//	pictures := tag.GetFrames(tag.CommonID("Attached picture"))
//	if pictures != nil {
//		for _, f := range pictures {
//			pic, ok := f.(id3v2.PictureFrame)
//			if !ok {
//				log.Fatal("Couldn't assert picture frame")
//			}
//
//			// Do some operations with picture frame:
//			fmt.Println(pic.Description) // For example, print description of picture frame
//		}
//	}
func (t *Tag) GetFrames(id string) []Framer {
	if f, exists := t.frames[id]; exists {
		return []Framer{f}
	} else if s, exists := t.sequences[id]; exists {
		return s.Frames()
	}
	return nil
}

// GetTextFrame returns text frame with corresponding id.
func (t Tag) GetTextFrame(id string) TextFrame {
	f := t.GetLastFrame(id)
	if f == nil {
		return TextFrame{}
	}
	tf := f.(TextFrame)
	return tf
}

// Count returns the number of frames in tag.
func (t Tag) Count() int {
	n := len(t.frames)
	for _, s := range t.sequences {
		n += s.Count()
	}
	return n
}

// HasAnyFrames checks if there is at least one frame in tag.
// It's much faster than tag.Count() > 0.
func (t Tag) HasAnyFrames() bool {
	return len(t.frames) > 0 || len(t.sequences) > 0
}

func (t Tag) Title() string {
	f := t.GetTextFrame(t.CommonID("Title/Songname/Content description"))
	return f.Text
}

func (t *Tag) SetTitle(title string) {
	t.AddFrame(t.CommonID("Title/Songname/Content description"), TextFrame{Encoding: ENUTF8, Text: title})
}

func (t Tag) Artist() string {
	f := t.GetTextFrame(t.CommonID("Lead artist/Lead performer/Soloist/Performing group"))
	return f.Text
}

func (t *Tag) SetArtist(artist string) {
	t.AddFrame(t.CommonID("Lead artist/Lead performer/Soloist/Performing group"), TextFrame{Encoding: ENUTF8, Text: artist})
}

func (t Tag) Album() string {
	f := t.GetTextFrame(t.CommonID("Album/Movie/Show title"))
	return f.Text
}

func (t *Tag) SetAlbum(album string) {
	t.AddFrame(t.CommonID("Album/Movie/Show title"), TextFrame{Encoding: ENUTF8, Text: album})
}

func (t Tag) Year() string {
	f := t.GetTextFrame(t.CommonID("Year"))
	return f.Text
}

func (t *Tag) SetYear(year string) {
	t.AddFrame(t.CommonID("Year"), TextFrame{Encoding: ENUTF8, Text: year})
}

func (t Tag) Genre() string {
	f := t.GetTextFrame(t.CommonID("Content type"))
	return f.Text
}

func (t *Tag) SetGenre(genre string) {
	t.AddFrame(t.CommonID("Content type"), TextFrame{Encoding: ENUTF8, Text: genre})
}

// Size returns the size of all ID3 tag in bytes.
func (t Tag) Size() int {
	if !t.HasAnyFrames() {
		return 0
	}

	var n int
	n += tagHeaderSize                     // Add the size of tag header
	n += t.Count() * frameHeaderSize       // Add the size of all frame headers
	for _, frames := range t.AllFrames() { // Add the size of all frame bodies
		for _, f := range frames {
			n += f.Size()
		}
	}

	return n
}

// Version returns current ID3v2 version of tag.
func (t Tag) Version() byte {
	return t.version
}

// SetVersion sets given ID3v2 version to tag.
// If version is less than 3 or more than 4, then this method will do nothing.
// If tag has some frames, which are deprecated or changed in given version,
// then to your notice you can delete, change or just stay them.
func (t *Tag) SetVersion(version byte) {
	if version < 3 || version > 4 {
		return
	}
	t.version = version
}

// Save writes tag to the file. If there are no frames in tag, Save will write
// only music part without any ID3v2 information.
func (t *Tag) Save() error {
	// Create a temp file for mp3 file, which will contain new tag
	newFile, err := ioutil.TempFile(filepath.Dir(t.file.Name()), "id3v2-")
	if err != nil {
		return err
	}

	// Make sure we clean up the temp file if it's still around
	defer os.Remove(newFile.Name())

	framesSize := t.Size() - tagHeaderSize

	// If there is at least one frame, write it
	if framesSize > 0 {
		// Form size of new frames
		framesSize, err := util.FormSize(framesSize)
		if err != nil {
			return err
		}

		// Write to new file new tag header
		if _, err = newFile.Write(formTagHeader(framesSize, t.version)); err != nil {
			return err
		}

		// Write to new file new frames
		if err = t.writeAllFrames(newFile); err != nil {
			return err
		}
	}

	// Seek to a music part of original file
	originalFile := t.file
	if _, err = originalFile.Seek(t.originalSize, os.SEEK_SET); err != nil {
		return err
	}

	// Write to new file the music part
	if _, err = io.Copy(newFile, originalFile); err != nil {
		return err
	}

	// Get original file mode
	originalFileStat, err := originalFile.Stat()
	if err != nil {
		return err
	}

	// Set original file mode to new file
	if err = os.Chmod(newFile.Name(), originalFileStat.Mode()); err != nil {
		return err
	}

	// Close files to allow replacing
	newFile.Close()
	originalFile.Close()

	// Replace original file with new file
	if err = os.Rename(newFile.Name(), originalFile.Name()); err != nil {
		return err
	}

	// Set t.file to new file with original name
	t.file, err = os.Open(originalFile.Name())
	if err != nil {
		return err
	}

	// Set t.originalSize to new size
	t.originalSize = int64(framesSize)

	return nil
}

// Close closes the tag's file, rendering it unusable for I/O.
// It returns an error, if any.
func (t *Tag) Close() error {
	return t.file.Close()
}

var errBlankID = errors.New("blank ID")

func (t Tag) writeAllFrames(w io.Writer) error {
	bw := bwpool.Get(w)
	defer bwpool.Put(bw)

	for id, frames := range t.AllFrames() {
		for _, f := range frames {
			err := writeFrame(bw, id, f)
			if err == errBlankID {
				continue
			}
			if err != nil {
				return err
			}
		}
	}

	return bw.Flush()
}

func writeFrame(bw *bufio.Writer, id string, frame Framer) error {
	if id == "" {
		return errBlankID
	}

	if err := writeFrameHeader(bw, id, frame.Size()); err != nil {
		return err
	}
	if _, err := frame.WriteTo(bw); err != nil {
		return err
	}

	return nil
}

func writeFrameHeader(bw *bufio.Writer, id string, frameSize int) error {
	size, err := util.FormSize(frameSize)
	if err != nil {
		return err
	}

	bw.WriteString(id)
	bw.Write(size)
	bw.Write([]byte{0, 0})
	return nil
}
<|endoftext|>


<commit_before>package tdx

import (
	"encoding/binary"
	"fmt"
	"os"
	"path"
)

// A dayBar is a single record in end-of-day bar (.day) files.
type dayBar struct {
	RawDate     uint32  // yyyymmdd
	RawOpen     uint32  // in cents
	RawHigh     uint32  // in cents
	RawLow      uint32  // in cents
	RawClose    uint32  // in cents
	RawTurnover float32 // in yuan
	RawVolume   uint32  // in shares
	Reserved    [4]byte // unknown
}

// A fiveBar in a single record in five-minute bar (.5) files.
type fiveBar struct {
	RawDate     uint16  // higher 5 bits: years since 2004, higher 11 bits: mmdd
	RawTime     uint16  // minutes since 00:00:00 (UTC+8)
	RawOpen     uint32  // in cents
	RawHigh     uint32  // in cents
	RawLow      uint32  // in cents
	RawClose    uint32  // in cents
	RawTurnover float32 // in yuan
	RawVolume   uint32  // in shares
	Reserved    [4]byte // unknown
}

// A lcnBar is a single record in minute bar (.lc5/.lc1) files.
type lcnBar struct {
	RawDate     uint16  // higher 5 bits: years since 2004, higher 11 bits: mmdd
	RawTime     uint16  // minutes since 00:00:00 (UTC+8)
	RawOpen     float32 // in yuan
	RawHigh     float32 // in yuan
	RawLow      float32 // in yuan
	RawClose    float32 // in yuan
	RawTurnover float32 // in yuan
	RawVolume   uint32  // in shares
	Reserved    [4]byte // unknown
}

// DecodeFile decodes a bar data file that has been encoded in any of
// the supported formats. It detects file format by the file extension.
func DecodeFile(filepath string) ([]interface{}, error) {
	f, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}

	fi, err := f.Stat()
	if err != nil {
		return nil, err
	}

	bars := make([]interface{}, fi.Size()/32)
	for i := 0; i < len(bars); i++ {
		var bar interface{}
		switch path.Ext(filepath) {
		case ".day":
			bar = new(dayBar)
		case ".5":
			bar = new(fiveBar)
		case ".lc1", ".lc5":
			bar = new(lcnBar)
		default:
			return nil, fmt.Errorf("unsupported file: %s", filepath)
		}

		err = binary.Read(f, binary.LittleEndian, bar)
		if err != nil {
			return nil, err
		}

		bars[i] = bar
	}

	return bars, nil
}
<commit_msg>Implement the generic Bar interface<commit_after>package tdx

import (
	"encoding/binary"
	"fmt"
	"os"
	"path"
	"time"
)

var tz = time.FixedZone("UTC+8", int(8*time.Hour/time.Second))

// Bar provides a generic representation of bar data.
type Bar interface {
	Time() time.Time
	Open() float32
	High() float32
	Low() float32
	Close() float32
	Volume() uint32
	Turnover() float32
}

// A dayBar is a single record in end-of-day bar (.day) files.
type dayBar struct {
	RawDate     uint32  // yyyymmdd
	RawOpen     uint32  // in cents
	RawHigh     uint32  // in cents
	RawLow      uint32  // in cents
	RawClose    uint32  // in cents
	RawTurnover float32 // in yuan
	RawVolume   uint32  // in shares
	Reserved    [4]byte // unknown
}

func (bar *dayBar) Time() time.Time {
	mmdd := int(bar.RawDate % 10000)

	return time.Date(int(bar.RawDate/10000), time.Month(mmdd/100), mmdd%100, 9, 30, 0, 0, tz)
}

func (bar *dayBar) Open() float32 {
	return float32(bar.RawOpen) / 100
}

func (bar *dayBar) High() float32 {
	return float32(bar.RawHigh) / 100
}

func (bar *dayBar) Low() float32 {
	return float32(bar.RawLow) / 100
}

func (bar *dayBar) Close() float32 {
	return float32(bar.RawLow) / 100
}

func (bar *dayBar) Volume() uint32 {
	return bar.RawVolume
}

func (bar *dayBar) Turnover() float32 {
	return bar.RawTurnover
}

// A fiveBar in a single record in five-minute bar (.5) files.
type fiveBar struct {
	RawDate     uint16  // higher 5 bits: years since 2004, higher 11 bits: mmdd
	RawTime     uint16  // minutes since 00:00:00 (UTC+8)
	RawOpen     uint32  // in cents
	RawHigh     uint32  // in cents
	RawLow      uint32  // in cents
	RawClose    uint32  // in cents
	RawTurnover float32 // in yuan
	RawVolume   uint32  // in shares
	Reserved    [4]byte // unknown
}

func (bar *fiveBar) Time() time.Time {
	mmdd := int(bar.RawDate & (^uint16(0) >> (16 - 11)))

	return time.Date(2004+int(bar.RawDate>>11), time.Month(mmdd/100), mmdd%100, int(bar.RawTime/60), int(bar.RawTime%60), 0, 0, tz)
}

func (bar *fiveBar) Open() float32 {
	return float32(bar.RawOpen) / 100
}

func (bar *fiveBar) High() float32 {
	return float32(bar.RawHigh) / 100
}

func (bar *fiveBar) Low() float32 {
	return float32(bar.RawLow) / 100
}

func (bar *fiveBar) Close() float32 {
	return float32(bar.RawLow) / 100
}

func (bar *fiveBar) Volume() uint32 {
	return bar.RawVolume
}

func (bar *fiveBar) Turnover() float32 {
	return bar.RawTurnover
}

// A lcnBar is a single record in minute bar (.lc5/.lc1) files.
type lcnBar struct {
	RawDate     uint16  // higher 5 bits: years since 2004, higher 11 bits: mmdd
	RawTime     uint16  // minutes since 00:00:00 (UTC+8)
	RawOpen     float32 // in yuan
	RawHigh     float32 // in yuan
	RawLow      float32 // in yuan
	RawClose    float32 // in yuan
	RawTurnover float32 // in yuan
	RawVolume   uint32  // in shares
	Reserved    [4]byte // unknown
}

func (bar *lcnBar) Time() time.Time {
	mmdd := int(bar.RawDate & (^uint16(0) >> (16 - 11)))

	return time.Date(2004+int(bar.RawDate>>11), time.Month(mmdd/100), mmdd%100, int(bar.RawTime/60), int(bar.RawTime%60), 0, 0, tz)
}

func (bar *lcnBar) Open() float32 {
	return bar.RawOpen
}

func (bar *lcnBar) High() float32 {
	return bar.RawHigh
}

func (bar *lcnBar) Low() float32 {
	return bar.RawLow
}

func (bar *lcnBar) Close() float32 {
	return bar.RawClose
}

func (bar *lcnBar) Volume() uint32 {
	return bar.RawVolume
}

func (bar *lcnBar) Turnover() float32 {
	return bar.RawTurnover
}

// DecodeFile decodes a bar data file that has been encoded in any of
// the supported formats. It detects file format by the file extension.
func DecodeFile(filepath string) ([]Bar, error) {
	f, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}

	fi, err := f.Stat()
	if err != nil {
		return nil, err
	}

	bars := make([]Bar, fi.Size()/32)
	for i := 0; i < len(bars); i++ {
		var bar Bar
		switch path.Ext(filepath) {
		case ".day":
			bar = new(dayBar)
		case ".5":
			bar = new(fiveBar)
		case ".lc1", ".lc5":
			bar = new(lcnBar)
		default:
			return nil, fmt.Errorf("unsupported file: %s", filepath)
		}

		err = binary.Read(f, binary.LittleEndian, bar)
		if err != nil {
			return nil, err
		}

		bars[i] = bar
	}

	return bars, nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	term "github.com/nsf/termbox-go"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"time"
)

// we use line, offset style. termbox use o, l style.
func SetCursor(l, o int) {
	term.SetCursor(o, l)
}

// parseFileArg returns (filepath, linenum, offset, error).
// if the linenum is given, but 0 or negative, it will be 1.
// if the offset is given, but negative, it will be 0.
// when only filepath is given, linenum and offset will be set to -1.
func parseFileArg(farg string) (string, int, int, error) {
	finfo := strings.Split(farg, ":")
	f := finfo[0]
	l, o := -1, -1
	err := error(nil)

	if len(finfo) >= 4 {
		return "", -1, -1, errors.New("too many colons in file argument")
	}

	if len(finfo) == 1 {
		return f, -1, -1, nil
	}
	if len(finfo) >= 2 {
		l, err = strconv.Atoi(finfo[1])
		if err != nil {
			return "", -1, -1, err
		}
		if len(finfo) == 3 {
			o, err = strconv.Atoi(finfo[2])
			if err != nil {
				return "", -1, -1, err
			}
		}
	}

	if l < 0 {
		l = 0
	}
	if o < 0 {
		o = 0
	}
	return f, l, o, nil
}

func main() {
	var newFlag, debugFlag bool
	flag.BoolVar(&newFlag, "new", false, "let tor to edit a new file.")
	flag.BoolVar(&debugFlag, "debug", false, "tor will create .history file for debugging.")
	flag.Parse()

	args := flag.Args()
	if len(args) == 0 {
		fmt.Println("please, set text file")
		os.Exit(1)
	}
	farg := args[len(args)-1]

	f, initL, initO, err := parseFileArg(farg)
	if err != nil {
		fmt.Println("file arg is invalid: ", err)
		os.Exit(1)
	}

	exist := true
	if _, err := os.Stat(f); os.IsNotExist(err) {
		exist = false
	}
	if !exist && !newFlag {
		fmt.Println("file not exist. please retry with -new flag.")
		os.Exit(1)
	} else if exist && newFlag {
		fmt.Println("file already exist.")
		os.Exit(1)
	}

	var text *Text
	if exist {
		text, err = open(f)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	} else {
		lines := make([]Line, 0)
		lines = append(lines, Line{""})
		text = &Text{lines:lines, tabToSpace:false, tabWidth:4}
	}

	err = term.Init()
	if err != nil {
		panic(err)
	}
	defer term.Close()
	term.SetInputMode(term.InputAlt)
	// term.SetOutputMode(term.Output256)
	term.Clear(term.ColorDefault, term.ColorDefault)
	term.Flush()

	termw, termh := term.Size()
	mainarea := NewArea(Point{0, 0}, Point{termh - 1, termw})
	win := NewWindow(mainarea.Size())

	cursor := NewCursor(text)
	if initL != -1 {
		l := initL
		// to internal line number
		if l != 0 {
			l--
		}
		cursor.GotoLine(l)
		if initO != -1 {
			cursor.SetO(initO)
		}
	} else {
		l, b := loadLastPosition(f)
		cursor.GotoLine(l)
		cursor.SetCloseToB(b)
	}

	findmode := &LineInputMode{}
	replacemode := &LineInputMode{}
	gotolinemode := &GotoLineMode{}
	selection := NewSelection()
	history := newHistory()

	mode := "normal"

	edited := false
	status := ""
	holdStatus := false
	lastActStr := ""
	copied := ""

	events := make(chan term.Event, 20)
	go func() {
		for {
			events <- term.PollEvent()
		}
	}()
	for {
		win.Follow(cursor, 3)
		clearScreen(mainarea)
		drawScreen(mainarea, win, text, selection, cursor, mode)

		if mode == "exit" {
			status = fmt.Sprintf("Buffer modified. Do you really want to quit? (y/n)")
		} else if mode == "gotoline" {
			status = fmt.Sprintf("goto : %v", gotolinemode.linestr)
		} else if mode == "find" {
			status = fmt.Sprintf("find : %v", findmode.str)
		} else if mode == "replace" {
			status = fmt.Sprintf("replace : %v", replacemode.str)
		} else if !holdStatus {
			status = fmt.Sprintf("%v:%v:%v", f, cursor.l+1, cursor.O())
			if findmode.set {
				status = fmt.Sprintf("find \"%v\": ctrl+d, ctrl+b", findmode.str)
			} else if replacemode.set {
				status = fmt.Sprintf("replace \"%v\": ctrl+j", replacemode.str)
			}
		}
		printStatus(status)
		holdStatus = false

		// reset variables
		findmode.set = false
		replacemode.set = false

		winP := cursor.Position().Sub(win.min)
		SetCursor(mainarea.min.l+winP.l, mainarea.min.o+winP.o)

		term.Flush()

		// wait for keyboard input
		select {
		case ev := <-events:
			switch ev.Type {
			case term.EventKey:
				if mode == "exit" {
					if ev.Ch == 'y' {
						saveLastPosition(f, cursor.l, cursor.b)
						return
					} else if ev.Ch == 'n' || ev.Key == term.KeyCtrlK {
						mode = "normal"
					}
					continue
				} else if mode == "gotoline" {
					gotolinemode.Handle(ev, cursor, &mode)
					continue
				} else if mode == "find" {
					findmode.Handle(ev, cursor, &mode)
					continue
				} else if mode == "replace" {
					replacemode.Handle(ev, cursor, &mode)
					continue
				}

				actions := parseEvent(ev, text, selection, &mode)
				for _, a := range actions {
					if a.kind == "modeChange" {
						if a.value == "find" {
							if selection.on {
								min, max := selection.MinMax()
								findmode.set = true
								findmode.str = text.DataInside(min, max)
								selection.on = false
								continue
							}
							findmode.start = true
						} else if a.value == "replace" {
							if selection.on {
								min, max := selection.MinMax()
								replacemode.set = true
								replacemode.str = text.DataInside(min, max)
								selection.on = false
								continue
							}
							replacemode.start = true
						}
						mode = a.value
						continue
					}

					beforeCursor := *cursor

					if a.kind == "exit" {
						if !edited {
							saveLastPosition(f, cursor.l, cursor.b)
							return
						}
						mode = "exit"
						continue
					} else if a.kind == "save" {
						err := save(f, text)
						if err != nil {
							panic(err)
						}
						edited = false
						status = fmt.Sprintf("successfully saved : %v", f)
						holdStatus = true
					} else if a.kind == "copy" {
						if selection.on {
							minc, maxc := selection.MinMax()
							copied = text.DataInside(minc, maxc)
						} else {
							r, _ := cursor.RuneAfter()
							copied = string(r)
						}
						saveCopyString(copied)
					} else if a.kind == "paste" {
						if copied == "" {
							copied = loadCopyString()
						}
						cursor.Insert(copied)
						a.value = copied
					} else if a.kind == "replace" {
						if replacemode.str != "" {
							cursor.Insert(replacemode.str)
							a.value = replacemode.str
						}
					} else {
						do(a, text, cursor, selection, history, &status, &holdStatus, findmode.str)
					}
					switch a.kind {
					case "insert", "delete", "backspace", "deleteSelection", "paste", "replace", "insertTab", "removeTab":
						// remember the action.
						edited = true
						nc := history.Cut(history.head)
						if nc != 0 {
							lastActStr = ""
						}
						if a.kind == "insert" || a.kind == "delete" || a.kind == "backspace" {
							if a.kind == lastActStr {
								lastAct, err := history.Pop()
								if err != nil {
									panic(err)
								}
								history.head--
								beforeCursor = lastAct.beforeCursor
								if a.kind == "insert" || a.kind == "delete" {
									a.value = lastAct.value + a.value
								} else if a.kind == "backspace" {
									a.value = a.value + lastAct.value
								}
							}
						}
						a.beforeCursor = beforeCursor
						if a.kind == "deleteSelection" {
							a.beforeCursor, _ = selection.MinMax()
						}
						a.afterCursor = *cursor
						history.Add(a)
						history.head++
					}
					lastActStr = a.kind
					lastAct := history.Last()
					if debugFlag && lastAct != nil {
						historyFileString := ""
						for i, a := range history.actions {
							if i != 0 {
								historyFileString += "\n"
							}
							historyFileString += fmt.Sprintf("%v, %v", a, history.head)
						}
						ioutil.WriteFile(extendFileName(f, ".history"), []byte(historyFileString), 0755)
					}
				}
			case term.EventResize:
				resizeScreen(mainarea, win)
			}
		case <-time.After(time.Second):
			holdStatus = true
			// It seems maximize the terminal
			// doesn't call term.EventResize.
			// So manually do it.
			resizeScreen(mainarea, win)
		}
	}
}
<commit_msg>clear screen before resize<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	term "github.com/nsf/termbox-go"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"time"
)

// we use line, offset style. termbox use o, l style.
func SetCursor(l, o int) {
	term.SetCursor(o, l)
}

// parseFileArg returns (filepath, linenum, offset, error).
// if the linenum is given, but 0 or negative, it will be 1.
// if the offset is given, but negative, it will be 0.
// when only filepath is given, linenum and offset will be set to -1.
func parseFileArg(farg string) (string, int, int, error) {
	finfo := strings.Split(farg, ":")
	f := finfo[0]
	l, o := -1, -1
	err := error(nil)

	if len(finfo) >= 4 {
		return "", -1, -1, errors.New("too many colons in file argument")
	}

	if len(finfo) == 1 {
		return f, -1, -1, nil
	}
	if len(finfo) >= 2 {
		l, err = strconv.Atoi(finfo[1])
		if err != nil {
			return "", -1, -1, err
		}
		if len(finfo) == 3 {
			o, err = strconv.Atoi(finfo[2])
			if err != nil {
				return "", -1, -1, err
			}
		}
	}

	if l < 0 {
		l = 0
	}
	if o < 0 {
		o = 0
	}
	return f, l, o, nil
}

func main() {
	var newFlag, debugFlag bool
	flag.BoolVar(&newFlag, "new", false, "let tor to edit a new file.")
	flag.BoolVar(&debugFlag, "debug", false, "tor will create .history file for debugging.")
	flag.Parse()

	args := flag.Args()
	if len(args) == 0 {
		fmt.Println("please, set text file")
		os.Exit(1)
	}
	farg := args[len(args)-1]

	f, initL, initO, err := parseFileArg(farg)
	if err != nil {
		fmt.Println("file arg is invalid: ", err)
		os.Exit(1)
	}

	exist := true
	if _, err := os.Stat(f); os.IsNotExist(err) {
		exist = false
	}
	if !exist && !newFlag {
		fmt.Println("file not exist. please retry with -new flag.")
		os.Exit(1)
	} else if exist && newFlag {
		fmt.Println("file already exist.")
		os.Exit(1)
	}

	var text *Text
	if exist {
		text, err = open(f)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	} else {
		lines := make([]Line, 0)
		lines = append(lines, Line{""})
		text = &Text{lines:lines, tabToSpace:false, tabWidth:4}
	}

	err = term.Init()
	if err != nil {
		panic(err)
	}
	defer term.Close()
	term.SetInputMode(term.InputAlt)
	// term.SetOutputMode(term.Output256)
	term.Clear(term.ColorDefault, term.ColorDefault)
	term.Flush()

	termw, termh := term.Size()
	mainarea := NewArea(Point{0, 0}, Point{termh - 1, termw})
	win := NewWindow(mainarea.Size())

	cursor := NewCursor(text)
	if initL != -1 {
		l := initL
		// to internal line number
		if l != 0 {
			l--
		}
		cursor.GotoLine(l)
		if initO != -1 {
			cursor.SetO(initO)
		}
	} else {
		l, b := loadLastPosition(f)
		cursor.GotoLine(l)
		cursor.SetCloseToB(b)
	}

	findmode := &LineInputMode{}
	replacemode := &LineInputMode{}
	gotolinemode := &GotoLineMode{}
	selection := NewSelection()
	history := newHistory()

	mode := "normal"

	edited := false
	status := ""
	holdStatus := false
	lastActStr := ""
	copied := ""

	events := make(chan term.Event, 20)
	go func() {
		for {
			events <- term.PollEvent()
		}
	}()
	for {
		win.Follow(cursor, 3)
		clearScreen(mainarea)
		drawScreen(mainarea, win, text, selection, cursor, mode)

		if mode == "exit" {
			status = fmt.Sprintf("Buffer modified. Do you really want to quit? (y/n)")
		} else if mode == "gotoline" {
			status = fmt.Sprintf("goto : %v", gotolinemode.linestr)
		} else if mode == "find" {
			status = fmt.Sprintf("find : %v", findmode.str)
		} else if mode == "replace" {
			status = fmt.Sprintf("replace : %v", replacemode.str)
		} else if !holdStatus {
			status = fmt.Sprintf("%v:%v:%v", f, cursor.l+1, cursor.O())
			if findmode.set {
				status = fmt.Sprintf("find \"%v\": ctrl+d, ctrl+b", findmode.str)
			} else if replacemode.set {
				status = fmt.Sprintf("replace \"%v\": ctrl+j", replacemode.str)
			}
		}
		printStatus(status)
		holdStatus = false

		// reset variables
		findmode.set = false
		replacemode.set = false

		winP := cursor.Position().Sub(win.min)
		SetCursor(mainarea.min.l+winP.l, mainarea.min.o+winP.o)

		term.Flush()

		// wait for keyboard input
		select {
		case ev := <-events:
			switch ev.Type {
			case term.EventKey:
				if mode == "exit" {
					if ev.Ch == 'y' {
						saveLastPosition(f, cursor.l, cursor.b)
						return
					} else if ev.Ch == 'n' || ev.Key == term.KeyCtrlK {
						mode = "normal"
					}
					continue
				} else if mode == "gotoline" {
					gotolinemode.Handle(ev, cursor, &mode)
					continue
				} else if mode == "find" {
					findmode.Handle(ev, cursor, &mode)
					continue
				} else if mode == "replace" {
					replacemode.Handle(ev, cursor, &mode)
					continue
				}

				actions := parseEvent(ev, text, selection, &mode)
				for _, a := range actions {
					if a.kind == "modeChange" {
						if a.value == "find" {
							if selection.on {
								min, max := selection.MinMax()
								findmode.set = true
								findmode.str = text.DataInside(min, max)
								selection.on = false
								continue
							}
							findmode.start = true
						} else if a.value == "replace" {
							if selection.on {
								min, max := selection.MinMax()
								replacemode.set = true
								replacemode.str = text.DataInside(min, max)
								selection.on = false
								continue
							}
							replacemode.start = true
						}
						mode = a.value
						continue
					}

					beforeCursor := *cursor

					if a.kind == "exit" {
						if !edited {
							saveLastPosition(f, cursor.l, cursor.b)
							return
						}
						mode = "exit"
						continue
					} else if a.kind == "save" {
						err := save(f, text)
						if err != nil {
							panic(err)
						}
						edited = false
						status = fmt.Sprintf("successfully saved : %v", f)
						holdStatus = true
					} else if a.kind == "copy" {
						if selection.on {
							minc, maxc := selection.MinMax()
							copied = text.DataInside(minc, maxc)
						} else {
							r, _ := cursor.RuneAfter()
							copied = string(r)
						}
						saveCopyString(copied)
					} else if a.kind == "paste" {
						if copied == "" {
							copied = loadCopyString()
						}
						cursor.Insert(copied)
						a.value = copied
					} else if a.kind == "replace" {
						if replacemode.str != "" {
							cursor.Insert(replacemode.str)
							a.value = replacemode.str
						}
					} else {
						do(a, text, cursor, selection, history, &status, &holdStatus, findmode.str)
					}
					switch a.kind {
					case "insert", "delete", "backspace", "deleteSelection", "paste", "replace", "insertTab", "removeTab":
						// remember the action.
						edited = true
						nc := history.Cut(history.head)
						if nc != 0 {
							lastActStr = ""
						}
						if a.kind == "insert" || a.kind == "delete" || a.kind == "backspace" {
							if a.kind == lastActStr {
								lastAct, err := history.Pop()
								if err != nil {
									panic(err)
								}
								history.head--
								beforeCursor = lastAct.beforeCursor
								if a.kind == "insert" || a.kind == "delete" {
									a.value = lastAct.value + a.value
								} else if a.kind == "backspace" {
									a.value = a.value + lastAct.value
								}
							}
						}
						a.beforeCursor = beforeCursor
						if a.kind == "deleteSelection" {
							a.beforeCursor, _ = selection.MinMax()
						}
						a.afterCursor = *cursor
						history.Add(a)
						history.head++
					}
					lastActStr = a.kind
					lastAct := history.Last()
					if debugFlag && lastAct != nil {
						historyFileString := ""
						for i, a := range history.actions {
							if i != 0 {
								historyFileString += "\n"
							}
							historyFileString += fmt.Sprintf("%v, %v", a, history.head)
						}
						ioutil.WriteFile(extendFileName(f, ".history"), []byte(historyFileString), 0755)
					}
				}
			case term.EventResize:
				term.Clear(term.ColorDefault, term.ColorDefault)
				resizeScreen(mainarea, win)
			}
		case <-time.After(time.Second):
			holdStatus = true
		}
	}
}
<|endoftext|>


<commit_before>// Package tuntap provides a portable interface to create and use
// TUN/TAP virtual network interfaces.
//
// Note that while this package lets you create the interface and pass
// packets to/from it, it does not provide an API to configure the
// interface. Interface configuration is a very large topic and should
// be dealt with separately.
package tuntap

import (
	"encoding/binary"
	"io"
	"os"
	"unsafe"
)

type DevKind int

const (
	// Receive/send IP frames.
	DevTun DevKind = iota
	// Receive/send Ethernet frames.
	DevTap
)

type Packet struct {
	Protocol int
	Truncated    bool
	Packet   []byte
}

type TunTap struct {
	DevName string
	In      <-chan *Packet
	Out     chan<- *Packet

	file     *os.File
	shutdown chan interface{}
}

func (t *TunTap) Close() error {
	close(t.shutdown)
	return t.file.Close()
}

func Open(ifPattern string, kind DevKind) (*TunTap, error) {
	file, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0)
	if err != nil {
		return nil, err
	}

	ifName, err := createInterface(file, ifPattern, kind)
	if err != nil {
		file.Close()
		return nil, err
	}

	in := make(chan *Packet)
	out := make(chan *Packet)
	shutdown := make(chan interface{})
	go reader(file, in, shutdown)
	go writer(file, out, shutdown)
	ret := &TunTap{
		DevName:  ifName,
		In:       in,
		Out:      out,
		file:     file,
		shutdown: shutdown,
	}
	return ret, nil
}

func reader(file io.Reader, ch chan *Packet, shutdown chan interface{}) {
	// Enough to read a jumbo ethernet frame. If that's not enough,
	// truncated packets will show up.
	buf := make([]byte, 10000)
	for {
		n, err := file.Read(buf)
		if err != nil {
			return
		}
		pkt := &Packet{Packet: buf[4:n]}
		pkt.Protocol = int(binary.BigEndian.Uint16(buf[2:4]))
		flags := *(*uint16)(unsafe.Pointer(&buf[0]))
		if flags & flagTruncated != 0 {
			pkt.Truncated = true
		}
		select {
		case ch <- pkt:
		case <-shutdown:
			return
		}
	}
}

func writer(file io.Writer, ch chan *Packet, shutdown chan interface{}) {
	for {
		select {
		case <-shutdown:
			return
		case pkt := <-ch:
			buf := make([]byte, len(pkt.Packet)+4)
			binary.BigEndian.PutUint16(buf[2:4], uint16(pkt.Protocol))
			copy(buf[4:], pkt.Packet)
			if n, err := file.Write(buf); err != nil || n != len(buf) {
				// tuntap should not be giving us short writes, since
				// we need to atomically pass one packet at a time.
				return
			}
		}
	}
}
<commit_msg>Apply gofmt to tuntap.<commit_after>// Package tuntap provides a portable interface to create and use
// TUN/TAP virtual network interfaces.
//
// Note that while this package lets you create the interface and pass
// packets to/from it, it does not provide an API to configure the
// interface. Interface configuration is a very large topic and should
// be dealt with separately.
package tuntap

import (
	"encoding/binary"
	"io"
	"os"
	"unsafe"
)

type DevKind int

const (
	// Receive/send IP frames.
	DevTun DevKind = iota
	// Receive/send Ethernet frames.
	DevTap
)

type Packet struct {
	// The Ethernet type of the packet. Commonly seen values are
	// 0x8000 for IPv4 and 0x86dd for IPv6.
	Protocol int
	// True if the packet was too large to be read completely.
	Truncated bool
	// The raw bytes of the Ethernet payload (for DevTun) or the full
	// Ethernet frame (for DevTap).
	Packet []byte
}

type TunTap struct {
	DevName string
	In      <-chan *Packet
	Out     chan<- *Packet

	file     *os.File
	shutdown chan interface{}
}

func (t *TunTap) Close() error {
	close(t.shutdown)
	return t.file.Close()
}

func Open(ifPattern string, kind DevKind) (*TunTap, error) {
	file, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0)
	if err != nil {
		return nil, err
	}

	ifName, err := createInterface(file, ifPattern, kind)
	if err != nil {
		file.Close()
		return nil, err
	}

	in := make(chan *Packet)
	out := make(chan *Packet)
	shutdown := make(chan interface{})
	go reader(file, in, shutdown)
	go writer(file, out, shutdown)
	ret := &TunTap{
		DevName:  ifName,
		In:       in,
		Out:      out,
		file:     file,
		shutdown: shutdown,
	}
	return ret, nil
}

func reader(file io.Reader, ch chan *Packet, shutdown chan interface{}) {
	// Enough to read a jumbo ethernet frame. If that's not enough,
	// truncated packets will show up.
	buf := make([]byte, 10000)
	for {
		n, err := file.Read(buf)
		if err != nil {
			return
		}
		pkt := &Packet{Packet: buf[4:n]}
		pkt.Protocol = int(binary.BigEndian.Uint16(buf[2:4]))
		flags := *(*uint16)(unsafe.Pointer(&buf[0]))
		if flags&flagTruncated != 0 {
			pkt.Truncated = true
		}
		select {
		case ch <- pkt:
		case <-shutdown:
			return
		}
	}
}

func writer(file io.Writer, ch chan *Packet, shutdown chan interface{}) {
	for {
		select {
		case <-shutdown:
			return
		case pkt := <-ch:
			buf := make([]byte, len(pkt.Packet)+4)
			binary.BigEndian.PutUint16(buf[2:4], uint16(pkt.Protocol))
			copy(buf[4:], pkt.Packet)
			if n, err := file.Write(buf); err != nil || n != len(buf) {
				// tuntap should not be giving us short writes, since
				// we need to atomically pass one packet at a time.
				return
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"html"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"
)

func main() {
	Start(os.Getenv("PORT"))
}

func Start(port string) {
	http.HandleFunc("/", handleAny)

	if port == "" {
		port = "5000"
	}
	log.Println("listening on port:", port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}

func handleAny(res http.ResponseWriter, req *http.Request) {
	log.Printf("method=%s path=%q", req.Method, req.URL.Path)

	path, err := os.Open(req.URL.Path)
	if err != nil {
		if os.IsNotExist(err) {
			handleError(res, req, err, http.StatusNotFound, "File not found")
			return
		}

		handleError(res, req, err, http.StatusBadRequest, "Error reading path")
		return
	}
	defer path.Close()

	pathInfo, err := path.Stat()
	if err != nil {
		handleError(res, req, err, http.StatusInternalServerError, "Error reading path info")
		return
	}

	switch req.Method {
	case "GET":
		handleGet(res, req, path, pathInfo)
	case "POST":
		handlePost(res, req, path, pathInfo)
	default:
		http.Error(res, "Only GET and POST supported", http.StatusMethodNotAllowed)
	}
}

func handleGet(res http.ResponseWriter, req *http.Request, path *os.File, pathInfo os.FileInfo) {
	if pathInfo.Mode().IsDir() {
		fileInfos, err := path.Readdir(0)
		if err != nil {
			handleError(res, req, err, http.StatusInternalServerError, "Error reading directory")
			return
		}

		if strings.Contains(req.Header.Get("Accept"), "html") {
			res.Header().Set("Content-Type", "text/html")
			fmt.Fprintf(res, "

")
			for _, fi := range fileInfos {
				label := fi.Name()
				if fi.IsDir() {
					label = "/" + label
				}
				fmt.Fprintf(res, "
%s
", path.Name(), fi.Name(), html.EscapeString(label))
			}
			fmt.Fprintf(res, "

")
		} else {
			res.Header().Set("Content-Type", "application/json")
			fileResponses := map[string]fileInfoDetails{}
			for _, fi := range fileInfos {
				fileResponses[fi.Name()] = toFileInfoDetails(fi)
			}
			fileResponsesJson, err := json.Marshal(fileResponses)
			if err != nil {
				handleError(res, req, err, http.StatusInternalServerError, "Error serializing response")
				return
			}
			res.Write(fileResponsesJson)
		}
	} else if pathInfo.Mode().IsRegular() {
		io.Copy(res, path)
	} else {
		handleError(res, req,
			fmt.Errorf("Invalid file type for GET. Only directories and regular files are supported."),
			http.StatusBadRequest, "Invalid file type")
	}
}

func handlePost(res http.ResponseWriter, req *http.Request, path *os.File, pathInfo os.FileInfo) {
	if pathInfo.Mode().IsDir() {
		resFlusherWriter := flushWriterWrapper{res.(flushWriter)}
		cmd := exec.Command("sh")
		cmd.Dir = path.Name()
		cmd.Env = []string{}
		cmd.Stdin = req.Body
		cmd.Stdout = resFlusherWriter
		cmd.Stderr = resFlusherWriter
		if err := cmd.Run(); err != nil {
			handleError(res, req, err, http.StatusInternalServerError, "Error running command")
			return
		}
	} else {
		handleError(res, req,
			fmt.Errorf("Invalid file type for POST. Only directories are supported"),
			http.StatusBadRequest, "Invalid file type")
	}
}

type fileInfoDetails struct {
	Size    int64     `json:"size"`
	Type    string    `json:"type"`
	Perm    int       `json:"permission"`
	ModTime time.Time `json:"updated_at"`
}

func toFileInfoDetails(fi os.FileInfo) fileInfoDetails {
	return fileInfoDetails{
		Size:    fi.Size(),
		Type:    string(fi.Mode().String()[0]),
		Perm:    int(fi.Mode().Perm()),
		ModTime: fi.ModTime(),
	}
}

type stdError struct {
	Message string `json:"message"`
	Cause   error  `json:"cause"`
}

func handleError(res http.ResponseWriter, req *http.Request, err error, httpStatus int, message string) {
	stdError := stdError{
		Message: message,
		Cause:   err,
	}

	log.Printf("method=%s path=%q message=%q cause=%q", req.Method, req.URL.Path, stdError.Message, stdError.Cause)

	stdErrorJson, err := json.Marshal(stdError)
	if err != nil {
		panic(err)
	}

	res.Header().Set("Content-Type", "application/json")
	http.Error(res, string(stdErrorJson), httpStatus)
}

type flushWriter interface {
	Flush()
	Write(buf []byte) (int, error)
}

type flushWriterWrapper struct {
	fw flushWriter
}

func (fww flushWriterWrapper) Write(p []byte) (n int, err error) {
	n, err = fww.fw.Write(p)
	fww.fw.Flush()
	return
}
<commit_msg>Remove extra slash<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"html"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"
)

func main() {
	Start(os.Getenv("PORT"))
}

func Start(port string) {
	http.HandleFunc("/", handleAny)

	if port == "" {
		port = "5000"
	}
	log.Println("listening on port:", port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}

func handleAny(res http.ResponseWriter, req *http.Request) {
	log.Printf("method=%s path=%q", req.Method, req.URL.Path)

	path, err := os.Open(req.URL.Path)
	if err != nil {
		if os.IsNotExist(err) {
			handleError(res, req, err, http.StatusNotFound, "File not found")
			return
		}

		handleError(res, req, err, http.StatusBadRequest, "Error reading path")
		return
	}
	defer path.Close()

	pathInfo, err := path.Stat()
	if err != nil {
		handleError(res, req, err, http.StatusInternalServerError, "Error reading path info")
		return
	}

	switch req.Method {
	case "GET":
		handleGet(res, req, path, pathInfo)
	case "POST":
		handlePost(res, req, path, pathInfo)
	default:
		http.Error(res, "Only GET and POST supported", http.StatusMethodNotAllowed)
	}
}

func handleGet(res http.ResponseWriter, req *http.Request, path *os.File, pathInfo os.FileInfo) {
	if pathInfo.Mode().IsDir() {
		fileInfos, err := path.Readdir(0)
		if err != nil {
			handleError(res, req, err, http.StatusInternalServerError, "Error reading directory")
			return
		}

		if strings.Contains(req.Header.Get("Accept"), "html") {
			res.Header().Set("Content-Type", "text/html")
			fmt.Fprintf(res, "

")
			for _, fi := range fileInfos {
				label := fi.Name()
				if fi.IsDir() {
					label = "/" + label
				}
				fmt.Fprintf(res, "
%s
", path.Name(), fi.Name(), html.EscapeString(label))
			}
			fmt.Fprintf(res, "

")
		} else {
			res.Header().Set("Content-Type", "application/json")
			fileResponses := map[string]fileInfoDetails{}
			for _, fi := range fileInfos {
				fileResponses[fi.Name()] = toFileInfoDetails(fi)
			}
			fileResponsesJson, err := json.Marshal(fileResponses)
			if err != nil {
				handleError(res, req, err, http.StatusInternalServerError, "Error serializing response")
				return
			}
			res.Write(fileResponsesJson)
		}
	} else if pathInfo.Mode().IsRegular() {
		io.Copy(res, path)
	} else {
		handleError(res, req,
			fmt.Errorf("Invalid file type for GET. Only directories and regular files are supported."),
			http.StatusBadRequest, "Invalid file type")
	}
}

func handlePost(res http.ResponseWriter, req *http.Request, path *os.File, pathInfo os.FileInfo) {
	if pathInfo.Mode().IsDir() {
		resFlusherWriter := flushWriterWrapper{res.(flushWriter)}
		cmd := exec.Command("sh")
		cmd.Dir = path.Name()
		cmd.Env = []string{}
		cmd.Stdin = req.Body
		cmd.Stdout = resFlusherWriter
		cmd.Stderr = resFlusherWriter
		if err := cmd.Run(); err != nil {
			handleError(res, req, err, http.StatusInternalServerError, "Error running command")
			return
		}
	} else {
		handleError(res, req,
			fmt.Errorf("Invalid file type for POST. Only directories are supported"),
			http.StatusBadRequest, "Invalid file type")
	}
}

type fileInfoDetails struct {
	Size    int64     `json:"size"`
	Type    string    `json:"type"`
	Perm    int       `json:"permission"`
	ModTime time.Time `json:"updated_at"`
}

func toFileInfoDetails(fi os.FileInfo) fileInfoDetails {
	return fileInfoDetails{
		Size:    fi.Size(),
		Type:    string(fi.Mode().String()[0]),
		Perm:    int(fi.Mode().Perm()),
		ModTime: fi.ModTime(),
	}
}

type stdError struct {
	Message string `json:"message"`
	Cause   error  `json:"cause"`
}

func handleError(res http.ResponseWriter, req *http.Request, err error, httpStatus int, message string) {
	stdError := stdError{
		Message: message,
		Cause:   err,
	}

	log.Printf("method=%s path=%q message=%q cause=%q", req.Method, req.URL.Path, stdError.Message, stdError.Cause)

	stdErrorJson, err := json.Marshal(stdError)
	if err != nil {
		panic(err)
	}

	res.Header().Set("Content-Type", "application/json")
	http.Error(res, string(stdErrorJson), httpStatus)
}

type flushWriter interface {
	Flush()
	Write(buf []byte) (int, error)
}

type flushWriterWrapper struct {
	fw flushWriter
}

func (fww flushWriterWrapper) Write(p []byte) (n int, err error) {
	n, err = fww.fw.Write(p)
	fww.fw.Flush()
	return
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	//	"fmt"
	"log"
	"net/http"
	"strings"
)

var host = flag.String("host", "127.0.0.1", "Host")
var port = flag.String("port", "8080", "Port")
var ip = flag.String("ip", "", "Request IP")

var staticContent = flag.String("staticPath", "./swagger-ui", "Path to folder with Swagger UI")

func IndexHandler(w http.ResponseWriter, r *http.Request) {
	if *ip != "" && *ip != strings.Split(r.RemoteAddr, ":")[0] {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	isJsonRequest := false

	if acceptHeaders, ok := r.Header["Accept"]; ok {
		for _, acceptHeader := range acceptHeaders {
			if strings.Contains(acceptHeader, "json") {
				isJsonRequest = true
				break
			}
		}
	}

	if isJsonRequest {
		w.Write([]byte(resourceListingJson))
	} else {
		http.Redirect(w, r, "/swagger-ui/", http.StatusFound)
	}
}

func ApiDescriptionHandler(w http.ResponseWriter, r *http.Request) {
	apiKey := strings.Trim(r.RequestURI, "/")

	if json, ok := apiDescriptionsJson[apiKey]; ok {
		w.Write([]byte(json))
	} else {
		w.WriteHeader(http.StatusNotFound)
	}
}

func main() {
	flag.Parse()

	// To serve a directory on disk (/tmp) under an alternate URL
	// path (/tmpfiles/), use StripPrefix to modify the request
	// URL's path before the FileServer sees it:
	http.HandleFunc("/", IndexHandler)
	http.Handle("/swagger-ui/", http.StripPrefix("/swagger-ui/", http.FileServer(http.Dir(*staticContent))))

	for apiKey, _ := range apiDescriptionsJson {
		http.HandleFunc("/"+apiKey+"/", ApiDescriptionHandler)
	}

	listenTo := *host + ":" + *port
	log.Printf("Start listen to %s", listenTo)

	if *ip != "" {
		log.Printf("Only acception connections from %s", *ip)
	}

	http.ListenAndServe(listenTo, http.DefaultServeMux)
	//http.ListenAndServe(":8080", http.StripPrefix("/swagger-ui/", http.FileServer(http.Dir("./swagger-ui")) )
}
<commit_msg>Log the request IP if blocked<commit_after>package main

import (
	"flag"
	//	"fmt"
	"log"
	"net/http"
	"strings"
)

var host = flag.String("host", "127.0.0.1", "Host")
var port = flag.String("port", "8080", "Port")
var ip = flag.String("ip", "", "Request IP")

var staticContent = flag.String("staticPath", "./swagger-ui", "Path to folder with Swagger UI")

func IndexHandler(w http.ResponseWriter, r *http.Request) {
	requestIP := strings.Split(r.RemoteAddr, ":")[0]
	if *ip != "" && *ip != requestIP {
		log.Printf("Blocked IP %s", requestIP)
		w.WriteHeader(http.StatusNotFound)
		return
	}

	isJsonRequest := false

	if acceptHeaders, ok := r.Header["Accept"]; ok {
		for _, acceptHeader := range acceptHeaders {
			if strings.Contains(acceptHeader, "json") {
				isJsonRequest = true
				break
			}
		}
	}

	if isJsonRequest {
		w.Write([]byte(resourceListingJson))
	} else {
		http.Redirect(w, r, "/swagger-ui/", http.StatusFound)
	}
}

func ApiDescriptionHandler(w http.ResponseWriter, r *http.Request) {
	apiKey := strings.Trim(r.RequestURI, "/")

	if json, ok := apiDescriptionsJson[apiKey]; ok {
		w.Write([]byte(json))
	} else {
		w.WriteHeader(http.StatusNotFound)
	}
}

func main() {
	flag.Parse()

	// To serve a directory on disk (/tmp) under an alternate URL
	// path (/tmpfiles/), use StripPrefix to modify the request
	// URL's path before the FileServer sees it:
	http.HandleFunc("/", IndexHandler)
	http.Handle("/swagger-ui/", http.StripPrefix("/swagger-ui/", http.FileServer(http.Dir(*staticContent))))

	for apiKey, _ := range apiDescriptionsJson {
		http.HandleFunc("/"+apiKey+"/", ApiDescriptionHandler)
	}

	listenTo := *host + ":" + *port
	log.Printf("Start listen to %s", listenTo)

	if *ip != "" {
		log.Printf("Only acception connections from %s", *ip)
	}

	http.ListenAndServe(listenTo, http.DefaultServeMux)
	//http.ListenAndServe(":8080", http.StripPrefix("/swagger-ui/", http.FileServer(http.Dir("./swagger-ui")) )
}
<|endoftext|>