<commit_before>package appdash

import (
	"errors"
	"fmt"
	"net/url"
	"reflect"
	"time"

	influxDBClient "github.com/influxdb/influxdb/client"
	influxDBServer "github.com/influxdb/influxdb/cmd/influxd/run"
	influxDBModels "github.com/influxdb/influxdb/models"
)

const (
	dbName               string = "appdash" // InfluxDB db name.
	spanMeasurementName  string = "spans"   // InfluxDB container name for trace spans.
	defaultTracesPerPage int    = 10        // Default number of traces per page.
)

// Compile-time "implements" check.
var _ interface {
	Store
	Queryer
} = (*InfluxDBStore)(nil)

// TODO: should be a constant.
var zeroID = fmt.Sprintf("%016x", uint64(0))

type InfluxDBStore struct {
	con           *influxDBClient.Client // InfluxDB client connection.
	server        *influxDBServer.Server // InfluxDB API server.
	tracesPerPage int                    // Number of traces per page.
}

func (in *InfluxDBStore) Collect(id SpanID, anns ...Annotation) error {
	// Current strategy is to remove existing span and save new one
	// instead of updating the existing one.
	// TODO: explore a more efficient alternative strategy.
	if err := in.removeSpanIfExists(id); err != nil {
		return err
	}

	// trace_id, span_id & parent_id are set as tags
	// because InfluxDB tags are indexed & those values
	// are uselater on queries.
	tags := map[string]string{
		"trace_id":  id.Trace.String(),
		"span_id":   id.Span.String(),
		"parent_id": id.Parent.String(),
	}

	// Saving annotations as InfluxDB measurement spans fields
	// which are not indexed.
	fields := make(map[string]interface{}, len(anns))
	for _, ann := range anns {
		fields[ann.Key] = string(ann.Value)
	}

	// InfluxDB point represents a single span.
	pts := []influxDBClient.Point{
		influxDBClient.Point{
			Measurement: spanMeasurementName,
			Tags:        tags,   // indexed metadata.
			Fields:      fields, // non-indexed metadata.
			Time:        time.Now().UTC(),
		},
	}
	bps := influxDBClient.BatchPoints{
		Points:          pts,
		Database:        dbName,
		RetentionPolicy: "default",
	}
	_, err := in.con.Write(bps)
	if err != nil {
		return err
	}
	return nil
}

func (in *InfluxDBStore) Trace(id ID) (*Trace, error) {
	trace := &Trace{}

	// GROUP BY * -> meaning group by all tags(trace_id, span_id & parent_id)
	// grouping by all tags includes those and it's values on the query response.
	q := fmt.Sprintf("SELECT * FROM spans WHERE trace_id='%s' GROUP BY *", id)
	result, err := in.executeOneQuery(q)
	if err != nil {
		return nil, err
	}

	// result.Series -> A slice containing all the spans.
	if len(result.Series) == 0 {
		return nil, errors.New("trace not found")
	}
	var isRootSpan bool

	// Iterate over series(spans) to create trace children's & set trace fields.
	for _, s := range result.Series {
		span, err := newSpanFromRow(&s)
		if err != nil {
			return nil, err
		}
		if span.ID.Parent == 0 && isRootSpan {
			// Must be a single root span.
			return nil, errors.New("unexpected multiple root spans")
		}
		if span.ID.Parent == 0 && !isRootSpan {
			isRootSpan = true
		}
		annotations, err := annotationsFromRow(&s)
		if err != nil {
			return trace, nil
		}
		span.Annotations = *annotations
		if isRootSpan { // root span.
			trace.Span = *span
		} else { // children span.
			trace.Sub = append(trace.Sub, &Trace{Span: *span})
		}
	}
	return trace, nil
}

func (in *InfluxDBStore) Traces() ([]*Trace, error) {
	traces := make([]*Trace, 0)

	// GROUP BY * -> meaning group by all tags(trace_id, span_id & parent_id)
	// grouping by all tags includes those and it's values on the query response.
	rootSpansQuery := fmt.Sprintf("SELECT * FROM spans WHERE parent_id='%s' GROUP BY * LIMIT %d", zeroID, in.tracesPerPage)
	rootSpansResult, err := in.executeOneQuery(rootSpansQuery)
	if err != nil {
		return nil, err
	}

	// result.Series -> A slice containing all the spans.
	if len(rootSpansResult.Series) == 0 {
		return traces, nil
	}

	// Cache to keep track of traces to be returned.
	tracesCache := make(map[ID]*Trace, 0)

	// Iterate over series(spans) to create traces.
	for _, s := range rootSpansResult.Series {
		span, err := newSpanFromRow(&s)
		if err != nil {
			return nil, err
		}
		annotations, err := annotationsFromRow(&s)
		if err != nil {
			return nil, err
		}
		span.Annotations = *annotations
		_, present := tracesCache[span.ID.Trace]
		if !present {
			tracesCache[span.ID.Trace] = &Trace{Span: *span}
		} else {
			return nil, errors.New("duplicated root span")
		}
	}

	// Using 'OR' since 'IN' not supported yet.
	where := `WHERE `
	var i int = 1
	for _, trace := range tracesCache {
		where += fmt.Sprintf("(trace_id='%s' AND parent_id!='%s')", trace.Span.ID.Trace, zeroID)

		// Adds 'OR' except for last iteration.
		if i != len(tracesCache) && len(tracesCache) > 1 {
			where += " OR "
		}
		i += 1
	}

	// Queries for all children spans of the traces to be returned.
	childrenSpansQuery := fmt.Sprintf("SELECT * FROM spans %s GROUP BY *", where)
	childrenSpansResult, err := in.executeOneQuery(childrenSpansQuery)
	if err != nil {
		return nil, err
	}

	// Iterate over series(children spans) to create sub-traces
	// and associates sub-traces with it's parent trace.
	for _, s := range childrenSpansResult.Series {
		span, err := newSpanFromRow(&s)
		if err != nil {
			return nil, err
		}
		annotations, err := annotationsFromRow(&s)
		if err != nil {
			return nil, err
		}
		span.Annotations = *annotations
		trace, present := tracesCache[span.ID.Trace]
		if !present { // Root trace not added.
			return nil, errors.New("parent not found")
		} else { // Root trace already added so append a sub-trace.
			trace.Sub = append(trace.Sub, &Trace{Span: *span})
		}
	}
	for _, trace := range tracesCache {
		traces = append(traces, trace)
	}
	return traces, nil
}

func (in *InfluxDBStore) Close() {
	in.server.Close()
}

func (in *InfluxDBStore) createDBIfNotExists() error {
	// If no errors query execution was successfully - either DB was created or already exists.
	response, err := in.con.Query(influxDBClient.Query{
		Command: fmt.Sprintf("%s %s", "CREATE DATABASE IF NOT EXISTS", dbName),
	})
	if err != nil {
		return err
	}
	if response.Error() != nil {
		return response.Error()
	}
	return nil
}

func (in *InfluxDBStore) executeOneQuery(command string) (*influxDBClient.Result, error) {
	response, err := in.con.Query(influxDBClient.Query{
		Command:  command,
		Database: dbName,
	})
	if err != nil {
		return nil, err
	}
	if response.Error() != nil {
		return nil, response.Error()
	}

	// Expecting one result, since a single query is executed.
	if len(response.Results) != 1 {
		return nil, errors.New("unexpected number of results for an influxdb single query")
	}
	return &response.Results[0], nil
}

func (in *InfluxDBStore) init(server *influxDBServer.Server) error {
	in.server = server
	url, err := url.Parse(fmt.Sprintf("http://%s:%d", influxDBClient.DefaultHost, influxDBClient.DefaultPort))
	if err != nil {
		return err
	}
	con, err := influxDBClient.NewClient(influxDBClient.Config{URL: *url})
	if err != nil {
		return err
	}
	in.con = con
	if err := in.createDBIfNotExists(); err != nil {
		return err
	}
	in.tracesPerPage = defaultTracesPerPage
	return nil
}

func (in *InfluxDBStore) removeSpanIfExists(id SpanID) error {
	cmd := fmt.Sprintf(`
		DROP SERIES FROM spans WHERE trace_id = '%s' AND span_id = '%s' AND parent_id = '%s'
	`, id.Trace.String(), id.Span.String(), id.Parent.String())
	_, err := in.executeOneQuery(cmd)
	if err != nil {
		return err
	}
	return nil
}

func annotationsFromRow(r *influxDBModels.Row) (*Annotations, error) {
	// Actually an influxDBModels.Row represents a single InfluxDB serie.
	// r.Values[n] is a slice containing span's annotation values.
	var fields []interface{}
	if len(r.Values) == 1 {
		fields = r.Values[0]
	}

	// len(r.Values) might be greater than one - meaning there are
	// some spans to drop, see: InfluxDBStore.Collect(...).
	// If so last one is picked.
	if len(r.Values) > 1 {
		fields = r.Values[len(r.Values)-1]
	}
	annotations := make(Annotations, len(fields))

	// Iterates over fields which represent span's annotation values.
	for i, field := range fields {
		// It is safe to do column[0] (eg. 'Server.Request.Method')
		// matches fields[0] (eg. 'GET')
		key := r.Columns[i]
		var value []byte
		switch field.(type) {
		case string:
			value = []byte(field.(string))
		case nil:
		default:
			return nil, fmt.Errorf("unexpected field type: %v", reflect.TypeOf(field))
		}
		a := Annotation{
			Key:   key,
			Value: value,
		}
		annotations = append(annotations, a)
	}
	return &annotations, nil
}

func newSpanFromRow(r *influxDBModels.Row) (*Span, error) {
	span := &Span{}
	traceID, err := ParseID(r.Tags["trace_id"])
	if err != nil {
		return nil, err
	}
	spanID, err := ParseID(r.Tags["span_id"])
	if err != nil {
		return nil, err
	}
	parentID, err := ParseID(r.Tags["parent_id"])
	if err != nil {
		return nil, err
	}
	span.ID = SpanID{
		Trace:  ID(traceID),
		Span:   ID(spanID),
		Parent: ID(parentID),
	}
	return span, nil
}

type InfluxDBStoreConfig struct {
	Server    *influxDBServer.Config
	BuildInfo *influxDBServer.BuildInfo
}

func NewInfluxDBStore(config InfluxDBStoreConfig) (*InfluxDBStore, error) {
	//TODO: add Authentication.
	s, err := influxDBServer.NewServer(config.Server, config.BuildInfo)
	if err != nil {
		return nil, err
	}
	if err := s.Open(); err != nil {
		return nil, err
	}
	var in InfluxDBStore
	if err := in.init(s); err != nil {
		return nil, err
	}
	return &in, nil
}
<commit_msg>improves strategy for replace existing spans on DB<commit_after>package appdash

import (
	"errors"
	"fmt"
	"net/url"
	"reflect"
	"time"

	influxDBClient "github.com/influxdb/influxdb/client"
	influxDBServer "github.com/influxdb/influxdb/cmd/influxd/run"
	influxDBModels "github.com/influxdb/influxdb/models"
)

const (
	dbName               string = "appdash" // InfluxDB db name.
	spanMeasurementName  string = "spans"   // InfluxDB container name for trace spans.
	defaultTracesPerPage int    = 10        // Default number of traces per page.
)

// Compile-time "implements" check.
var _ interface {
	Store
	Queryer
} = (*InfluxDBStore)(nil)

// TODO: should be a constant.
var zeroID = fmt.Sprintf("%016x", uint64(0))

type InfluxDBStore struct {
	con           *influxDBClient.Client // InfluxDB client connection.
	server        *influxDBServer.Server // InfluxDB API server.
	tracesPerPage int                    // Number of traces per page.
}

func (in *InfluxDBStore) Collect(id SpanID, anns ...Annotation) error {
	p, err := in.findSpanPoint(id)
	if err != nil {
		return err
	}

	// trace_id, span_id & parent_id are set as tags
	// because InfluxDB tags are indexed & those values
	// are uselater on queries.
	tags := map[string]string{
		"trace_id":  id.Trace.String(),
		"span_id":   id.Span.String(),
		"parent_id": id.Parent.String(),
	}

	// Saving annotations as InfluxDB measurement spans fields
	// which are not indexed.
	fields := make(map[string]interface{}, len(anns))
	for _, ann := range anns {
		fields[ann.Key] = string(ann.Value)
	}

	if p != nil { // span exists on DB.
		p.Measurement = spanMeasurementName
		p.Tags = tags
		p.Fields = fields
	} else { // new span to be saved on DB.
		p = &influxDBClient.Point{
			Measurement: spanMeasurementName,
			Tags:        tags,   // indexed metadata.
			Fields:      fields, // non-indexed metadata.
			Time:        time.Now().UTC(),
		}
	}

	// InfluxDB point represents a single span.
	pts := []influxDBClient.Point{*p}
	bps := influxDBClient.BatchPoints{
		Points:          pts,
		Database:        dbName,
		RetentionPolicy: "default",
	}
	_, writeErr := in.con.Write(bps)
	if writeErr != nil {
		return writeErr
	}
	return nil
}

func (in *InfluxDBStore) Trace(id ID) (*Trace, error) {
	trace := &Trace{}

	// GROUP BY * -> meaning group by all tags(trace_id, span_id & parent_id)
	// grouping by all tags includes those and it's values on the query response.
	q := fmt.Sprintf("SELECT * FROM spans WHERE trace_id='%s' GROUP BY *", id)
	result, err := in.executeOneQuery(q)
	if err != nil {
		return nil, err
	}

	// result.Series -> A slice containing all the spans.
	if len(result.Series) == 0 {
		return nil, errors.New("trace not found")
	}
	var isRootSpan bool

	// Iterate over series(spans) to create trace children's & set trace fields.
	for _, s := range result.Series {
		span, err := newSpanFromRow(&s)
		if err != nil {
			return nil, err
		}
		if span.ID.Parent == 0 && isRootSpan {
			// Must be a single root span.
			return nil, errors.New("unexpected multiple root spans")
		}
		if span.ID.Parent == 0 && !isRootSpan {
			isRootSpan = true
		}
		annotations, err := annotationsFromRow(&s)
		if err != nil {
			return trace, nil
		}
		span.Annotations = *annotations
		if isRootSpan { // root span.
			trace.Span = *span
		} else { // children span.
			trace.Sub = append(trace.Sub, &Trace{Span: *span})
		}
	}
	return trace, nil
}

