<commit_before>package flv

import (
	"bytes"
	"fmt"
	"github.com/metachord/amf.go/amf0"
	"io"
	"os"
)

type Header struct {
	Version uint16
	Body    []byte
}

type Frame interface {
	WriteFrame(io.Writer) error
	GetBody() *[]byte
	GetStream() uint32
	GetDts() uint32
	SetDts(dts uint32)
	GetType() TagType
	GetPrevTagSize() uint32
	String() string
}

type CFrame struct {
	Stream      uint32
	Dts         uint32
	Type        TagType
	Flavor      Flavor
	Position    int64
	Body        []byte
	PrevTagSize uint32
}

type VideoFrame struct {
	CFrame
	CodecId VideoCodec
	Width   uint16
	Height  uint16
}

type AudioFrame struct {
	CFrame
	CodecId  AudioCodec
	Rate     uint32
	BitSize  AudioSize
	Channels AudioType
}

type MetaFrame struct {
	CFrame
}

func (f CFrame) WriteFrame(w io.Writer) error {
	bl := uint32(len(f.Body))
	var err error
	err = writeType(w, f.Type)
	if err != nil {
		return err
	}
	err = writeBodyLength(w, bl)
	err = writeDts(w, f.Dts)
	err = writeStream(w, f.Stream)
	err = writeBody(w, f.Body)
	prevTagSize := bl + uint32(TAG_HEADER_LENGTH)
	err = writePrevTagSize(w, prevTagSize)
	return nil
}

func (f CFrame) GetBody() *[]byte {
	return &f.Body
}
func (f CFrame) GetStream() uint32 {
	return f.Stream
}
func (f CFrame) GetDts() uint32 {
	return f.Dts
}
func (f CFrame) SetDts(dts uint32) {
	f.Dts = dts
}
func (f CFrame) GetType() TagType {
	return f.Type
}
func (f CFrame) GetPrevTagSize() uint32 {
	return f.PrevTagSize
}

func writeType(w io.Writer, t TagType) error {
	_, err := w.Write([]byte{byte(t)})
	return err
}

func writeBodyLength(w io.Writer, bl uint32) error {
	_, err := w.Write([]byte{byte(bl >> 16), byte((bl >> 8) & 0xFF), byte(bl & 0xFF)})
	return err
}

func writeDts(w io.Writer, dts uint32) error {
	_, err := w.Write([]byte{byte((dts >> 16) & 0xFF), byte((dts >> 8) & 0xFF), byte(dts & 0xFF)})
	if err != nil {
		return err
	}
	_, err = w.Write([]byte{byte((dts >> 24) & 0xFF)})
	return err
}

func writeStream(w io.Writer, stream uint32) error {
	_, err := w.Write([]byte{byte(stream >> 16), byte((stream >> 8) & 0xFF), byte(stream & 0xFF)})
	return err
}

func writeBody(w io.Writer, body []byte) error {
	_, err := w.Write(body)
	return err
}

func writePrevTagSize(w io.Writer, prevTagSize uint32) error {
	_, err := w.Write([]byte{byte((prevTagSize >> 24) & 0xFF), byte((prevTagSize >> 16) & 0xFF), byte((prevTagSize >> 8) & 0xFF), byte(prevTagSize & 0xFF)})
	return err
}

func (f VideoFrame) String() string {
	s := ""
	switch f.Flavor {
	case KEYFRAME:
		s = fmt.Sprintf("%10d\t%d\t%d\t%s\t%s\t{%dx%d,%d bytes} keyframe", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, f.CodecId, f.Width, f.Height, len(f.CFrame.Body))
	default:
		s = fmt.Sprintf("%10d\t%d\t%d\t%s\t%s\t{%dx%d,%d bytes}", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, f.CodecId, f.Width, f.Height, len(f.CFrame.Body))
	}
	return s
}

func (f AudioFrame) String() string {
	return fmt.Sprintf("%10d\t%d\t%d\t%s\t%s\t{%d,%s,%s}", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, f.CodecId, f.Rate, f.BitSize, f.Channels)
}

func (f MetaFrame) String() string {
	buf := bytes.NewReader(f.CFrame.Body)
	dec := amf0.NewDecoder(buf)
	evName, err := dec.Decode()
	mds := ""
	if err == nil {
		switch evName {
		case amf0.StringType("onMetaData"):
			md, err := dec.Decode()
			if err == nil {

				var ea map[amf0.StringType]interface{}
				switch md := md.(type) {
				case *amf0.EcmaArrayType:
					ea = *md
				case *amf0.ObjectType:
					ea = *md
				}
				for k, v := range ea {
					mds += fmt.Sprintf("%v=%+v;", k, v)
				}
			}
		}
	}

	return fmt.Sprintf("%10d\t%d\t%d\t%s\t%s", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, mds)
}

type FlvReader struct {
	InFile *os.File
	width  uint16
	height uint16
}

func NewReader(inFile *os.File) *FlvReader {
	return &FlvReader{
		InFile: inFile,
		width:  0,
		height: 0,
	}
}

type FlvWriter struct {
	OutFile *os.File
}

func NewWriter(outFile *os.File) *FlvWriter {
	return &FlvWriter{
		OutFile: outFile,
	}
}

func (frReader *FlvReader) ReadHeader() (*Header, error) {
	header := make([]byte, HEADER_LENGTH+4)
	_, err := frReader.InFile.Read(header)
	if err != nil {
		return nil, err
	}

	sig := header[0:3]
	if bytes.Compare(sig, []byte(SIG)) != 0 {
		return nil, fmt.Errorf("bad file format")
	}
	version := (uint16(header[3]) << 8) | (uint16(header[4]) << 0)
	//skip := header[4:5]
	//offset := header[5:9]
	//next_id := header[9:13]

	return &Header{Version: version, Body: header}, nil
}

func (frWriter *FlvWriter) WriteHeader(header *Header) error {
	_, err := frWriter.OutFile.Write(header.Body)
	if err != nil {
		return err
	}
	return nil
}

func (frReader *FlvReader) ReadFrame() (fr Frame, e error) {

	var n int
	var err error

	curPos, _ := frReader.InFile.Seek(0, os.SEEK_CUR)

	tagHeaderB := make([]byte, TAG_HEADER_LENGTH)
	n, err = frReader.InFile.Read(tagHeaderB)
	if n == 0 {
		return nil, nil
	}
	if TagSize(n) != TAG_HEADER_LENGTH {
		return nil, fmt.Errorf("bad tag length: %d", n)
	}
	if err != nil {
		return nil, err
	}

	tagType := TagType(tagHeaderB[0])
	bodyLen := (uint32(tagHeaderB[1]) << 16) | (uint32(tagHeaderB[2]) << 8) | (uint32(tagHeaderB[3]) << 0)
	ts := (uint32(tagHeaderB[4]) << 16) | (uint32(tagHeaderB[5]) << 8) | (uint32(tagHeaderB[6]) << 0)
	tsExt := uint32(tagHeaderB[7])
	stream := (uint32(tagHeaderB[8]) << 16) | (uint32(tagHeaderB[9]) << 8) | (uint32(tagHeaderB[10]) << 0)

	var dts uint32
	dts = (tsExt << 24) | ts

	bodyBuf := make([]byte, bodyLen)
	n, err = frReader.InFile.Read(bodyBuf)
	if err != nil {
		return nil, err
	}

	prevTagSizeB := make([]byte, PREV_TAG_SIZE_LENGTH)
	n, err = frReader.InFile.Read(prevTagSizeB)
	if err != nil {
		return nil, err
	}
	prevTagSize := (uint32(prevTagSizeB[0]) << 24) | (uint32(prevTagSizeB[1]) << 16) | (uint32(prevTagSizeB[2]) << 8) | (uint32(prevTagSizeB[3]) << 0)

	pFrame := CFrame{
		Stream:      stream,
		Dts:         dts,
		Type:        tagType,
		Position:    curPos,
		Body:        bodyBuf,
		PrevTagSize: prevTagSize,
	}

	var resFrame Frame

	switch tagType {
	case TAG_TYPE_META:
		pFrame.Flavor = METADATA
		resFrame = MetaFrame{CFrame: pFrame}
	case TAG_TYPE_VIDEO:
		if len(bodyBuf) > 0 {
			vft := VideoFrameType(uint8(bodyBuf[0]) >> 4)
			codecId := VideoCodec(uint8(bodyBuf[0]) & 0x0F)
			switch vft {
			case VIDEO_FRAME_TYPE_KEYFRAME:
				pFrame.Flavor = KEYFRAME
				switch codecId {
				case VIDEO_CODEC_ON2VP6:
					hHelper := (uint16(bodyBuf[1]) >> 4) & 0x0F
					wHelper := uint16(bodyBuf[1]) & 0x0F
					w := uint16(bodyBuf[5])
					h := uint16(bodyBuf[6])

					frReader.width = w*16 - wHelper
					frReader.height = h*16 - hHelper
				}
			default:
				pFrame.Flavor = FRAME
			}
			resFrame = VideoFrame{CFrame: pFrame, CodecId: codecId, Width: frReader.width, Height: frReader.height}
		} else {
			resFrame = VideoFrame{CFrame: pFrame, CodecId: VIDEO_CODEC_UNDEFINED, Width: frReader.width, Height: frReader.height}
		}
	case TAG_TYPE_AUDIO:
		pFrame.Flavor = FRAME
		if len(bodyBuf) > 0 {
			codecId := AudioCodec(uint8(bodyBuf[0]) >> 4)
			rate := audioRate(AudioRate((uint8(bodyBuf[0]) >> 2) & 0x03))
			bitSize := AudioSize((uint8(bodyBuf[0]) >> 1) & 0x01)
			channels := AudioType(uint8(bodyBuf[0]) & 0x01)
			resFrame = AudioFrame{CFrame: pFrame, CodecId: codecId, Rate: rate, BitSize: bitSize, Channels: channels}
		} else {
			resFrame = AudioFrame{CFrame: pFrame, CodecId: AUDIO_CODEC_UNDEFINED, Rate: audioRate(AUDIO_RATE_UNDEFINED), BitSize: AUDIO_SIZE_UNDEFINED, Channels: AUDIO_TYPE_UNDEFINED}
		}
	}

	return resFrame, nil
}

func audioRate(ar AudioRate) uint32 {
	var ret uint32
	switch ar {
	case AUDIO_RATE_5_5:
		ret = 5500
	case AUDIO_RATE_11:
		ret = 11000
	case AUDIO_RATE_22:
		ret = 22000
	case AUDIO_RATE_44:
		ret = 44000
	default:
		ret = 0
	}
	return ret
}

func (frWriter *FlvWriter) WriteFrame(fr Frame) (e error) {
	return fr.WriteFrame(frWriter.OutFile)
}
<commit_msg>Use pointer to CFrame<commit_after>package flv

import (
	"bytes"
	"fmt"
	"github.com/metachord/amf.go/amf0"
	"io"
	"os"
)

type Header struct {
	Version uint16
	Body    []byte
}

type Frame interface {
	WriteFrame(io.Writer) error
	GetBody() *[]byte
	GetStream() uint32
	GetDts() uint32
	SetDts(dts uint32)
	GetType() TagType
	GetPrevTagSize() uint32
	String() string
}

type CFrame struct {
	Stream      uint32
	Dts         uint32
	Type        TagType
	Flavor      Flavor
	Position    int64
	Body        []byte
	PrevTagSize uint32
}

type VideoFrame struct {
	*CFrame
	CodecId VideoCodec
	Width   uint16
	Height  uint16
}

type AudioFrame struct {
	*CFrame
	CodecId  AudioCodec
	Rate     uint32
	BitSize  AudioSize
	Channels AudioType
}

type MetaFrame struct {
	*CFrame
}

func (f *CFrame) WriteFrame(w io.Writer) error {
	bl := uint32(len(f.Body))
	var err error
	err = writeType(w, f.Type)
	if err != nil {
		return err
	}
	err = writeBodyLength(w, bl)
	err = writeDts(w, f.Dts)
	err = writeStream(w, f.Stream)
	err = writeBody(w, f.Body)
	prevTagSize := bl + uint32(TAG_HEADER_LENGTH)
	err = writePrevTagSize(w, prevTagSize)
	return nil
}

func (f *CFrame) GetBody() *[]byte {
	return &f.Body
}
func (f *CFrame) GetStream() uint32 {
	return f.Stream
}
func (f *CFrame) GetDts() uint32 {
	return f.Dts
}
func (f *CFrame) SetDts(dts uint32) {
	f.Dts = dts
}
func (f *CFrame) GetType() TagType {
	return f.Type
}
func (f *CFrame) GetPrevTagSize() uint32 {
	return f.PrevTagSize
}

func writeType(w io.Writer, t TagType) error {
	_, err := w.Write([]byte{byte(t)})
	return err
}

func writeBodyLength(w io.Writer, bl uint32) error {
	_, err := w.Write([]byte{byte(bl >> 16), byte((bl >> 8) & 0xFF), byte(bl & 0xFF)})
	return err
}

func writeDts(w io.Writer, dts uint32) error {
	_, err := w.Write([]byte{byte((dts >> 16) & 0xFF), byte((dts >> 8) & 0xFF), byte(dts & 0xFF)})
	if err != nil {
		return err
	}
	_, err = w.Write([]byte{byte((dts >> 24) & 0xFF)})
	return err
}

func writeStream(w io.Writer, stream uint32) error {
	_, err := w.Write([]byte{byte(stream >> 16), byte((stream >> 8) & 0xFF), byte(stream & 0xFF)})
	return err
}

func writeBody(w io.Writer, body []byte) error {
	_, err := w.Write(body)
	return err
}

func writePrevTagSize(w io.Writer, prevTagSize uint32) error {
	_, err := w.Write([]byte{byte((prevTagSize >> 24) & 0xFF), byte((prevTagSize >> 16) & 0xFF), byte((prevTagSize >> 8) & 0xFF), byte(prevTagSize & 0xFF)})
	return err
}

func (f VideoFrame) String() string {
	s := ""
	switch f.Flavor {
	case KEYFRAME:
		s = fmt.Sprintf("%10d\t%d\t%d\t%s\t%s\t{%dx%d,%d bytes} keyframe", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, f.CodecId, f.Width, f.Height, len(f.CFrame.Body))
	default:
		s = fmt.Sprintf("%10d\t%d\t%d\t%s\t%s\t{%dx%d,%d bytes}", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, f.CodecId, f.Width, f.Height, len(f.CFrame.Body))
	}
	return s
}