func (in *InfluxDBStore) Traces() ([]*Trace, error) {
	traces := make([]*Trace, 0)

	// GROUP BY * -> meaning group by all tags(trace_id, span_id & parent_id)
	// grouping by all tags includes those and it's values on the query response.
	rootSpansQuery := fmt.Sprintf("SELECT * FROM spans WHERE parent_id='%s' GROUP BY * LIMIT %d", zeroID, in.tracesPerPage)
	rootSpansResult, err := in.executeOneQuery(rootSpansQuery)
	if err != nil {
		return nil, err
	}

	// result.Series -> A slice containing all the spans.
	if len(rootSpansResult.Series) == 0 {
		return traces, nil
	}

	// Cache to keep track of traces to be returned.
	tracesCache := make(map[ID]*Trace, 0)

	// Iterate over series(spans) to create traces.
	for _, s := range rootSpansResult.Series {
		span, err := newSpanFromRow(&s)
		if err != nil {
			return nil, err
		}
		annotations, err := annotationsFromRow(&s)
		if err != nil {
			return nil, err
		}
		span.Annotations = *annotations
		_, present := tracesCache[span.ID.Trace]
		if !present {
			tracesCache[span.ID.Trace] = &Trace{Span: *span}
		} else {
			return nil, errors.New("duplicated root span")
		}
	}

	// Using 'OR' since 'IN' not supported yet.
	where := `WHERE `
	var i int = 1
	for _, trace := range tracesCache {
		where += fmt.Sprintf("(trace_id='%s' AND parent_id!='%s')", trace.Span.ID.Trace, zeroID)

		// Adds 'OR' except for last iteration.
		if i != len(tracesCache) && len(tracesCache) > 1 {
			where += " OR "
		}
		i += 1
	}

	// Queries for all children spans of the traces to be returned.
	childrenSpansQuery := fmt.Sprintf("SELECT * FROM spans %s GROUP BY *", where)
	childrenSpansResult, err := in.executeOneQuery(childrenSpansQuery)
	if err != nil {
		return nil, err
	}

	// Iterate over series(children spans) to create sub-traces
	// and associates sub-traces with it's parent trace.
	for _, s := range childrenSpansResult.Series {
		span, err := newSpanFromRow(&s)
		if err != nil {
			return nil, err
		}
		annotations, err := annotationsFromRow(&s)
		if err != nil {
			return nil, err
		}
		span.Annotations = *annotations
		trace, present := tracesCache[span.ID.Trace]
		if !present { // Root trace not added.
			return nil, errors.New("parent not found")
		} else { // Root trace already added so append a sub-trace.
			trace.Sub = append(trace.Sub, &Trace{Span: *span})
		}
	}
	for _, trace := range tracesCache {
		traces = append(traces, trace)
	}
	return traces, nil
}

func (in *InfluxDBStore) Close() {
	in.server.Close()
}

func (in *InfluxDBStore) createDBIfNotExists() error {
	// If no errors query execution was successfully - either DB was created or already exists.
	response, err := in.con.Query(influxDBClient.Query{
		Command: fmt.Sprintf("%s %s", "CREATE DATABASE IF NOT EXISTS", dbName),
	})
	if err != nil {
		return err
	}
	if response.Error() != nil {
		return response.Error()
	}
	return nil
}

func (in *InfluxDBStore) executeOneQuery(command string) (*influxDBClient.Result, error) {
	response, err := in.con.Query(influxDBClient.Query{
		Command:  command,
		Database: dbName,
	})
	if err != nil {
		return nil, err
	}
	if response.Error() != nil {
		return nil, response.Error()
	}

	// Expecting one result, since a single query is executed.
	if len(response.Results) != 1 {
		return nil, errors.New("unexpected number of results for an influxdb single query")
	}
	return &response.Results[0], nil
}

func (in *InfluxDBStore) findSpanPoint(ID SpanID) (*influxDBClient.Point, error) {
	q := fmt.Sprintf(`
		SELECT * FROM spans WHERE trace_id='%s' AND span_id='%s' AND parent_id='%s' GROUP BY *
	`, ID.Trace, ID.Span, ID.Parent)
	result, err := in.executeOneQuery(q)
	if err != nil {
		return nil, err
	}
	if len(result.Series) == 0 {
		return nil, nil
	}
	if len(result.Series) > 1 {
		return nil, errors.New("unexpected multiple series")
	}
	r := result.Series[0]
	if len(r.Values) == 0 {
		return nil, errors.New("unexpected empty series")
	}
	p := influxDBClient.Point{}
	fields := r.Values[0]
	for i, field := range fields {
		key := r.Columns[i]
		if key == "time" {
			switch field.(type) {
			case string:
				t, err := time.Parse(time.RFC3339Nano, field.(string))
				if err != nil {
					return nil, err
				}
				p.Time = t
			default:
				return nil, fmt.Errorf("unexpected time type: %v", reflect.TypeOf(field))
			}
		}
	}
	return &p, err
}

func (in *InfluxDBStore) init(server *influxDBServer.Server) error {
	in.server = server
	url, err := url.Parse(fmt.Sprintf("http://%s:%d", influxDBClient.DefaultHost, influxDBClient.DefaultPort))
	if err != nil {
		return err
	}
	con, err := influxDBClient.NewClient(influxDBClient.Config{URL: *url})
	if err != nil {
		return err
	}
	in.con = con
	if err := in.createDBIfNotExists(); err != nil {
		return err
	}
	in.tracesPerPage = defaultTracesPerPage
	return nil
}

func annotationsFromRow(r *influxDBModels.Row) (*Annotations, error) {
	// Actually an influxDBModels.Row represents a single InfluxDB serie.
	// r.Values[n] is a slice containing span's annotation values.
	var fields []interface{}
	if len(r.Values) == 1 {
		fields = r.Values[0]
	}

	// len(r.Values) might be greater than one - meaning there are
	// some spans to drop, see: InfluxDBStore.Collect(...).
	// If so last one is picked.
	if len(r.Values) > 1 {
		fields = r.Values[len(r.Values)-1]
	}
	annotations := make(Annotations, len(fields))

	// Iterates over fields which represent span's annotation values.
	for i, field := range fields {
		// It is safe to do column[0] (eg. 'Server.Request.Method')
		// matches fields[0] (eg. 'GET')
		key := r.Columns[i]
		var value []byte
		switch field.(type) {
		case string:
			value = []byte(field.(string))
		case nil:
		default:
			return nil, fmt.Errorf("unexpected field type: %v", reflect.TypeOf(field))
		}
		a := Annotation{
			Key:   key,
			Value: value,
		}
		annotations = append(annotations, a)
	}
	return &annotations, nil
}

func newSpanFromRow(r *influxDBModels.Row) (*Span, error) {
	span := &Span{}
	traceID, err := ParseID(r.Tags["trace_id"])
	if err != nil {
		return nil, err
	}
	spanID, err := ParseID(r.Tags["span_id"])
	if err != nil {
		return nil, err
	}
	parentID, err := ParseID(r.Tags["parent_id"])
	if err != nil {
		return nil, err
	}
	span.ID = SpanID{
		Trace:  ID(traceID),
		Span:   ID(spanID),
		Parent: ID(parentID),
	}
	return span, nil
}

type InfluxDBStoreConfig struct {
	Server    *influxDBServer.Config
	BuildInfo *influxDBServer.BuildInfo
}

func NewInfluxDBStore(config InfluxDBStoreConfig) (*InfluxDBStore, error) {
	//TODO: add Authentication.
	s, err := influxDBServer.NewServer(config.Server, config.BuildInfo)
	if err != nil {
		return nil, err
	}
	if err := s.Open(); err != nil {
		return nil, err
	}
	var in InfluxDBStore
	if err := in.init(s); err != nil {
		return nil, err
	}
	return &in, nil
}
<|endoftext|>


<commit_before>package wikifier

import (
	"strconv"
	"strings"

	"github.com/pkg/errors"
)

type galleryBlock struct {
	thumbnailHeight string
	images          []galleryEntry
	*Map
}

type galleryEntry struct {
	thumbnailPath string
	img           *imageBlock
}

func newGalleryBlock(name string, b *parserBlock) block {
	return &galleryBlock{
		thumbnailHeight: "320",
		Map:             newMapBlock("", b).(*Map),
	}
}

func (g *galleryBlock) parse(page *Page) {
	g.Map.parse(page)

	// sort out the map
	for _, imgKey := range g.OrderedKeys() {
		switch imgKey {

		// thumbnail height
		case "thumb_height":
			var err error
			g.thumbnailHeight, err = g.GetStr(imgKey)
			if err != nil {
				g.warn(g.openPos, errors.Wrap(err, imgKey).Error()) // FIXME: use key position
			}

		// unknown key
		default:
			if strings.HasPrefix(imgKey, "anon_") {
				// anonymous image is OK
				blk, err := g.GetBlock(imgKey)
				if err == nil {
					// it is indeed a block
					img, ok := blk.(*imageBlock)
					if ok {
						// it is indeed an image!
						g.addImage(page, img)
					} else {
						// block other than image
						g.warn(g.openPos, imgKey+": expected Block<image{}>") // FIXME: use key position
					}
				} else {
					// non-block
					g.warn(g.openPos, errors.Wrap(err, imgKey).Error()) // FIXME: use key position
				}
			} else {
				// unknown key
				g.warn(g.openPos, "Invalid key '"+imgKey+"'") // FIXME: use key position
			}
		}
	}

}

func (g *galleryBlock) addImage(page *Page, img *imageBlock) {

	// get full-size path
	img.path = page.Opt.Root.Image + "/" + img.file
	entry := galleryEntry{img.path, img}

	// generate the thumbnail
	if page.Opt.Image.SizeMethod == "server" {

		// Calc and Sizer must be present
		if page.Opt.Image.Sizer == nil || page.Opt.Image.Calc == nil {
			img.warn(img.openPos, "image.sizer and image.calc required with image.size_method 'server'")
			img.parseFailed = true
			return
		}

		// calculate missing dimension
		height, err := strconv.Atoi(g.thumbnailHeight)
		if err != nil {
			g.warn(g.openPos, "thumb_height: expected integer")
			height = 320
		}
		width, height := page.Opt.Image.Calc(
			img.file,
			0,
			height,
			page,
		)

		// create thumbnail path
		entry.thumbnailPath = page.Opt.Image.Sizer(
			img.file,
			width,
			height,
			page,
		)
	}

	// add the image
	g.images = append(g.images, entry)
}

func (g *galleryBlock) html(page *Page, el element) {
	//g.Map.html(page, nil) -- skip since we don't want to convert to HTML, right?

	// create gallery options
	options := `{
		"thumbnailHeight": "` + g.thumbnailHeight + `",
		"thumbnailWidth": "auto",
		"thumbnailBorderVertical": 0,
		"thumbnailBorderHorizontal": 0,
		"colorScheme": {
			"thumbnail": {
				"borderColor": "rgba(0,0,0,0)"
			}
		},
		"thumbnailDisplayTransition": "flipUp",
		"thumbnailDisplayTransitionDuration": 500,
		"thumbnailLabel": {
			"displayDescription": true,
			"descriptionMultiLine": true
		},
		"thumbnailHoverEffect2": "descriptionSlideUp",
		"thumbnailAlignment": "center",
		"thumbnailGutterWidth": 10,
		"thumbnailGutterHeight": 10
	}`

	// set options
	el.setAttr("data-nanogallery2", options)

	// add images
	for _, entry := range g.images {

		// determine desc
		// consider: this could be extracted in image{} parse.
		// I didn't do it since image{} usually didn't have a desc.
		desc, _ := entry.img.GetStr("description")
		if desc == "" {
			desc, _ = entry.img.GetStr("desc")
		}

		// create gallery item
		a := el.createChild("a", "")
		a.setAttr("href", entry.img.path)
		a.setAttr("data-ngthumb", entry.thumbnailPath)
		a.setAttr("data-ngdesc", desc)
	}
}
<commit_msg>clean up gallery{}<commit_after>package wikifier

import (
	"strconv"
	"strings"

	"github.com/pkg/errors"
)

type galleryBlock struct {
	thumbnailHeight int
	images          []*galleryEntry
	*Map
}

type galleryEntry struct {
	thumbnailPath string
	img           *imageBlock
}

func newGalleryBlock(name string, b *parserBlock) block {
	return &galleryBlock{
		thumbnailHeight: 320,
		Map:             newMapBlock("", b).(*Map),
	}
}

func (g *galleryBlock) parse(page *Page) {
	g.Map.parse(page)

	// sort out the map
	for _, imgKey := range g.OrderedKeys() {
		switch imgKey {

		// thumbnail height
		case "thumb_height":
			thumbHeight, err := g.GetStr(imgKey)

			// not a string
			if err != nil {
				g.warn(g.openPos, errors.Wrap(err, imgKey).Error()) // FIXME: use key position
				break
			}

			// convert to int
			height, err := strconv.Atoi(thumbHeight)
			if err != nil {
				g.warn(g.openPos, "thumb_height: expected integer")
				break
			}

			// good
			g.thumbnailHeight = height

		default:

			// unknown key
			if !strings.HasPrefix(imgKey, "anon_") {
				g.warn(g.openPos, "Invalid key '"+imgKey+"'") // FIXME: use key position
				break
			}

			// anonymous image is OK
			blk, err := g.GetBlock(imgKey)

			// non-block
			if err != nil {
				g.warn(g.openPos, errors.Wrap(err, imgKey).Error()) // FIXME: use key position
				break
			}

			// it is indeed a block, but is it an image?
			img, ok := blk.(*imageBlock)
			if !ok {
				// block other than image
				g.warn(g.openPos, imgKey+": expected Block<image{}>") // FIXME: use key position
				break
			}

			// it is indeed an image!
			g.addImage(page, img)
		}
	}
}

func (g *galleryBlock) addImage(page *Page, img *imageBlock) {

	// get full-size path
	img.path = page.Opt.Root.Image + "/" + img.file
	entry := &galleryEntry{img.path, img}

	// generate the thumbnail
	if page.Opt.Image.SizeMethod == "server" {

		// Calc and Sizer must be present
		if page.Opt.Image.Sizer == nil || page.Opt.Image.Calc == nil {
			img.warn(img.openPos, "image.sizer and image.calc required with image.size_method 'server'")
			img.parseFailed = true
			return
		}

		// calculate missing dimension
		width, height := page.Opt.Image.Calc(
			img.file,
			0,
			g.thumbnailHeight,
			page,
		)

		// create thumbnail path
		entry.thumbnailPath = page.Opt.Image.Sizer(
			img.file,
			width,
			height,
			page,
		)
	}

	// add the image
	g.images = append(g.images, entry)
}

func (g *galleryBlock) html(page *Page, el element) {
	//g.Map.html(page, nil) -- skip since we don't want to convert to HTML, right?

	// create gallery options
	options := `{
		"thumbnailHeight": "` + strconv.Itoa(g.thumbnailHeight) + `",
		"thumbnailWidth": "auto",
		"thumbnailBorderVertical": 0,
		"thumbnailBorderHorizontal": 0,
		"colorScheme": {
			"thumbnail": {
				"borderColor": "rgba(0,0,0,0)"
			}
		},
		"thumbnailDisplayTransition": "flipUp",
		"thumbnailDisplayTransitionDuration": 500,
		"thumbnailLabel": {
			"displayDescription": true,
			"descriptionMultiLine": true
		},
		"thumbnailHoverEffect2": "descriptionSlideUp",
		"thumbnailAlignment": "center",
		"thumbnailGutterWidth": 10,
		"thumbnailGutterHeight": 10
	}`

	// set options
	el.setAttr("data-nanogallery2", options)

	// add images
	for _, entry := range g.images {

		// determine desc
		// consider: this could be extracted in image{} parse.
		// I didn't do it since image{} usually didn't have a desc.
		desc, _ := entry.img.GetStr("description")
		if desc == "" {
			desc, _ = entry.img.GetStr("desc")
		}

		// create gallery item
		a := el.createChild("a", "")
		a.setAttr("href", entry.img.path)
		a.setAttr("data-ngthumb", entry.thumbnailPath)
		a.setAttr("data-ngdesc", desc)
	}
}
<|endoftext|>


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

// +build windows

package registry

import (
	"errors"
	"io"
	"syscall"
	"unicode/utf16"
	"unsafe"
)

const (
	// Registry value types.
	NONE                       = 0
	SZ                         = 1
	EXPAND_SZ                  = 2
	BINARY                     = 3
	DWORD                      = 4
	DWORD_BIG_ENDIAN           = 5
	LINK                       = 6
	MULTI_SZ                   = 7
	RESOURCE_LIST              = 8
	FULL_RESOURCE_DESCRIPTOR   = 9
	RESOURCE_REQUIREMENTS_LIST = 10
	QWORD                      = 11
)

var (
	// ErrShortBuffer is returned when the buffer was too short for the operation.
	ErrShortBuffer = syscall.ERROR_MORE_DATA

	// ErrNotExist is returned when a registry key or value does not exist.
	ErrNotExist = syscall.ERROR_FILE_NOT_FOUND

	// ErrUnexpectedType is returned by Get*Value when the value's type was unexpected.
	ErrUnexpectedType = errors.New("unexpected key value type")
)

// GetValue retrieves the type and data for the specified value associated
// with an open key k. It fills up buffer buf and returns the retrieved
// byte count n. If buf is too small to fit the stored value it returns
// ErrShortBuffer error along with the required buffer size n.
// If no buffer is provided, it returns true and actual buffer size n.
// If no buffer is provided, GetValue returns the value's type only.
// If the value does not exist, the error returned is ErrNotExist.
//
// GetValue is a low level function. If value's type is known, use the appropriate
// Get*Value function instead.
func (k Key) GetValue(name string, buf []byte) (n int, valtype uint32, err error) {
	pname, err := syscall.UTF16PtrFromString(name)
	if err != nil {
		return 0, 0, err
	}
	var pbuf *byte
	if len(buf) > 0 {
		pbuf = (*byte)(unsafe.Pointer(&buf[0]))
	}
	l := uint32(len(buf))
	err = syscall.RegQueryValueEx(syscall.Handle(k), pname, nil, &valtype, pbuf, &l)
	if err != nil {
		return int(l), valtype, err
	}
	return int(l), valtype, nil
}

func (k Key) getValue(name string, buf []byte) (data []byte, valtype uint32, err error) {
	p, err := syscall.UTF16PtrFromString(name)
	if err != nil {
		return nil, 0, err
	}
	var t uint32
	n := uint32(len(buf))
	for {
		err = syscall.RegQueryValueEx(syscall.Handle(k), p, nil, &t, (*byte)(unsafe.Pointer(&buf[0])), &n)
		if err == nil {
			return buf[:n], t, nil
		}
		if err != syscall.ERROR_MORE_DATA {
			return nil, 0, err
		}
		if n <= uint32(len(buf)) {
			return nil, 0, err
		}
		buf = make([]byte, n)
	}
}

// GetStringValue retrieves the string value for the specified
// value name associated with an open key k. It also returns the value's type.
// If value does not exist, GetStringValue returns ErrNotExist.
// If value is not SZ or EXPAND_SZ, it will return the correct value
// type and ErrUnexpectedType.
func (k Key) GetStringValue(name string) (val string, valtype uint32, err error) {
	data, typ, err2 := k.getValue(name, make([]byte, 64))
	if err2 != nil {
		return "", typ, err2
	}
	switch typ {
	case SZ, EXPAND_SZ:
	default:
		return "", typ, ErrUnexpectedType
	}
	if len(data) == 0 {
		return "", typ, nil
	}
	u := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[:len(data)/2]
	return syscall.UTF16ToString(u), typ, nil
}

// GetMUIStringValue retrieves the localized string value for
// the specified value name associated with an open key k.
// If the value name doesn't exist or the localized string value
// can't be resolved, GetMUIStringValue returns ErrNotExist.
// GetMUIStringValue panics if the system doesn't support
// regLoadMUIString; use LoadRegLoadMUIString to check if
// regLoadMUIString is supported before calling this function.
func (k Key) GetMUIStringValue(name string) (string, error) {
	pname, err := syscall.UTF16PtrFromString(name)
	if err != nil {
		return "", err
	}

	buf := make([]uint16, 1024)
	var buflen uint32
	var pdir *uint16

	err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
	if err == syscall.ERROR_FILE_NOT_FOUND { // Try fallback path

		// Try to resolve the string value using the system directory as
		// a DLL search path; this assumes the string value is of the form
		// @[path]\dllname,-strID but with no path given, e.g. @tzres.dll,-320.

		// This approach works with tzres.dll but may have to be revised
		// in the future to allow callers to provide custom search paths.

		var s string
		s, err = ExpandString("%SystemRoot%\\system32\\")
		if err != nil {
			return "", err
		}
		pdir, err = syscall.UTF16PtrFromString(s)
		if err != nil {
			return "", err
		}

		err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
	}

	for err == syscall.ERROR_MORE_DATA { // Grow buffer if needed
		if buflen <= uint32(len(buf)) {
			break // Buffer not growing, assume race; break
		}
		buf = make([]uint16, buflen)
		err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
	}

	if err != nil {
		return "", err
	}

	return syscall.UTF16ToString(buf), nil
}

// ExpandString expands environment-variable strings and replaces
// them with the values defined for the current user.
// Use ExpandString to expand EXPAND_SZ strings.
func ExpandString(value string) (string, error) {
	if value == "" {
		return "", nil
	}
	p, err := syscall.UTF16PtrFromString(value)
	if err != nil {
		return "", err
	}
	r := make([]uint16, 100)
	for {
		n, err := expandEnvironmentStrings(p, &r[0], uint32(len(r)))
		if err != nil {
			return "", err
		}
		if n <= uint32(len(r)) {
			u := (*[1 << 29]uint16)(unsafe.Pointer(&r[0]))[:]
			return syscall.UTF16ToString(u), nil
		}
		r = make([]uint16, n)
	}
}

// GetStringsValue retrieves the []string value for the specified
// value name associated with an open key k. It also returns the value's type.
// If value does not exist, GetStringsValue returns ErrNotExist.
// If value is not MULTI_SZ, it will return the correct value
// type and ErrUnexpectedType.
func (k Key) GetStringsValue(name string) (val []string, valtype uint32, err error) {
	data, typ, err2 := k.getValue(name, make([]byte, 64))
	if err2 != nil {
		return nil, typ, err2
	}
	if typ != MULTI_SZ {
		return nil, typ, ErrUnexpectedType
	}
	if len(data) == 0 {
		return nil, typ, nil
	}
	p := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[:len(data)/2]
	if len(p) == 0 {
		return nil, typ, nil
	}
	if p[len(p)-1] == 0 {
		p = p[:len(p)-1] // remove terminating null
	}
	val = make([]string, 0, 5)
	from := 0
	for i, c := range p {
		if c == 0 {
			val = append(val, string(utf16.Decode(p[from:i])))
			from = i + 1
		}
	}
	return val, typ, nil
}

// GetIntegerValue retrieves the integer value for the specified
// value name associated with an open key k. It also returns the value's type.
// If value does not exist, GetIntegerValue returns ErrNotExist.
// If value is not DWORD or QWORD, it will return the correct value
// type and ErrUnexpectedType.
func (k Key) GetIntegerValue(name string) (val uint64, valtype uint32, err error) {
	data, typ, err2 := k.getValue(name, make([]byte, 8))
	if err2 != nil {
		return 0, typ, err2
	}
	switch typ {
	case DWORD:
		if len(data) != 4 {
			return 0, typ, errors.New("DWORD value is not 4 bytes long")
		}
		var val32 uint32
		copy((*[4]byte)(unsafe.Pointer(&val32))[:], data)
		return uint64(val32), DWORD, nil
	case QWORD:
		if len(data) != 8 {
			return 0, typ, errors.New("QWORD value is not 8 bytes long")
		}
		copy((*[8]byte)(unsafe.Pointer(&val))[:], data)
		return val, QWORD, nil
	default:
		return 0, typ, ErrUnexpectedType
	}
}

// GetBinaryValue retrieves the binary value for the specified
// value name associated with an open key k. It also returns the value's type.
// If value does not exist, GetBinaryValue returns ErrNotExist.
// If value is not BINARY, it will return the correct value
// type and ErrUnexpectedType.
func (k Key) GetBinaryValue(name string) (val []byte, valtype uint32, err error) {
	data, typ, err2 := k.getValue(name, make([]byte, 64))
	if err2 != nil {
		return nil, typ, err2
	}
	if typ != BINARY {
		return nil, typ, ErrUnexpectedType
	}
	return data, typ, nil
}

func (k Key) setValue(name string, valtype uint32, data []byte) error {
	p, err := syscall.UTF16PtrFromString(name)
	if err != nil {
		return err
	}
	if len(data) == 0 {
		return regSetValueEx(syscall.Handle(k), p, 0, valtype, nil, 0)
	}
	return regSetValueEx(syscall.Handle(k), p, 0, valtype, &data[0], uint32(len(data)))
}

// SetDWordValue sets the data and type of a name value
// under key k to value and DWORD.
func (k Key) SetDWordValue(name string, value uint32) error {
	return k.setValue(name, DWORD, (*[4]byte)(unsafe.Pointer(&value))[:])
}

// SetQWordValue sets the data and type of a name value
// under key k to value and QWORD.
func (k Key) SetQWordValue(name string, value uint64) error {
	return k.setValue(name, QWORD, (*[8]byte)(unsafe.Pointer(&value))[:])
}

func (k Key) setStringValue(name string, valtype uint32, value string) error {
	v, err := syscall.UTF16FromString(value)
	if err != nil {
		return err
	}
	buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[:len(v)*2]
	return k.setValue(name, valtype, buf)
}

// SetStringValue sets the data and type of a name value
// under key k to value and SZ. The value must not contain a zero byte.
func (k Key) SetStringValue(name, value string) error {
	return k.setStringValue(name, SZ, value)
}

// SetExpandStringValue sets the data and type of a name value
// under key k to value and EXPAND_SZ. The value must not contain a zero byte.
func (k Key) SetExpandStringValue(name, value string) error {
	return k.setStringValue(name, EXPAND_SZ, value)
}

// SetStringsValue sets the data and type of a name value
// under key k to value and MULTI_SZ. The value strings
// must not contain a zero byte.
func (k Key) SetStringsValue(name string, value []string) error {
	ss := ""
	for _, s := range value {
		for i := 0; i < len(s); i++ {
			if s[i] == 0 {
				return errors.New("string cannot have 0 inside")
			}
		}
		ss += s + "\x00"
	}
	v := utf16.Encode([]rune(ss + "\x00"))
	buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[:len(v)*2]
	return k.setValue(name, MULTI_SZ, buf)
}

// SetBinaryValue sets the data and type of a name value
// under key k to value and BINARY.
func (k Key) SetBinaryValue(name string, value []byte) error {
	return k.setValue(name, BINARY, value)
}

// DeleteValue removes a named value from the key k.
func (k Key) DeleteValue(name string) error {
	return regDeleteValue(syscall.Handle(k), syscall.StringToUTF16Ptr(name))
}