func (f AudioFrame) String() string {
	return fmt.Sprintf("%10d\t%d\t%d\t%s\t%s\t{%d,%s,%s}", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, f.CodecId, f.Rate, f.BitSize, f.Channels)
}

func (f MetaFrame) String() string {
	buf := bytes.NewReader(f.CFrame.Body)
	dec := amf0.NewDecoder(buf)
	evName, err := dec.Decode()
	mds := ""
	if err == nil {
		switch evName {
		case amf0.StringType("onMetaData"):
			md, err := dec.Decode()
			if err == nil {

				var ea map[amf0.StringType]interface{}
				switch md := md.(type) {
				case *amf0.EcmaArrayType:
					ea = *md
				case *amf0.ObjectType:
					ea = *md
				}
				for k, v := range ea {
					mds += fmt.Sprintf("%v=%+v;", k, v)
				}
			}
		}
	}

	return fmt.Sprintf("%10d\t%d\t%d\t%s\t%s", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, mds)
}

type FlvReader struct {
	InFile *os.File
	width  uint16
	height uint16
}

func NewReader(inFile *os.File) *FlvReader {
	return &FlvReader{
		InFile: inFile,
		width:  0,
		height: 0,
	}
}

type FlvWriter struct {
	OutFile *os.File
}

func NewWriter(outFile *os.File) *FlvWriter {
	return &FlvWriter{
		OutFile: outFile,
	}
}

func (frReader *FlvReader) ReadHeader() (*Header, error) {
	header := make([]byte, HEADER_LENGTH+4)
	_, err := frReader.InFile.Read(header)
	if err != nil {
		return nil, err
	}

	sig := header[0:3]
	if bytes.Compare(sig, []byte(SIG)) != 0 {
		return nil, fmt.Errorf("bad file format")
	}
	version := (uint16(header[3]) << 8) | (uint16(header[4]) << 0)
	//skip := header[4:5]
	//offset := header[5:9]
	//next_id := header[9:13]

	return &Header{Version: version, Body: header}, nil
}

func (frWriter *FlvWriter) WriteHeader(header *Header) error {
	_, err := frWriter.OutFile.Write(header.Body)
	if err != nil {
		return err
	}
	return nil
}

func (frReader *FlvReader) ReadFrame() (fr Frame, e error) {

	var n int
	var err error

	curPos, _ := frReader.InFile.Seek(0, os.SEEK_CUR)

	tagHeaderB := make([]byte, TAG_HEADER_LENGTH)
	n, err = frReader.InFile.Read(tagHeaderB)
	if n == 0 {
		return nil, nil
	}
	if TagSize(n) != TAG_HEADER_LENGTH {
		return nil, fmt.Errorf("bad tag length: %d", n)
	}
	if err != nil {
		return nil, err
	}

	tagType := TagType(tagHeaderB[0])
	bodyLen := (uint32(tagHeaderB[1]) << 16) | (uint32(tagHeaderB[2]) << 8) | (uint32(tagHeaderB[3]) << 0)
	ts := (uint32(tagHeaderB[4]) << 16) | (uint32(tagHeaderB[5]) << 8) | (uint32(tagHeaderB[6]) << 0)
	tsExt := uint32(tagHeaderB[7])
	stream := (uint32(tagHeaderB[8]) << 16) | (uint32(tagHeaderB[9]) << 8) | (uint32(tagHeaderB[10]) << 0)

	var dts uint32
	dts = (tsExt << 24) | ts

	bodyBuf := make([]byte, bodyLen)
	n, err = frReader.InFile.Read(bodyBuf)
	if err != nil {
		return nil, err
	}

	prevTagSizeB := make([]byte, PREV_TAG_SIZE_LENGTH)
	n, err = frReader.InFile.Read(prevTagSizeB)
	if err != nil {
		return nil, err
	}
	prevTagSize := (uint32(prevTagSizeB[0]) << 24) | (uint32(prevTagSizeB[1]) << 16) | (uint32(prevTagSizeB[2]) << 8) | (uint32(prevTagSizeB[3]) << 0)

	pFrame := &CFrame{
		Stream:      stream,
		Dts:         dts,
		Type:        tagType,
		Position:    curPos,
		Body:        bodyBuf,
		PrevTagSize: prevTagSize,
	}

	var resFrame Frame

	switch tagType {
	case TAG_TYPE_META:
		pFrame.Flavor = METADATA
		resFrame = MetaFrame{CFrame: pFrame}
	case TAG_TYPE_VIDEO:
		if len(bodyBuf) > 0 {
			vft := VideoFrameType(uint8(bodyBuf[0]) >> 4)
			codecId := VideoCodec(uint8(bodyBuf[0]) & 0x0F)
			switch vft {
			case VIDEO_FRAME_TYPE_KEYFRAME:
				pFrame.Flavor = KEYFRAME
				switch codecId {
				case VIDEO_CODEC_ON2VP6:
					hHelper := (uint16(bodyBuf[1]) >> 4) & 0x0F
					wHelper := uint16(bodyBuf[1]) & 0x0F
					w := uint16(bodyBuf[5])
					h := uint16(bodyBuf[6])

					frReader.width = w*16 - wHelper
					frReader.height = h*16 - hHelper
				}
			default:
				pFrame.Flavor = FRAME
			}
			resFrame = VideoFrame{CFrame: pFrame, CodecId: codecId, Width: frReader.width, Height: frReader.height}
		} else {
			resFrame = VideoFrame{CFrame: pFrame, CodecId: VIDEO_CODEC_UNDEFINED, Width: frReader.width, Height: frReader.height}
		}
	case TAG_TYPE_AUDIO:
		pFrame.Flavor = FRAME
		if len(bodyBuf) > 0 {
			codecId := AudioCodec(uint8(bodyBuf[0]) >> 4)
			rate := audioRate(AudioRate((uint8(bodyBuf[0]) >> 2) & 0x03))
			bitSize := AudioSize((uint8(bodyBuf[0]) >> 1) & 0x01)
			channels := AudioType(uint8(bodyBuf[0]) & 0x01)
			resFrame = AudioFrame{CFrame: pFrame, CodecId: codecId, Rate: rate, BitSize: bitSize, Channels: channels}
		} else {
			resFrame = AudioFrame{CFrame: pFrame, CodecId: AUDIO_CODEC_UNDEFINED, Rate: audioRate(AUDIO_RATE_UNDEFINED), BitSize: AUDIO_SIZE_UNDEFINED, Channels: AUDIO_TYPE_UNDEFINED}
		}
	}

	return resFrame, nil
}

func audioRate(ar AudioRate) uint32 {
	var ret uint32
	switch ar {
	case AUDIO_RATE_5_5:
		ret = 5500
	case AUDIO_RATE_11:
		ret = 11000
	case AUDIO_RATE_22:
		ret = 22000
	case AUDIO_RATE_44:
		ret = 44000
	default:
		ret = 0
	}
	return ret
}

func (frWriter *FlvWriter) WriteFrame(fr Frame) (e error) {
	return fr.WriteFrame(frWriter.OutFile)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"time"

	"crypto/tls"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"github.com/codegangsta/cli"
	"github.com/concourse/atc"
	"github.com/concourse/fly/eventstream"
	"github.com/concourse/turbine"
	"github.com/pivotal-golang/archiver/compressor"
	"github.com/tedsuo/rata"
	"github.com/vito/go-sse/sse"
	"gopkg.in/yaml.v2"
)

type Input struct {
	Name string
	Path string
	Pipe atc.Pipe
}

func execute(c *cli.Context) {
	atcURL := c.GlobalString("atcURL")
	buildConfig := c.String("config")
	insecure := c.GlobalBool("insecure")
	excludeIgnored := c.GlobalBool("exclude-ignored")

	atcRequester := newAtcRequester(atcURL, insecure)

	inputMappings := c.StringSlice("input")
	if len(inputMappings) == 0 {
		wd, err := os.Getwd()
		if err != nil {
			log.Fatalln(err)
		}

		inputMappings = append(inputMappings, filepath.Base(wd)+"="+wd)
	}

	inputs := []Input{}
	for _, i := range inputMappings {
		segs := strings.SplitN(i, "=", 2)
		if len(segs) < 2 {
			log.Println("malformed input:", i)
			os.Exit(1)
		}

		inputName := segs[0]

		absPath, err := filepath.Abs(segs[1])
		if err != nil {
			log.Printf("could not locate input %s: %s\n", inputName, err)
			os.Exit(1)
		}

		pipe := createPipe(atcRequester)

		inputs = append(inputs, Input{
			Name: inputName,
			Path: absPath,
			Pipe: pipe,
		})
	}

	absConfig, err := filepath.Abs(buildConfig)
	if err != nil {
		log.Println("could not locate config file:", err)
		os.Exit(1)
	}

	build := createBuild(
		atcRequester,
		c.Bool("privileged"),
		inputs,
		loadConfig(absConfig, c.Args()),
	)

	terminate := make(chan os.Signal, 1)

	go abortOnSignal(atcRequester, terminate, build)

	signal.Notify(terminate, syscall.SIGINT, syscall.SIGTERM)

	eventSource := &sse.EventSource{
		Client: atcRequester.httpClient,
		CreateRequest: func() *http.Request {
			logOutput, err := atcRequester.CreateRequest(
				atc.BuildEvents,
				rata.Params{"build_id": strconv.Itoa(build.ID)},
				nil,
			)
			if err != nil {
				log.Fatalln(err)
			}

			return logOutput
		},
		DefaultRetryInterval: 1 * time.Second,
	}

	go func() {
		for _, i := range inputs {
			upload(i, excludeIgnored, atcRequester)
		}
	}()

	exitCode, err := eventstream.RenderStream(eventSource)
	if err != nil {
		log.Println("failed to render stream:", err)
		os.Exit(1)
	}

	eventSource.Close()

	os.Exit(exitCode)
}