// ReadValueNames returns the value names of key k.
// The parameter n controls the number of returned names,
// analogous to the way os.File.Readdirnames works.
func (k Key) ReadValueNames(n int) ([]string, error) {
	ki, err := k.Stat()
	if err != nil {
		return nil, err
	}
	names := make([]string, 0, ki.ValueCount)
	buf := make([]uint16, ki.MaxValueNameLen+1) // extra room for terminating null character
loopItems:
	for i := uint32(0); ; i++ {
		if n > 0 {
			if len(names) == n {
				return names, nil
			}
		}
		l := uint32(len(buf))
		for {
			err := regEnumValue(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil)
			if err == nil {
				break
			}
			if err == syscall.ERROR_MORE_DATA {
				// Double buffer size and try again.
				l = uint32(2 * len(buf))
				buf = make([]uint16, l)
				continue
			}
			if err == _ERROR_NO_MORE_ITEMS {
				break loopItems
			}
			return names, err
		}
		names = append(names, syscall.UTF16ToString(buf[:l]))
	}
	if n > len(names) {
		return names, io.EOF
	}
	return names, nil
}
<commit_msg>windows/registry: make '-gcflags=all=-d=checkptr' flag work<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

package registry

import (
	"errors"
	"io"
	"syscall"
	"unicode/utf16"
	"unsafe"
)

const (
	// Registry value types.
	NONE                       = 0
	SZ                         = 1
	EXPAND_SZ                  = 2
	BINARY                     = 3
	DWORD                      = 4
	DWORD_BIG_ENDIAN           = 5
	LINK                       = 6
	MULTI_SZ                   = 7
	RESOURCE_LIST              = 8
	FULL_RESOURCE_DESCRIPTOR   = 9
	RESOURCE_REQUIREMENTS_LIST = 10
	QWORD                      = 11
)

var (
	// ErrShortBuffer is returned when the buffer was too short for the operation.
	ErrShortBuffer = syscall.ERROR_MORE_DATA

	// ErrNotExist is returned when a registry key or value does not exist.
	ErrNotExist = syscall.ERROR_FILE_NOT_FOUND

	// ErrUnexpectedType is returned by Get*Value when the value's type was unexpected.
	ErrUnexpectedType = errors.New("unexpected key value type")
)

// GetValue retrieves the type and data for the specified value associated
// with an open key k. It fills up buffer buf and returns the retrieved
// byte count n. If buf is too small to fit the stored value it returns
// ErrShortBuffer error along with the required buffer size n.
// If no buffer is provided, it returns true and actual buffer size n.
// If no buffer is provided, GetValue returns the value's type only.
// If the value does not exist, the error returned is ErrNotExist.
//
// GetValue is a low level function. If value's type is known, use the appropriate
// Get*Value function instead.
func (k Key) GetValue(name string, buf []byte) (n int, valtype uint32, err error) {
	pname, err := syscall.UTF16PtrFromString(name)
	if err != nil {
		return 0, 0, err
	}
	var pbuf *byte
	if len(buf) > 0 {
		pbuf = (*byte)(unsafe.Pointer(&buf[0]))
	}
	l := uint32(len(buf))
	err = syscall.RegQueryValueEx(syscall.Handle(k), pname, nil, &valtype, pbuf, &l)
	if err != nil {
		return int(l), valtype, err
	}
	return int(l), valtype, nil
}

func (k Key) getValue(name string, buf []byte) (data []byte, valtype uint32, err error) {
	p, err := syscall.UTF16PtrFromString(name)
	if err != nil {
		return nil, 0, err
	}
	var t uint32
	n := uint32(len(buf))
	for {
		err = syscall.RegQueryValueEx(syscall.Handle(k), p, nil, &t, (*byte)(unsafe.Pointer(&buf[0])), &n)
		if err == nil {
			return buf[:n], t, nil
		}
		if err != syscall.ERROR_MORE_DATA {
			return nil, 0, err
		}
		if n <= uint32(len(buf)) {
			return nil, 0, err
		}
		buf = make([]byte, n)
	}
}

// GetStringValue retrieves the string value for the specified
// value name associated with an open key k. It also returns the value's type.
// If value does not exist, GetStringValue returns ErrNotExist.
// If value is not SZ or EXPAND_SZ, it will return the correct value
// type and ErrUnexpectedType.
func (k Key) GetStringValue(name string) (val string, valtype uint32, err error) {
	data, typ, err2 := k.getValue(name, make([]byte, 64))
	if err2 != nil {
		return "", typ, err2
	}
	switch typ {
	case SZ, EXPAND_SZ:
	default:
		return "", typ, ErrUnexpectedType
	}
	if len(data) == 0 {
		return "", typ, nil
	}
	u := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[: len(data)/2 : len(data)/2]
	return syscall.UTF16ToString(u), typ, nil
}

// GetMUIStringValue retrieves the localized string value for
// the specified value name associated with an open key k.
// If the value name doesn't exist or the localized string value
// can't be resolved, GetMUIStringValue returns ErrNotExist.
// GetMUIStringValue panics if the system doesn't support
// regLoadMUIString; use LoadRegLoadMUIString to check if
// regLoadMUIString is supported before calling this function.
func (k Key) GetMUIStringValue(name string) (string, error) {
	pname, err := syscall.UTF16PtrFromString(name)
	if err != nil {
		return "", err
	}

	buf := make([]uint16, 1024)
	var buflen uint32
	var pdir *uint16

	err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
	if err == syscall.ERROR_FILE_NOT_FOUND { // Try fallback path

		// Try to resolve the string value using the system directory as
		// a DLL search path; this assumes the string value is of the form
		// @[path]\dllname,-strID but with no path given, e.g. @tzres.dll,-320.

		// This approach works with tzres.dll but may have to be revised
		// in the future to allow callers to provide custom search paths.

		var s string
		s, err = ExpandString("%SystemRoot%\\system32\\")
		if err != nil {
			return "", err
		}
		pdir, err = syscall.UTF16PtrFromString(s)
		if err != nil {
			return "", err
		}

		err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
	}

	for err == syscall.ERROR_MORE_DATA { // Grow buffer if needed
		if buflen <= uint32(len(buf)) {
			break // Buffer not growing, assume race; break
		}
		buf = make([]uint16, buflen)
		err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
	}

	if err != nil {
		return "", err
	}

	return syscall.UTF16ToString(buf), nil
}

// ExpandString expands environment-variable strings and replaces
// them with the values defined for the current user.
// Use ExpandString to expand EXPAND_SZ strings.
func ExpandString(value string) (string, error) {
	if value == "" {
		return "", nil
	}
	p, err := syscall.UTF16PtrFromString(value)
	if err != nil {
		return "", err
	}
	r := make([]uint16, 100)
	for {
		n, err := expandEnvironmentStrings(p, &r[0], uint32(len(r)))
		if err != nil {
			return "", err
		}
		if n <= uint32(len(r)) {
			return syscall.UTF16ToString(r[:n]), nil
		}
		r = make([]uint16, n)
	}
}

// GetStringsValue retrieves the []string value for the specified
// value name associated with an open key k. It also returns the value's type.
// If value does not exist, GetStringsValue returns ErrNotExist.
// If value is not MULTI_SZ, it will return the correct value
// type and ErrUnexpectedType.
func (k Key) GetStringsValue(name string) (val []string, valtype uint32, err error) {
	data, typ, err2 := k.getValue(name, make([]byte, 64))
	if err2 != nil {
		return nil, typ, err2
	}
	if typ != MULTI_SZ {
		return nil, typ, ErrUnexpectedType
	}
	if len(data) == 0 {
		return nil, typ, nil
	}
	p := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[: len(data)/2 : len(data)/2]
	if len(p) == 0 {
		return nil, typ, nil
	}
	if p[len(p)-1] == 0 {
		p = p[:len(p)-1] // remove terminating null
	}
	val = make([]string, 0, 5)
	from := 0
	for i, c := range p {
		if c == 0 {
			val = append(val, string(utf16.Decode(p[from:i])))
			from = i + 1
		}
	}
	return val, typ, nil
}

// GetIntegerValue retrieves the integer value for the specified
// value name associated with an open key k. It also returns the value's type.
// If value does not exist, GetIntegerValue returns ErrNotExist.
// If value is not DWORD or QWORD, it will return the correct value
// type and ErrUnexpectedType.
func (k Key) GetIntegerValue(name string) (val uint64, valtype uint32, err error) {
	data, typ, err2 := k.getValue(name, make([]byte, 8))
	if err2 != nil {
		return 0, typ, err2
	}
	switch typ {
	case DWORD:
		if len(data) != 4 {
			return 0, typ, errors.New("DWORD value is not 4 bytes long")
		}
		var val32 uint32
		copy((*[4]byte)(unsafe.Pointer(&val32))[:], data)
		return uint64(val32), DWORD, nil
	case QWORD:
		if len(data) != 8 {
			return 0, typ, errors.New("QWORD value is not 8 bytes long")
		}
		copy((*[8]byte)(unsafe.Pointer(&val))[:], data)
		return val, QWORD, nil
	default:
		return 0, typ, ErrUnexpectedType
	}
}

// GetBinaryValue retrieves the binary value for the specified
// value name associated with an open key k. It also returns the value's type.
// If value does not exist, GetBinaryValue returns ErrNotExist.
// If value is not BINARY, it will return the correct value
// type and ErrUnexpectedType.
func (k Key) GetBinaryValue(name string) (val []byte, valtype uint32, err error) {
	data, typ, err2 := k.getValue(name, make([]byte, 64))
	if err2 != nil {
		return nil, typ, err2
	}
	if typ != BINARY {
		return nil, typ, ErrUnexpectedType
	}
	return data, typ, nil
}

func (k Key) setValue(name string, valtype uint32, data []byte) error {
	p, err := syscall.UTF16PtrFromString(name)
	if err != nil {
		return err
	}
	if len(data) == 0 {
		return regSetValueEx(syscall.Handle(k), p, 0, valtype, nil, 0)
	}
	return regSetValueEx(syscall.Handle(k), p, 0, valtype, &data[0], uint32(len(data)))
}

// SetDWordValue sets the data and type of a name value
// under key k to value and DWORD.
func (k Key) SetDWordValue(name string, value uint32) error {
	return k.setValue(name, DWORD, (*[4]byte)(unsafe.Pointer(&value))[:])
}

// SetQWordValue sets the data and type of a name value
// under key k to value and QWORD.
func (k Key) SetQWordValue(name string, value uint64) error {
	return k.setValue(name, QWORD, (*[8]byte)(unsafe.Pointer(&value))[:])
}

func (k Key) setStringValue(name string, valtype uint32, value string) error {
	v, err := syscall.UTF16FromString(value)
	if err != nil {
		return err
	}
	buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[: len(v)*2 : len(v)*2]
	return k.setValue(name, valtype, buf)
}

// SetStringValue sets the data and type of a name value
// under key k to value and SZ. The value must not contain a zero byte.
func (k Key) SetStringValue(name, value string) error {
	return k.setStringValue(name, SZ, value)
}

// SetExpandStringValue sets the data and type of a name value
// under key k to value and EXPAND_SZ. The value must not contain a zero byte.
func (k Key) SetExpandStringValue(name, value string) error {
	return k.setStringValue(name, EXPAND_SZ, value)
}

// SetStringsValue sets the data and type of a name value
// under key k to value and MULTI_SZ. The value strings
// must not contain a zero byte.
func (k Key) SetStringsValue(name string, value []string) error {
	ss := ""
	for _, s := range value {
		for i := 0; i < len(s); i++ {
			if s[i] == 0 {
				return errors.New("string cannot have 0 inside")
			}
		}
		ss += s + "\x00"
	}
	v := utf16.Encode([]rune(ss + "\x00"))
	buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[: len(v)*2 : len(v)*2]
	return k.setValue(name, MULTI_SZ, buf)
}

// SetBinaryValue sets the data and type of a name value
// under key k to value and BINARY.
func (k Key) SetBinaryValue(name string, value []byte) error {
	return k.setValue(name, BINARY, value)
}

// DeleteValue removes a named value from the key k.
func (k Key) DeleteValue(name string) error {
	return regDeleteValue(syscall.Handle(k), syscall.StringToUTF16Ptr(name))
}

// ReadValueNames returns the value names of key k.
// The parameter n controls the number of returned names,
// analogous to the way os.File.Readdirnames works.
func (k Key) ReadValueNames(n int) ([]string, error) {
	ki, err := k.Stat()
	if err != nil {
		return nil, err
	}
	names := make([]string, 0, ki.ValueCount)
	buf := make([]uint16, ki.MaxValueNameLen+1) // extra room for terminating null character
loopItems:
	for i := uint32(0); ; i++ {
		if n > 0 {
			if len(names) == n {
				return names, nil
			}
		}
		l := uint32(len(buf))
		for {
			err := regEnumValue(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil)
			if err == nil {
				break
			}
			if err == syscall.ERROR_MORE_DATA {
				// Double buffer size and try again.
				l = uint32(2 * len(buf))
				buf = make([]uint16, l)
				continue
			}
			if err == _ERROR_NO_MORE_ITEMS {
				break loopItems
			}
			return names, err
		}
		names = append(names, syscall.UTF16ToString(buf[:l]))
	}
	if n > len(names) {
		return names, io.EOF
	}
	return names, nil
}
<|endoftext|>


<commit_before>package vagrant

import (
	"fmt"
	"path/filepath"
	"strings"

	"github.com/hashicorp/otto/app"
	"github.com/hashicorp/otto/directory"
)

// DevOptions is the configuration struct used for Dev.
type DevOptions struct {
	// Dir is the path to the directory with the Vagrantfile. This
	// will default to `#{ctx.Dir}/dev` if empty.
	Dir string

	// DataDir is the path to the directory where Vagrant should store its data.
	// Defaults to `#{ctx.LocalDir/vagrant}` if empty.
	DataDir string

	// Instructions are help text that is shown after creating the
	// development environment.
	Instructions string
}

// Dev can be used as an implementation of app.App.Dev to automatically
// handle creating a development environment and forwarding commands down
// to Vagrant.
func Dev(opts *DevOptions) *app.Router {
	return &app.Router{
		Actions: map[string]*app.Action{
			"": &app.Action{
				Execute:  opts.actionUp,
				Synopsis: actionUpSyn,
				Help:     strings.TrimSpace(actionUpHelp),
			},

			"destroy": &app.Action{
				Execute:  opts.actionDestroy,
				Synopsis: actionDestroySyn,
				Help:     strings.TrimSpace(actionDestroyHelp),
			},

			"ssh": &app.Action{
				Execute:  opts.actionSSH,
				Synopsis: actionSSHSyn,
				Help:     strings.TrimSpace(actionSSHHelp),
			},

			"vagrant": &app.Action{
				Execute:  opts.actionRaw,
				Synopsis: actionVagrantSyn,
				Help:     strings.TrimSpace(actionVagrantHelp),
			},
		},
	}
}

func (opts *DevOptions) actionDestroy(ctx *app.Context) error {
	project := Project(&ctx.Shared)
	if err := project.InstallIfNeeded(); err != nil {
		return err
	}

	ctx.Ui.Header("Destroying the local development environment...")

	if err := opts.vagrant(ctx).Execute("destroy", "-f"); err != nil {
		return err
	}
	ctx.Ui.Raw("\n")

	// Store the dev status into the directory. We just do this before
	// since there are a lot of cases where Vagrant fails but still imported.
	// We just override any prior dev.
	ctx.Ui.Header("Deleting development environment metadata...")
	dev := &directory.Dev{Lookup: directory.Lookup{AppID: ctx.Appfile.ID}}
	if err := ctx.Directory.DeleteDev(dev); err != nil {
		return fmt.Errorf(
			"Error deleting dev environment metadata: %s", err)
	}

	ctx.Ui.Header("[green]Development environment has been destroyed!")
	return nil
}

func (opts *DevOptions) actionRaw(ctx *app.Context) error {
	project := Project(&ctx.Shared)
	if err := project.InstallIfNeeded(); err != nil {
		return err
	}

	ctx.Ui.Header(fmt.Sprintf(
		"Executing: 'vagrant %s'", strings.Join(ctx.ActionArgs, " ")))

	if err := opts.vagrant(ctx).Execute(ctx.ActionArgs...); err != nil {
		return err
	}

	return nil
}

func (opts *DevOptions) actionSSH(ctx *app.Context) error {
	project := Project(&ctx.Shared)
	if err := project.InstallIfNeeded(); err != nil {
		return err
	}

	ctx.Ui.Header("Executing SSH. This may take a few seconds...")
	if err := opts.vagrant(ctx).Execute("ssh"); err != nil {
		return err
	}

	return nil
}

func (opts *DevOptions) actionUp(ctx *app.Context) error {
	project := Project(&ctx.Shared)
	if err := project.InstallIfNeeded(); err != nil {
		return err
	}

	// Output some info the user prior to running
	ctx.Ui.Header(
		"Creating local development environment with Vagrant if it doesn't exist...")
	ctx.Ui.Message(
		"Raw Vagrant output will begin streaming in below. Otto does\n" +
			"not create this output. It is mirrored directly from Vagrant\n" +
			"while the development environment is being created.\n\n")

	// Store the dev status into the directory. We just do this before
	// since there are a lot of cases where Vagrant fails but still imported.
	// We just override any prior dev.
	dev := &directory.Dev{Lookup: directory.Lookup{AppID: ctx.Appfile.ID}}
	dev.MarkReady()
	if err := ctx.Directory.PutDev(dev); err != nil {
		return fmt.Errorf(
			"Error saving dev environment metadata: %s", err)
	}

	// Run it!
	if err := opts.vagrant(ctx).Execute("up"); err != nil {
		return err
	}
	// Success, let the user know whats up
	ctx.Ui.Raw("\n")
	ctx.Ui.Header("[green]Development environment successfully created!")
	if opts.Instructions != "" {
		ctx.Ui.Message(opts.Instructions)
	}

	return nil
}

func (opts *DevOptions) vagrant(ctx *app.Context) *Vagrant {
	dir := opts.Dir
	if dir == "" {
		dir = filepath.Join(ctx.Dir, "dev")
	}
	dataDir := opts.DataDir
	if dataDir == "" {
		dataDir = filepath.Join(ctx.LocalDir, "vagrant")
	}
	return &Vagrant{
		Dir:     dir,
		DataDir: dataDir,
		Ui:      ctx.Ui,
	}
}

// Synopsis text for actions
const (
	actionUpSyn      = "Starts the development environment"
	actionDestroySyn = "Destroy the development environment"
	actionSSHSyn     = "SSH into the development environment"
	actionVagrantSyn = "Run arbitrary Vagrant commands"
)

// Help text for actions
const actionUpHelp = `
Usage: otto dev

  Builds and starts the development environment.

  The development environment runs locally via Vagrant. Otto manages
  Vagrant for you. All upstream dependencies will automatically be started
  and running within the development environment.

  At the end of running this command, help text will be shown that tell
  you how to interact with the build environment.
`

const actionDestroyHelp = `
Usage: otto dev destroy

  Destroys the development environment.

  This command will stop and delete the development environment.
  Any data that was put onto the development environment will be deleted,
  except for your own project's code (the directory and any subdirectories
  where the Appfile exists).

`

const actionSSHHelp = `
Usage: otto dev ssh

  Connect to the development environment via SSH.

  The development environment typically is headless, meaning that the
  preferred way to access it is SSH. This command will automatically SSH
  you into the development environment.

`

const actionVagrantHelp = `
Usage: otto dev vagrant [command...]

  Run arbitrary Vagrant commands against the development environment.

  This is for advanced users who know and are comfortable with Vagrant.
  In average day to day usage, this command isn't needed.

  Because the development environment is backed by Vagrant, this command
  lets you access it directly via Vagrant. For example, if you want to
  run "vagrant ssh-config" against the environment, you can use
  "otto dev vagrant ssh-config"

`
<commit_msg>helper/vagrant: cache SSH info<commit_after>package vagrant

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/hashicorp/otto/app"
	"github.com/hashicorp/otto/directory"
	"github.com/hashicorp/otto/ui"
)

// DevOptions is the configuration struct used for Dev.
type DevOptions struct {
	// Dir is the path to the directory with the Vagrantfile. This
	// will default to `#{ctx.Dir}/dev` if empty.
	Dir string

	// DataDir is the path to the directory where Vagrant should store its data.
	// Defaults to `#{ctx.LocalDir/vagrant}` if empty.
	DataDir string

	// Instructions are help text that is shown after creating the
	// development environment.
	Instructions string
}

// Dev can be used as an implementation of app.App.Dev to automatically
// handle creating a development environment and forwarding commands down
// to Vagrant.
func Dev(opts *DevOptions) *app.Router {
	return &app.Router{
		Actions: map[string]*app.Action{
			"": &app.Action{
				Execute:  opts.actionUp,
				Synopsis: actionUpSyn,
				Help:     strings.TrimSpace(actionUpHelp),
			},

			"destroy": &app.Action{
				Execute:  opts.actionDestroy,
				Synopsis: actionDestroySyn,
				Help:     strings.TrimSpace(actionDestroyHelp),
			},

			"ssh": &app.Action{
				Execute:  opts.actionSSH,
				Synopsis: actionSSHSyn,
				Help:     strings.TrimSpace(actionSSHHelp),
			},

			"vagrant": &app.Action{
				Execute:  opts.actionRaw,
				Synopsis: actionVagrantSyn,
				Help:     strings.TrimSpace(actionVagrantHelp),
			},
		},
	}
}

func (opts *DevOptions) actionDestroy(ctx *app.Context) error {
	project := Project(&ctx.Shared)
	if err := project.InstallIfNeeded(); err != nil {
		return err
	}

	ctx.Ui.Header("Destroying the local development environment...")

	if err := opts.vagrant(ctx).Execute("destroy", "-f"); err != nil {
		return err
	}
	ctx.Ui.Raw("\n")

	// Store the dev status into the directory. We just do this before
	// since there are a lot of cases where Vagrant fails but still imported.
	// We just override any prior dev.
	ctx.Ui.Header("Deleting development environment metadata...")
	dev := &directory.Dev{Lookup: directory.Lookup{AppID: ctx.Appfile.ID}}
	if err := ctx.Directory.DeleteDev(dev); err != nil {
		return fmt.Errorf(
			"Error deleting dev environment metadata: %s", err)
	}

	ctx.Ui.Header("[green]Development environment has been destroyed!")
	return nil
}

func (opts *DevOptions) actionRaw(ctx *app.Context) error {
	project := Project(&ctx.Shared)
	if err := project.InstallIfNeeded(); err != nil {
		return err
	}

	ctx.Ui.Header(fmt.Sprintf(
		"Executing: 'vagrant %s'", strings.Join(ctx.ActionArgs, " ")))

	if err := opts.vagrant(ctx).Execute(ctx.ActionArgs...); err != nil {
		return err
	}

	return nil
}

func (opts *DevOptions) actionSSH(ctx *app.Context) error {
	// Check if we have an SSH cache. If so, use that.
	path := filepath.Join(ctx.CacheDir, "dev_ssh_cache")
	if _, err := os.Stat(path); err == nil {
		ctx.Ui.Header("Executing SSH with cached SSH info...")
		cmd := exec.Command("ssh", "-F", path, "default")
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Start(); err != nil {
			return err
		}
		if err := cmd.Wait(); err != nil {
			return err
		}
		return nil
	}

	project := Project(&ctx.Shared)
	if err := project.InstallIfNeeded(); err != nil {
		return err
	}

	ctx.Ui.Header("Executing SSH. This may take a few seconds...")
	if err := opts.vagrant(ctx).Execute("ssh"); err != nil {
		return err
	}

	return nil
}

func (opts *DevOptions) actionUp(ctx *app.Context) error {
	project := Project(&ctx.Shared)
	if err := project.InstallIfNeeded(); err != nil {
		return err
	}

	// Output some info the user prior to running
	ctx.Ui.Header(
		"Creating local development environment with Vagrant if it doesn't exist...")
	ctx.Ui.Message(
		"Raw Vagrant output will begin streaming in below. Otto does\n" +
			"not create this output. It is mirrored directly from Vagrant\n" +
			"while the development environment is being created.\n\n")

	// Store the dev status into the directory. We just do this before
	// since there are a lot of cases where Vagrant fails but still imported.
	// We just override any prior dev.
	dev := &directory.Dev{Lookup: directory.Lookup{AppID: ctx.Appfile.ID}}
	dev.MarkReady()
	if err := ctx.Directory.PutDev(dev); err != nil {
		return fmt.Errorf(
			"Error saving dev environment metadata: %s", err)
	}

	// Run it!
	if err := opts.vagrant(ctx).Execute("up"); err != nil {
		return err
	}

	// Cache the SSH info
	ctx.Ui.Header("Caching SSH credentials from Vagrant...")
	if err := opts.sshCacheInit(ctx); err != nil {
		return err
	}

	// Success, let the user know whats up
	ctx.Ui.Header("[green]Development environment successfully created!")
	if opts.Instructions != "" {
		ctx.Ui.Message(opts.Instructions)
	}

	return nil
}

func (opts *DevOptions) vagrant(ctx *app.Context) *Vagrant {
	dir := opts.Dir
	if dir == "" {
		dir = filepath.Join(ctx.Dir, "dev")
	}
	dataDir := opts.DataDir
	if dataDir == "" {
		dataDir = filepath.Join(ctx.LocalDir, "vagrant")
	}
	return &Vagrant{
		Dir:     dir,
		DataDir: dataDir,
		Ui:      ctx.Ui,
	}
}

func (opts *DevOptions) sshCacheInit(ctx *app.Context) error {
	// Create a Vagrant instance with a mock UI so we can capture
	// all the output in memory.
	var mockUi ui.Mock
	vagrant := opts.vagrant(ctx)
	vagrant.Ui = &mockUi
	if err := vagrant.Execute("ssh-config"); err != nil {
		return err
	}

	// Write the output to the cache
	path := filepath.Join(ctx.CacheDir, "dev_ssh_cache")
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	defer f.Close()

	for _, raw := range mockUi.RawBuf {
		if _, err := io.Copy(f, strings.NewReader(raw)); err != nil {
			return err
		}
	}

	return nil
}

// Synopsis text for actions
const (
	actionUpSyn      = "Starts the development environment"
	actionDestroySyn = "Destroy the development environment"
	actionSSHSyn     = "SSH into the development environment"
	actionVagrantSyn = "Run arbitrary Vagrant commands"
)

// Help text for actions
const actionUpHelp = `
Usage: otto dev

  Builds and starts the development environment.

  The development environment runs locally via Vagrant. Otto manages
  Vagrant for you. All upstream dependencies will automatically be started
  and running within the development environment.

  At the end of running this command, help text will be shown that tell
  you how to interact with the build environment.
`

const actionDestroyHelp = `
Usage: otto dev destroy

  Destroys the development environment.

  This command will stop and delete the development environment.
  Any data that was put onto the development environment will be deleted,
  except for your own project's code (the directory and any subdirectories
  where the Appfile exists).

`

const actionSSHHelp = `
Usage: otto dev ssh

  Connect to the development environment via SSH.

  The development environment typically is headless, meaning that the
  preferred way to access it is SSH. This command will automatically SSH
  you into the development environment.

`

const actionVagrantHelp = `
Usage: otto dev vagrant [command...]

  Run arbitrary Vagrant commands against the development environment.

  This is for advanced users who know and are comfortable with Vagrant.
  In average day to day usage, this command isn't needed.

  Because the development environment is backed by Vagrant, this command
  lets you access it directly via Vagrant. For example, if you want to
  run "vagrant ssh-config" against the environment, you can use
  "otto dev vagrant ssh-config"

`
<|endoftext|>