func createPipe(atcRequester *atcRequester) atc.Pipe {
	cPipe, err := atcRequester.CreateRequest(atc.CreatePipe, nil, nil)
	if err != nil {
		log.Fatalln(err)
	}

	response, err := atcRequester.httpClient.Do(cPipe)
	if err != nil {
		log.Fatalln("request failed:", err)
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusCreated {
		log.Println("bad response when creating pipe:", response)
		response.Write(os.Stderr)
		os.Exit(1)
	}

	var pipe atc.Pipe
	err = json.NewDecoder(response.Body).Decode(&pipe)
	if err != nil {
		log.Println("malformed response when creating pipe:", err)
		os.Exit(1)
	}

	return pipe
}

func loadConfig(configPath string, args []string) turbine.Config {
	configFile, err := ioutil.ReadFile(configPath)
	if err != nil {
		log.Fatalln("could not open config file:", err)
	}

	var config turbine.Config

	err = yaml.Unmarshal(configFile, &config)
	if err != nil {
		log.Fatalln("could not parse config file:", err)
	}

	config.Run.Args = append(config.Run.Args, args...)

	for k, _ := range config.Params {
		env, found := syscall.Getenv(k)
		if found {
			config.Params[k] = env
		}
	}

	return config
}

func createBuild(
	atcRequester *atcRequester,
	privileged bool,
	inputs []Input,
	config turbine.Config,
) atc.Build {
	buffer := &bytes.Buffer{}

	buildInputs := make([]turbine.Input, len(inputs))
	for idx, i := range inputs {
		readPipe, err := atcRequester.CreateHTTPRequest(
			atc.ReadPipe,
			rata.Params{"pipe_id": i.Pipe.ID},
			nil,
		)
		if err != nil {
			log.Fatalln(err)
		}

		readPipe.URL.Host = i.Pipe.PeerAddr

		buildInputs[idx] = turbine.Input{
			Name: i.Name,
			Type: "archive",
			Source: turbine.Source{
				"uri": readPipe.URL.String(),
			},
		}
	}

	turbineBuild := turbine.Build{
		Privileged: privileged,
		Config:     config,
		Inputs:     buildInputs,
	}

	err := json.NewEncoder(buffer).Encode(turbineBuild)
	if err != nil {
		log.Fatalln("encoding build failed:", err)
	}

	createBuild, err := atcRequester.CreateRequest(atc.CreateBuild, nil, buffer)
	if err != nil {
		log.Fatalln(err)
	}

	createBuild.Header.Set("Content-Type", "application/json")

	response, err := atcRequester.httpClient.Do(createBuild)
	if err != nil {
		log.Fatalln("request failed:", err)
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusCreated {
		log.Println("bad response when creating build:", response)
		response.Write(os.Stderr)
		os.Exit(1)
	}

	var build atc.Build
	err = json.NewDecoder(response.Body).Decode(&build)
	if err != nil {
		log.Fatalln("response decoding failed:", err)
	}

	return build
}

func abortOnSignal(
	atcRequester *atcRequester,
	terminate <-chan os.Signal,
	build atc.Build,
) {
	<-terminate

	println("\naborting...")

	abortReq, err := atcRequester.CreateRequest(
		atc.AbortBuild,
		rata.Params{"build_id": strconv.Itoa(build.ID)},
		nil,
	)

	if err != nil {
		log.Fatalln(err)
	}

	resp, err := atcRequester.httpClient.Do(abortReq)
	if err != nil {
		log.Println("failed to abort:", err)
	}

	resp.Body.Close()

	// if told to terminate again, exit immediately
	<-terminate
	println("exiting immediately")
	os.Exit(2)
}

func upload(input Input, excludeIgnored bool, atcRequester *atcRequester) {
	path := input.Path
	pipe := input.Pipe

	var archive io.ReadCloser
	if tarPath, err := exec.LookPath("tar"); err != nil {
		compressor := compressor.NewTgz()

		tmpfile, err := ioutil.TempFile("", "fly")
		if err != nil {
			log.Fatalln("creating tempfile failed:", err)
		}

		tmpfile.Close()

		defer os.Remove(tmpfile.Name())

		err = compressor.Compress(path+"/", tmpfile.Name())
		if err != nil {
			log.Fatalln("creating archive failed:", err)
		}

		archive, err = os.Open(tmpfile.Name())
		if err != nil {
			log.Fatalln("could not open archive:", err)
		}
	} else {
		var files []string

		if excludeIgnored {
			files, err = getGitFiles(path)
			if err != nil {
				log.Fatalln("could not determine ignored files:", err)
			}
		} else {
			files = []string{"."}
		}

		tarCmd := exec.Command(tarPath, append([]string{"-czf", "-"}, files...)...)
		tarCmd.Dir = path
		tarCmd.Stderr = os.Stderr

		archive, err = tarCmd.StdoutPipe()
		if err != nil {
			log.Fatalln("could not create tar pipe:", err)
		}

		err = tarCmd.Start()
		if err != nil {
			log.Fatalln("could not run tar:", err)
		}
	}

	defer archive.Close()

	uploadBits, err := atcRequester.CreateRequest(
		atc.WritePipe,
		rata.Params{"pipe_id": pipe.ID},
		archive,
	)
	if err != nil {
		log.Fatalln(err)
	}

	response, err := atcRequester.httpClient.Do(uploadBits)
	if err != nil {
		log.Fatalln("request failed:", err)
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		log.Println("bad response when uploading bits:", response)
		response.Write(os.Stderr)
		os.Exit(1)
	}
}

type atcRequester struct {
	*rata.RequestGenerator
	httpClient *http.Client
}

func newAtcRequester(atcUrl string, insecure bool) *atcRequester {
	tlsClientConfig := &tls.Config{InsecureSkipVerify: insecure}

	return &atcRequester{
		rata.NewRequestGenerator(atcUrl, atc.Routes),
		&http.Client{Transport: &http.Transport{TLSClientConfig: tlsClientConfig}},
	}
}

func (ar *atcRequester) CreateHTTPRequest(
	name string,
	params rata.Params,
	body io.Reader,
) (*http.Request, error) {
	request, err := ar.CreateRequest(name, params, body)
	if err != nil {
		return nil, err
	}

	url := request.URL
	url.Scheme = "http"
	request.URL = url
	return request, nil
}

func getGitFiles(dir string) ([]string, error) {
	tracked, err := gitLS(dir)
	if err != nil {
		return nil, err
	}

	untracked, err := gitLS(dir, "--others", "--exclude-standard")
	if err != nil {
		return nil, err
	}

	return append(tracked, untracked...), nil
}

func gitLS(dir string, flags ...string) ([]string, error) {
	files := []string{}

	gitLS := exec.Command("git", append([]string{"ls-files", "-z"}, flags...)...)
	gitLS.Dir = dir

	gitOut, err := gitLS.StdoutPipe()
	if err != nil {
		return nil, err
	}

	outScan := bufio.NewScanner(gitOut)
	outScan.Split(scanNull)

	err = gitLS.Start()
	if err != nil {
		return nil, err
	}

	for outScan.Scan() {
		files = append(files, outScan.Text())
	}

	err = gitLS.Wait()
	if err != nil {
		return nil, err
	}

	return files, nil
}

func scanNull(data []byte, atEOF bool) (int, []byte, error) {
	// eof, no more data; terminate
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	// look for terminating null byte
	if i := bytes.IndexByte(data, 0); i >= 0 {
		return i + 1, data[0:i], nil
	}

	// no final terminator; return what's left
	if atEOF {
		return len(data), data, nil
	}

	// request more data
	return 0, nil, nil
}
<commit_msg>use sse's default retry interval (already 1s)<commit_after>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"

	"crypto/tls"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"github.com/codegangsta/cli"
	"github.com/concourse/atc"
	"github.com/concourse/fly/eventstream"
	"github.com/concourse/turbine"
	"github.com/pivotal-golang/archiver/compressor"
	"github.com/tedsuo/rata"
	"github.com/vito/go-sse/sse"
	"gopkg.in/yaml.v2"
)

type Input struct {
	Name string
	Path string
	Pipe atc.Pipe
}

func execute(c *cli.Context) {
	atcURL := c.GlobalString("atcURL")
	buildConfig := c.String("config")
	insecure := c.GlobalBool("insecure")
	excludeIgnored := c.GlobalBool("exclude-ignored")

	atcRequester := newAtcRequester(atcURL, insecure)

	inputMappings := c.StringSlice("input")
	if len(inputMappings) == 0 {
		wd, err := os.Getwd()
		if err != nil {
			log.Fatalln(err)
		}

		inputMappings = append(inputMappings, filepath.Base(wd)+"="+wd)
	}

	inputs := []Input{}
	for _, i := range inputMappings {
		segs := strings.SplitN(i, "=", 2)
		if len(segs) < 2 {
			log.Println("malformed input:", i)
			os.Exit(1)
		}

		inputName := segs[0]

		absPath, err := filepath.Abs(segs[1])
		if err != nil {
			log.Printf("could not locate input %s: %s\n", inputName, err)
			os.Exit(1)
		}

		pipe := createPipe(atcRequester)

		inputs = append(inputs, Input{
			Name: inputName,
			Path: absPath,
			Pipe: pipe,
		})
	}

	absConfig, err := filepath.Abs(buildConfig)
	if err != nil {
		log.Println("could not locate config file:", err)
		os.Exit(1)
	}

	build := createBuild(
		atcRequester,
		c.Bool("privileged"),
		inputs,
		loadConfig(absConfig, c.Args()),
	)

	terminate := make(chan os.Signal, 1)

	go abortOnSignal(atcRequester, terminate, build)

	signal.Notify(terminate, syscall.SIGINT, syscall.SIGTERM)

	eventSource := &sse.EventSource{
		Client: atcRequester.httpClient,
		CreateRequest: func() *http.Request {
			logOutput, err := atcRequester.CreateRequest(
				atc.BuildEvents,
				rata.Params{"build_id": strconv.Itoa(build.ID)},
				nil,
			)
			if err != nil {
				log.Fatalln(err)
			}

			return logOutput
		},
	}

	go func() {
		for _, i := range inputs {
			upload(i, excludeIgnored, atcRequester)
		}
	}()

	exitCode, err := eventstream.RenderStream(eventSource)
	if err != nil {
		log.Println("failed to render stream:", err)
		os.Exit(1)
	}

	eventSource.Close()

	os.Exit(exitCode)
}

func createPipe(atcRequester *atcRequester) atc.Pipe {
	cPipe, err := atcRequester.CreateRequest(atc.CreatePipe, nil, nil)
	if err != nil {
		log.Fatalln(err)
	}

	response, err := atcRequester.httpClient.Do(cPipe)
	if err != nil {
		log.Fatalln("request failed:", err)
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusCreated {
		log.Println("bad response when creating pipe:", response)
		response.Write(os.Stderr)
		os.Exit(1)
	}

	var pipe atc.Pipe
	err = json.NewDecoder(response.Body).Decode(&pipe)
	if err != nil {
		log.Println("malformed response when creating pipe:", err)
		os.Exit(1)
	}

	return pipe
}

func loadConfig(configPath string, args []string) turbine.Config {
	configFile, err := ioutil.ReadFile(configPath)
	if err != nil {
		log.Fatalln("could not open config file:", err)
	}

	var config turbine.Config

	err = yaml.Unmarshal(configFile, &config)
	if err != nil {
		log.Fatalln("could not parse config file:", err)
	}

	config.Run.Args = append(config.Run.Args, args...)

	for k, _ := range config.Params {
		env, found := syscall.Getenv(k)
		if found {
			config.Params[k] = env
		}
	}

	return config
}

func createBuild(
	atcRequester *atcRequester,
	privileged bool,
	inputs []Input,
	config turbine.Config,
) atc.Build {
	buffer := &bytes.Buffer{}

	buildInputs := make([]turbine.Input, len(inputs))
	for idx, i := range inputs {
		readPipe, err := atcRequester.CreateHTTPRequest(
			atc.ReadPipe,
			rata.Params{"pipe_id": i.Pipe.ID},
			nil,
		)
		if err != nil {
			log.Fatalln(err)
		}

		readPipe.URL.Host = i.Pipe.PeerAddr

		buildInputs[idx] = turbine.Input{
			Name: i.Name,
			Type: "archive",
			Source: turbine.Source{
				"uri": readPipe.URL.String(),
			},
		}
	}

	turbineBuild := turbine.Build{
		Privileged: privileged,
		Config:     config,
		Inputs:     buildInputs,
	}

	err := json.NewEncoder(buffer).Encode(turbineBuild)
	if err != nil {
		log.Fatalln("encoding build failed:", err)
	}

	createBuild, err := atcRequester.CreateRequest(atc.CreateBuild, nil, buffer)
	if err != nil {
		log.Fatalln(err)
	}

	createBuild.Header.Set("Content-Type", "application/json")

	response, err := atcRequester.httpClient.Do(createBuild)
	if err != nil {
		log.Fatalln("request failed:", err)
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusCreated {
		log.Println("bad response when creating build:", response)
		response.Write(os.Stderr)
		os.Exit(1)
	}

	var build atc.Build
	err = json.NewDecoder(response.Body).Decode(&build)
	if err != nil {
		log.Fatalln("response decoding failed:", err)
	}

	return build
}

func abortOnSignal(
	atcRequester *atcRequester,
	terminate <-chan os.Signal,
	build atc.Build,
) {
	<-terminate

	println("\naborting...")

	abortReq, err := atcRequester.CreateRequest(
		atc.AbortBuild,
		rata.Params{"build_id": strconv.Itoa(build.ID)},
		nil,
	)

	if err != nil {
		log.Fatalln(err)
	}

	resp, err := atcRequester.httpClient.Do(abortReq)
	if err != nil {
		log.Println("failed to abort:", err)
	}

	resp.Body.Close()

	// if told to terminate again, exit immediately
	<-terminate
	println("exiting immediately")
	os.Exit(2)
}

func upload(input Input, excludeIgnored bool, atcRequester *atcRequester) {
	path := input.Path
	pipe := input.Pipe

	var archive io.ReadCloser
	if tarPath, err := exec.LookPath("tar"); err != nil {
		compressor := compressor.NewTgz()

		tmpfile, err := ioutil.TempFile("", "fly")
		if err != nil {
			log.Fatalln("creating tempfile failed:", err)
		}

		tmpfile.Close()

		defer os.Remove(tmpfile.Name())

		err = compressor.Compress(path+"/", tmpfile.Name())
		if err != nil {
			log.Fatalln("creating archive failed:", err)
		}

		archive, err = os.Open(tmpfile.Name())
		if err != nil {
			log.Fatalln("could not open archive:", err)
		}
	} else {
		var files []string

		if excludeIgnored {
			files, err = getGitFiles(path)
			if err != nil {
				log.Fatalln("could not determine ignored files:", err)
			}
		} else {
			files = []string{"."}
		}

		tarCmd := exec.Command(tarPath, append([]string{"-czf", "-"}, files...)...)
		tarCmd.Dir = path
		tarCmd.Stderr = os.Stderr

		archive, err = tarCmd.StdoutPipe()
		if err != nil {
			log.Fatalln("could not create tar pipe:", err)
		}

		err = tarCmd.Start()
		if err != nil {
			log.Fatalln("could not run tar:", err)
		}
	}

	defer archive.Close()

	uploadBits, err := atcRequester.CreateRequest(
		atc.WritePipe,
		rata.Params{"pipe_id": pipe.ID},
		archive,
	)
	if err != nil {
		log.Fatalln(err)
	}

	response, err := atcRequester.httpClient.Do(uploadBits)
	if err != nil {
		log.Fatalln("request failed:", err)
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		log.Println("bad response when uploading bits:", response)
		response.Write(os.Stderr)
		os.Exit(1)
	}
}

type atcRequester struct {
	*rata.RequestGenerator
	httpClient *http.Client
}

func newAtcRequester(atcUrl string, insecure bool) *atcRequester {
	tlsClientConfig := &tls.Config{InsecureSkipVerify: insecure}

	return &atcRequester{
		rata.NewRequestGenerator(atcUrl, atc.Routes),
		&http.Client{Transport: &http.Transport{TLSClientConfig: tlsClientConfig}},
	}
}

func (ar *atcRequester) CreateHTTPRequest(
	name string,
	params rata.Params,
	body io.Reader,
) (*http.Request, error) {
	request, err := ar.CreateRequest(name, params, body)
	if err != nil {
		return nil, err
	}

	url := request.URL
	url.Scheme = "http"
	request.URL = url
	return request, nil
}

func getGitFiles(dir string) ([]string, error) {
	tracked, err := gitLS(dir)
	if err != nil {
		return nil, err
	}

	untracked, err := gitLS(dir, "--others", "--exclude-standard")
	if err != nil {
		return nil, err
	}

	return append(tracked, untracked...), nil
}

func gitLS(dir string, flags ...string) ([]string, error) {
	files := []string{}

	gitLS := exec.Command("git", append([]string{"ls-files", "-z"}, flags...)...)
	gitLS.Dir = dir

	gitOut, err := gitLS.StdoutPipe()
	if err != nil {
		return nil, err
	}

	outScan := bufio.NewScanner(gitOut)
	outScan.Split(scanNull)

	err = gitLS.Start()
	if err != nil {
		return nil, err
	}

	for outScan.Scan() {
		files = append(files, outScan.Text())
	}

	err = gitLS.Wait()
	if err != nil {
		return nil, err
	}

	return files, nil
}

func scanNull(data []byte, atEOF bool) (int, []byte, error) {
	// eof, no more data; terminate
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	// look for terminating null byte
	if i := bytes.IndexByte(data, 0); i >= 0 {
		return i + 1, data[0:i], nil
	}

	// no final terminator; return what's left
	if atEOF {
		return len(data), data, nil
	}

	// request more data
	return 0, nil, nil
}
<|endoftext|>


<commit_before>// exp 包中是用于 SQL 语句生成的各种组件
//
// exp.go 中保存了 exp 包的一些未分类组件
//
// TODO
//
// 普通的表达式没有处理字符串转义，需要接受外部传入的数据的话，请一定参数化
//
// 目前还没有对纯文本文法中的命名做映射，要享受转换的能力，请使用 table 和 field 类型
package exp

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/lib/pq"
)

var argFinder, _ = regexp.Compile("([^\\$]\\$\\d)|(^\\$d)")
var dollar, _ = regexp.Compile("\\$\\$")

type Env interface {
	// Type Name to Table Name
	// NOTE: 需要注意的是当前使用type的 Name()，其中包含 packages 名
	TynaToTana(typename string) string
	// Struct Field Name to Table Column Name
	FinaToCona(typename string, fieldname string) string
	Scope() Exp
	SetScope(Exp)
}

type Exp interface {
	Eval(Env) string
}

type equal struct {
	x, y Exp
}

func Equal(x, y Exp) Exp {
	return &equal{x, y}
}
func (e equal) Eval(env Env) string {
	return fmt.Sprintf("%s=%s", e.x.Eval(env), e.y.Eval(env))
}

type notequal struct {
	x, y Exp
}

func NotEqual(x, y Exp) Exp {
	return &notequal{x, y}
}
func (e notequal) Eval(env Env) string {
	return fmt.Sprintf("%s!=%s", e.x.Eval(env), e.y.Eval(env))
}

type great struct {
	x, y Exp
}

func Great(x, y Exp) Exp {
	return &great{x, y}
}
func (e great) Eval(env Env) string {
	return fmt.Sprintf("%s>%s", e.x.Eval(env), e.y.Eval(env))
}

type less struct {
	x, y Exp
}

func Less(x, y Exp) Exp {
	return &less{x, y}
}
func (e less) Eval(env Env) string {
	return fmt.Sprintf("%s<%s", e.x.Eval(env), e.y.Eval(env))
}

type and struct {
	x, y Exp
}

func And(x, y Exp) Exp {
	return &and{x, y}
}
func (a and) Eval(env Env) string {
	return fmt.Sprintf("(%s) and (%s)", a.x.Eval(env), a.y.Eval(env))
}

type or struct {
	x, y Exp
}

func Or(x, y Exp) Exp {
	return &or{x, y}
}
func (o or) Eval(env Env) string {
	return fmt.Sprintf("(%s) or (%s)", o.x.Eval(env), o.y.Eval(env))
}

type text struct {
	data string
}

// Text 函数用于生成text类型的表达式。
// text 就是 PostgreSQL 的 text 类型。由于作者太懒，先用这个代替所有的文本和字符串类型用吧，
// 在 PG 里其实一般的规模好像也问题不大……
func Text(data string) Exp {
	return &text{data}
}

// 从 text 对象到 SQL 片段的解析函数实现
func (t *text) Eval(env Env) string {
	return fmt.Sprintf("'%s'", t.data)
}

type integer struct {
	data int
}

// Integer 函数生成 integer 类型表达式，这个 integer 指 PostgreSQL 中的 integer 类型
func Integer(data int) Exp {
	return &integer{data}
}

// 从 integer 对象到 SQL 片段的解析函数实现
func (i *integer) Eval(env Env) string {
	return fmt.Sprintf("%d", i.data)
}

type timestamp struct {
	data time.Time
}

// Timestamp 函数用于生成 timestamp 类型的表达式。
// timestamp 类型即 PostgreSQL 的 timestamp 时间戳。由于作者太懒，目前还没有 Date
// 和 Datetime 类型的支持。
func Timestamp(data time.Time) Exp {
	return &timestamp{data}
}

// 从 时间戳对象到 SQL 片段的解析函数实现
func (ts *timestamp) Eval(env Env) string {
	dbtime := pq.NullTime{Time: ts.data, Valid: true}
	val, err := dbtime.Value()
	if err != nil {
		panic(err)
	} else {
		if ret, ok := val.([]byte); ok {
			return string(ret)
		} else {
			panic(ts.data)
		}
	}
}

// 之所以用 arg 而不是 parameter ， 完全是为了少写几个字母……
type arg struct {
	Order int
}

// 我也觉得自动生成序列号比较省力气啊。不过想了半天， $%d 就是为了可以指定参数插入位置啊，
// 自动生成顺序就白瞎了啊……
// 将来可能会把这个变成支持命名的，order虽然更省事儿，但是命名相对来说更省心
func Arg(order int) *arg {
	return &arg{order}
}
func (a arg) Eval(env Env) string {
	return fmt.Sprintf("$%d", a.Order)
}

// IncOrder 其实在 pgears 之外应该不太有机会用到，这个是内部生成表达式的时候偶尔
// 需要调整表达式中参数的 order，这个就是起这种作用的，其实如果全命名化了也就没这个问题了。
// 之所以公开是因为 Engine 会用到。
func IncOrder(e Exp, step int) {
	switch ex := e.(type) {
	case *equal:
		IncOrder((*ex).x, step)
		IncOrder((*ex).y, step)
	case *notequal:
		IncOrder((*ex).x, step)
		IncOrder((*ex).y, step)
	case *and:
		IncOrder((*ex).x, step)
		IncOrder((*ex).y, step)
	case *or:
		IncOrder((*ex).x, step)
		IncOrder((*ex).y, step)
	case *great:
		IncOrder((*ex).x, step)
		IncOrder((*ex).y, step)
	case *less:
		IncOrder((*ex).x, step)
		IncOrder((*ex).y, step)
	case *not:
		IncOrder((*ex).exp, step)
	case *arg:
		ex.Order += step
	}
}

type function struct {
	name string
	args []Exp
}

func Func(name string, args ...Exp) Exp {
	return &function{name, args}
}
func (f *function) Eval(env Env) string {
	var args = make([]string, len(f.args))
	for idx, arg := range f.args {
		args[idx] = arg.Eval(env)
	}
	return fmt.Sprintf("%s(%s)", f.name, strings.Join(args, ","))
}

type not struct {
	exp Exp
}

func Not(exp Exp) *not {
	return &not{exp}
}
func (n not) Eval(env Env) string {
	return fmt.Sprintf("not (%s)", n.exp.Eval(env))
}

type fieldIsNull struct {
	exp Exp
}

func FieldIsNull(field Exp) fieldIsNull {
	return fieldIsNull{field}
}
func (isnull *fieldIsNull) Eval(env Env) string {
	return fmt.Sprintf("%s is NULL", isnull.exp.Eval(env))
}

type isNullFunc struct {
	field Exp
	exp   Exp
}

func IsNullFunc(field Exp, exp Exp) Exp {
	return isNullFunc{field, exp}
}
func (isnull isNullFunc) Eval(env Env) string {
	return fmt.Sprintf("isNull(%s, %s)", isnull.field.Eval(env),
		isnull.exp.Eval(env))
}

type nullif struct {
	field Exp
	exp   Exp
}

func NullIf(field Exp, exp Exp) Exp {
	return nullif{field, exp}
}
func (nullif nullif) Eval(env Env) string {
	return fmt.Sprintf("nullif(%s, %s)", nullif.field.Eval(env),
		nullif.exp.Eval(env))
}

type fullTextSearch struct {
	field Exp
	query Exp
}

func FTS(field Exp, query Exp) *fullTextSearch {
	return &fullTextSearch{field, query}
}
func (fts fullTextSearch) Eval(env Env) string {
	return fmt.Sprintf("%s @@ %s", fts.field.Eval(env),
		fts.query.Eval(env))
}

// snippet 就是直接插入一段纯文本咯，不做任何检测咯，玩脱了是你活该咯……
type snippet struct {
	str *string
}

func Snippet(s string) *snippet {
	return &snippet{&s}
}
func (s *snippet) Eval(env Env) string {
	return *s.str
}

// 虽然理论上说这个内涵的 field 肯定会是field，但是这里姑且还是假定为 exp.Exp
type desc struct {
	field Exp
}

// Desc 生成一个用于orderby desc的表达式
func Desc(field Exp) Exp {
	return &desc{field}
}

func (d desc) Eval(env Env) string {
	return d.field.Eval(env) + " desc"
}

type count struct {
	fields []Exp
	isAll  bool
}

// TODO: postgresql 是不是允许count多个字段列啊……
func Count(fields ...Exp) Exp {
	return &count{fields, false}
}

// count(*)
func Counts() Exp {
	return &count{[]Exp{}, true}
}
func (d count) Eval(env Env) string {
	if d.isAll {
		return "count(*)"
	} else {
		fs := make([]string, 0, len(d.fields))
		for _, f := range d.fields {
			fs = append(fs, f.Eval(env))
		}
		fields := strings.Join(fs, ",")
		return fmt.Sprintf("count(%s)", fields)
	}
}

type allfield struct {
}

func X() Exp {
	return allfield{}
}

func (x allfield) Eval(env Env) string {
	return "*"
}
<commit_msg>增加 in 表达式支持<commit_after>// exp 包中是用于 SQL 语句生成的各种组件
//
// exp.go 中保存了 exp 包的一些未分类组件
//
// TODO
//
// 普通的表达式没有处理字符串转义，需要接受外部传入的数据的话，请一定参数化
//
// 目前还没有对纯文本文法中的命名做映射，要享受转换的能力，请使用 table 和 field 类型
package exp

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/lib/pq"
)

var argFinder, _ = regexp.Compile("([^\\$]\\$\\d)|(^\\$d)")
var dollar, _ = regexp.Compile("\\$\\$")

type Env interface {
	// Type Name to Table Name
	// NOTE: 需要注意的是当前使用type的 Name()，其中包含 packages 名
	TynaToTana(typename string) string
	// Struct Field Name to Table Column Name
	FinaToCona(typename string, fieldname string) string
	Scope() Exp
	SetScope(Exp)
}

type Exp interface {
	Eval(Env) string
}

type equal struct {
	x, y Exp
}

func Equal(x, y Exp) Exp {
	return &equal{x, y}
}
func (e equal) Eval(env Env) string {
	return fmt.Sprintf("%s=%s", e.x.Eval(env), e.y.Eval(env))
}

type notequal struct {
	x, y Exp
}

func NotEqual(x, y Exp) Exp {
	return &notequal{x, y}
}
func (e notequal) Eval(env Env) string {
	return fmt.Sprintf("%s!=%s", e.x.Eval(env), e.y.Eval(env))
}

type great struct {
	x, y Exp
}

func Great(x, y Exp) Exp {
	return &great{x, y}
}
func (e great) Eval(env Env) string {
	return fmt.Sprintf("%s>%s", e.x.Eval(env), e.y.Eval(env))
}

type less struct {
	x, y Exp
}

func Less(x, y Exp) Exp {
	return &less{x, y}
}
func (e less) Eval(env Env) string {
	return fmt.Sprintf("%s<%s", e.x.Eval(env), e.y.Eval(env))
}

type and struct {
	x, y Exp
}

func And(x, y Exp) Exp {
	return &and{x, y}
}
func (a and) Eval(env Env) string {
	return fmt.Sprintf("(%s) and (%s)", a.x.Eval(env), a.y.Eval(env))
}

type or struct {
	x, y Exp
}

func Or(x, y Exp) Exp {
	return &or{x, y}
}
func (o or) Eval(env Env) string {
	return fmt.Sprintf("(%s) or (%s)", o.x.Eval(env), o.y.Eval(env))
}

type in struct {
	test Exp
	set  []Exp
}

func In(test Exp, set ...Exp) Exp {
	return &in{test, set}
}
func (i in) Eval(env Env) string {
	them := make([]string, 0, len(i.set))
	for _, element := range i.set {
		them = append(them, element.Eval(env))
	}
	set := strings.Join(them, ", ")
	return fmt.Sprintf("%s in (%s)", i.test.Eval(env), set)
}

type text struct {
	data string
}

// Text 函数用于生成text类型的表达式。
// text 就是 PostgreSQL 的 text 类型。由于作者太懒，先用这个代替所有的文本和字符串类型用吧，
// 在 PG 里其实一般的规模好像也问题不大……
func Text(data string) Exp {
	return &text{data}
}

// 从 text 对象到 SQL 片段的解析函数实现
func (t *text) Eval(env Env) string {
	return fmt.Sprintf("'%s'", t.data)
}

type integer struct {
	data int
}

// Integer 函数生成 integer 类型表达式，这个 integer 指 PostgreSQL 中的 integer 类型
func Integer(data int) Exp {
	return &integer{data}
}

// 从 integer 对象到 SQL 片段的解析函数实现
func (i *integer) Eval(env Env) string {
	return fmt.Sprintf("%d", i.data)
}

type timestamp struct {
	data time.Time
}

// Timestamp 函数用于生成 timestamp 类型的表达式。
// timestamp 类型即 PostgreSQL 的 timestamp 时间戳。由于作者太懒，目前还没有 Date
// 和 Datetime 类型的支持。
func Timestamp(data time.Time) Exp {
	return &timestamp{data}
}

// 从 时间戳对象到 SQL 片段的解析函数实现
func (ts *timestamp) Eval(env Env) string {
	dbtime := pq.NullTime{Time: ts.data, Valid: true}
	val, err := dbtime.Value()
	if err != nil {
		panic(err)
	} else {
		if ret, ok := val.([]byte); ok {
			return string(ret)
		} else {
			panic(ts.data)
		}
	}
}

// 之所以用 arg 而不是 parameter ， 完全是为了少写几个字母……
type arg struct {
	Order int
}

// 我也觉得自动生成序列号比较省力气啊。不过想了半天， $%d 就是为了可以指定参数插入位置啊，
// 自动生成顺序就白瞎了啊……
// 将来可能会把这个变成支持命名的，order虽然更省事儿，但是命名相对来说更省心
func Arg(order int) *arg {
	return &arg{order}
}
func (a arg) Eval(env Env) string {
	return fmt.Sprintf("$%d", a.Order)
}

// IncOrder 其实在 pgears 之外应该不太有机会用到，这个是内部生成表达式的时候偶尔
// 需要调整表达式中参数的 order，这个就是起这种作用的，其实如果全命名化了也就没这个问题了。
// 之所以公开是因为 Engine 会用到。
func IncOrder(e Exp, step int) {
	switch ex := e.(type) {
	case *equal:
		IncOrder((*ex).x, step)
		IncOrder((*ex).y, step)
	case *notequal:
		IncOrder((*ex).x, step)
		IncOrder((*ex).y, step)
	case *and:
		IncOrder((*ex).x, step)
		IncOrder((*ex).y, step)
	case *or:
		IncOrder((*ex).x, step)
		IncOrder((*ex).y, step)
	case *great:
		IncOrder((*ex).x, step)
		IncOrder((*ex).y, step)
	case *less:
		IncOrder((*ex).x, step)
		IncOrder((*ex).y, step)
	case *not:
		IncOrder((*ex).exp, step)
	case *arg:
		ex.Order += step
	}
}

type function struct {
	name string
	args []Exp
}

func Func(name string, args ...Exp) Exp {
	return &function{name, args}
}
func (f *function) Eval(env Env) string {
	var args = make([]string, len(f.args))
	for idx, arg := range f.args {
		args[idx] = arg.Eval(env)
	}
	return fmt.Sprintf("%s(%s)", f.name, strings.Join(args, ","))
}

type not struct {
	exp Exp
}

func Not(exp Exp) *not {
	return &not{exp}
}
func (n not) Eval(env Env) string {
	return fmt.Sprintf("not (%s)", n.exp.Eval(env))
}

type fieldIsNull struct {
	exp Exp
}

func FieldIsNull(field Exp) fieldIsNull {
	return fieldIsNull{field}
}
func (isnull *fieldIsNull) Eval(env Env) string {
	return fmt.Sprintf("%s is NULL", isnull.exp.Eval(env))
}

type isNullFunc struct {
	field Exp
	exp   Exp
}

func IsNullFunc(field Exp, exp Exp) Exp {
	return isNullFunc{field, exp}
}
func (isnull isNullFunc) Eval(env Env) string {
	return fmt.Sprintf("isNull(%s, %s)", isnull.field.Eval(env),
		isnull.exp.Eval(env))
}

type nullif struct {
	field Exp
	exp   Exp
}

func NullIf(field Exp, exp Exp) Exp {
	return nullif{field, exp}
}
func (nullif nullif) Eval(env Env) string {
	return fmt.Sprintf("nullif(%s, %s)", nullif.field.Eval(env),
		nullif.exp.Eval(env))
}

type fullTextSearch struct {
	field Exp
	query Exp
}

func FTS(field Exp, query Exp) *fullTextSearch {
	return &fullTextSearch{field, query}
}
func (fts fullTextSearch) Eval(env Env) string {
	return fmt.Sprintf("%s @@ %s", fts.field.Eval(env),
		fts.query.Eval(env))
}

// snippet 就是直接插入一段纯文本咯，不做任何检测咯，玩脱了是你活该咯……
type snippet struct {
	str *string
}

func Snippet(s string) *snippet {
	return &snippet{&s}
}
func (s *snippet) Eval(env Env) string {
	return *s.str
}

// 虽然理论上说这个内涵的 field 肯定会是field，但是这里姑且还是假定为 exp.Exp
type desc struct {
	field Exp
}

// Desc 生成一个用于orderby desc的表达式
func Desc(field Exp) Exp {
	return &desc{field}
}

func (d desc) Eval(env Env) string {
	return d.field.Eval(env) + " desc"
}

type count struct {
	fields []Exp
	isAll  bool
}

// TODO: postgresql 是不是允许count多个字段列啊……
func Count(fields ...Exp) Exp {
	return &count{fields, false}
}

// count(*)
func Counts() Exp {
	return &count{[]Exp{}, true}
}
func (d count) Eval(env Env) string {
	if d.isAll {
		return "count(*)"
	} else {
		fs := make([]string, 0, len(d.fields))
		for _, f := range d.fields {
			fs = append(fs, f.Eval(env))
		}
		fields := strings.Join(fs, ",")
		return fmt.Sprintf("count(%s)", fields)
	}
}

type allfield struct {
}

func X() Exp {
	return allfield{}
}

func (x allfield) Eval(env Env) string {
	return "*"
}
<|endoftext|>


<commit_before>package gallium

/*
#cgo CFLAGS: -mmacosx-version-min=10.8
#cgo LDFLAGS: -Fdist
#cgo LDFLAGS: -framework Gallium
#cgo LDFLAGS: -Wl,-rpath,@executable_path/../Frameworks
#cgo LDFLAGS: -Wl,-rpath,${SRCDIR}/dist
#cgo LDFLAGS: -mmacosx-version-min=10.8

#include <stdlib.h>
#include "lib/api/gallium.h"
#include "lib/api/menu.h"
*/
import "C"
import (
	"fmt"
	"log"
	"time"
	"unsafe"
)

// cerr holds a C-allocated error, which must be freed explicitly.
type cerr struct {
	st *C.struct_gallium_error
}

// newCerr allocates a new error struct. It must be freed explicitly.
func newCerr() cerr {
	return cerr{
		st: (*C.struct_gallium_error)(C.malloc(C.sizeof_struct_gallium_error)),
	}
}

func (e cerr) free() {
	C.free(unsafe.Pointer(e.st))
}

func (e *cerr) err() error {
	// TODO
	return fmt.Errorf("C error")
}

// Loop starts the browser loop and does not return unless there is an initialization error
func Loop(args []string, onready func(*App)) error {
	log.Println("=== gallium.Loop ===")
	cerr := newCerr()
	defer cerr.free()
	go func() {
		time.Sleep(time.Second) // TODO: find out when the browser is actually ready
		onready(&App{})
	}()
	//C.SetUIApplication()
	C.GalliumLoop(C.CString(args[0]), &cerr.st)
	return cerr.err()
}

// App is the handle that allows you to create windows and menus
type App struct{}

// NewWindow creates a window that will oad the given URL and will display
// the given title
func (b *App) NewWindow(url, title string) error {
	log.Println("=== gallium.NewWindow ===")
	cerr := newCerr()
	defer cerr.free()
	C.GalliumCreateWindow(C.CString(url), C.CString(title), &cerr.st)
	return nil
}
<commit_msg>add SRCDIR to -F directive<commit_after>package gallium

/*
#cgo CFLAGS: -mmacosx-version-min=10.8
#cgo LDFLAGS: -F${SRCDIR}/dist
#cgo LDFLAGS: -framework Gallium
#cgo LDFLAGS: -Wl,-rpath,@executable_path/../Frameworks
#cgo LDFLAGS: -Wl,-rpath,${SRCDIR}/dist
#cgo LDFLAGS: -mmacosx-version-min=10.8

#include <stdlib.h>
#include "lib/api/gallium.h"
#include "lib/api/menu.h"
*/
import "C"
import (
	"fmt"
	"log"
	"time"
	"unsafe"
)

// cerr holds a C-allocated error, which must be freed explicitly.
type cerr struct {
	st *C.struct_gallium_error
}

// newCerr allocates a new error struct. It must be freed explicitly.
func newCerr() cerr {
	return cerr{
		st: (*C.struct_gallium_error)(C.malloc(C.sizeof_struct_gallium_error)),
	}
}

func (e cerr) free() {
	C.free(unsafe.Pointer(e.st))
}

func (e *cerr) err() error {
	// TODO
	return fmt.Errorf("C error")
}

// Loop starts the browser loop and does not return unless there is an initialization error
func Loop(args []string, onready func(*App)) error {
	log.Println("=== gallium.Loop ===")
	cerr := newCerr()
	defer cerr.free()
	go func() {
		time.Sleep(time.Second) // TODO: find out when the browser is actually ready
		onready(&App{})
	}()
	//C.SetUIApplication()
	C.GalliumLoop(C.CString(args[0]), &cerr.st)
	return cerr.err()
}

// App is the handle that allows you to create windows and menus
type App struct{}

// NewWindow creates a window that will oad the given URL and will display
// the given title
func (b *App) NewWindow(url, title string) error {
	log.Println("=== gallium.NewWindow ===")
	cerr := newCerr()
	defer cerr.free()
	C.GalliumCreateWindow(C.CString(url), C.CString(title), &cerr.st)
	return nil
}
<|endoftext|>


<commit_before>package main

import (
//	_ "github.com/lib/pq"
	"log"
	"bytes"
	"os"
	"io/ioutil"
	"net/http"
	"encoding/xml"
//	"database/sql"
	s "strings"
)

type Epg struct {
	Date string `xml:"id,attr"`
	LastModified string `xml:"last_modified,attr"`
	DisplayTimeZone string `xml:"display_time_zone,attr"`
	EpgGames []EpgGame `xml:"game"`
}

/*
gids look like: gid_2014_07_22_nynmlb_seamlb_1

Doesn't yet handle doubleheader days. It'll just return the first match it finds for the team.
 */
func (e Epg) GidForTeam(teamCode string) string {
	for _, game := range e.EpgGames {
		if s.Contains(game.Gameday, s.Join([]string{"_", teamCode, "mlb_"}, "")) {
			return "gid_" + game.Gameday
		}
	}
	return "" // return an error here as well?
}

type EpgGame struct {
	CalendarEventId string `xml:"calendar_event_id,attr"`
	Start string `xml:"start,attr"`
	Id string `xml:"id,attr"`
	Gameday string `xml:"gameday,attr"`
}

type Game struct {
	XMLName xml.Name `xml:"game"`
	GameType string `xml:"type,attr"`
	LocalGameTime string `xml:"local_game_time,attr"`
	Teams []Team `xml:"team"`
	Stadium Stadium `xml:"stadium"`
}

type Team struct {
	XMLName xml.Name `xml:"team"`
	TeamType string `xml:"type,attr"`
	Code string `xml:"code,attr"`
	FileCode string `xml:"file_code,attr"`
}

type Stadium struct {
	XMLName xml.Name `xml:"stadium"`
	Id string `xml:"id,attr"`
	Name string `xml:"name,attr"`
}

func main() {
	args := os.Args[1:]
	if (len(args) != 2) {
		log.Fatal("Usage: gameday teamCode date")
	}

	teamCode := args[0]
	date := args[1]

	log.Println("Fetching game for " + teamCode + " on " + date)

	epgResp, err := http.Get(epgUrl(date))
	if err != nil {
		log.Fatal(err)
	}
	defer epgResp.Body.Close()
	epgBody, err := ioutil.ReadAll(epgResp.Body)
	if err != nil {
		log.Fatal(err)
	}

	var epg Epg
	xml.Unmarshal(epgBody, &epg)
	log.Println("Fetching from: " + gameUrl(date, epg.GidForTeam(teamCode)))

	resp, err := http.Get(gameUrl(date, epg.GidForTeam(teamCode)))
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	var game Game
	xml.Unmarshal(body, &game)
	log.Println(resp.Status)
	log.Println(string(body))

//	Assumes a pg database exists named go-gameday, a role that can access it.
//	Assumes a table called pitches with a character column called code.
//	db, err := sql.Open("postgres", "user=go-gameday dbname=go-gameday sslmode=disable")
//	issues := db.Ping()
//	log.Println(issue)
//
//	if err != nil {
//		log.Fatal(err)
//	}
//
//	rows, err :=  db.Query("SELECT code FROM pitches")
//
//	if err != nil {
//		log.Fatal(err)
//	}
//
//	defer rows.Close()
//
//	for rows.Next() {
//		var code string
//		err = rows.Scan(&code)
//		log.Println(code)
//	}
}

func baseUrl() string {
	return "http://gd2.mlb.com/components/game/mlb/"
}

func dateUrl(date string) string {
	var buffer bytes.Buffer
	buffer.WriteString(baseUrl())
	buffer.WriteString(datePath(date))
	return buffer.String()
}

func epgUrl(date string) string {
	var buffer bytes.Buffer
	buffer.WriteString(dateUrl(date))
	buffer.WriteString("/epg.xml")
	return buffer.String()
}

func gameDirectoryUrl(date string, gid string) string { // parse the date out of the gid to not have to pass both around
	var buffer bytes.Buffer
	buffer.WriteString(baseUrl())
	buffer.WriteString(datePath(date))
	buffer.WriteString("/")
	buffer.WriteString(gid)
	buffer.WriteString("/")
	return buffer.String()
}

func gameUrl(date string, gid string) string { // parse the date out of the gid to not have to pass both around
	return gameDirectoryUrl(date, gid) + "game.xml"
}

func datePath(date string) string {
	// firx this to be date parsing, validating
	datePieces := s.Split(date, "-")
	var buffer bytes.Buffer
	buffer.WriteString("year_")
	buffer.WriteString(datePieces[0])
	buffer.WriteString("/month_")
	buffer.WriteString(datePieces[1])
	buffer.WriteString("/day_")
	buffer.WriteString(datePieces[2])
	return buffer.String()
}

<commit_msg>Initial cut at caching the retrieved file. Doesn't check first to see if it's cached already.<commit_after>package main

import (
	"os/user"
	"log"
	"bytes"
	"os"
	"io/ioutil"
	"net/http"
	"encoding/xml"
	s "strings"
)

type Epg struct {
	Date string `xml:"id,attr"`
	LastModified string `xml:"last_modified,attr"`
	DisplayTimeZone string `xml:"display_time_zone,attr"`
	EpgGames []EpgGame `xml:"game"`
}

/*
gids look like: gid_2014_07_22_nynmlb_seamlb_1

Doesn't yet handle doubleheader days. It'll just return the first match it finds for the team.
 */
func (e Epg) GidForTeam(teamCode string) string {
	for _, game := range e.EpgGames {
		if s.Contains(game.Gameday, s.Join([]string{"_", teamCode, "mlb_"}, "")) {
			return "gid_" + game.Gameday
		}
	}
	return "" // return an error here as well?
}

type EpgGame struct {
	CalendarEventId string `xml:"calendar_event_id,attr"`
	Start string `xml:"start,attr"`
	Id string `xml:"id,attr"`
	Gameday string `xml:"gameday,attr"`
}

type Game struct {
	XMLName xml.Name `xml:"game"`
	GameType string `xml:"type,attr"`
	LocalGameTime string `xml:"local_game_time,attr"`
	Teams []Team `xml:"team"`
	Stadium Stadium `xml:"stadium"`
}

type Team struct {
	XMLName xml.Name `xml:"team"`
	TeamType string `xml:"type,attr"`
	Code string `xml:"code,attr"`
	FileCode string `xml:"file_code,attr"`
}

type Stadium struct {
	XMLName xml.Name `xml:"stadium"`
	Id string `xml:"id,attr"`
	Name string `xml:"name,attr"`
}

func main() {
	args := os.Args[1:]
	if (len(args) != 2) {
		log.Fatal("Usage: gameday teamCode date")
	}

	teamCode := args[0]
	date := args[1]

	log.Println("Fetching game for " + teamCode + " on " + date)

	epgResp, err := http.Get(epgUrl(date))
	if err != nil {
		log.Fatal(err)
	}
	defer epgResp.Body.Close()
	epgBody, err := ioutil.ReadAll(epgResp.Body)
	if err != nil {
		log.Fatal(err)
	}

	var epg Epg
	xml.Unmarshal(epgBody, &epg)
	log.Println("Fetching from: " + gameUrl(date, epg.GidForTeam(teamCode)))

	game := fetchGame(date, epg.GidForTeam(teamCode))
	log.Println(game)
}

func fetchGame(date string, gid string) Game {
	// firx to check to see if it is cached

	resp, err := http.Get(gameUrl(date, gid))
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	var game Game
	xml.Unmarshal(body, &game)
	log.Println(resp.Status)
	log.Println(string(body))
	cacheResponse(gid, "game.xml", body)
	return game
}

func cacheResponse(gid string, filename string, body []byte) {
	cachePath := homeDir() + "/go-gameday-cache/" + s.Split(gid, "_")[1] + "/"
	os.MkdirAll(cachePath, (os.FileMode)(0775))
	f, err := os.Create(cachePath + gid + "-" + filename)
	f.Write(body)
	check(err)
	defer f.Close()
}

func baseUrl() string {
	return "http://gd2.mlb.com/components/game/mlb/"
}

func dateUrl(date string) string {
	var buffer bytes.Buffer
	buffer.WriteString(baseUrl())
	buffer.WriteString(datePath(date))
	return buffer.String()
}

func epgUrl(date string) string {
	var buffer bytes.Buffer
	buffer.WriteString(dateUrl(date))
	buffer.WriteString("/epg.xml")
	return buffer.String()
}

func gameDirectoryUrl(date string, gid string) string { // parse the date out of the gid to not have to pass both around
	var buffer bytes.Buffer
	buffer.WriteString(baseUrl())
	buffer.WriteString(datePath(date))
	buffer.WriteString("/")
	buffer.WriteString(gid)
	buffer.WriteString("/")
	return buffer.String()
}

func gameUrl(date string, gid string) string { // parse the date out of the gid to not have to pass both around
	return gameDirectoryUrl(date, gid) + "game.xml"
}

func datePath(date string) string {
	// firx this to be date parsing, validating
	datePieces := s.Split(date, "-")
	var buffer bytes.Buffer
	buffer.WriteString("year_")
	buffer.WriteString(datePieces[0])
	buffer.WriteString("/month_")
	buffer.WriteString(datePieces[1])
	buffer.WriteString("/day_")
	buffer.WriteString(datePieces[2])
	return buffer.String()
}

func homeDir() string {
	usr, err := user.Current()
	if err != nil {
		log.Fatal( err )
	}
	return usr.HomeDir
}

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


<commit_before>package x509

import (
	"bytes"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"encoding/asn1"
	"encoding/pem"
	"errors"

	"github.com/Hyperledger-TWGC/tjfoc-gm/sm2"
)

func ReadPrivateKeyFromMem(privateKeyPem []byte, pwd []byte) (*sm2.PrivateKey, error) {
	var block *pem.Block
	block, _ = pem.Decode(privateKeyPem)
	if block == nil {
		return nil, errors.New("failed to decode private key")
	}
	priv, err := ParsePKCS8PrivateKey(block.Bytes, pwd)
	return priv, err
}

func WritePrivateKeyToMem(key *sm2.PrivateKey, pwd []byte) ([]byte, error) {
	var block *pem.Block
	der, err := MarshalSm2PrivateKey(key, pwd) //Convert private key to DER format
	if err != nil {
		return nil, err
	}
	if pwd != nil {
		block = &pem.Block{
			Type:  "ENCRYPTED PRIVATE KEY",
			Bytes: der,
		}
	} else {
		block = &pem.Block{
			Type:  "PRIVATE KEY",
			Bytes: der,
		}
	}
	certPem := pem.EncodeToMemory(block)
	return certPem, nil
}

func ReadPublicKeyFromMem(publicKeyPem []byte) (*sm2.PublicKey, error) {
	block, _ := pem.Decode(publicKeyPem)
	if block == nil || block.Type != "PUBLIC KEY" {
		return nil, errors.New("failed to decode public key")
	}
	return ParseSm2PublicKey(block.Bytes)
}

func WritePublicKeyToMem(key *sm2.PublicKey) ([]byte, error) {
	der, err := MarshalSm2PublicKey(key) //Convert publick key to DER format
	if err != nil {
		return nil, err
	}
	block := &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: der,
	}
	certPem := pem.EncodeToMemory(block)
	return certPem, nil
}

func ReadCertificateRequestFromPem(certPem []byte) (*CertificateRequest, error) {
	block, _ := pem.Decode(certPem)
	if block == nil {
		return nil, errors.New("failed to decode certificate request")
	}
	return ParseCertificateRequest(block.Bytes)
}

func CreateCertificateRequestToPem(template *CertificateRequest, privKey *sm2.PrivateKey) ([]byte, error) {
	der, err := CreateCertificateRequest(rand.Reader, template, privKey)
	if err != nil {
		return nil, err
	}
	block := &pem.Block{
		Type:  "CERTIFICATE REQUEST",
		Bytes: der,
	}
	certPem := pem.EncodeToMemory(block)
	return certPem, nil
}

func ReadCertificateFromPem(certPem []byte) (*Certificate, error) {
	block, _ := pem.Decode(certPem)
	if block == nil {
		return nil, errors.New("failed to decode certificate request")
	}
	return ParseCertificate(block.Bytes)
}

func CreateCertificate(template, parent *Certificate, publicKey, privateKey interface{}) ([]byte, error) {
	if template.SerialNumber == nil {
		return nil, errors.New("x509: no SerialNumber given")
	}

	privKey, ok := privateKey.(crypto.Signer)
	if !ok {
		return nil, errors.New("x509: certificate private key does not implement crypto.Signer")
	}

	hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(privKey.Public(), template.SignatureAlgorithm)
	if err != nil {
		return nil, err
	}

	publicKeyBytes, publicKeyAlgorithm, err := marshalPublicKey(publicKey)
	if err != nil {
		return nil, err
	}

	asn1Issuer, err := subjectBytes(parent)
	if err != nil {
		return nil, err
	}

	asn1Subject, err := subjectBytes(template)
	if err != nil {
		return nil, err
	}

	if !bytes.Equal(asn1Issuer, asn1Subject) && len(parent.SubjectKeyId) > 0 {
		template.AuthorityKeyId = parent.SubjectKeyId
	}

	extensions, err := buildExtensions(template)
	if err != nil {
		return nil, err
	}
	encodedPublicKey := asn1.BitString{BitLength: len(publicKeyBytes) * 8, Bytes: publicKeyBytes}
	c := tbsCertificate{
		Version:            2,
		SerialNumber:       template.SerialNumber,
		SignatureAlgorithm: signatureAlgorithm,
		Issuer:             asn1.RawValue{FullBytes: asn1Issuer},
		Validity:           validity{template.NotBefore.UTC(), template.NotAfter.UTC()},
		Subject:            asn1.RawValue{FullBytes: asn1Subject},
		PublicKey:          publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey},
		Extensions:         extensions,
	}

	tbsCertContents, err := asn1.Marshal(c)
	if err != nil {
		return nil, err
	}

	c.Raw = tbsCertContents

	digest := tbsCertContents
	switch template.SignatureAlgorithm {
	case SM2WithSM3, SM2WithSHA1, SM2WithSHA256:
		break
	default:
		h := hashFunc.New()
		h.Write(tbsCertContents)
		digest = h.Sum(nil)
	}

	var signerOpts crypto.SignerOpts
	signerOpts = hashFunc
	if template.SignatureAlgorithm != 0 && template.SignatureAlgorithm.isRSAPSS() {
		signerOpts = &rsa.PSSOptions{
			SaltLength: rsa.PSSSaltLengthEqualsHash,
			Hash:       crypto.Hash(hashFunc),
		}
	}

	var signature []byte
	signature, err = privKey.Sign(rand.Reader, digest, signerOpts)
	if err != nil {
		return nil, err
	}
	der, err := asn1.Marshal(certificate{
		nil,
		c,
		signatureAlgorithm,
		asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
	})
	return der, err
}