<commit_before>package hookworm

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"strings"
	"text/template"
	"time"
)

var (
	rfc2822DateFmt        = "Mon, 02 Jan 2006 15:04:05 -0700"
	hostname              string
	secretCommitEmailTmpl = template.Must(template.New("email").Parse(`From: {{.From}}
To: {{.Recipients}}
Subject: [hookworm] Secret commit! {{.Repo}} {{.Ref}} {{.HeadCommitId}}
Date: {{.Date}}
Message-ID: <{{.MessageId}}@{{.Hostname}}>
List-ID: {{.Repo}} <hookworm.github.com>
Content-Type: multipart/alternative;
  boundary="--==ZOMGBOUNDARAAAYYYYY";
  charset=UTF-8
Content-Transfer-Encoding: 7bit

----==ZOMGBOUNDARAAAYYYYY
Date: {{.Date}}
Mime-Version: 1.0
Content-Type: text/plain; charset=utf8
Content-Transfer-Encoding: 7bit

Secret commit detected on {{.Repo}} {{.Ref}}

Id        {{.HeadCommitUrl}}
Message   {{.HeadCommitMessage}}
Author    {{.HeadCommitAuthor}}
Committer {{.HeadCommitCommitter}}
Timestamp {{.HeadCommitTimestamp}}

----==ZOMGBOUNDARAAAYYYYY
Date: {{.Date}}
Mime-Version: 1.0
Content-Type: text/html; charset=utf8
Content-Transfer-Encoding: 7bit

Secret commit detected on {{.Repo}} {{.Ref}}

Id:	{{.HeadCommitId}}

Message:	{{.HeadCommitMessage}}

Author:	{{.HeadCommitAuthor}}

Committer:	{{.HeadCommitCommitter}}

Timestamp:	{{.HeadCommitTimestamp}}

----==ZOMGBOUNDARAAAYYYYY--
`))
)

func init() {
	var err error
	hostname, err = os.Hostname()
	if err != nil {
		hostname = "somewhere.local"
	}
}

type SecretSquirrelCommitHandler struct {
	debug           bool
	emailer         *Emailer
	fromAddr        string
	recipients      []string
	policedBranches []string
	nextHandler     Handler
}

type secretCommitEmailContext struct {
	From                string
	Recipients          string
	Date                string
	MessageId           string
	Hostname            string
	Repo                string
	Ref                 string
	HeadCommitId        string
	HeadCommitUrl       string
	HeadCommitAuthor    string
	HeadCommitCommitter string
	HeadCommitMessage   string
	HeadCommitTimestamp string
}

func (me *SecretSquirrelCommitHandler) HandlePayload(payload *Payload) error {
	if !me.isPolicedBranch(payload.Ref.String()) {
		if me.debug {
			log.Printf("%v is not a policed branch, yay!\n", payload.Ref.String())
		}
		return nil
	}

	if me.debug {
		log.Printf("%v is a policed branch!\n", payload.Ref.String())
	}

	if payload.IsPullRequestMerge() {
		if me.debug {
			log.Printf("%v is a pull request merge, yay!\n", payload.HeadCommit.Id.String())
		}
		return nil
	}

	if me.debug {
		log.Printf("%v is not a pull request merge!\n", payload.HeadCommit.Id.String())
	}

	if err := me.alert(payload); err != nil {
		if me.debug {
			log.Printf("ERROR sending alert: %+v\n", err)
		}
		return err
	}

	if me.debug {
		log.Printf("Sent alert to %+v\n", me.recipients)
	}
	return nil
}

func (me *SecretSquirrelCommitHandler) SetNextHandler(handler Handler) {
	me.nextHandler = handler
}

func (me *SecretSquirrelCommitHandler) NextHandler() Handler {
	return me.nextHandler
}

func (me *SecretSquirrelCommitHandler) isPolicedBranch(ref string) bool {
	sansRefsHeads := strings.Replace(ref, "refs/heads/", "", 1)
	for _, ref := range me.policedBranches {
		if ref == sansRefsHeads {
			return true
		}
	}
	return false
}

func (me *SecretSquirrelCommitHandler) alert(payload *Payload) error {
	log.Printf("WARNING secret squirrel commit! %+v, head commit: %+v\n",
		payload, payload.HeadCommit)
	if len(me.recipients) == 0 {
		log.Println("No email recipients specified, so no emailing!")
		return nil
	}

	hc := payload.HeadCommit
	ctx := &secretCommitEmailContext{
		From:       me.fromAddr,
		Recipients: strings.Join(me.recipients, ", "),
		Date:       time.Now().UTC().Format(rfc2822DateFmt),
		MessageId:  fmt.Sprintf("%v", time.Now().UTC().UnixNano()),
		Hostname:   hostname,
		Repo: fmt.Sprintf("%s/%s", payload.Repository.Owner.Name.String(),
			payload.Repository.Name.String()),
		Ref:                 payload.Ref.String(),
		HeadCommitId:        hc.Id.String(),
		HeadCommitUrl:       hc.Url.String(),
		HeadCommitAuthor:    hc.Author.Name.String(),
		HeadCommitCommitter: hc.Committer.Name.String(),
		HeadCommitMessage:   hc.Message.String(),
		HeadCommitTimestamp: hc.Timestamp.String(),
	}
	var emailBuf bytes.Buffer

	err := secretCommitEmailTmpl.Execute(&emailBuf, ctx)
	if err != nil {
		return err
	}

	if me.debug {
		log.Printf("Email message:\n%v\n", string(emailBuf.Bytes()))
	}
	return me.emailer.Send(me.fromAddr, me.recipients, emailBuf.Bytes())
}
<commit_msg>More email template changes after showing it to Michael<commit_after>package hookworm

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"strings"
	"text/template"
	"time"
)

var (
	rfc2822DateFmt        = "Mon, 02 Jan 2006 15:04:05 -0700"
	hostname              string
	secretCommitEmailTmpl = template.Must(template.New("email").Parse(`From: {{.From}}
To: {{.Recipients}}
Subject: [hookworm] Secret commit by {{.HeadCommitAuthor}} to {{.Repo}} {{.Ref}} ({{.HeadCommitId}})
Date: {{.Date}}
Message-ID: <{{.MessageId}}@{{.Hostname}}>
List-ID: {{.Repo}} <hookworm.github.com>
Content-Type: multipart/alternative;
  boundary="--==ZOMGBOUNDARAAAYYYYY";
  charset=UTF-8
Content-Transfer-Encoding: 7bit

----==ZOMGBOUNDARAAAYYYYY
Date: {{.Date}}
Mime-Version: 1.0
Content-Type: text/plain; charset=utf8
Content-Transfer-Encoding: 7bit

Secret commit detected!

Repo      {{.Repo}}
Ref       {{.Ref}}
Id        {{.HeadCommitUrl}}
Message   {{.HeadCommitMessage}}
Author    {{.HeadCommitAuthor}}
Committer {{.HeadCommitCommitter}}
Timestamp {{.HeadCommitTimestamp}}

----==ZOMGBOUNDARAAAYYYYY
Date: {{.Date}}
Mime-Version: 1.0
Content-Type: text/html; charset=utf8
Content-Transfer-Encoding: 7bit

Secret commit detected!

Repo:	{{.Repo}}

Ref:	{{.Ref}}

Id:	{{.HeadCommitId}}

Message:	{{.HeadCommitMessage}}

Author:	{{.HeadCommitAuthor}}

Committer:	{{.HeadCommitCommitter}}

Timestamp:	{{.HeadCommitTimestamp}}

----==ZOMGBOUNDARAAAYYYYY--
`))
)

func init() {
	var err error
	hostname, err = os.Hostname()
	if err != nil {
		hostname = "somewhere.local"
	}
}

type SecretSquirrelCommitHandler struct {
	debug           bool
	emailer         *Emailer
	fromAddr        string
	recipients      []string
	policedBranches []string
	nextHandler     Handler
}

type secretCommitEmailContext struct {
	From                string
	Recipients          string
	Date                string
	MessageId           string
	Hostname            string
	Repo                string
	Ref                 string
	HeadCommitId        string
	HeadCommitUrl       string
	HeadCommitAuthor    string
	HeadCommitCommitter string
	HeadCommitMessage   string
	HeadCommitTimestamp string
}

func (me *SecretSquirrelCommitHandler) HandlePayload(payload *Payload) error {
	if !me.isPolicedBranch(payload.Ref.String()) {
		if me.debug {
			log.Printf("%v is not a policed branch, yay!\n", payload.Ref.String())
		}
		return nil
	}

	if me.debug {
		log.Printf("%v is a policed branch!\n", payload.Ref.String())
	}

	if payload.IsPullRequestMerge() {
		if me.debug {
			log.Printf("%v is a pull request merge, yay!\n", payload.HeadCommit.Id.String())
		}
		return nil
	}

	if me.debug {
		log.Printf("%v is not a pull request merge!\n", payload.HeadCommit.Id.String())
	}

	if err := me.alert(payload); err != nil {
		if me.debug {
			log.Printf("ERROR sending alert: %+v\n", err)
		}
		return err
	}

	if me.debug {
		log.Printf("Sent alert to %+v\n", me.recipients)
	}
	return nil
}

func (me *SecretSquirrelCommitHandler) SetNextHandler(handler Handler) {
	me.nextHandler = handler
}

func (me *SecretSquirrelCommitHandler) NextHandler() Handler {
	return me.nextHandler
}

func (me *SecretSquirrelCommitHandler) isPolicedBranch(ref string) bool {
	sansRefsHeads := strings.Replace(ref, "refs/heads/", "", 1)
	for _, ref := range me.policedBranches {
		if ref == sansRefsHeads {
			return true
		}
	}
	return false
}

func (me *SecretSquirrelCommitHandler) alert(payload *Payload) error {
	log.Printf("WARNING secret squirrel commit! %+v, head commit: %+v\n",
		payload, payload.HeadCommit)
	if len(me.recipients) == 0 {
		log.Println("No email recipients specified, so no emailing!")
		return nil
	}

	hc := payload.HeadCommit
	ctx := &secretCommitEmailContext{
		From:       me.fromAddr,
		Recipients: strings.Join(me.recipients, ", "),
		Date:       time.Now().UTC().Format(rfc2822DateFmt),
		MessageId:  fmt.Sprintf("%v", time.Now().UTC().UnixNano()),
		Hostname:   hostname,
		Repo: fmt.Sprintf("%s/%s", payload.Repository.Owner.Name.String(),
			payload.Repository.Name.String()),
		Ref:                 payload.Ref.String(),
		HeadCommitId:        hc.Id.String(),
		HeadCommitUrl:       hc.Url.String(),
		HeadCommitAuthor:    hc.Author.Name.String(),
		HeadCommitCommitter: hc.Committer.Name.String(),
		HeadCommitMessage:   hc.Message.String(),
		HeadCommitTimestamp: hc.Timestamp.String(),
	}
	var emailBuf bytes.Buffer

	err := secretCommitEmailTmpl.Execute(&emailBuf, ctx)
	if err != nil {
		return err
	}

	if me.debug {
		log.Printf("Email message:\n%v\n", string(emailBuf.Bytes()))
	}
	return me.emailer.Send(me.fromAddr, me.recipients, emailBuf.Bytes())
}
<|endoftext|>


<commit_before>// handler package implements a library for handling run lambda requests from
// the worker server.
package handler

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path"
	"sync"
	"sync/atomic"
	"time"

	"github.com/open-lambda/open-lambda/worker/config"
	"github.com/open-lambda/open-lambda/worker/handler/state"
	"github.com/open-lambda/open-lambda/worker/pool-manager/policy"
	"github.com/open-lambda/open-lambda/worker/registry"

	pmanager "github.com/open-lambda/open-lambda/worker/pool-manager"
	sb "github.com/open-lambda/open-lambda/worker/sandbox"
)

// HandlerSet represents a collection of Handlers of a worker server. It
// manages the Handler by HandlerLRU.
type HandlerSet struct {
	mutex     sync.Mutex
	handlers  map[string]*Handler
	regMgr    registry.RegistryManager
	sbFactory sb.SandboxFactory
	poolMgr   pmanager.PoolManager
	config    *config.Config
	lru       *HandlerLRU
	workerDir string
	pipMirror string
	hits      *int64
	misses    *int64
}

// Handler handles requests to run a lambda on a worker server. It handles
// concurrency and communicates with the sandbox manager to change the
// state of the container that servers the lambda.
type Handler struct {
	name       string
	mutex      sync.Mutex
	hset       *HandlerSet
	sandbox    sb.Sandbox
	lastPull   *time.Time
	state      state.HandlerState
	runners    int
	code       []byte
	codeDir    string
	pkgs       []string
	sandboxDir string
	fs         *policy.ForkServer
	usage      int
}

// NewHandlerSet creates an empty HandlerSet
func NewHandlerSet(opts *config.Config) (handlerSet *HandlerSet, err error) {
	rm, err := registry.InitRegistryManager(opts)
	if err != nil {
		return nil, err
	}

	sf, err := sb.InitSandboxFactory(opts)
	if err != nil {
		return nil, err
	}

	pm, err := pmanager.InitPoolManager(opts)
	if err != nil {
		return nil, err
	}

	var hits int64 = 0
	var misses int64 = 0
	handlers := make(map[string]*Handler)
	handlerSet = &HandlerSet{
		handlers:  handlers,
		regMgr:    rm,
		sbFactory: sf,
		poolMgr:   pm,
		lru:       NewHandlerLRU(&handlers, opts.Handler_cache_size), //kb
		workerDir: opts.Worker_dir,
		pipMirror: opts.Pip_mirror,
		hits:      &hits,
		misses:    &misses,
	}

	return handlerSet, nil
}