func CreateCertificateToPem(template, parent *Certificate, pubKey *sm2.PublicKey, privKey *sm2.PrivateKey) ([]byte, error) {
	der, err := CreateCertificate(template, parent, pubKey, privKey)
	if err != nil {
		return nil, err
	}
	block := &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: der,
	}
	certPem := pem.EncodeToMemory(block)
	return certPem, nil
}
<commit_msg>refactor: Add function description of CreateCertificate<commit_after>package x509

import (
	"bytes"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"encoding/asn1"
	"encoding/pem"
	"errors"

	"github.com/Hyperledger-TWGC/tjfoc-gm/sm2"
)

func ReadPrivateKeyFromMem(privateKeyPem []byte, pwd []byte) (*sm2.PrivateKey, error) {
	var block *pem.Block
	block, _ = pem.Decode(privateKeyPem)
	if block == nil {
		return nil, errors.New("failed to decode private key")
	}
	priv, err := ParsePKCS8PrivateKey(block.Bytes, pwd)
	return priv, err
}

func WritePrivateKeyToMem(key *sm2.PrivateKey, pwd []byte) ([]byte, error) {
	var block *pem.Block
	der, err := MarshalSm2PrivateKey(key, pwd) //Convert private key to DER format
	if err != nil {
		return nil, err
	}
	if pwd != nil {
		block = &pem.Block{
			Type:  "ENCRYPTED PRIVATE KEY",
			Bytes: der,
		}
	} else {
		block = &pem.Block{
			Type:  "PRIVATE KEY",
			Bytes: der,
		}
	}
	certPem := pem.EncodeToMemory(block)
	return certPem, nil
}

func ReadPublicKeyFromMem(publicKeyPem []byte) (*sm2.PublicKey, error) {
	block, _ := pem.Decode(publicKeyPem)
	if block == nil || block.Type != "PUBLIC KEY" {
		return nil, errors.New("failed to decode public key")
	}
	return ParseSm2PublicKey(block.Bytes)
}

func WritePublicKeyToMem(key *sm2.PublicKey) ([]byte, error) {
	der, err := MarshalSm2PublicKey(key) //Convert publick key to DER format
	if err != nil {
		return nil, err
	}
	block := &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: der,
	}
	certPem := pem.EncodeToMemory(block)
	return certPem, nil
}

func ReadCertificateRequestFromPem(certPem []byte) (*CertificateRequest, error) {
	block, _ := pem.Decode(certPem)
	if block == nil {
		return nil, errors.New("failed to decode certificate request")
	}
	return ParseCertificateRequest(block.Bytes)
}

func CreateCertificateRequestToPem(template *CertificateRequest, privKey *sm2.PrivateKey) ([]byte, error) {
	der, err := CreateCertificateRequest(rand.Reader, template, privKey)
	if err != nil {
		return nil, err
	}
	block := &pem.Block{
		Type:  "CERTIFICATE REQUEST",
		Bytes: der,
	}
	certPem := pem.EncodeToMemory(block)
	return certPem, nil
}

func ReadCertificateFromPem(certPem []byte) (*Certificate, error) {
	block, _ := pem.Decode(certPem)
	if block == nil {
		return nil, errors.New("failed to decode certificate request")
	}
	return ParseCertificate(block.Bytes)
}

// CreateCertificate creates a new certificate based on a template. The
// following members of template are used: SerialNumber, Subject, NotBefore,
// NotAfter, KeyUsage, ExtKeyUsage, UnknownExtKeyUsage, BasicConstraintsValid,
// IsCA, MaxPathLen, SubjectKeyId, DNSNames, PermittedDNSDomainsCritical,
// PermittedDNSDomains, SignatureAlgorithm.
//
// The certificate is signed by parent. If parent is equal to template then the
// certificate is self-signed. The parameter pub is the public key of the
// signee and priv is the private key of the signer.
//
// The returned slice is the certificate in DER encoding.
//
// All keys types that are implemented via crypto.Signer are supported (This
// includes *rsa.PublicKey and *ecdsa.PublicKey.)
func CreateCertificate(template, parent *Certificate, publicKey, privateKey interface{}) ([]byte, error) {
	if template.SerialNumber == nil {
		return nil, errors.New("x509: no SerialNumber given")
	}

	privKey, ok := privateKey.(crypto.Signer)
	if !ok {
		return nil, errors.New("x509: certificate private key does not implement crypto.Signer")
	}

	hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(privKey.Public(), template.SignatureAlgorithm)
	if err != nil {
		return nil, err
	}

	publicKeyBytes, publicKeyAlgorithm, err := marshalPublicKey(publicKey)
	if err != nil {
		return nil, err
	}

	asn1Issuer, err := subjectBytes(parent)
	if err != nil {
		return nil, err
	}

	asn1Subject, err := subjectBytes(template)
	if err != nil {
		return nil, err
	}

	if !bytes.Equal(asn1Issuer, asn1Subject) && len(parent.SubjectKeyId) > 0 {
		template.AuthorityKeyId = parent.SubjectKeyId
	}

	extensions, err := buildExtensions(template)
	if err != nil {
		return nil, err
	}
	encodedPublicKey := asn1.BitString{BitLength: len(publicKeyBytes) * 8, Bytes: publicKeyBytes}
	c := tbsCertificate{
		Version:            2,
		SerialNumber:       template.SerialNumber,
		SignatureAlgorithm: signatureAlgorithm,
		Issuer:             asn1.RawValue{FullBytes: asn1Issuer},
		Validity:           validity{template.NotBefore.UTC(), template.NotAfter.UTC()},
		Subject:            asn1.RawValue{FullBytes: asn1Subject},
		PublicKey:          publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey},
		Extensions:         extensions,
	}

	tbsCertContents, err := asn1.Marshal(c)
	if err != nil {
		return nil, err
	}

	c.Raw = tbsCertContents

	digest := tbsCertContents
	switch template.SignatureAlgorithm {
	case SM2WithSM3, SM2WithSHA1, SM2WithSHA256:
		break
	default:
		h := hashFunc.New()
		h.Write(tbsCertContents)
		digest = h.Sum(nil)
	}

	var signerOpts crypto.SignerOpts
	signerOpts = hashFunc
	if template.SignatureAlgorithm != 0 && template.SignatureAlgorithm.isRSAPSS() {
		signerOpts = &rsa.PSSOptions{
			SaltLength: rsa.PSSSaltLengthEqualsHash,
			Hash:       crypto.Hash(hashFunc),
		}
	}

	var signature []byte
	signature, err = privKey.Sign(rand.Reader, digest, signerOpts)
	if err != nil {
		return nil, err
	}
	return asn1.Marshal(certificate{
		nil,
		c,
		signatureAlgorithm,
		asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
	})
}

// CreateCertificateToPem creates a new certificate based on a template and
// encodes it to PEM format. It uses CreateCertificate to create certificate
// and returns its PEM format.
func CreateCertificateToPem(template, parent *Certificate, pubKey *sm2.PublicKey, privKey *sm2.PrivateKey) ([]byte, error) {
	der, err := CreateCertificate(template, parent, pubKey, privKey)
	if err != nil {
		return nil, err
	}
	block := &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: der,
	}
	certPem := pem.EncodeToMemory(block)
	return certPem, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"syscall"

	"github.com/dickeyxxx/gode"
	"github.com/dickeyxxx/golock"
)

// Plugin represents a javascript plugin
type Plugin struct {
	Name     string     `json:"name"`
	Topics   TopicSet   `json:"topics"`
	Topic    *Topic     `json:"topic"`
	Commands CommandSet `json:"commands"`
}

var node = gode.NewClient(AppDir)

func init() {
	node.Registry = "https://d3nfsbmspisrno.cloudfront.net"
	node.NodeVersion = "2.2.0"
	node.NpmVersion = "2.11.0"
}