// Get always returns a Handler, creating one if necessarily.
func (h *HandlerSet) Get(name string) *Handler {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	handler := h.handlers[name]
	if handler == nil {
		sandboxDir := path.Join(h.workerDir, "handlers", name, "sandbox")
		handler = &Handler{
			name:       name,
			hset:       h,
			state:      state.Unitialized,
			runners:    0,
			pkgs:       []string{},
			sandboxDir: sandboxDir,
		}
		h.handlers[name] = handler
	}

	return handler
}

// Dump prints the name and state of the Handlers currently in the HandlerSet.
func (h *HandlerSet) Dump() {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	log.Printf("HANDLERS:\n")
	for k, v := range h.handlers {
		log.Printf("> %v: %v\n", k, v.state.String())
	}
}

// RunStart runs the lambda handled by this Handler. It checks if the code has
// been pulled, sandbox been created, and sandbox been started. The channel of
// the sandbox of this lambda is returned.
func (h *Handler) RunStart() (ch *sb.SandboxChannel, err error) {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	// get code if needed
	if h.lastPull == nil {
		codeDir, pkgs, err := h.hset.regMgr.Pull(h.name)
		if err != nil {
			return nil, err
		}

		now := time.Now()
		h.lastPull = &now
		h.codeDir = codeDir
		h.pkgs = pkgs
	}

	// create sandbox if needed
	if h.sandbox == nil {
		atomic.AddInt64(h.hset.misses, 1)
		if err := os.MkdirAll(h.sandboxDir, 0666); err != nil {
			return nil, err
		}

		sandbox, err := h.hset.sbFactory.Create(h.codeDir, h.sandboxDir, h.hset.pipMirror)
		if err != nil {
			return nil, err
		}

		h.sandbox = sandbox
		if h.state, err = sandbox.State(); err != nil {
			return nil, err
		}

		// newly created sandbox could be in any state; let it run
		if h.state == state.Stopped {
			if err := sandbox.Start(); err != nil {
				return nil, err
			}
		} else if h.state == state.Paused {
			if err := sandbox.Unpause(); err != nil {
				return nil, err
			}
		}

		if h.hset.poolMgr != nil {
			containerSB, ok := h.sandbox.(sb.ContainerSandbox)
			if !ok {
				return nil, errors.New("forkenter only supported with ContainerSandbox")
			}

			if h.fs, err = h.hset.poolMgr.Provision(containerSB, h.sandboxDir, h.pkgs); err != nil {
				return nil, err
			}
		}

		sockPath := fmt.Sprintf("%s/ol.sock", h.sandboxDir)

		// wait up to 30s for server to initialize
		start := time.Now()
		for ok := true; ok; ok = os.IsNotExist(err) {
			_, err = os.Stat(sockPath)
			if time.Since(start).Seconds() > 30 {
				return nil, errors.New(fmt.Sprintf("handler server failed to initialize after 30s"))
			}
		}

	} else if h.state == state.Paused { // unpause if paused
		atomic.AddInt64(h.hset.hits, 1)
		if err := h.sandbox.Unpause(); err != nil {
			return nil, err
		}
		h.hset.lru.Remove(h)
	}

	h.state = state.Running
	h.runners += 1

	log.Printf("HANDLER CACHE - hits: %v, misses: %v", h.hset.hits, h.hset.misses)
	return h.sandbox.Channel()
}

// RunFinish notifies that a request to run the lambda has completed. If no
// request is being run in its sandbox, sandbox will be paused and the handler
// be added to the HandlerLRU.
func (h *Handler) RunFinish() {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	h.runners -= 1

	// are we the last?
	if h.runners == 0 {
		if err := h.sandbox.Pause(); err != nil {
			// TODO(tyler): better way to handle this?  If
			// we can't pause, the handler gets to keep
			// running for free...
			log.Printf("Could not pause %v!  Error: %v\n", h.name, err)
		}
		h.state = state.Paused
		h.hset.lru.Add(h)
	}
}

// StopIfPaused stops the sandbox if it is paused.
func (h *Handler) StopIfPaused() {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	if h.state != state.Paused {
		return
	}

	h.state = state.Stopped
	if h.fs != nil {
		h.fs.Mutex.Lock()
		h.fs.Runners = false
		h.fs.Mutex.Unlock()
	}

	go func() {
		if err := h.sandbox.Unpause(); err != nil {
			log.Printf("Could not unpause %v to kill it!  Error: %v\n", h.name, err)
		} else if err := h.sandbox.Stop(); err != nil {
			log.Printf("Could not kill %v after unpausing!  Error: %v\n", h.name, err)
		} else {
		}
	}()
}

// Sandbox returns the sandbox of this Handler.
func (h *Handler) Sandbox() sb.Sandbox {
	return h.sandbox
}
<commit_msg>fix handler cache hit counting<commit_after>// handler package implements a library for handling run lambda requests from
// the worker server.
package handler

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path"
	"sync"
	"sync/atomic"
	"time"

	"github.com/open-lambda/open-lambda/worker/config"
	"github.com/open-lambda/open-lambda/worker/handler/state"
	"github.com/open-lambda/open-lambda/worker/pool-manager/policy"
	"github.com/open-lambda/open-lambda/worker/registry"

	pmanager "github.com/open-lambda/open-lambda/worker/pool-manager"
	sb "github.com/open-lambda/open-lambda/worker/sandbox"
)

// HandlerSet represents a collection of Handlers of a worker server. It
// manages the Handler by HandlerLRU.
type HandlerSet struct {
	mutex     sync.Mutex
	handlers  map[string]*Handler
	regMgr    registry.RegistryManager
	sbFactory sb.SandboxFactory
	poolMgr   pmanager.PoolManager
	config    *config.Config
	lru       *HandlerLRU
	workerDir string
	pipMirror string
	hits      *int64
	misses    *int64
}

// Handler handles requests to run a lambda on a worker server. It handles
// concurrency and communicates with the sandbox manager to change the
// state of the container that servers the lambda.
type Handler struct {
	name       string
	mutex      sync.Mutex
	hset       *HandlerSet
	sandbox    sb.Sandbox
	lastPull   *time.Time
	state      state.HandlerState
	runners    int
	code       []byte
	codeDir    string
	pkgs       []string
	sandboxDir string
	fs         *policy.ForkServer
	usage      int
}

// NewHandlerSet creates an empty HandlerSet
func NewHandlerSet(opts *config.Config) (handlerSet *HandlerSet, err error) {
	rm, err := registry.InitRegistryManager(opts)
	if err != nil {
		return nil, err
	}

	sf, err := sb.InitSandboxFactory(opts)
	if err != nil {
		return nil, err
	}

	pm, err := pmanager.InitPoolManager(opts)
	if err != nil {
		return nil, err
	}

	var hits int64 = 0
	var misses int64 = 0
	handlers := make(map[string]*Handler)
	handlerSet = &HandlerSet{
		handlers:  handlers,
		regMgr:    rm,
		sbFactory: sf,
		poolMgr:   pm,
		lru:       NewHandlerLRU(&handlers, opts.Handler_cache_size), //kb
		workerDir: opts.Worker_dir,
		pipMirror: opts.Pip_mirror,
		hits:      &hits,
		misses:    &misses,
	}

	return handlerSet, nil
}

// Get always returns a Handler, creating one if necessarily.
func (h *HandlerSet) Get(name string) *Handler {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	handler := h.handlers[name]
	if handler == nil {
		sandboxDir := path.Join(h.workerDir, "handlers", name, "sandbox")
		handler = &Handler{
			name:       name,
			hset:       h,
			state:      state.Unitialized,
			runners:    0,
			pkgs:       []string{},
			sandboxDir: sandboxDir,
		}
		h.handlers[name] = handler
	}

	return handler
}

// Dump prints the name and state of the Handlers currently in the HandlerSet.
func (h *HandlerSet) Dump() {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	log.Printf("HANDLERS:\n")
	for k, v := range h.handlers {
		log.Printf("> %v: %v\n", k, v.state.String())
	}
}

// RunStart runs the lambda handled by this Handler. It checks if the code has
// been pulled, sandbox been created, and sandbox been started. The channel of
// the sandbox of this lambda is returned.
func (h *Handler) RunStart() (ch *sb.SandboxChannel, err error) {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	// get code if needed
	if h.lastPull == nil {
		codeDir, pkgs, err := h.hset.regMgr.Pull(h.name)
		if err != nil {
			return nil, err
		}

		now := time.Now()
		h.lastPull = &now
		h.codeDir = codeDir
		h.pkgs = pkgs
	}

	// create sandbox if needed
	if h.sandbox == nil {
		atomic.AddInt64(h.hset.misses, 1)
		if err := os.MkdirAll(h.sandboxDir, 0666); err != nil {
			return nil, err
		}

		sandbox, err := h.hset.sbFactory.Create(h.codeDir, h.sandboxDir, h.hset.pipMirror)
		if err != nil {
			return nil, err
		}

		h.sandbox = sandbox
		if h.state, err = sandbox.State(); err != nil {
			return nil, err
		}

		// newly created sandbox could be in any state; let it run
		if h.state == state.Stopped {
			if err := sandbox.Start(); err != nil {
				return nil, err
			}
		} else if h.state == state.Paused {
			if err := sandbox.Unpause(); err != nil {
				return nil, err
			}
		}

		if h.hset.poolMgr != nil {
			containerSB, ok := h.sandbox.(sb.ContainerSandbox)
			if !ok {
				return nil, errors.New("forkenter only supported with ContainerSandbox")
			}

			if h.fs, err = h.hset.poolMgr.Provision(containerSB, h.sandboxDir, h.pkgs); err != nil {
				return nil, err
			}
		}

		sockPath := fmt.Sprintf("%s/ol.sock", h.sandboxDir)

		// wait up to 30s for server to initialize
		start := time.Now()
		for ok := true; ok; ok = os.IsNotExist(err) {
			_, err = os.Stat(sockPath)
			if time.Since(start).Seconds() > 45 {
				return nil, errors.New(fmt.Sprintf("handler server failed to initialize after 30s"))
			}
		}

	} else if h.state == state.Paused { // unpause if paused
		atomic.AddInt64(h.hset.hits, 1)
		if err := h.sandbox.Unpause(); err != nil {
			return nil, err
		}
		h.hset.lru.Remove(h)
	} else {
		atomic.AddInt64(h.hset.hits, 1)
	}

	h.state = state.Running
	h.runners += 1

	log.Printf("HANDLER CACHE - hits: %v, misses: %v", *h.hset.hits, *h.hset.misses)
	return h.sandbox.Channel()
}

// RunFinish notifies that a request to run the lambda has completed. If no
// request is being run in its sandbox, sandbox will be paused and the handler
// be added to the HandlerLRU.
func (h *Handler) RunFinish() {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	h.runners -= 1

	// are we the last?
	if h.runners == 0 {
		if err := h.sandbox.Pause(); err != nil {
			// TODO(tyler): better way to handle this?  If
			// we can't pause, the handler gets to keep
			// running for free...
			log.Printf("Could not pause %v!  Error: %v\n", h.name, err)
		}
		h.state = state.Paused
		h.hset.lru.Add(h)
	}
}

// StopIfPaused stops the sandbox if it is paused.
func (h *Handler) StopIfPaused() {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	if h.state != state.Paused {
		return
	}

	h.state = state.Stopped
	if h.fs != nil {
		h.fs.Mutex.Lock()
		h.fs.Runners = false
		h.fs.Mutex.Unlock()
	}

	go func() {
		if err := h.sandbox.Unpause(); err != nil {
			log.Printf("Could not unpause %v to kill it!  Error: %v\n", h.name, err)
		} else if err := h.sandbox.Stop(); err != nil {
			log.Printf("Could not kill %v after unpausing!  Error: %v\n", h.name, err)
		} else {
		}
	}()
}

// Sandbox returns the sandbox of this Handler.
func (h *Handler) Sandbox() sb.Sandbox {
	return h.sandbox
}
<|endoftext|>


<commit_before>// Copyright 2013 Artur Grabowski. All rights reserved.
// Use of this source code is governed by a ISC-style
// license that can be found in the LICENSE file.
package bconf

import (
	"encoding/json"
)

// Populate a Bconf with data from a byte array that contains json. Returns json parsing errors.
func (bc *Bconf) LoadJson(js []byte) error {
	err := json.Unmarshal(js, bc)

	if bc != nil && len(*bc) > 0 {
		*bc = normalize(*bc)
	}

	return err
}

/*
 * Normalize what we read from json by changing the data types from
 * map[string]interface{} into Bconf
 */
func normalize(bc Bconf) Bconf {
	nb := make(Bconf)
	for k, v := range bc {
		if iv, ok := v.(map[string]interface{}); ok {
			nb[k] = normalize(iv)
		} else {
			nb[k] = v
		}
	}
	return nb
}
<commit_msg>JSON from io.Reader.<commit_after>// Copyright 2013 Artur Grabowski. All rights reserved.
// Use of this source code is governed by a ISC-style
// license that can be found in the LICENSE file.
package bconf

import (
	"encoding/json"
	"io"
)

// Populate a Bconf with data from a byte array that contains json. Returns json parsing errors.
func (bc *Bconf) LoadJson(js []byte) error {
	err := json.Unmarshal(js, bc)

	if bc != nil && len(*bc) > 0 {
		*bc = normalize(*bc)
	}

	return err
}

func (bc *Bconf) LoadJSONReader(r io.Reader) error {
	d := json.NewDecoder(r)
	err := d.Decode(bc)

	if bc != nil && len(*bc) > 0 {
		*bc = normalize(*bc)
	}

	return err
}

/*
 * Normalize what we read from json by changing the data types from
 * map[string]interface{} into Bconf
 */
func normalize(bc Bconf) Bconf {
	nb := make(Bconf)
	for k, v := range bc {
		if iv, ok := v.(map[string]interface{}); ok {
			nb[k] = normalize(iv)
		} else {
			nb[k] = v
		}
	}
	return nb
}
<|endoftext|>


<commit_before>package zog

import "fmt"

type instruction interface {
	String() string
}