// SetupNode sets up node and npm in ~/.heroku
func SetupNode() {
	if !node.IsSetup() {
		LogIfError(golock.Lock(updateLockPath))
		defer golock.Unlock(updateLockPath)
		if node.IsSetup() {
			return
		}
		Debugln("setting up iojs", node.NodeVersion)
		ExitIfError(node.Setup())
		clearOldNodeInstalls()
		Debugln("done setting up iojs")
	}
}

func clearOldNodeInstalls() {
	files, _ := ioutil.ReadDir(AppDir)
	for _, f := range files {
		name := f.Name()
		if name != node.NodeBase() && strings.HasPrefix(name, "iojs-v") {
			LogIfError(os.RemoveAll(filepath.Join(AppDir, name)))
		}
	}
}

// LoadPlugins loads the topics and commands from the JavaScript plugins into the CLI
func (cli *Cli) LoadPlugins(plugins []Plugin) {
	for _, plugin := range plugins {
		for _, topic := range plugin.Topics {
			cli.AddTopic(topic)
		}
		if plugin.Topic != nil {
			cli.AddTopic(plugin.Topic)
		}
		for _, command := range plugin.Commands {
			if !cli.AddCommand(command) {
				Errf("WARNING: command %s has already been defined\n", command)
			}
		}
	}
}

var pluginsTopic = &Topic{
	Name:        "plugins",
	Description: "manage plugins",
}

var pluginsInstallCmd = &Command{
	Topic:       "plugins",
	Command:     "install",
	Hidden:      true,
	Args:        []Arg{{Name: "name"}},
	Description: "Installs a plugin into the CLI",
	Help: `Install a Heroku plugin

  Example:
  $ heroku plugins:install dickeyxxx/heroku-production-status`,

	Run: func(ctx *Context) {
		name := ctx.Args.(map[string]string)["name"]
		if len(name) == 0 {
			Errln("Must specify a plugin name")
			return
		}
		Errf("Installing plugin %s... ", name)
		err := node.InstallPackage(name)
		ExitIfError(err)
		plugin := getPlugin(name, false)
		if plugin == nil || len(plugin.Commands) == 0 {
			Err("\nThis does not appear to be a Heroku plugin, uninstalling... ")
			ExitIfError(node.RemovePackage(name))
		}
		ClearPluginCache()
		WritePluginCache(GetPlugins())
		Errln("done")
	},
}

var pluginsLinkCmd = &Command{
	Topic:       "plugins",
	Command:     "link",
	Description: "Links a local plugin into CLI",
	Args:        []Arg{{Name: "path", Optional: true}},
	Help: `Links a local plugin into CLI.
	This is useful when developing plugins locally.
	It simply symlinks the specified path into ~/.heroku/node_modules

  Example:
	$ heroku plugins:link .`,

	Run: func(ctx *Context) {
		path := ctx.Args.(map[string]string)["path"]
		if path == "" {
			path = "."
		}
		path, err := filepath.Abs(path)
		if err != nil {
			panic(err)
		}
		if _, err := os.Stat(path); err != nil {
			panic(err)
		}
		name := filepath.Base(path)
		newPath := filepath.Join(ctx.HerokuDir, "node_modules", name)
		os.Remove(newPath)
		os.RemoveAll(newPath)
		err = os.Symlink(path, newPath)
		if err != nil {
			panic(err)
		}
		plugin := getPlugin(name, false)
		if plugin == nil || len(plugin.Commands) == 0 {
			Errln(name + " does not appear to be a Heroku plugin")
			if err := os.Remove(newPath); err != nil {
				panic(err)
			}
			return
		}
		if name != plugin.Name {
			path = newPath
			newPath = filepath.Join(ctx.HerokuDir, "node_modules", plugin.Name)
			os.Remove(newPath)
			os.RemoveAll(newPath)
			os.Rename(path, newPath)
		}
		Println("symlinked", plugin.Name)
		Err("Updating plugin cache... ")
		ClearPluginCache()
		WritePluginCache(GetPlugins())
		Errln("done")
	},
}

var pluginsUninstallCmd = &Command{
	Topic:       "plugins",
	Command:     "uninstall",
	Hidden:      true,
	Args:        []Arg{{Name: "name"}},
	Description: "Uninstalls a plugin from the CLI",
	Help: `Uninstalls a Heroku plugin

  Example:
  $ heroku plugins:uninstall heroku-production-status`,

	Run: func(ctx *Context) {
		name := ctx.Args.(map[string]string)["name"]
		Errf("Uninstalling plugin %s... ", name)
		err := node.RemovePackage(name)
		ExitIfError(err)
		Errln("done")
	},
}

var pluginsListCmd = &Command{
	Topic:       "plugins",
	Hidden:      true,
	Description: "Lists the installed plugins",
	Help: `Lists installed plugins

  Example:
  $ heroku plugins`,

	Run: func(ctx *Context) {
		packages, err := node.Packages()
		if err != nil {
			panic(err)
		}
		for _, pkg := range packages {
			Println(pkg.Name, pkg.Version)
		}
	},
}

func runFn(module, topic, command string) func(ctx *Context) {
	return func(ctx *Context) {
		lockfile := updateLockPath + "." + module
		if exists, _ := fileExists(lockfile); exists {
			golock.Lock(lockfile)
			golock.Unlock(lockfile)
		}
		ctx.Dev = isPluginSymlinked(module)
		ctx.Version = ctx.Version + " " + module + " iojs-v" + node.NodeVersion
		ctxJSON, err := json.Marshal(ctx)
		if err != nil {
			panic(err)
		}
		script := fmt.Sprintf(`
		'use strict';
		var moduleName = '%s';
		var topic = '%s';
		var command = '%s';
		var ctx = %s;
		var logPath = %s;
		process.chdir(ctx.cwd);
		function repair (name) {
			console.error('Attempting to repair ' + name + '...');
			require('child_process')
			.spawnSync('heroku', ['plugins:install', name],
			{stdio: [0,1,2]});
			console.error('Repair complete. Try running your command again.');
		}
		if (!ctx.dev) {
			process.on('uncaughtException', function (err) {
				console.error(' !   Error in ' + moduleName + ':')
				if (err.message) {
					console.error(' !   ' + err.message);
					if (err.message.indexOf('Cannot find module') != -1) {
						repair(moduleName);
						process.exit(1);
					}
				} else {
					console.error(' !   ' + err);
				}
				if (err.stack) {
					var fs = require('fs');
					var log = function (line) {
						var d = new Date().toISOString()
						.replace(/T/, ' ')
						.replace(/-/g, '/')
						.replace(/\..+/, '');
						fs.appendFileSync(logPath, d + ' ' + line + '\n');
					}
					log('Error during ' + topic + ':' + command);
					log(err.stack);
					console.error(' !   See ' + logPath + ' for more info.');
				}
				process.exit(1);
			});
		}
		if (command === '') { command = null }
		var module = require(moduleName);
		var cmd = module.commands.filter(function (c) {
			return c.topic === topic && c.command == command;
		})[0];
		cmd.run(ctx);`, module, topic, command, ctxJSON, strconv.Quote(ErrLogPath))

		// swallow sigint since the plugin will handle it
		swallowSignal(os.Interrupt)

		cmd := node.RunScript(script)
		if ctx.Flags["debugger"] == true {
			cmd = node.DebugScript(script)
		}
		os.Chdir(cmd.Dir)
		execBin(cmd.Path, cmd.Args)
	}
}

func execBin(bin string, args []string) {
	if runtime.GOOS == "windows" {
		cmd := exec.Command(bin, args[1:]...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			os.Exit(getExitCode(err))
		}
	} else {
		if err := syscall.Exec(bin, args, os.Environ()); err != nil {
			panic(err)
		}
	}
}

func swallowSignal(s os.Signal) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, s)
	go func() {
		<-c
	}()
}

func getExitCode(err error) int {
	switch e := err.(type) {
	case *exec.ExitError:
		status, ok := e.Sys().(syscall.WaitStatus)
		if !ok {
			panic(err)
		}
		return status.ExitStatus()
	default:
		panic(err)
	}
}

func getPlugin(name string, attemptReinstall bool) *Plugin {
	script := `
	var plugin = require('` + name + `');
	plugin.name = require('` + name + `/package.json').name;
	console.log(JSON.stringify(plugin))`
	cmd := node.RunScript(script)
	output, err := cmd.CombinedOutput()
	if err != nil {
		if attemptReinstall && strings.Contains(string(output), "Error: Cannot find module") {
			Errf("Error reading plugin %s. Reinstalling... ", name)
			if err := node.InstallPackage(name); err != nil {
				panic(errors.New(string(output)))
			}
			Errln("done")
			return getPlugin(name, false)
		}
		Errf("Error reading plugin: %s. See %s for more information.\n", name, ErrLogPath)
		Logln(err, "\n", string(output))
		return nil
	}
	var plugin Plugin
	json.Unmarshal([]byte(output), &plugin)
	return &plugin
}

// GetPlugins goes through all the node plugins and returns them in Go stucts
func GetPlugins() []Plugin {
	cache := FetchPluginCache()
	names := PluginNames()
	plugins := make([]Plugin, 0, len(names))
	for _, name := range names {
		plugin := cache[name]
		if plugin == nil {
			plugin = getPlugin(name, true)
		}
		if plugin != nil {
			for _, command := range plugin.Commands {
				command.Plugin = name
				command.Run = runFn(name, command.Topic, command.Command)
				command.Help = strings.TrimSpace(command.Help)
			}
			plugins = append(plugins, *plugin)
		}
	}
	return plugins
}

// PluginNames just lists the files in ~/.heroku/node_modules
func PluginNames() []string {
	files, _ := ioutil.ReadDir(filepath.Join(AppDir, "node_modules"))
	names := make([]string, 0, len(files))
	for _, f := range files {
		if !ignorePlugin(f.Name()) {
			names = append(names, f.Name())
		}
	}
	return names
}

func ignorePlugin(plugin string) bool {
	ignored := []string{".bin", "node-inspector"}
	for _, p := range ignored {
		if plugin == p {
			return true
		}
	}
	return false
}

func isPluginSymlinked(plugin string) bool {
	path := filepath.Join(AppDir, "node_modules", plugin)
	fi, err := os.Lstat(path)
	if err != nil {
		panic(err)
	}
	return fi.Mode()&os.ModeSymlink != 0
}
<commit_msg>updated iojs to 2.2.1<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"syscall"

	"github.com/dickeyxxx/gode"
	"github.com/dickeyxxx/golock"
)

// Plugin represents a javascript plugin
type Plugin struct {
	Name     string     `json:"name"`
	Topics   TopicSet   `json:"topics"`
	Topic    *Topic     `json:"topic"`
	Commands CommandSet `json:"commands"`
}

var node = gode.NewClient(AppDir)

func init() {
	node.Registry = "https://d3nfsbmspisrno.cloudfront.net"
	node.NodeVersion = "2.2.1"
	node.NpmVersion = "2.11.0"
}

// SetupNode sets up node and npm in ~/.heroku
func SetupNode() {
	if !node.IsSetup() {
		LogIfError(golock.Lock(updateLockPath))
		defer golock.Unlock(updateLockPath)
		if node.IsSetup() {
			return
		}
		Debugln("setting up iojs", node.NodeVersion)
		ExitIfError(node.Setup())
		clearOldNodeInstalls()
		Debugln("done setting up iojs")
	}
}

func clearOldNodeInstalls() {
	files, _ := ioutil.ReadDir(AppDir)
	for _, f := range files {
		name := f.Name()
		if name != node.NodeBase() && strings.HasPrefix(name, "iojs-v") {
			LogIfError(os.RemoveAll(filepath.Join(AppDir, name)))
		}
	}
}

// LoadPlugins loads the topics and commands from the JavaScript plugins into the CLI
func (cli *Cli) LoadPlugins(plugins []Plugin) {
	for _, plugin := range plugins {
		for _, topic := range plugin.Topics {
			cli.AddTopic(topic)
		}
		if plugin.Topic != nil {
			cli.AddTopic(plugin.Topic)
		}
		for _, command := range plugin.Commands {
			if !cli.AddCommand(command) {
				Errf("WARNING: command %s has already been defined\n", command)
			}
		}
	}
}

var pluginsTopic = &Topic{
	Name:        "plugins",
	Description: "manage plugins",
}

var pluginsInstallCmd = &Command{
	Topic:       "plugins",
	Command:     "install",
	Hidden:      true,
	Args:        []Arg{{Name: "name"}},
	Description: "Installs a plugin into the CLI",
	Help: `Install a Heroku plugin

  Example:
  $ heroku plugins:install dickeyxxx/heroku-production-status`,

	Run: func(ctx *Context) {
		name := ctx.Args.(map[string]string)["name"]
		if len(name) == 0 {
			Errln("Must specify a plugin name")
			return
		}
		Errf("Installing plugin %s... ", name)
		err := node.InstallPackage(name)
		ExitIfError(err)
		plugin := getPlugin(name, false)
		if plugin == nil || len(plugin.Commands) == 0 {
			Err("\nThis does not appear to be a Heroku plugin, uninstalling... ")
			ExitIfError(node.RemovePackage(name))
		}
		ClearPluginCache()
		WritePluginCache(GetPlugins())
		Errln("done")
	},
}

var pluginsLinkCmd = &Command{
	Topic:       "plugins",
	Command:     "link",
	Description: "Links a local plugin into CLI",
	Args:        []Arg{{Name: "path", Optional: true}},
	Help: `Links a local plugin into CLI.
	This is useful when developing plugins locally.
	It simply symlinks the specified path into ~/.heroku/node_modules

  Example:
	$ heroku plugins:link .`,

	Run: func(ctx *Context) {
		path := ctx.Args.(map[string]string)["path"]
		if path == "" {
			path = "."
		}
		path, err := filepath.Abs(path)
		if err != nil {
			panic(err)
		}
		if _, err := os.Stat(path); err != nil {
			panic(err)
		}
		name := filepath.Base(path)
		newPath := filepath.Join(ctx.HerokuDir, "node_modules", name)
		os.Remove(newPath)
		os.RemoveAll(newPath)
		err = os.Symlink(path, newPath)
		if err != nil {
			panic(err)
		}
		plugin := getPlugin(name, false)
		if plugin == nil || len(plugin.Commands) == 0 {
			Errln(name + " does not appear to be a Heroku plugin")
			if err := os.Remove(newPath); err != nil {
				panic(err)
			}
			return
		}
		if name != plugin.Name {
			path = newPath
			newPath = filepath.Join(ctx.HerokuDir, "node_modules", plugin.Name)
			os.Remove(newPath)
			os.RemoveAll(newPath)
			os.Rename(path, newPath)
		}
		Println("symlinked", plugin.Name)
		Err("Updating plugin cache... ")
		ClearPluginCache()
		WritePluginCache(GetPlugins())
		Errln("done")
	},
}

var pluginsUninstallCmd = &Command{
	Topic:       "plugins",
	Command:     "uninstall",
	Hidden:      true,
	Args:        []Arg{{Name: "name"}},
	Description: "Uninstalls a plugin from the CLI",
	Help: `Uninstalls a Heroku plugin

  Example:
  $ heroku plugins:uninstall heroku-production-status`,

	Run: func(ctx *Context) {
		name := ctx.Args.(map[string]string)["name"]
		Errf("Uninstalling plugin %s... ", name)
		err := node.RemovePackage(name)
		ExitIfError(err)
		Errln("done")
	},
}

var pluginsListCmd = &Command{
	Topic:       "plugins",
	Hidden:      true,
	Description: "Lists the installed plugins",
	Help: `Lists installed plugins

  Example:
  $ heroku plugins`,

	Run: func(ctx *Context) {
		packages, err := node.Packages()
		if err != nil {
			panic(err)
		}
		for _, pkg := range packages {
			Println(pkg.Name, pkg.Version)
		}
	},
}

func runFn(module, topic, command string) func(ctx *Context) {
	return func(ctx *Context) {
		lockfile := updateLockPath + "." + module
		if exists, _ := fileExists(lockfile); exists {
			golock.Lock(lockfile)
			golock.Unlock(lockfile)
		}
		ctx.Dev = isPluginSymlinked(module)
		ctx.Version = ctx.Version + " " + module + " iojs-v" + node.NodeVersion
		ctxJSON, err := json.Marshal(ctx)
		if err != nil {
			panic(err)
		}
		script := fmt.Sprintf(`
		'use strict';
		var moduleName = '%s';
		var topic = '%s';
		var command = '%s';
		var ctx = %s;
		var logPath = %s;
		process.chdir(ctx.cwd);
		function repair (name) {
			console.error('Attempting to repair ' + name + '...');
			require('child_process')
			.spawnSync('heroku', ['plugins:install', name],
			{stdio: [0,1,2]});
			console.error('Repair complete. Try running your command again.');
		}
		if (!ctx.dev) {
			process.on('uncaughtException', function (err) {
				console.error(' !   Error in ' + moduleName + ':')
				if (err.message) {
					console.error(' !   ' + err.message);
					if (err.message.indexOf('Cannot find module') != -1) {
						repair(moduleName);
						process.exit(1);
					}
				} else {
					console.error(' !   ' + err);
				}
				if (err.stack) {
					var fs = require('fs');
					var log = function (line) {
						var d = new Date().toISOString()
						.replace(/T/, ' ')
						.replace(/-/g, '/')
						.replace(/\..+/, '');
						fs.appendFileSync(logPath, d + ' ' + line + '\n');
					}
					log('Error during ' + topic + ':' + command);
					log(err.stack);
					console.error(' !   See ' + logPath + ' for more info.');
				}
				process.exit(1);
			});
		}
		if (command === '') { command = null }
		var module = require(moduleName);
		var cmd = module.commands.filter(function (c) {
			return c.topic === topic && c.command == command;
		})[0];
		cmd.run(ctx);`, module, topic, command, ctxJSON, strconv.Quote(ErrLogPath))

		// swallow sigint since the plugin will handle it
		swallowSignal(os.Interrupt)

		cmd := node.RunScript(script)
		if ctx.Flags["debugger"] == true {
			cmd = node.DebugScript(script)
		}
		os.Chdir(cmd.Dir)
		execBin(cmd.Path, cmd.Args)
	}
}

func execBin(bin string, args []string) {
	if runtime.GOOS == "windows" {
		cmd := exec.Command(bin, args[1:]...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			os.Exit(getExitCode(err))
		}
	} else {
		if err := syscall.Exec(bin, args, os.Environ()); err != nil {
			panic(err)
		}
	}
}

func swallowSignal(s os.Signal) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, s)
	go func() {
		<-c
	}()
}

func getExitCode(err error) int {
	switch e := err.(type) {
	case *exec.ExitError:
		status, ok := e.Sys().(syscall.WaitStatus)
		if !ok {
			panic(err)
		}
		return status.ExitStatus()
	default:
		panic(err)
	}
}

func getPlugin(name string, attemptReinstall bool) *Plugin {
	script := `
	var plugin = require('` + name + `');
	plugin.name = require('` + name + `/package.json').name;
	console.log(JSON.stringify(plugin))`
	cmd := node.RunScript(script)
	output, err := cmd.CombinedOutput()
	if err != nil {
		if attemptReinstall && strings.Contains(string(output), "Error: Cannot find module") {
			Errf("Error reading plugin %s. Reinstalling... ", name)
			if err := node.InstallPackage(name); err != nil {
				panic(errors.New(string(output)))
			}
			Errln("done")
			return getPlugin(name, false)
		}
		Errf("Error reading plugin: %s. See %s for more information.\n", name, ErrLogPath)
		Logln(err, "\n", string(output))
		return nil
	}
	var plugin Plugin
	json.Unmarshal([]byte(output), &plugin)
	return &plugin
}

// GetPlugins goes through all the node plugins and returns them in Go stucts
func GetPlugins() []Plugin {
	cache := FetchPluginCache()
	names := PluginNames()
	plugins := make([]Plugin, 0, len(names))
	for _, name := range names {
		plugin := cache[name]
		if plugin == nil {
			plugin = getPlugin(name, true)
		}
		if plugin != nil {
			for _, command := range plugin.Commands {
				command.Plugin = name
				command.Run = runFn(name, command.Topic, command.Command)
				command.Help = strings.TrimSpace(command.Help)
			}
			plugins = append(plugins, *plugin)
		}
	}
	return plugins
}

// PluginNames just lists the files in ~/.heroku/node_modules
func PluginNames() []string {
	files, _ := ioutil.ReadDir(filepath.Join(AppDir, "node_modules"))
	names := make([]string, 0, len(files))
	for _, f := range files {
		if !ignorePlugin(f.Name()) {
			names = append(names, f.Name())
		}
	}
	return names
}

func ignorePlugin(plugin string) bool {
	ignored := []string{".bin", "node-inspector"}
	for _, p := range ignored {
		if plugin == p {
			return true
		}
	}
	return false
}

func isPluginSymlinked(plugin string) bool {
	path := filepath.Join(AppDir, "node_modules", plugin)
	fi, err := os.Lstat(path)
	if err != nil {
		panic(err)
	}
	return fi.Mode()&os.ModeSymlink != 0
}
<|endoftext|>


<commit_before>package main

import (
	"sync"

	"github.com/prometheus/common/model"

	"github.com/prometheus/alertmanager/config"
	"github.com/prometheus/alertmanager/provider"
	"github.com/prometheus/alertmanager/types"
)

type Inhibitor struct {
	alerts provider.Alerts
	rules  []*InhibitRule

	mtx sync.RWMutex
}

func (ih *Inhibitor) Mutes(lset model.LabelSet) bool {
	ih.mtx.RLock()
	defer ih.mtx.RUnlock()

	alerts, err := ih.alerts.All()
	if err != nil {
		// TODO(fabxc): log error.
		return false
	}

	for _, alert := range alerts {
		if alert.Resolved() {
			continue
		}
		for _, rule := range ih.rules {
			if rule.Mutes(alert.Labels, lset) {
				return true
			}
		}
	}
	return false
}

func (ih *Inhibitor) ApplyConfig(conf *config.Config) {
	ih.mtx.Lock()
	defer ih.mtx.Unlock()

	ih.rules = []*InhibitRule{}
	for _, cr := range conf.InhibitRules {
		ih.rules = append(ih.rules, NewInhibitRule(cr))
	}
}

// An InhibitRule specifies that a class of (source) alerts should inhibit
// notifications for another class of (target) alerts if all specified matching
// labels are equal between the two alerts. This may be used to inhibit alerts
// from sending notifications if their meaning is logically a subset of a
// higher-level alert.
type InhibitRule struct {
	// The set of Filters which define the group of source alerts (which inhibit
	// the target alerts).
	SourceMatchers types.Matchers
	// The set of Filters which define the group of target alerts (which are
	// inhibited by the source alerts).
	TargetMatchers types.Matchers
	// A set of label names whose label values need to be identical in source and
	// target alerts in order for the inhibition to take effect.
	Equal map[model.LabelName]struct{}
}

func NewInhibitRule(cr *config.InhibitRule) *InhibitRule {
	var (
		sourcem types.Matchers
		targetm types.Matchers
		equal   map[model.LabelName]struct{}
	)

	for ln, lv := range cr.SourceMatch {
		sourcem = append(sourcem, types.NewMatcher(model.LabelName(ln), lv))
	}
	for ln, lv := range cr.SourceMatchRE {
		m, err := types.NewRegexMatcher(model.LabelName(ln), lv.String())
		if err != nil {
			// Must have been sanitized during config validation.
			panic(err)
		}
		sourcem = append(sourcem, m)
	}

	for ln, lv := range cr.TargetMatch {
		targetm = append(targetm, types.NewMatcher(model.LabelName(ln), lv))
	}
	for ln, lv := range cr.TargetMatchRE {
		m, err := types.NewRegexMatcher(model.LabelName(ln), lv.String())
		if err != nil {
			// Must have been sanitized during config validation.
			panic(err)
		}
		targetm = append(targetm, m)
	}

	for _, ln := range cr.Equal {
		equal[ln] = struct{}{}
	}

	return &InhibitRule{
		SourceMatchers: sourcem,
		TargetMatchers: targetm,
		Equal:          equal,
	}
}

func (r *InhibitRule) Mutes(source, target model.LabelSet) bool {
	return r.TargetMatchers.Match(target) && r.SourceMatchers.Match(source)
}
<commit_msg>Make inhibitor consider equality labels<commit_after>package main

import (
	"sync"

	"github.com/prometheus/common/model"

	"github.com/prometheus/alertmanager/config"
	"github.com/prometheus/alertmanager/provider"
	"github.com/prometheus/alertmanager/types"
)

type Inhibitor struct {
	alerts provider.Alerts
	rules  []*InhibitRule

	mtx sync.RWMutex
}

func (ih *Inhibitor) Mutes(lset model.LabelSet) bool {
	ih.mtx.RLock()
	defer ih.mtx.RUnlock()

	alerts, err := ih.alerts.All()
	if err != nil {
		// TODO(fabxc): log error.
		return false
	}

	for _, alert := range alerts {
		if alert.Resolved() {
			continue
		}
		for _, rule := range ih.rules {
			if rule.Mutes(alert.Labels, lset) {
				return true
			}
		}
	}
	return false
}

func (ih *Inhibitor) ApplyConfig(conf *config.Config) {
	ih.mtx.Lock()
	defer ih.mtx.Unlock()

	ih.rules = []*InhibitRule{}
	for _, cr := range conf.InhibitRules {
		ih.rules = append(ih.rules, NewInhibitRule(cr))
	}
}

// An InhibitRule specifies that a class of (source) alerts should inhibit
// notifications for another class of (target) alerts if all specified matching
// labels are equal between the two alerts. This may be used to inhibit alerts
// from sending notifications if their meaning is logically a subset of a
// higher-level alert.
type InhibitRule struct {
	// The set of Filters which define the group of source alerts (which inhibit
	// the target alerts).
	SourceMatchers types.Matchers
	// The set of Filters which define the group of target alerts (which are
	// inhibited by the source alerts).
	TargetMatchers types.Matchers
	// A set of label names whose label values need to be identical in source and
	// target alerts in order for the inhibition to take effect.
	Equal map[model.LabelName]struct{}
}

func NewInhibitRule(cr *config.InhibitRule) *InhibitRule {
	var (
		sourcem types.Matchers
		targetm types.Matchers
		equal   map[model.LabelName]struct{}
	)

	for ln, lv := range cr.SourceMatch {
		sourcem = append(sourcem, types.NewMatcher(model.LabelName(ln), lv))
	}
	for ln, lv := range cr.SourceMatchRE {
		m, err := types.NewRegexMatcher(model.LabelName(ln), lv.String())
		if err != nil {
			// Must have been sanitized during config validation.
			panic(err)
		}
		sourcem = append(sourcem, m)
	}

	for ln, lv := range cr.TargetMatch {
		targetm = append(targetm, types.NewMatcher(model.LabelName(ln), lv))
	}
	for ln, lv := range cr.TargetMatchRE {
		m, err := types.NewRegexMatcher(model.LabelName(ln), lv.String())
		if err != nil {
			// Must have been sanitized during config validation.
			panic(err)
		}
		targetm = append(targetm, m)
	}

	for _, ln := range cr.Equal {
		equal[ln] = struct{}{}
	}

	return &InhibitRule{
		SourceMatchers: sourcem,
		TargetMatchers: targetm,
		Equal:          equal,
	}
}

func (r *InhibitRule) Mutes(source, target model.LabelSet) bool {
	if r.TargetMatchers.Match(target) && r.SourceMatchers.Match(source) {
		for ln := range r.Equal {
			if source[ln] != target[ln] {
				return false
			}
		}
	} else {
		return false
	}

	return true
}
<|endoftext|>


<commit_before>package youtube_dl

import "os/exec"

// Youtube-dl SRT Downloader
type YoutubeDl struct {
	Path string
}

func (youtubedl YoutubeDl) DownloadVideo(id string) error{
	filename := "\"" + youtubedl.Path + "/" + id + ".srt\""
	commandParams := " --write-auto-sub --skip-download -o " + filename + " -- " + id
	commandName := "youtube-dl"
	command := commandName + " " + commandParams
	cmd := exec.Command("bash", "-c", command)
	err := cmd.Run() // waits until the commands runs and finishes
	return err
}

<commit_msg>enabling the download of subtitles to bring autosuggested and others<commit_after>package youtube_dl

import "os/exec"

// Youtube-dl SRT Downloader
type YoutubeDl struct {
	Path string
}

func (youtubedl YoutubeDl) DownloadVideo(id string) error{
	filename := "\"" + youtubedl.Path + "/" + id + ".srt\""
	commandParams := " --write-auto-sub --skip-download --sub-lang en -o " + filename + " -- " + id
	commandName := "youtube-dl"
	command := commandName + " " + commandParams
	cmd := exec.Command("bash", "-c", command)
	err := cmd.Run() // waits until the commands runs and finishes
	return err
}

<|endoftext|>