type LD8 struct {
	dst Dst8
	src Src8
}

func (l *LD8) String() string {
	return fmt.Sprintf("LD %s, %s", l.dst, l.src)
}

type INC8 struct {
	l Loc8
}

func (i *INC8) String() string {
	return fmt.Sprintf("INC %s", i.l)
}

type DEC8 struct {
	l Loc8
}

func (d *DEC8) String() string {
	return fmt.Sprintf("DEC %s", d.l)
}

type LD16 struct {
	dst Dst16
	src Src16
}

func (l *LD16) String() string {
	return fmt.Sprintf("LD %s, %s", l.dst, l.src)
}

type ADD16 struct {
	dst Dst16
	src Src16
}

func (a *ADD16) String() string {
	return fmt.Sprintf("ADD %s, %s", a.dst, a.src)
}

type INC16 struct {
	l Loc16
}

func (i *INC16) String() string {
	return fmt.Sprintf("INC %s", i.l)
}

type DEC16 struct {
	l Loc16
}

func (d *DEC16) String() string {
	return fmt.Sprintf("DEC %s", d.l)
}

type EX struct {
	dst Dst16
	src Src16
}

func (ex *EX) String() string {
	return fmt.Sprintf("EX %s, %s", ex.dst, ex.src)
}

type DJNZ struct {
	d Disp
}

func (d *DJNZ) String() string {
	return fmt.Sprintf("DJNZ %s", d.d)
}

type JR struct {
	c Conditional
	d Disp
}

func (j *JR) String() string {
	if j.c == True {
		return fmt.Sprintf("JR %s", j.d)
	} else {
		return fmt.Sprintf("JR %s, %s", j.c, j.d)
	}
}

type JP struct {
	c    Conditional
	addr Src16
}

func (jp *JP) String() string {
	if jp.c == True {
		return fmt.Sprintf("JP %s", jp.addr)
	} else {
		return fmt.Sprintf("JP %s, %s", jp.c, jp.addr)
	}
}

type CALL struct {
	c    Conditional
	addr Src16
}

func (c *CALL) String() string {
	if c.c == True {
		return fmt.Sprintf("CALL %s", c.addr)
	} else {
		return fmt.Sprintf("CALL %s, %s", c.c, c.addr)
	}
}

type OUT struct {
	port  Src8
	value Src8
}

func (o *OUT) String() string {
	return fmt.Sprintf("OUT (%s), %s", o.port, o.value)
}

type IN struct {
	dst  Dst8
	port Src8
}

func (i *IN) String() string {
	return fmt.Sprintf("IN %s, (%s)", i.dst, i.port)
}

type PUSH struct {
	src Src16
}

func (p *PUSH) String() string {
	return fmt.Sprintf("PUSH %s", p.src)
}

type POP struct {
	dst Dst16
}

func (p *POP) String() string {
	return fmt.Sprintf("POP %s", p.dst)
}

type RST struct {
	addr byte
}

func (r *RST) String() string {
	return fmt.Sprintf("RST %d", r.addr)
}

type RET struct {
	c Conditional
}

func (r *RET) String() string {
	if r.c == True {
		return "RET"
	} else {
		return fmt.Sprintf("RET %s", r.c)
	}
}

type AccumFunc func(a, b byte) byte

type Accum struct {
	//	f    AccumFunc
	src  Src8
	name string
}

func (a Accum) String() string {
	switch a.name {
	case "ADD", "ADC", "SBC":
		return fmt.Sprintf("%s A, %s", a.name, a.src)
	default:
		return fmt.Sprintf("%s %s", a.name, a.src)
	}
}

type ROT struct {
	name string
	r    Loc8
}

func (r *ROT) String() string {
	return fmt.Sprintf("%s %s", r.name, r.r)
}

type BIT struct {
	num byte
	r   Loc8
}

func (b *BIT) String() string {
	return fmt.Sprintf("BIT %d, %s", b.num, b.r)
}

type RES struct {
	num byte
	r   Loc8
}

func (r *RES) String() string {
	return fmt.Sprintf("RES %d, %s", r.num, r.r)
}

type SET struct {
	num byte
	r   Loc8
}

func (s *SET) String() string {
	return fmt.Sprintf("SET %d, %s", s.num, s.r)
}

type Simple byte

const (
	NOP Simple = 0x00

	HALT Simple = 0x76

	RLCA Simple = 0x07
	RRCA Simple = 0x0f
	RLA  Simple = 0x17
	RRA  Simple = 0x1f
	DAA  Simple = 0x27
	CPL  Simple = 0x2f
	SCF  Simple = 0x37
	CCF  Simple = 0x3f

	EXX Simple = 0xd9

	DI Simple = 0xf3
	EI Simple = 0xfb
)

func (s Simple) String() string {
	switch s {
	case NOP:
		return "NOP"

	case HALT:
		return "HALT"

	case RLCA:
		return "RLCA"
	case RRCA:
		return "RRCA"
	case RLA:
		return "RLA"
	case RRA:
		return "RRA"
	case DAA:
		return "DAA"
	case CPL:
		return "CPL"
	case SCF:
		return "SCF"
	case CCF:
		return "CCF"

	case EXX:
		return "EXX"

	case DI:
		return "DI"
	case EI:
		return "EI"
	default:
		panic(fmt.Sprintf("Unknown simple instruction: %02X", byte(s)))
	}
}
<commit_msg>use table instead of switch for simple name<commit_after>package zog

import "fmt"

type instruction interface {
	String() string
}

type LD8 struct {
	dst Dst8
	src Src8
}

func (l *LD8) String() string {
	return fmt.Sprintf("LD %s, %s", l.dst, l.src)
}

type INC8 struct {
	l Loc8
}

func (i *INC8) String() string {
	return fmt.Sprintf("INC %s", i.l)
}

type DEC8 struct {
	l Loc8
}

func (d *DEC8) String() string {
	return fmt.Sprintf("DEC %s", d.l)
}

type LD16 struct {
	dst Dst16
	src Src16
}

func (l *LD16) String() string {
	return fmt.Sprintf("LD %s, %s", l.dst, l.src)
}

type ADD16 struct {
	dst Dst16
	src Src16
}

func (a *ADD16) String() string {
	return fmt.Sprintf("ADD %s, %s", a.dst, a.src)
}

type INC16 struct {
	l Loc16
}

func (i *INC16) String() string {
	return fmt.Sprintf("INC %s", i.l)
}

type DEC16 struct {
	l Loc16
}

func (d *DEC16) String() string {
	return fmt.Sprintf("DEC %s", d.l)
}

type EX struct {
	dst Dst16
	src Src16
}

func (ex *EX) String() string {
	return fmt.Sprintf("EX %s, %s", ex.dst, ex.src)
}

type DJNZ struct {
	d Disp
}

func (d *DJNZ) String() string {
	return fmt.Sprintf("DJNZ %s", d.d)
}

type JR struct {
	c Conditional
	d Disp
}

func (j *JR) String() string {
	if j.c == True {
		return fmt.Sprintf("JR %s", j.d)
	} else {
		return fmt.Sprintf("JR %s, %s", j.c, j.d)
	}
}

type JP struct {
	c    Conditional
	addr Src16
}

func (jp *JP) String() string {
	if jp.c == True {
		return fmt.Sprintf("JP %s", jp.addr)
	} else {
		return fmt.Sprintf("JP %s, %s", jp.c, jp.addr)
	}
}

type CALL struct {
	c    Conditional
	addr Src16
}

func (c *CALL) String() string {
	if c.c == True {
		return fmt.Sprintf("CALL %s", c.addr)
	} else {
		return fmt.Sprintf("CALL %s, %s", c.c, c.addr)
	}
}

type OUT struct {
	port  Src8
	value Src8
}

func (o *OUT) String() string {
	return fmt.Sprintf("OUT (%s), %s", o.port, o.value)
}

type IN struct {
	dst  Dst8
	port Src8
}

func (i *IN) String() string {
	return fmt.Sprintf("IN %s, (%s)", i.dst, i.port)
}

type PUSH struct {
	src Src16
}

func (p *PUSH) String() string {
	return fmt.Sprintf("PUSH %s", p.src)
}

type POP struct {
	dst Dst16
}

func (p *POP) String() string {
	return fmt.Sprintf("POP %s", p.dst)
}

type RST struct {
	addr byte
}

func (r *RST) String() string {
	return fmt.Sprintf("RST %d", r.addr)
}

type RET struct {
	c Conditional
}

func (r *RET) String() string {
	if r.c == True {
		return "RET"
	} else {
		return fmt.Sprintf("RET %s", r.c)
	}
}

type AccumFunc func(a, b byte) byte

type Accum struct {
	//	f    AccumFunc
	src  Src8
	name string
}

func (a Accum) String() string {
	switch a.name {
	case "ADD", "ADC", "SBC":
		return fmt.Sprintf("%s A, %s", a.name, a.src)
	default:
		return fmt.Sprintf("%s %s", a.name, a.src)
	}
}

type ROT struct {
	name string
	r    Loc8
}

func (r *ROT) String() string {
	return fmt.Sprintf("%s %s", r.name, r.r)
}

type BIT struct {
	num byte
	r   Loc8
}

func (b *BIT) String() string {
	return fmt.Sprintf("BIT %d, %s", b.num, b.r)
}

type RES struct {
	num byte
	r   Loc8
}

func (r *RES) String() string {
	return fmt.Sprintf("RES %d, %s", r.num, r.r)
}

type SET struct {
	num byte
	r   Loc8
}

func (s *SET) String() string {
	return fmt.Sprintf("SET %d, %s", s.num, s.r)
}

type Simple byte

const (
	NOP Simple = 0x00

	HALT Simple = 0x76

	RLCA Simple = 0x07
	RRCA Simple = 0x0f
	RLA  Simple = 0x17
	RRA  Simple = 0x1f
	DAA  Simple = 0x27
	CPL  Simple = 0x2f
	SCF  Simple = 0x37
	CCF  Simple = 0x3f

	EXX Simple = 0xd9

	DI Simple = 0xf3
	EI Simple = 0xfb
)

var simpleName map[Simple]string = map[Simple]string{
	NOP: "NOP",

	HALT: "HALT",

	RLCA: "RLCA",
	RRCA: "RRCA",
	RLA:  "RLA",
	RRA:  "RRA",
	DAA:  "DAA",
	CPL:  "CPL",
	SCF:  "SCF",
	CCF:  "CCF",

	EXX: "EXX",

	DI: "DI",
	EI: "EI",
}

func (s Simple) String() string {
	name, ok := simpleName[s]
	if !ok {
		panic(fmt.Sprintf("Unknown simple instruction: %02X", byte(s)))
	}

	return name
}
<|endoftext|>


<commit_before>package internal

// VERSION of sail
const VERSION = "0.5.2"
<commit_msg>[auto] bump version to v0.6.0<commit_after>package internal

// VERSION of sail
const VERSION = "0.6.0"
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"text/template"
	"time"
)

const (
	letterBytes = "abcdefghijklmnopqrstuvwxyz0123456789"
	_rootdir    = "setup/first"
	_mkdirdir   = "setup/first"
)

type Server struct {
	Port string
}

type NewProject struct {
	Type string
	Name string
}

type Page struct {
	Result string
}

func randURL(n int) string {
	b := make([]byte, n)
	rand.Seed(time.Now().UnixNano())
	for i := range b {
		b[i] = letterBytes[rand.Intn(36)]
	}
	return string(b)
}

func (n NewProject) Run() string {
	n.Name += ".9en.co"
	if n.Type == "beta" {
		n.Name = randURL(5) + "-" + n.Name
	}
	err := os.MkdirAll(_mkdirdir+"/"+n.Name+"/public_html", 0755)
	if err != nil {
		panic(err)
	}
	return n.Name
}

func Handler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	r.ParseForm()
	t := ""
	if r.Method == "POST" && r.Form["appName"][0] != "" {
		n := &NewProject{Name: r.Form["appName"][0], Type: r.Form["appType"][0]}
		t = n.Run()
	}
	html, _ := ioutil.ReadFile(_rootdir + "/tmpl/prot-design.html")
	data := Page{Result: t}
	tmpl, _ := template.New("page").Parse(string(html))
	tmpl.Execute(w, data)
}

func main() {
	http.HandleFunc("/", Handler)
	http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir(_rootdir+"/css"))))
	http.ListenAndServe(":9000", nil)
}
<commit_msg>fix internalSystem.go<commit_after>package main

import (
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"text/template"
	"time"
)

const (
	letterBytes = "abcdefghijklmnopqrstuvwxyz0123456789"
	_rootdir    = "setup/first"
	_mkdirdir   = "setup/first"
)

type Server struct {
	Port string
}

type NewProject struct {
	Type string
	Name string
}

type Page struct {
	Result string
}

func randURL(n int) string {
	b := make([]byte, n)
	rand.Seed(time.Now().UnixNano())
	for i := range b {
		b[i] = letterBytes[rand.Intn(36)]
	}
	return string(b)
}

func (n NewProject) Run() string {
	n.Name += ".9en.co"
	if n.Type == "beta" {
		n.Name = randURL(5) + "-" + n.Name
	}
	err := os.MkdirAll(_mkdirdir+"/"+n.Name+"/public_html", 0775)
	if err != nil {
		panic(err)
	}
	return n.Name
}

func Handler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	r.ParseForm()
	t := ""
	if r.Method == "POST" && r.Form["appName"][0] != "" {
		n := &NewProject{Name: r.Form["appName"][0], Type: r.Form["appType"][0]}
		t = n.Run()
	}
	html, _ := ioutil.ReadFile(_rootdir + "/tmpl/prot-design.html")
	data := Page{Result: t}
	tmpl, _ := template.New("page").Parse(string(html))
	tmpl.Execute(w, data)
}

func main() {
	http.HandleFunc("/", Handler)
	http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir(_rootdir+"/css"))))
	http.ListenAndServe(":9000", nil)
}
<|endoftext|>