<commit_before>package gojsonschema

import (
	"bytes"
	"text/template"
)

var errorTemplates *template.Template

type (
	// RequiredError. ErrorDetails: property string
	RequiredError struct {
		ResultErrorFields
	}

	// InvalidTypeError. ErrorDetails: expected, given
	InvalidTypeError struct {
		ResultErrorFields
	}

	// NumberAnyOfError. ErrorDetails: -
	NumberAnyOfError struct {
		ResultErrorFields
	}

	// NumberOneOfError. ErrorDetails: -
	NumberOneOfError struct {
		ResultErrorFields
	}

	// NumberAllOfError. ErrorDetails: -
	NumberAllOfError struct {
		ResultErrorFields
	}

	// NumberNotError. ErrorDetails: -
	NumberNotError struct {
		ResultErrorFields
	}

	// MissingDependencyError. ErrorDetails: dependency
	MissingDependencyError struct {
		ResultErrorFields
	}

	// InternalError. ErrorDetails: error
	InternalError struct {
		ResultErrorFields
	}

	// EnumError. ErrorDetails: allowed
	EnumError struct {
		ResultErrorFields
	}

	// ArrayNoAdditionalItemsError. ErrorDetails: -
	ArrayNoAdditionalItemsError struct {
		ResultErrorFields
	}

	// ArrayMinItemsError. ErrorDetails: min
	ArrayMinItemsError struct {
		ResultErrorFields
	}

	// ArrayMaxItemsError. ErrorDetails: max
	ArrayMaxItemsError struct {
		ResultErrorFields
	}

	// ItemsMustBeUniqueError. ErrorDetails: type
	ItemsMustBeUniqueError struct {
		ResultErrorFields
	}

	// ArrayMinPropertiesError. ErrorDetails: min
	ArrayMinPropertiesError struct {
		ResultErrorFields
	}

	// ArrayMaxPropertiesError. ErrorDetails: max
	ArrayMaxPropertiesError struct {
		ResultErrorFields
	}

	// AdditionalPropertyNotAllowedError. ErrorDetails: property
	AdditionalPropertyNotAllowedError struct {
		ResultErrorFields
	}

	// InvalidPropertyPatternError. ErrorDetails: property, pattern
	InvalidPropertyPatternError struct {
		ResultErrorFields
	}

	// StringLengthGTEError. ErrorDetails: min
	StringLengthGTEError struct {
		ResultErrorFields
	}

	// StringLengthLTEError. ErrorDetails: max
	StringLengthLTEError struct {
		ResultErrorFields
	}

	// DoesNotMatchPatternError. ErrorDetails: pattern
	DoesNotMatchPatternError struct {
		ResultErrorFields
	}

	// DoesNotMatchFormatError. ErrorDetails: format
	DoesNotMatchFormatError struct {
		ResultErrorFields
	}

	// MultipleOfError. ErrorDetails: multiple
	MultipleOfError struct {
		ResultErrorFields
	}

	// NumberGTEError. ErrorDetails: min
	NumberGTEError struct {
		ResultErrorFields
	}

	// NumberGTError. ErrorDetails: min
	NumberGTError struct {
		ResultErrorFields
	}

	// NumberLTEError. ErrorDetails: max
	NumberLTEError struct {
		ResultErrorFields
	}

	// NumberLTError. ErrorDetails: max
	NumberLTError struct {
		ResultErrorFields
	}
)

// newError takes a ResultError type and sets the type, context, description, details, value, and field
func newError(err ResultError, context *jsonContext, value interface{}, locale locale, details ErrorDetails) {
	var t string
	var d string
	switch err.(type) {
	case *RequiredError:
		t = "required"
		d = locale.Required()
	case *InvalidTypeError:
		t = "invalid_type"
		d = locale.InvalidType()
	case *NumberAnyOfError:
		t = "number_any_of"
		d = locale.NumberAnyOf()
	case *NumberOneOfError:
		t = "number_one_of"
		d = locale.NumberOneOf()
	case *NumberAllOfError:
		t = "number_all_of"
		d = locale.NumberAllOf()
	case *NumberNotError:
		t = "number_not"
		d = locale.NumberNot()
	case *MissingDependencyError:
		t = "missing_dependency"
		d = locale.MissingDependency()
	case *InternalError:
		t = "internal"
		d = locale.Internal()
	case *EnumError:
		t = "enum"
		d = locale.Enum()
	case *ArrayNoAdditionalItemsError:
		t = "array_no_additional_items"
		d = locale.ArrayNoAdditionalItems()
	case *ArrayMinItemsError:
		t = "array_min_items"
		d = locale.ArrayMinItems()
	case *ArrayMaxItemsError:
		t = "array_max_items"
		d = locale.ArrayMaxItems()
	case *ItemsMustBeUniqueError:
		t = "unique"
		d = locale.Unique()
	case *ArrayMinPropertiesError:
		t = "array_min_properties"
		d = locale.ArrayMinProperties()
	case *ArrayMaxPropertiesError:
		t = "array_max_properties"
		d = locale.ArrayMaxProperties()
	case *AdditionalPropertyNotAllowedError:
		t = "additional_property_not_allowed"
		d = locale.AdditionalPropertyNotAllowed()
	case *InvalidPropertyPatternError:
		t = "invalid_property_pattern"
		d = locale.InvalidPropertyPattern()
	case *StringLengthGTEError:
		t = "string_gte"
		d = locale.StringGTE()
	case *StringLengthLTEError:
		t = "string_lte"
		d = locale.StringLTE()
	case *DoesNotMatchPatternError:
		t = "pattern"
		d = locale.DoesNotMatchPattern()
	case *DoesNotMatchFormatError:
		t = "format"
		d = locale.DoesNotMatchFormat()
	case *MultipleOfError:
		t = "multiple_of"
		d = locale.MultipleOf()
	case *NumberGTEError:
		t = "number_gte"
		d = locale.NumberGTE()
	case *NumberGTError:
		t = "number_gt"
		d = locale.NumberGT()
	case *NumberLTEError:
		t = "number_lte"
		d = locale.NumberLTE()
	case *NumberLTError:
		t = "number_lt"
		d = locale.NumberLT()
	}

	err.SetType(t)
	err.SetContext(context)
	err.SetValue(value)
	err.SetDetails(details)
	details["field"] = err.Field()
	err.SetDescription(formatErrorDescription(d, details))
}

// formatErrorDescription takes a string in the default text/template
// format and converts it to a string with replacements. The fields come
// from the ErrorDetails struct and vary for each type of error.
func formatErrorDescription(s string, details ErrorDetails) string {

	var tpl *template.Template
	var descrAsBuffer bytes.Buffer
	var err error

	if errorTemplates == nil {
		errorTemplates = template.New("all-errors")
	}

	tpl = errorTemplates.Lookup(s)
	if tpl == nil {
		tpl = errorTemplates.New(s)
		tpl, err = tpl.Parse(s)
		if err != nil {
			return err.Error()
		}
	}

	err = tpl.Execute(&descrAsBuffer, details)
	if err != nil {
		return err.Error()
	}

	return descrAsBuffer.String()
}
<commit_msg>Add a RWMutex to errorTemplates to make formatErrorDescription thread-safe.<commit_after>package gojsonschema

import (
	"bytes"
	"sync"
	"text/template"
)

var errorTemplates errorTemplate = errorTemplate{template.New("errors-new"),sync.RWMutex{}}

// template.Template is not thread-safe for writing, so some locking is done
// sync.RWMutex is used for efficiently locking when new templates are created
type errorTemplate struct {
	*template.Template
	sync.RWMutex
}

type (
	// RequiredError. ErrorDetails: property string
	RequiredError struct {
		ResultErrorFields
	}

	// InvalidTypeError. ErrorDetails: expected, given
	InvalidTypeError struct {
		ResultErrorFields
	}

	// NumberAnyOfError. ErrorDetails: -
	NumberAnyOfError struct {
		ResultErrorFields
	}

	// NumberOneOfError. ErrorDetails: -
	NumberOneOfError struct {
		ResultErrorFields
	}

	// NumberAllOfError. ErrorDetails: -
	NumberAllOfError struct {
		ResultErrorFields
	}

	// NumberNotError. ErrorDetails: -
	NumberNotError struct {
		ResultErrorFields
	}

	// MissingDependencyError. ErrorDetails: dependency
	MissingDependencyError struct {
		ResultErrorFields
	}

	// InternalError. ErrorDetails: error
	InternalError struct {
		ResultErrorFields
	}

	// EnumError. ErrorDetails: allowed
	EnumError struct {
		ResultErrorFields
	}

	// ArrayNoAdditionalItemsError. ErrorDetails: -
	ArrayNoAdditionalItemsError struct {
		ResultErrorFields
	}

	// ArrayMinItemsError. ErrorDetails: min
	ArrayMinItemsError struct {
		ResultErrorFields
	}

	// ArrayMaxItemsError. ErrorDetails: max
	ArrayMaxItemsError struct {
		ResultErrorFields
	}

	// ItemsMustBeUniqueError. ErrorDetails: type
	ItemsMustBeUniqueError struct {
		ResultErrorFields
	}

	// ArrayMinPropertiesError. ErrorDetails: min
	ArrayMinPropertiesError struct {
		ResultErrorFields
	}

	// ArrayMaxPropertiesError. ErrorDetails: max
	ArrayMaxPropertiesError struct {
		ResultErrorFields
	}

	// AdditionalPropertyNotAllowedError. ErrorDetails: property
	AdditionalPropertyNotAllowedError struct {
		ResultErrorFields
	}

	// InvalidPropertyPatternError. ErrorDetails: property, pattern
	InvalidPropertyPatternError struct {
		ResultErrorFields
	}

	// StringLengthGTEError. ErrorDetails: min
	StringLengthGTEError struct {
		ResultErrorFields
	}

	// StringLengthLTEError. ErrorDetails: max
	StringLengthLTEError struct {
		ResultErrorFields
	}

	// DoesNotMatchPatternError. ErrorDetails: pattern
	DoesNotMatchPatternError struct {
		ResultErrorFields
	}

	// DoesNotMatchFormatError. ErrorDetails: format
	DoesNotMatchFormatError struct {
		ResultErrorFields
	}

	// MultipleOfError. ErrorDetails: multiple
	MultipleOfError struct {
		ResultErrorFields
	}

	// NumberGTEError. ErrorDetails: min
	NumberGTEError struct {
		ResultErrorFields
	}

	// NumberGTError. ErrorDetails: min
	NumberGTError struct {
		ResultErrorFields
	}

	// NumberLTEError. ErrorDetails: max
	NumberLTEError struct {
		ResultErrorFields
	}

	// NumberLTError. ErrorDetails: max
	NumberLTError struct {
		ResultErrorFields
	}
)

// newError takes a ResultError type and sets the type, context, description, details, value, and field
func newError(err ResultError, context *jsonContext, value interface{}, locale locale, details ErrorDetails) {
	var t string
	var d string
	switch err.(type) {
	case *RequiredError:
		t = "required"
		d = locale.Required()
	case *InvalidTypeError:
		t = "invalid_type"
		d = locale.InvalidType()
	case *NumberAnyOfError:
		t = "number_any_of"
		d = locale.NumberAnyOf()
	case *NumberOneOfError:
		t = "number_one_of"
		d = locale.NumberOneOf()
	case *NumberAllOfError:
		t = "number_all_of"
		d = locale.NumberAllOf()
	case *NumberNotError:
		t = "number_not"
		d = locale.NumberNot()
	case *MissingDependencyError:
		t = "missing_dependency"
		d = locale.MissingDependency()
	case *InternalError:
		t = "internal"
		d = locale.Internal()
	case *EnumError:
		t = "enum"
		d = locale.Enum()
	case *ArrayNoAdditionalItemsError:
		t = "array_no_additional_items"
		d = locale.ArrayNoAdditionalItems()
	case *ArrayMinItemsError:
		t = "array_min_items"
		d = locale.ArrayMinItems()
	case *ArrayMaxItemsError:
		t = "array_max_items"
		d = locale.ArrayMaxItems()
	case *ItemsMustBeUniqueError:
		t = "unique"
		d = locale.Unique()
	case *ArrayMinPropertiesError:
		t = "array_min_properties"
		d = locale.ArrayMinProperties()
	case *ArrayMaxPropertiesError:
		t = "array_max_properties"
		d = locale.ArrayMaxProperties()
	case *AdditionalPropertyNotAllowedError:
		t = "additional_property_not_allowed"
		d = locale.AdditionalPropertyNotAllowed()
	case *InvalidPropertyPatternError:
		t = "invalid_property_pattern"
		d = locale.InvalidPropertyPattern()
	case *StringLengthGTEError:
		t = "string_gte"
		d = locale.StringGTE()
	case *StringLengthLTEError:
		t = "string_lte"
		d = locale.StringLTE()
	case *DoesNotMatchPatternError:
		t = "pattern"
		d = locale.DoesNotMatchPattern()
	case *DoesNotMatchFormatError:
		t = "format"
		d = locale.DoesNotMatchFormat()
	case *MultipleOfError:
		t = "multiple_of"
		d = locale.MultipleOf()
	case *NumberGTEError:
		t = "number_gte"
		d = locale.NumberGTE()
	case *NumberGTError:
		t = "number_gt"
		d = locale.NumberGT()
	case *NumberLTEError:
		t = "number_lte"
		d = locale.NumberLTE()
	case *NumberLTError:
		t = "number_lt"
		d = locale.NumberLT()
	}

	err.SetType(t)
	err.SetContext(context)
	err.SetValue(value)
	err.SetDetails(details)
	details["field"] = err.Field()
	err.SetDescription(formatErrorDescription(d, details))
}

// formatErrorDescription takes a string in the default text/template
// format and converts it to a string with replacements. The fields come
// from the ErrorDetails struct and vary for each type of error.
func formatErrorDescription(s string, details ErrorDetails) string {

	var tpl *template.Template
	var descrAsBuffer bytes.Buffer
	var err error

	errorTemplates.RLock()
	tpl = errorTemplates.Lookup(s)
	errorTemplates.RUnlock()

	if tpl == nil {
		errorTemplates.Lock()
		tpl = errorTemplates.New(s)

		tpl, err = tpl.Parse(s)
		errorTemplates.Unlock()

		if err != nil {
			return err.Error()
		}
	}

	err = tpl.Execute(&descrAsBuffer, details)
	if err != nil {
		return err.Error()
	}

	return descrAsBuffer.String()
}
<|endoftext|>


<commit_before>package jo

const (
	Continue Event = iota
	Done

	ObjectStart
	ArrayStart
	KeyStart
	StringStart
	NumberStart
	BoolStart
	NullStart

	ObjectEnd
	ArrayEnd
	KeyEnd
	StringEnd
	NumberEnd
	BoolEnd
	NullEnd

	SyntaxError
)

// Represents a change in parser context.
type Event int

var names = map[Event]string{
	Continue:    "Continue",
	Done:        "Done",
	ObjectStart: "ObjectStart",
	ObjectEnd:   "ObjectEnd",
	KeyStart:    "KeyStart",
	KeyEnd:      "KeyEnd",
	ArrayStart:  "ArrayStart",
	ArrayEnd:    "ArrayEnd",
	StringStart: "StringStart",
	StringEnd:   "StringEnd",
	NumberStart: "NumberStart",
	NumberEnd:   "NumberEnd",
	BoolStart:   "BoolStart",
	BoolEnd:     "BoolEnd",
	NullStart:   "NullStart",
	NullEnd:     "NullEnd",
	SyntaxError: "SyntaxError",
}

// Returns the event's name.
func (e Event) String() string {
	name, ok := names[e]
	if !ok {
		return "INVALID"
	}

	return name
}
<commit_msg>Shuffle Event declarations in events.go, for easier comparison<commit_after>package jo

const (
	Continue Event = iota
	Done

	KeyStart
	ObjectStart
	ArrayStart
	StringStart
	NumberStart
	BoolStart
	NullStart

	KeyEnd
	ObjectEnd
	ArrayEnd
	StringEnd
	NumberEnd
	BoolEnd
	NullEnd

	SyntaxError
)

// Represents a change in parser context.
type Event int

var names = map[Event]string{
	Continue:    "Continue",
	Done:        "Done",
	ObjectStart: "ObjectStart",
	ObjectEnd:   "ObjectEnd",
	KeyStart:    "KeyStart",
	KeyEnd:      "KeyEnd",
	ArrayStart:  "ArrayStart",
	ArrayEnd:    "ArrayEnd",
	StringStart: "StringStart",
	StringEnd:   "StringEnd",
	NumberStart: "NumberStart",
	NumberEnd:   "NumberEnd",
	BoolStart:   "BoolStart",
	BoolEnd:     "BoolEnd",
	NullStart:   "NullStart",
	NullEnd:     "NullEnd",
	SyntaxError: "SyntaxError",
}

// Returns the event's name.
func (e Event) String() string {
	name, ok := names[e]
	if !ok {
		return "INVALID"
	}

	return name
}
<|endoftext|>


<commit_before>package gerrit

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/url"
	"time"
)

// PatchSet contains detailed information about a specific patch set.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/json.html#patchSet
type PatchSet struct {
	Number    Number      `json:"number"`
	Revision  string      `json:"revision"`
	Parents   []string    `json:"parents"`
	Ref       string      `json:"ref"`
	Uploader  AccountInfo `json:"uploader"`
	Author    AccountInfo `json:"author"`
	CreatedOn int         `json:"createdOn"`
	IsDraft   bool        `json:"isDraft"`
	Kind      string      `json:"kind"`
}

// RefUpdate contains data about a reference update.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/json.html#refUpdate
type RefUpdate struct {
	OldRev  string `json:"oldRev"`
	NewRev  string `json:"newRev"`
	RefName string `json:"refName"`
	Project string `json:"project"`
}

// EventInfo contains information about an event emitted by Gerrit.  This
// structure can be used either when parsing streamed events or when reading
// the output of the events-log plugin.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/cmd-stream-events.html#events
type EventInfo struct {
	Type           string        `json:"type"`
	Change         ChangeInfo    `json:"change,omitempty"`
	ChangeKey      ChangeInfo    `json:"changeKey,omitempty"`
	PatchSet       PatchSet      `json:"patchSet,omitempty"`
	EventCreatedOn int           `json:"eventCreatedOn,omitempty"`
	Reason         string        `json:"reason,omitempty"`
	Abandoner      AccountInfo   `json:"abandoner,omitempty"`
	Restorer       AccountInfo   `json:"restorer,omitempty"`
	Submitter      AccountInfo   `json:"submitter,omitempty"`
	Author         AccountInfo   `json:"author,omitempty"`
	Uploader       AccountInfo   `json:"uploader,omitempty"`
	Approvals      []AccountInfo `json:"approvals,omitempty"`
	Comment        string        `json:"comment,omitempty"`
	Editor         AccountInfo   `json:"editor,omitempty"`
	Added          []string      `json:"added,omitempty"`
	Removed        []string      `json:"removed,omitempty"`
	Hashtags       []string      `json:"hashtags,omitempty"`
	RefUpdate      RefUpdate     `json:"refUpdate,omitempty"`
	Project        ProjectInfo   `json:"project,omitempty"`
	Reviewer       AccountInfo   `json:"reviewer,omitempty"`
	OldTopic       string        `json:"oldTopic,omitempty"`
	Changer        AccountInfo   `json:"changer,omitempty"`
}

// EventsLogService contains functions for querying the API provided
// by the optional events-log plugin.
type EventsLogService struct {
	client *Client
}

// EventsLogOptions contains options for querying events from the events-logs
// plugin.
type EventsLogOptions struct {
	From time.Time
	To   time.Time

	// IgnoreUnmarshalErrors will cause GetEvents to ignore any errors
	// that come up when calling json.Unmarshal. This can be useful in
	// cases where the events-log plugin was not kept up to date with
	// the Gerrit version for some reason. In these cases the events-log
	// plugin will return data structs that don't match the EventInfo
	// struct which in turn causes issues for json.Unmarshal.
	IgnoreUnmarshalErrors bool
}

// getURL returns the url that should be used in the request.  This will vary
// depending on the options provided to GetEvents.
func (events *EventsLogService) getURL(options *EventsLogOptions) (string, error) {
	parsed, err := url.Parse("/plugins/events-log/events/")
	if err != nil {
		return "", err
	}

	query := parsed.Query()

	if !options.From.IsZero() {
		query.Set("t1", options.From.Format("2006-01-02 15:04:05"))
	}

	if !options.To.IsZero() {
		query.Set("t2", options.To.Format("2006-01-02 15:04:05"))
	}

	encoded := query.Encode()
	if len(encoded) > 0 {
		parsed.RawQuery = encoded
	}

	return parsed.String(), nil
}

// GetEvents returns a list of events for the given input options.  Use of this
// function requires an authenticated user and for the events-log plugin to be
// installed. This function returns the unmarshalled EventInfo structs, response,
// failed lines and errors. Marshaling errors will cause this function to return
// before processing is complete unless you set EventsLogOptions.IgnoreUnmarshalErrors
// to true. This can be useful in cases where the events-log plugin got out of sync
// with the Gerrit version which in turn produced events which can't be transformed
// unmarshalled into EventInfo.
//
// Gerrit API docs: https://<yourserver>/plugins/events-log/Documentation/rest-api-events.html
func (events *EventsLogService) GetEvents(options *EventsLogOptions) ([]EventInfo, *Response, [][]byte, error) {
	info := []EventInfo{}
	failures := [][]byte{}
	requestURL, err := events.getURL(options)

	if err != nil {
		return info, nil, failures, err
	}

	request, err := events.client.NewRequest("GET", requestURL, nil)
	if err != nil {
		return info, nil, failures, err
	}

	// Perform the request but do not pass in a structure to unpack
	// the response into.  The format of the response is one EventInfo
	// object per line so we need to manually handle the response here.
	response, err := events.client.Do(request, nil)
	if err != nil {
		return info, response, failures, err
	}

	body, err := ioutil.ReadAll(response.Body)
	defer response.Body.Close()
	if err != nil {
		return info, response, failures, err
	}

	for _, line := range bytes.Split(body, []byte("\n")) {
		if len(line) > 0 {
			event := EventInfo{}
			if err := json.Unmarshal(line, &event); err != nil {
				failures = append(failures, line)

				if !options.IgnoreUnmarshalErrors {
					return info, response, failures, err
				}
				continue
			}
			info = append(info, event)
		}
	}
	return info, response, failures, err
}
<commit_msg>ignore shadow issue (no scoping problems)<commit_after>package gerrit

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/url"
	"time"
)

// PatchSet contains detailed information about a specific patch set.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/json.html#patchSet
type PatchSet struct {
	Number    Number      `json:"number"`
	Revision  string      `json:"revision"`
	Parents   []string    `json:"parents"`
	Ref       string      `json:"ref"`
	Uploader  AccountInfo `json:"uploader"`
	Author    AccountInfo `json:"author"`
	CreatedOn int         `json:"createdOn"`
	IsDraft   bool        `json:"isDraft"`
	Kind      string      `json:"kind"`
}

// RefUpdate contains data about a reference update.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/json.html#refUpdate
type RefUpdate struct {
	OldRev  string `json:"oldRev"`
	NewRev  string `json:"newRev"`
	RefName string `json:"refName"`
	Project string `json:"project"`
}

// EventInfo contains information about an event emitted by Gerrit.  This
// structure can be used either when parsing streamed events or when reading
// the output of the events-log plugin.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/cmd-stream-events.html#events
type EventInfo struct {
	Type           string        `json:"type"`
	Change         ChangeInfo    `json:"change,omitempty"`
	ChangeKey      ChangeInfo    `json:"changeKey,omitempty"`
	PatchSet       PatchSet      `json:"patchSet,omitempty"`
	EventCreatedOn int           `json:"eventCreatedOn,omitempty"`
	Reason         string        `json:"reason,omitempty"`
	Abandoner      AccountInfo   `json:"abandoner,omitempty"`
	Restorer       AccountInfo   `json:"restorer,omitempty"`
	Submitter      AccountInfo   `json:"submitter,omitempty"`
	Author         AccountInfo   `json:"author,omitempty"`
	Uploader       AccountInfo   `json:"uploader,omitempty"`
	Approvals      []AccountInfo `json:"approvals,omitempty"`
	Comment        string        `json:"comment,omitempty"`
	Editor         AccountInfo   `json:"editor,omitempty"`
	Added          []string      `json:"added,omitempty"`
	Removed        []string      `json:"removed,omitempty"`
	Hashtags       []string      `json:"hashtags,omitempty"`
	RefUpdate      RefUpdate     `json:"refUpdate,omitempty"`
	Project        ProjectInfo   `json:"project,omitempty"`
	Reviewer       AccountInfo   `json:"reviewer,omitempty"`
	OldTopic       string        `json:"oldTopic,omitempty"`
	Changer        AccountInfo   `json:"changer,omitempty"`
}

// EventsLogService contains functions for querying the API provided
// by the optional events-log plugin.
type EventsLogService struct {
	client *Client
}

// EventsLogOptions contains options for querying events from the events-logs
// plugin.
type EventsLogOptions struct {
	From time.Time
	To   time.Time

	// IgnoreUnmarshalErrors will cause GetEvents to ignore any errors
	// that come up when calling json.Unmarshal. This can be useful in
	// cases where the events-log plugin was not kept up to date with
	// the Gerrit version for some reason. In these cases the events-log
	// plugin will return data structs that don't match the EventInfo
	// struct which in turn causes issues for json.Unmarshal.
	IgnoreUnmarshalErrors bool
}

// getURL returns the url that should be used in the request.  This will vary
// depending on the options provided to GetEvents.
func (events *EventsLogService) getURL(options *EventsLogOptions) (string, error) {
	parsed, err := url.Parse("/plugins/events-log/events/")
	if err != nil {
		return "", err
	}

	query := parsed.Query()

	if !options.From.IsZero() {
		query.Set("t1", options.From.Format("2006-01-02 15:04:05"))
	}

	if !options.To.IsZero() {
		query.Set("t2", options.To.Format("2006-01-02 15:04:05"))
	}

	encoded := query.Encode()
	if len(encoded) > 0 {
		parsed.RawQuery = encoded
	}

	return parsed.String(), nil
}

// GetEvents returns a list of events for the given input options.  Use of this
// function requires an authenticated user and for the events-log plugin to be
// installed. This function returns the unmarshalled EventInfo structs, response,
// failed lines and errors. Marshaling errors will cause this function to return
// before processing is complete unless you set EventsLogOptions.IgnoreUnmarshalErrors
// to true. This can be useful in cases where the events-log plugin got out of sync
// with the Gerrit version which in turn produced events which can't be transformed
// unmarshalled into EventInfo.
//
// Gerrit API docs: https://<yourserver>/plugins/events-log/Documentation/rest-api-events.html
func (events *EventsLogService) GetEvents(options *EventsLogOptions) ([]EventInfo, *Response, [][]byte, error) {
	info := []EventInfo{}
	failures := [][]byte{}
	requestURL, err := events.getURL(options)

	if err != nil {
		return info, nil, failures, err
	}

	request, err := events.client.NewRequest("GET", requestURL, nil)
	if err != nil {
		return info, nil, failures, err
	}

	// Perform the request but do not pass in a structure to unpack
	// the response into.  The format of the response is one EventInfo
	// object per line so we need to manually handle the response here.
	response, err := events.client.Do(request, nil)
	if err != nil {
		return info, response, failures, err
	}

	body, err := ioutil.ReadAll(response.Body)
	defer response.Body.Close()
	if err != nil {
		return info, response, failures, err
	}

	for _, line := range bytes.Split(body, []byte("\n")) {
		if len(line) > 0 {
			event := EventInfo{}
			if err := json.Unmarshal(line, &event); err != nil { // nolint: vetshadow
				failures = append(failures, line)

				if !options.IgnoreUnmarshalErrors {
					return info, response, failures, err
				}
				continue
			}
			info = append(info, event)
		}
	}
	return info, response, failures, err
}
<|endoftext|>


<commit_before>package exif44

import (
	"bytes"
	"errors"
	tiff "github.com/garyhouston/tiff66"
)

// Tags in the Exif IFD.
const (
	ExposureTime              = 0x829A
	FNumber                   = 0x829D
	ExposureProgram           = 0x8822
	SpectralSensitivity       = 0x8824
	PhotographicSensitivity   = 0x8827
	OECF                      = 0x8828
	SensitivityType           = 0x8830
	StandardOutputSensitivity = 0x8831
	RecommendedExposureIndex  = 0x8832
	ISOSpeed                  = 0x8833
	ISOSpeedLatitudeyyy       = 0x8834
	ISOSpeedLatitudezzz       = 0x8835
	ExifVersion               = 0x9000
	DateTimeOriginal          = 0x9003
	DateTimeDigitized         = 0x9004
	ComponentsConfiguration   = 0x9101
	CompressedBitsPerPixel    = 0x9102
	ShutterSpeedValue         = 0x9201
	ApertureValue             = 0x9202
	BrightnessValue           = 0x9203
	ExposureBiasValue         = 0x9204
	MaxApertureValue          = 0x9205
	SubjectDistance           = 0x9206
	MeteringMode              = 0x9207
	LightSource               = 0x9208
	Flash                     = 0x9209
	FocalLength               = 0x920A
	SubjectArea               = 0x9214
	MakerNote                 = 0x927C
	UserComment               = 0x9286
	SubSecTime                = 0x9290
	SubSecTimeOriginal        = 0x9291
	SubSecTimeDigitized       = 0x9292
	FlashpixVersion           = 0xA000
	ColorSpace                = 0xA001
	PixelXDimension           = 0xA002
	PixelYDimension           = 0xA003
	RelatedSoundFile          = 0xA004
	InteroperabilityIFD       = 0xA005
	FlashEnergy               = 0xA20B
	SpatialFrequencyResponse  = 0xA20C
	FocalPlaneXResolution     = 0xA20E
	FocalPlaneYResolution     = 0xA20F
	FocalPlaneResolutionUnit  = 0xA210
	SubjectLocation           = 0xA214
	ExposureIndex             = 0xA215
	SensingMethod             = 0xA217
	FileSource                = 0xA300
	SceneType                 = 0xA301
	CFAPattern                = 0xA302
	CustomRendered            = 0xA401
	ExposureMode              = 0xA402
	WhiteBalance              = 0xA403
	DigitalZoomRatio          = 0xA404
	FocalLengthIn35mmFilm     = 0xA405
	SceneCaptureType          = 0xA406
	GainControl               = 0xA407
	Contrast                  = 0xA408
	Saturation                = 0xA409
	Sharpness                 = 0xA40A
	DeviceSettingDescription  = 0xA40B
	SubjectDistanceRange      = 0xA40C
	ImageUniqueID             = 0xA420
	CameraOwnerName           = 0xA430
	BodySerialNumber          = 0xA431
	LensSpecification         = 0xA432
	LensMake                  = 0xA433
	LensModel                 = 0xA434
	LensSerialNumber          = 0xA435
	Gamma                     = 0xA500
)

// Mapping from Exif tags to strings.
var ExifTagNames = map[tiff.Tag]string{
	ExposureTime:            "ExposureTime",
	FNumber:                 "FNumber",
	ExposureProgram:         "ExposureProgram",
	SpectralSensitivity:     "SpectralSensitivity",
	PhotographicSensitivity: "PhotographicSensitivity",
	OECF:                      "OECF",
	SensitivityType:           "SensitivityType",
	StandardOutputSensitivity: "StandardOutputSensitivity",
	RecommendedExposureIndex:  "RecommendedExposureIndex",
	ISOSpeed:                  "ISOSpeed",
	ISOSpeedLatitudeyyy:       "ISOSpeedLatitudeyyy",
	ISOSpeedLatitudezzz:       "ISOSpeedLatitudezzz",
	ExifVersion:               "ExifVersion",
	DateTimeOriginal:          "DateTimeOriginal",
	DateTimeDigitized:         "DateTimeDigitized",
	ComponentsConfiguration:   "ComponentsConfiguration",
	CompressedBitsPerPixel:    "CompressedBitsPerPixel",
	ShutterSpeedValue:         "ShutterSpeedValue",
	ApertureValue:             "ApertureValue",
	BrightnessValue:           "BrightnessValue",
	ExposureBiasValue:         "ExposureBiasValue",
	MaxApertureValue:          "MaxApertureValue",
	SubjectDistance:           "SubjectDistance",
	MeteringMode:              "MeteringMode",
	LightSource:               "LightSource",
	Flash:                     "Flash",
	FocalLength:               "FocalLength",
	SubjectArea:               "SubjectArea",
	MakerNote:                 "MakerNote",
	UserComment:               "UserComment",
	SubSecTime:                "SubSecTime",
	SubSecTimeOriginal:        "SubSecTimeOriginal",
	SubSecTimeDigitized:       "SubSecTimeDigitized",
	FlashpixVersion:           "FlashpixVersion",
	ColorSpace:                "ColorSpace",
	PixelXDimension:           "PixelXDimension",
	PixelYDimension:           "PixelYDimension",
	RelatedSoundFile:          "RelatedSoundFile",
	InteroperabilityIFD:       "InteroperabilityIFD",
	FlashEnergy:               "FlashEnergy",
	SpatialFrequencyResponse:  "SpatialFrequencyResponse",
	FocalPlaneXResolution:     "FocalPlaneXResolution",
	FocalPlaneYResolution:     "FocalPlaneYResolution",
	FocalPlaneResolutionUnit:  "FocalPlaneResolutionUnit",
	SubjectLocation:           "SubjectLocation",
	ExposureIndex:             "ExposureIndex",
	SensingMethod:             "SensingMethod",
	FileSource:                "FileSource",
	SceneType:                 "SceneType",
	CFAPattern:                "CFAPattern",
	CustomRendered:            "CustomRendered",
	ExposureMode:              "ExposureMode",
	WhiteBalance:              "WhiteBalance",
	DigitalZoomRatio:          "DigitalZoomRatio",
	FocalLengthIn35mmFilm:     "FocalLengthIn35mmFilm",
	SceneCaptureType:          "SceneCaptureType",
	GainControl:               "GainControl",
	Contrast:                  "Contrast",
	Saturation:                "Saturation",
	Sharpness:                 "Sharpness",
	DeviceSettingDescription:  "DeviceSettingDescription",
	SubjectDistanceRange:      "SubjectDistanceRange",
	ImageUniqueID:             "ImageUniqueID",
	CameraOwnerName:           "CameraOwnerName",
	BodySerialNumber:          "BodySerialNumber",
	LensSpecification:         "LensSpecification",
	LensMake:                  "LensMake",
	LensModel:                 "LensModel",
	LensSerialNumber:          "LensSerialNumber",
	Gamma:                     "Gamma",
}

// Tags in the Interoperability IFD.
const (
	InteroperabilityIndex   = 0x1
	InteroperabilityVersion = 0x2
)

// Mapping from Interoperability tags to strings.
var InteropTagNames = map[tiff.Tag]string{
	InteroperabilityIndex:   "InteroperabilityIndex",
	InteroperabilityVersion: "InteroperabilityVersion",
}

// Tags in the GPS IFD.
const (
	GPSVersionID         = 0x00
	GPSLatitudeRef       = 0x01
	GPSLatitude          = 0x02
	GPSLongitudeRef      = 0x03
	GPSLongitude         = 0x04
	GPSAltitudeRef       = 0x05
	GPSAltitude          = 0x06
	GPSTimeStamp         = 0x07
	GPSSatellites        = 0x08
	GPSStatus            = 0x09
	GPSMeasureMode       = 0x0A
	GPSDOP               = 0x0B
	GPSSpeedRef          = 0x0C
	GPSSpeed             = 0x0D
	GPSTrackRef          = 0x0E
	GPSTrack             = 0x0F
	GPSImgDirectionRef   = 0x10
	GPSImgDirection      = 0x11
	GPSMapDatum          = 0x12
	GPSDestLatitudeRef   = 0x13
	GPSDestLatitude      = 0x14
	GPSDestLongitudeRef  = 0x15
	GPSDestLongitude     = 0x16
	GPSDestBearingRef    = 0x17
	GPSDestBearing       = 0x18
	GPSDestDistanceRef   = 0x19
	GPSDestDistance      = 0x1A
	GPSProcessingMethod  = 0x1B
	GPSAreaInformation   = 0x1C
	GPSDateStamp         = 0x1D
	GPSDifferential      = 0x1E
	GPSHPositioningError = 0x1F
)

// Mapping from GPS tags to strings.
var GPSTagNames = map[tiff.Tag]string{
	GPSVersionID:         "GPSVersionID",
	GPSLatitudeRef:       "GPSLatitudeRef",
	GPSLatitude:          "GPSLatitude",
	GPSLongitudeRef:      "GPSLongitudeRef",
	GPSLongitude:         "GPSLongitude",
	GPSAltitudeRef:       "GPSAltitudeRef",
	GPSAltitude:          "GPSAltitude",
	GPSTimeStamp:         "GPSTimeStamp",
	GPSSatellites:        "GPSSatellites",
	GPSStatus:            "GPSStatus",
	GPSMeasureMode:       "GPSMeasureMode",
	GPSDOP:               "GPSDOP",
	GPSSpeedRef:          "GPSSpeedRef",
	GPSSpeed:             "GPSSpeed",
	GPSTrackRef:          "GPSTrackRef",
	GPSTrack:             "GPSTrack",
	GPSImgDirectionRef:   "GPSImgDirectionRef",
	GPSImgDirection:      "GPSImgDirection",
	GPSMapDatum:          "GPSMapDatum",
	GPSDestLatitudeRef:   "GPSDestLatitudeRef",
	GPSDestLatitude:      "GPSDestLatitude",
	GPSDestLongitudeRef:  "GPSDestLongitudeRef",
	GPSDestLongitude:     "GPSDestLongitude",
	GPSDestBearingRef:    "GPSDestBearingRef",
	GPSDestBearing:       "GPSDestBearing",
	GPSDestDistanceRef:   "GPSDestDistanceRef",
	GPSDestDistance:      "GPSDestDistance",
	GPSProcessingMethod:  "GPSProcessingMethod",
	GPSAreaInformation:   "GPSAreaInformation",
	GPSDateStamp:         "GPSDateStamp",
	GPSDifferential:      "GPSDifferential",
	GPSHPositioningError: "GPSHPositioningError",
}

// Return the tag->name map for given namespace, or a nil map if the space
// is unknown.
func TagNameMap(space tiff.TagSpace) map[tiff.Tag]string {
	var names map[tiff.Tag]string
	switch space {
	case tiff.TIFFSpace:
		names = tiff.TagNames
	case tiff.ExifSpace:
		names = ExifTagNames
	case tiff.InteropSpace:
		names = InteropTagNames
	case tiff.GPSSpace:
		names = GPSTagNames
	case tiff.Panasonic1Space:
		names = Panasonic1TagNames
	case tiff.Nikon1Space:
		names = Nikon1TagNames
	}
	return names
}

// Exif header, as found in a JPEG APP1 segment.
var header = []byte("Exif\000\000")

// Size of an Exif header.
const HeaderSize = 6

// Check if a slice starts with an Exif header, as found in a JPEG
// APP1 segment.  Returns a flag and the position of the next byte.
func GetHeader(buf []byte) (bool, uint32) {
	if uint32(len(buf)) >= HeaderSize && bytes.Compare(buf[:HeaderSize], header) == 0 {
		return true, HeaderSize
	} else {
		return false, 0
	}
}

// Put an Exif header, as for a JPEG APP1 segment, at the start of a slice,
// returning the position of the next byte.
func PutHeader(buf []byte) uint32 {
	copy(buf, header)
	return HeaderSize
}

type Exif struct {
	TIFF      *tiff.IFDNode // Pointer to top-level TIFF node, IFD0. This is the root of the tree containing all other nodes.
	Exif      *tiff.IFDNode // Pointer to Exif in Tree.
	GPS       *tiff.IFDNode // Pointer to GPS in Tree.
	Interop   *tiff.IFDNode // Pointer to Interop in Tree.
	Thumbnail *tiff.IFDNode // Pointer to TIFF IFD1 in Tree.
	MakerNote *tiff.IFDNode // Pointer to maker note in Tree.
}

// Unpack a TIFF header and tree from a slice, as for GetHeader and
// GetIFDTree from tiff66, and also return pointers to any Exif
// subIFDs that are present. As for GetIFDTree, field data in the
// structures points into the original byte slice, so modifying one
// will modify the other.
func GetExifTree(buf []byte) (*Exif, error) {
	valid, order, ifdpos := tiff.GetHeader(buf)
	if !valid {
		return nil, errors.New("GetExifTree: Invalid Tiff header")
	}
	node, err := tiff.GetIFDTree(buf, order, ifdpos, tiff.TIFFSpace)
	if err != nil {
		return nil, err
	}
	exif := Exif{}
	exif.TIFF = node
	for _, sub := range node.SubIFDs {
		if sub.Node.Space == tiff.ExifSpace {
			exif.Exif = sub.Node
			for _, esub := range sub.Node.SubIFDs {
				if esub.Node.Space == tiff.InteropSpace {
					exif.Interop = esub.Node
				} else if esub.Node.Space.IsMakerNote() {
					exif.MakerNote = esub.Node
				}
			}
		} else if sub.Node.Space == tiff.GPSSpace {
			exif.GPS = sub.Node
		}
	}
	if node.Next != nil {
		exif.Thumbnail = node.Next
	}
	return &exif, nil
}

// Return the size of a buffer needed to serialize the tree in an Exif
// structure in TIFF format, including the TIFF header, but excluding
// the Exif header used in JPEG files.
func (exif Exif) TreeSize() uint32 {
	return tiff.HeaderSize + exif.TIFF.TreeSize()
}

// Pack Exif data into a slice in TIFF format. The slice should start
// with the first byte following any Exif header. Returns the position
// following the last byte used.
func (exif *Exif) Put(buf []byte) (uint32, error) {
	tiff.PutHeader(buf, exif.TIFF.Order, tiff.HeaderSize)
	return exif.TIFF.PutIFDTree(buf, tiff.HeaderSize)
}
<commit_msg>Update for changes in tiff66 library<commit_after>package exif44

import (
	"bytes"
	"errors"
	tiff "github.com/garyhouston/tiff66"
)

// Tags in the Exif IFD.
const (
	ExposureTime              = 0x829A
	FNumber                   = 0x829D
	ExposureProgram           = 0x8822
	SpectralSensitivity       = 0x8824
	PhotographicSensitivity   = 0x8827
	OECF                      = 0x8828
	SensitivityType           = 0x8830
	StandardOutputSensitivity = 0x8831
	RecommendedExposureIndex  = 0x8832
	ISOSpeed                  = 0x8833
	ISOSpeedLatitudeyyy       = 0x8834
	ISOSpeedLatitudezzz       = 0x8835
	ExifVersion               = 0x9000
	DateTimeOriginal          = 0x9003
	DateTimeDigitized         = 0x9004
	ComponentsConfiguration   = 0x9101
	CompressedBitsPerPixel    = 0x9102
	ShutterSpeedValue         = 0x9201
	ApertureValue             = 0x9202
	BrightnessValue           = 0x9203
	ExposureBiasValue         = 0x9204
	MaxApertureValue          = 0x9205
	SubjectDistance           = 0x9206
	MeteringMode              = 0x9207
	LightSource               = 0x9208
	Flash                     = 0x9209
	FocalLength               = 0x920A
	SubjectArea               = 0x9214
	MakerNote                 = 0x927C
	UserComment               = 0x9286
	SubSecTime                = 0x9290
	SubSecTimeOriginal        = 0x9291
	SubSecTimeDigitized       = 0x9292
	FlashpixVersion           = 0xA000
	ColorSpace                = 0xA001
	PixelXDimension           = 0xA002
	PixelYDimension           = 0xA003
	RelatedSoundFile          = 0xA004
	InteroperabilityIFD       = 0xA005
	FlashEnergy               = 0xA20B
	SpatialFrequencyResponse  = 0xA20C
	FocalPlaneXResolution     = 0xA20E
	FocalPlaneYResolution     = 0xA20F
	FocalPlaneResolutionUnit  = 0xA210
	SubjectLocation           = 0xA214
	ExposureIndex             = 0xA215
	SensingMethod             = 0xA217
	FileSource                = 0xA300
	SceneType                 = 0xA301
	CFAPattern                = 0xA302
	CustomRendered            = 0xA401
	ExposureMode              = 0xA402
	WhiteBalance              = 0xA403
	DigitalZoomRatio          = 0xA404
	FocalLengthIn35mmFilm     = 0xA405
	SceneCaptureType          = 0xA406
	GainControl               = 0xA407
	Contrast                  = 0xA408
	Saturation                = 0xA409
	Sharpness                 = 0xA40A
	DeviceSettingDescription  = 0xA40B
	SubjectDistanceRange      = 0xA40C
	ImageUniqueID             = 0xA420
	CameraOwnerName           = 0xA430
	BodySerialNumber          = 0xA431
	LensSpecification         = 0xA432
	LensMake                  = 0xA433
	LensModel                 = 0xA434
	LensSerialNumber          = 0xA435
	Gamma                     = 0xA500
)

// Mapping from Exif tags to strings.
var ExifTagNames = map[tiff.Tag]string{
	ExposureTime:            "ExposureTime",
	FNumber:                 "FNumber",
	ExposureProgram:         "ExposureProgram",
	SpectralSensitivity:     "SpectralSensitivity",
	PhotographicSensitivity: "PhotographicSensitivity",
	OECF:                      "OECF",
	SensitivityType:           "SensitivityType",
	StandardOutputSensitivity: "StandardOutputSensitivity",
	RecommendedExposureIndex:  "RecommendedExposureIndex",
	ISOSpeed:                  "ISOSpeed",
	ISOSpeedLatitudeyyy:       "ISOSpeedLatitudeyyy",
	ISOSpeedLatitudezzz:       "ISOSpeedLatitudezzz",
	ExifVersion:               "ExifVersion",
	DateTimeOriginal:          "DateTimeOriginal",
	DateTimeDigitized:         "DateTimeDigitized",
	ComponentsConfiguration:   "ComponentsConfiguration",
	CompressedBitsPerPixel:    "CompressedBitsPerPixel",
	ShutterSpeedValue:         "ShutterSpeedValue",
	ApertureValue:             "ApertureValue",
	BrightnessValue:           "BrightnessValue",
	ExposureBiasValue:         "ExposureBiasValue",
	MaxApertureValue:          "MaxApertureValue",
	SubjectDistance:           "SubjectDistance",
	MeteringMode:              "MeteringMode",
	LightSource:               "LightSource",
	Flash:                     "Flash",
	FocalLength:               "FocalLength",
	SubjectArea:               "SubjectArea",
	MakerNote:                 "MakerNote",
	UserComment:               "UserComment",
	SubSecTime:                "SubSecTime",
	SubSecTimeOriginal:        "SubSecTimeOriginal",
	SubSecTimeDigitized:       "SubSecTimeDigitized",
	FlashpixVersion:           "FlashpixVersion",
	ColorSpace:                "ColorSpace",
	PixelXDimension:           "PixelXDimension",
	PixelYDimension:           "PixelYDimension",
	RelatedSoundFile:          "RelatedSoundFile",
	InteroperabilityIFD:       "InteroperabilityIFD",
	FlashEnergy:               "FlashEnergy",
	SpatialFrequencyResponse:  "SpatialFrequencyResponse",
	FocalPlaneXResolution:     "FocalPlaneXResolution",
	FocalPlaneYResolution:     "FocalPlaneYResolution",
	FocalPlaneResolutionUnit:  "FocalPlaneResolutionUnit",
	SubjectLocation:           "SubjectLocation",
	ExposureIndex:             "ExposureIndex",
	SensingMethod:             "SensingMethod",
	FileSource:                "FileSource",
	SceneType:                 "SceneType",
	CFAPattern:                "CFAPattern",
	CustomRendered:            "CustomRendered",
	ExposureMode:              "ExposureMode",
	WhiteBalance:              "WhiteBalance",
	DigitalZoomRatio:          "DigitalZoomRatio",
	FocalLengthIn35mmFilm:     "FocalLengthIn35mmFilm",
	SceneCaptureType:          "SceneCaptureType",
	GainControl:               "GainControl",
	Contrast:                  "Contrast",
	Saturation:                "Saturation",
	Sharpness:                 "Sharpness",
	DeviceSettingDescription:  "DeviceSettingDescription",
	SubjectDistanceRange:      "SubjectDistanceRange",
	ImageUniqueID:             "ImageUniqueID",
	CameraOwnerName:           "CameraOwnerName",
	BodySerialNumber:          "BodySerialNumber",
	LensSpecification:         "LensSpecification",
	LensMake:                  "LensMake",
	LensModel:                 "LensModel",
	LensSerialNumber:          "LensSerialNumber",
	Gamma:                     "Gamma",
}

// Tags in the Interoperability IFD.
const (
	InteroperabilityIndex   = 0x1
	InteroperabilityVersion = 0x2
)

// Mapping from Interoperability tags to strings.
var InteropTagNames = map[tiff.Tag]string{
	InteroperabilityIndex:   "InteroperabilityIndex",
	InteroperabilityVersion: "InteroperabilityVersion",
}

// Tags in the GPS IFD.
const (
	GPSVersionID         = 0x00
	GPSLatitudeRef       = 0x01
	GPSLatitude          = 0x02
	GPSLongitudeRef      = 0x03
	GPSLongitude         = 0x04
	GPSAltitudeRef       = 0x05
	GPSAltitude          = 0x06
	GPSTimeStamp         = 0x07
	GPSSatellites        = 0x08
	GPSStatus            = 0x09
	GPSMeasureMode       = 0x0A
	GPSDOP               = 0x0B
	GPSSpeedRef          = 0x0C
	GPSSpeed             = 0x0D
	GPSTrackRef          = 0x0E
	GPSTrack             = 0x0F
	GPSImgDirectionRef   = 0x10
	GPSImgDirection      = 0x11
	GPSMapDatum          = 0x12
	GPSDestLatitudeRef   = 0x13
	GPSDestLatitude      = 0x14
	GPSDestLongitudeRef  = 0x15
	GPSDestLongitude     = 0x16
	GPSDestBearingRef    = 0x17
	GPSDestBearing       = 0x18
	GPSDestDistanceRef   = 0x19
	GPSDestDistance      = 0x1A
	GPSProcessingMethod  = 0x1B
	GPSAreaInformation   = 0x1C
	GPSDateStamp         = 0x1D
	GPSDifferential      = 0x1E
	GPSHPositioningError = 0x1F
)

// Mapping from GPS tags to strings.
var GPSTagNames = map[tiff.Tag]string{
	GPSVersionID:         "GPSVersionID",
	GPSLatitudeRef:       "GPSLatitudeRef",
	GPSLatitude:          "GPSLatitude",
	GPSLongitudeRef:      "GPSLongitudeRef",
	GPSLongitude:         "GPSLongitude",
	GPSAltitudeRef:       "GPSAltitudeRef",
	GPSAltitude:          "GPSAltitude",
	GPSTimeStamp:         "GPSTimeStamp",
	GPSSatellites:        "GPSSatellites",
	GPSStatus:            "GPSStatus",
	GPSMeasureMode:       "GPSMeasureMode",
	GPSDOP:               "GPSDOP",
	GPSSpeedRef:          "GPSSpeedRef",
	GPSSpeed:             "GPSSpeed",
	GPSTrackRef:          "GPSTrackRef",
	GPSTrack:             "GPSTrack",
	GPSImgDirectionRef:   "GPSImgDirectionRef",
	GPSImgDirection:      "GPSImgDirection",
	GPSMapDatum:          "GPSMapDatum",
	GPSDestLatitudeRef:   "GPSDestLatitudeRef",
	GPSDestLatitude:      "GPSDestLatitude",
	GPSDestLongitudeRef:  "GPSDestLongitudeRef",
	GPSDestLongitude:     "GPSDestLongitude",
	GPSDestBearingRef:    "GPSDestBearingRef",
	GPSDestBearing:       "GPSDestBearing",
	GPSDestDistanceRef:   "GPSDestDistanceRef",
	GPSDestDistance:      "GPSDestDistance",
	GPSProcessingMethod:  "GPSProcessingMethod",
	GPSAreaInformation:   "GPSAreaInformation",
	GPSDateStamp:         "GPSDateStamp",
	GPSDifferential:      "GPSDifferential",
	GPSHPositioningError: "GPSHPositioningError",
}

// Return the tag->name map for given namespace, or a nil map if the space
// is unknown.
func TagNameMap(space tiff.TagSpace) map[tiff.Tag]string {
	var names map[tiff.Tag]string
	switch space {
	case tiff.TIFFSpace:
		names = tiff.TagNames
	case tiff.ExifSpace:
		names = ExifTagNames
	case tiff.InteropSpace:
		names = InteropTagNames
	case tiff.GPSSpace:
		names = GPSTagNames
	case tiff.Panasonic1Space:
		names = Panasonic1TagNames
	case tiff.Nikon1Space:
		names = Nikon1TagNames
	case tiff.Nikon2Space:
		names = Nikon2TagNames
	case tiff.Nikon2PreviewSpace:
		names = Nikon2PreviewIFDTagNames
	case tiff.Nikon2ScanSpace:
		names = Nikon2ScanIFDTagNames
	}
	return names
}

// Exif header, as found in a JPEG APP1 segment.
var header = []byte("Exif\000\000")

// Size of an Exif header.
const HeaderSize = 6

// Check if a slice starts with an Exif header, as found in a JPEG
// APP1 segment.  Returns a flag and the position of the next byte.
func GetHeader(buf []byte) (bool, uint32) {
	if uint32(len(buf)) >= HeaderSize && bytes.Compare(buf[:HeaderSize], header) == 0 {
		return true, HeaderSize
	} else {
		return false, 0
	}
}

// Put an Exif header, as for a JPEG APP1 segment, at the start of a slice,
// returning the position of the next byte.
func PutHeader(buf []byte) uint32 {
	copy(buf, header)
	return HeaderSize
}

type Exif struct {
	TIFF      *tiff.IFDNode // Pointer to top-level TIFF node, IFD0. This is the root of the tree containing all other nodes.
	Exif      *tiff.IFDNode // Pointer to Exif in Tree.
	GPS       *tiff.IFDNode // Pointer to GPS in Tree.
	Interop   *tiff.IFDNode // Pointer to Interop in Tree.
	Thumbnail *tiff.IFDNode // Pointer to TIFF IFD1 in Tree.
	MakerNote *tiff.IFDNode // Pointer to maker note in Tree.
}

// Unpack a TIFF header and tree from a slice, as for GetHeader and
// GetIFDTree from tiff66, and also return pointers to any Exif
// subIFDs that are present. As for GetIFDTree, field data in the
// structures points into the original byte slice, so modifying one
// will modify the other.
func GetExifTree(buf []byte) (*Exif, error) {
	valid, order, ifdpos := tiff.GetHeader(buf)
	if !valid {
		return nil, errors.New("GetExifTree: Invalid Tiff header")
	}
	node, err := tiff.GetIFDTree(buf, order, ifdpos, tiff.TIFFSpace)
	if err != nil {
		return nil, err
	}
	exif := Exif{}
	exif.TIFF = node
	for _, sub := range node.SubIFDs {
		if sub.Node.GetSpace() == tiff.ExifSpace {
			exif.Exif = sub.Node
			for _, esub := range sub.Node.SubIFDs {
				if esub.Node.GetSpace() == tiff.InteropSpace {
					exif.Interop = esub.Node
				} else if esub.Node.IsMakerNote() {
					exif.MakerNote = esub.Node
				}
			}
		} else if sub.Node.GetSpace() == tiff.GPSSpace {
			exif.GPS = sub.Node
		}
	}
	if node.Next != nil {
		exif.Thumbnail = node.Next
	}
	return &exif, nil
}

// Return the size of a buffer needed to serialize the tree in an Exif
// structure in TIFF format, including the TIFF header, but excluding
// the Exif header used in JPEG files.
func (exif Exif) TreeSize() uint32 {
	return tiff.HeaderSize + exif.TIFF.TreeSize()
}

// Pack Exif data into a slice in TIFF format. The slice should start
// with the first byte following any Exif header. Returns the position
// following the last byte used.
func (exif *Exif) Put(buf []byte) (uint32, error) {
	tiff.PutHeader(buf, exif.TIFF.Order, tiff.HeaderSize)
	return exif.TIFF.PutIFDTree(buf, tiff.HeaderSize)
}
<|endoftext|>


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

package trackprocessor

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path"

	"github.com/bogem/id3v2"
	"github.com/bogem/nehm/applescript"
	"github.com/bogem/nehm/track"
	"github.com/bogem/nehm/ui"
)

type TrackProcessor struct {
	DownloadFolder string // In this folder tracks will be downloaded
	ItunesPlaylist string // In this playlist tracks will be added
}

func (tp TrackProcessor) ProcessAll(tracks []track.Track) {
	if len(tracks) == 0 {
		ui.Term(nil, "There are no tracks to download.")
	}
	// Start with last track
	for i := len(tracks) - 1; i >= 0; i-- {
		track := tracks[i]
		tp.Process(track)
		ui.Newline()
	}
	ui.Success("Done!")
	ui.Quit()
}

func (tp TrackProcessor) Process(t track.Track) {
	// Download track
	trackPath := path.Join(tp.DownloadFolder, t.Filename())
	if _, err := os.OpenFile(trackPath, os.O_CREATE, 0644); err != nil {
		ui.Term(err, "couldn't create track file")
	}
	downloadTrack(t, trackPath)

	// Download artwork
	artworkFile, err := ioutil.TempFile("", "")
	if err != nil {
		ui.Term(err, "creation of artwork file failed")
	}
	artworkPath := artworkFile.Name()
	downloadArtwork(t, artworkPath)

	// Tag track
	tag(t, trackPath, artworkFile)

	// Delete artwork
	artworkFile.Close()
	os.Remove(artworkPath)

	// Add to iTunes
	if tp.ItunesPlaylist != "" {
		applescript.AddTrackToPlaylist(trackPath, tp.ItunesPlaylist)
	}
}

func downloadTrack(t track.Track, path string) {
	ui.Say("Downloading " + t.Artist() + " - " + t.Title())
	runDownloadCmd(path, t.URL())
}

func downloadArtwork(t track.Track, path string) {
	ui.Say("Downloading artwork")
	runDownloadCmd(path, t.ArtworkURL())
}

func runDownloadCmd(path, url string) {
	cmd := exec.Command("curl", "-#", "-o", path, "-L", url)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		ui.Term(err, "download failed")
	}
}

func tag(t track.Track, trackPath string, artworkFile *os.File) {
	tag, err := id3v2.Open(trackPath)
	if err != nil {
		ui.Term(err, "couldn't open track file")
	}

	tag.SetArtist(t.Artist())
	tag.SetTitle(t.Title())
	tag.SetYear(t.Year())

	pic := id3v2.PictureFrame{
		Encoding:    id3v2.ENUTF8,
		MimeType:    "image/jpeg",
		PictureType: id3v2.PTFrontCover,
		Picture:     artworkFile,
	}
	tag.AddAttachedPicture(pic)

	if err := tag.Flush(); err != nil {
		ui.Term(err, "couldn't write tag to file")
	}
}
<commit_msg>Change track permissions to 0766<commit_after>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package trackprocessor

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path"

	"github.com/bogem/id3v2"
	"github.com/bogem/nehm/applescript"
	"github.com/bogem/nehm/track"
	"github.com/bogem/nehm/ui"
)

type TrackProcessor struct {
	DownloadFolder string // In this folder tracks will be downloaded
	ItunesPlaylist string // In this playlist tracks will be added
}

func (tp TrackProcessor) ProcessAll(tracks []track.Track) {
	if len(tracks) == 0 {
		ui.Term(nil, "There are no tracks to download.")
	}
	// Start with last track
	for i := len(tracks) - 1; i >= 0; i-- {
		track := tracks[i]
		tp.Process(track)
		ui.Newline()
	}
	ui.Success("Done!")
	ui.Quit()
}

func (tp TrackProcessor) Process(t track.Track) {
	// Download track
	trackPath := path.Join(tp.DownloadFolder, t.Filename())
	if _, err := os.OpenFile(trackPath, os.O_CREATE, 0766); err != nil {
		ui.Term(err, "couldn't create track file")
	}
	downloadTrack(t, trackPath)

	// Download artwork
	artworkFile, err := ioutil.TempFile("", "")
	if err != nil {
		ui.Term(err, "creation of artwork file failed")
	}
	artworkPath := artworkFile.Name()
	downloadArtwork(t, artworkPath)

	// Tag track
	tag(t, trackPath, artworkFile)

	// Delete artwork
	artworkFile.Close()
	os.Remove(artworkPath)

	// Add to iTunes
	if tp.ItunesPlaylist != "" {
		applescript.AddTrackToPlaylist(trackPath, tp.ItunesPlaylist)
	}
}

func downloadTrack(t track.Track, path string) {
	ui.Say("Downloading " + t.Artist() + " - " + t.Title())
	runDownloadCmd(path, t.URL())
}

func downloadArtwork(t track.Track, path string) {
	ui.Say("Downloading artwork")
	runDownloadCmd(path, t.ArtworkURL())
}

func runDownloadCmd(path, url string) {
	cmd := exec.Command("curl", "-#", "-o", path, "-L", url)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		ui.Term(err, "download failed")
	}
}

func tag(t track.Track, trackPath string, artworkFile *os.File) {
	tag, err := id3v2.Open(trackPath)
	if err != nil {
		ui.Term(err, "couldn't open track file")
	}

	tag.SetArtist(t.Artist())
	tag.SetTitle(t.Title())
	tag.SetYear(t.Year())

	pic := id3v2.PictureFrame{
		Encoding:    id3v2.ENUTF8,
		MimeType:    "image/jpeg",
		PictureType: id3v2.PTFrontCover,
		Picture:     artworkFile,
	}
	tag.AddAttachedPicture(pic)

	if err := tag.Flush(); err != nil {
		ui.Term(err, "couldn't write tag to file")
	}
}
<|endoftext|>


<commit_before>package aspect

import (
	"database/sql"
	"fmt"
	"reflect"
	"unicode"
)

const tagLabel = "db"

var scannerType = reflect.TypeOf((*sql.Scanner)(nil)).Elem()

type field struct {
	index   []int  // struct field indexes - with possible embedding
	column  string // SQL column name
	table   string // SQL table name
	options options
}

func (f field) Exists() bool {
	return len(f.index) > 0
}

type fields []field

func (f fields) Empty() bool {
	for _, field := range f {
		if field.Exists() {
			return false
		}
	}
	return true
}

func SelectFields(v interface{}) ([]field, error) {
	value := reflect.ValueOf(v)
	if value.Kind() != reflect.Ptr {
		return nil, fmt.Errorf(
			"aspect: can only take select into pointer values, received %s",
			value.Kind(),
		)
	}
	return recurse([]int{}, reflect.TypeOf(v).Elem()), nil
}

func SelectFieldsFromElem(elem reflect.Type) []field {
	return recurse([]int{}, elem)
}

func recurse(indexes []int, elem reflect.Type) (fields []field) {
	if elem.Kind() != reflect.Struct {
		return nil
	}

	for i := 0; i < elem.NumField(); i += 1 {
		f := elem.Field(i)
		if f.Type.Kind() == reflect.Struct && !reflect.PtrTo(f.Type).Implements(scannerType) {
			switch f.Type.String() { // TODO switch on the actual type
			case "time.Time":
			default:
				fields = append(fields, recurse(append(indexes, i), f.Type)...)
				continue
			}
		}

		// TODO ignore un-exported fields?
		// TODO fallback to json tags?

		// Field is valid, create a new Field
		var tag string
		field := field{index: append(indexes, i)}
		tag, field.options = parseTag(f.Tag.Get(tagLabel))
		if tag == "-" {
			continue
		} else if tag == "" {
			// Fallback to the column name, but only if exported
			if unicode.IsLower(rune(f.Name[0])) {
				continue
			}
			tag = f.Name
		}
		field.table, field.column = splitName(tag)
		fields = append(fields, field)
	}
	return
}

// AlignColumns will reorder the given fields array to match the columns.
// Columns that do not match fields will be given empty field structs.
func AlignColumns(columns []string, fields []field) fields {
	aligned := make([]field, len(columns))
	// TODO aliases? tables? check if the columns first matches the fields?
	for i, column := range columns {
		for _, field := range fields {
			if field.column == column {
				aligned[i] = field
				break
			}
		}
	}
	return aligned
}
<commit_msg>Replaced instances of type []field with fields<commit_after>package aspect

import (
	"database/sql"
	"fmt"
	"reflect"
	"unicode"
)

const tagLabel = "db"

var scannerType = reflect.TypeOf((*sql.Scanner)(nil)).Elem()

type field struct {
	index   []int  // struct field indexes - with possible embedding
	column  string // SQL column name
	table   string // SQL table name
	options options
}

// Exists returns true if the field contains a valid recursive field index
func (f field) Exists() bool {
	return len(f.index) > 0
}

type fields []field

// Empty returns true if all of its fields do not exist
func (f fields) Empty() bool {
	for _, field := range f {
		if field.Exists() {
			return false
		}
	}
	return true
}

// SelectFields returns the ordered list of fields from the given interface.
// The interface must be a pointer to a struct.
func SelectFields(v interface{}) (fields, error) {
	value := reflect.ValueOf(v)
	if value.Kind() != reflect.Ptr {
		return nil, fmt.Errorf(
			"aspect: can only take select into pointer values, received %s",
			value.Kind(),
		)
	}
	return recurse([]int{}, reflect.TypeOf(v).Elem()), nil
}

// SelectFieldsFromElem returns the ordered list of fields from the given
// reflect Type
func SelectFieldsFromElem(elem reflect.Type) fields {
	return recurse([]int{}, elem)
}

func recurse(indexes []int, elem reflect.Type) (fields fields) {
	if elem.Kind() != reflect.Struct {
		return nil
	}

	for i := 0; i < elem.NumField(); i += 1 {
		f := elem.Field(i)
		if f.Type.Kind() == reflect.Struct && !reflect.PtrTo(f.Type).Implements(scannerType) {
			switch f.Type.String() { // TODO switch on the actual type
			case "time.Time":
			default:
				fields = append(fields, recurse(append(indexes, i), f.Type)...)
				continue
			}
		}

		// TODO ignore un-exported fields?
		// TODO fallback to json tags?

		// Field is valid, create a new Field
		var tag string
		field := field{index: append(indexes, i)}
		tag, field.options = parseTag(f.Tag.Get(tagLabel))
		if tag == "-" {
			continue
		} else if tag == "" {
			// Fallback to the column name, but only if exported
			if unicode.IsLower(rune(f.Name[0])) {
				continue
			}
			tag = f.Name
		}
		field.table, field.column = splitName(tag)
		fields = append(fields, field)
	}
	return
}

// AlignColumns will reorder the given fields array to match the columns.
// Columns that do not match fields will be given empty field structs.
func AlignColumns(columns []string, fields []field) fields {
	aligned := make([]field, len(columns))
	// TODO aliases? tables? check if the columns first matches the fields?
	for i, column := range columns {
		for _, field := range fields {
			if field.column == column {
				aligned[i] = field
				break
			}
		}
	}
	return aligned
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

// Package hex implements hexadecimal encoding and decoding.
package hex

import (
	"bytes"
	"errors"
	"fmt"
	"io"
)

const hextable = "0123456789abcdef"

// EncodedLen returns the length of an encoding of n source bytes.
func EncodedLen(n int) int { return n * 2 }

// Encode encodes src into EncodedLen(len(src))
// bytes of dst.  As a convenience, it returns the number
// of bytes written to dst, but this value is always EncodedLen(len(src)).
// Encode implements hexadecimal encoding.
func Encode(dst, src []byte) int {
	for i, v := range src {
		dst[i*2] = hextable[v>>4]
		dst[i*2+1] = hextable[v&0x0f]
	}

	return len(src) * 2
}

// ErrLength results from decoding an odd length slice.
var ErrLength = errors.New("encoding/hex: odd length hex string")

// InvalidByteError values describe errors resulting from an invalid byte in a hex string.
type InvalidByteError byte

func (e InvalidByteError) Error() string {
	return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune(e))
}

func DecodedLen(x int) int { return x / 2 }

// Decode decodes src into DecodedLen(len(src)) bytes, returning the actual
// number of bytes written to dst.
//
// If Decode encounters invalid input, it returns an error describing the failure.
func Decode(dst, src []byte) (int, error) {
	if len(src)%2 == 1 {
		return 0, ErrLength
	}

	for i := 0; i < len(src)/2; i++ {
		a, ok := fromHexChar(src[i*2])
		if !ok {
			return 0, InvalidByteError(src[i*2])
		}
		b, ok := fromHexChar(src[i*2+1])
		if !ok {
			return 0, InvalidByteError(src[i*2+1])
		}
		dst[i] = (a << 4) | b
	}

	return len(src) / 2, nil
}

// fromHexChar converts a hex character into its value and a success flag.
func fromHexChar(c byte) (byte, bool) {
	switch {
	case '0' <= c && c <= '9':
		return c - '0', true
	case 'a' <= c && c <= 'f':
		return c - 'a' + 10, true
	case 'A' <= c && c <= 'F':
		return c - 'A' + 10, true
	}

	return 0, false
}

// EncodeToString returns the hexadecimal encoding of src.
func EncodeToString(src []byte) string {
	dst := make([]byte, EncodedLen(len(src)))
	Encode(dst, src)
	return string(dst)
}

// DecodeString returns the bytes represented by the hexadecimal string s.
func DecodeString(s string) ([]byte, error) {
	src := []byte(s)
	dst := make([]byte, DecodedLen(len(src)))
	_, err := Decode(dst, src)
	if err != nil {
		return nil, err
	}
	return dst, nil
}

// Dump returns a string that contains a hex dump of the given data. The format
// of the hex dump matches the output of `hexdump -C` on the command line.
func Dump(data []byte) string {
	var buf bytes.Buffer
	dumper := Dumper(&buf)
	dumper.Write(data)
	dumper.Close()
	return string(buf.Bytes())
}

// Dumper returns a WriteCloser that writes a hex dump of all written data to
// w. The format of the dump matches the output of `hexdump -C` on the command
// line.
func Dumper(w io.Writer) io.WriteCloser {
	return &dumper{w: w}
}

type dumper struct {
	w          io.Writer
	rightChars [18]byte
	buf        [14]byte
	used       int  // number of bytes in the current line
	n          uint // number of bytes, total
}

func toChar(b byte) byte {
	if b < 32 || b > 126 {
		return '.'
	}
	return b
}

func (h *dumper) Write(data []byte) (n int, err error) {
	// Output lines look like:
	// 00000010  2e 2f 30 31 32 33 34 35  36 37 38 39 3a 3b 3c 3d  |./0123456789:;<=|
	// ^ offset                          ^ extra space              ^ ASCII of line.
	for i := range data {
		if h.used == 0 {
			// At the beginning of a line we print the current
			// offset in hex.
			h.buf[0] = byte(h.n >> 24)
			h.buf[1] = byte(h.n >> 16)
			h.buf[2] = byte(h.n >> 8)
			h.buf[3] = byte(h.n)
			Encode(h.buf[4:], h.buf[:4])
			h.buf[12] = ' '
			h.buf[13] = ' '
			_, err = h.w.Write(h.buf[4:])
		}
		Encode(h.buf[:], data[i:i+1])
		h.buf[2] = ' '
		l := 3
		if h.used == 7 {
			// There's an additional space after the 8th byte.
			h.buf[3] = ' '
			l = 4
		} else if h.used == 15 {
			// At the end of the line there's an extra space and
			// the bar for the right column.
			h.buf[3] = ' '
			h.buf[4] = '|'
			l = 5
		}
		_, err = h.w.Write(h.buf[:l])
		if err != nil {
			return
		}
		n++
		h.rightChars[h.used] = toChar(data[i])
		h.used++
		h.n++
		if h.used == 16 {
			h.rightChars[16] = '|'
			h.rightChars[17] = '\n'
			_, err = h.w.Write(h.rightChars[:])
			if err != nil {
				return
			}
			h.used = 0
		}
	}
	return
}

func (h *dumper) Close() (err error) {
	// See the comments in Write() for the details of this format.
	if h.used == 0 {
		return
	}
	h.buf[0] = ' '
	h.buf[1] = ' '
	h.buf[2] = ' '
	h.buf[3] = ' '
	h.buf[4] = '|'
	nBytes := h.used
	for h.used < 16 {
		l := 3
		if h.used == 7 {
			l = 4
		} else if h.used == 15 {
			l = 5
		}
		_, err = h.w.Write(h.buf[:l])
		if err != nil {
			return
		}
		h.used++
	}
	h.rightChars[nBytes] = '|'
	h.rightChars[nBytes+1] = '\n'
	_, err = h.w.Write(h.rightChars[:nBytes+2])
	return
}
<commit_msg>encoding/hex: add error check for write error.<commit_after>// Copyright 2009 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.

// Package hex implements hexadecimal encoding and decoding.
package hex

import (
	"bytes"
	"errors"
	"fmt"
	"io"
)

const hextable = "0123456789abcdef"

// EncodedLen returns the length of an encoding of n source bytes.
func EncodedLen(n int) int { return n * 2 }

// Encode encodes src into EncodedLen(len(src))
// bytes of dst.  As a convenience, it returns the number
// of bytes written to dst, but this value is always EncodedLen(len(src)).
// Encode implements hexadecimal encoding.
func Encode(dst, src []byte) int {
	for i, v := range src {
		dst[i*2] = hextable[v>>4]
		dst[i*2+1] = hextable[v&0x0f]
	}

	return len(src) * 2
}

// ErrLength results from decoding an odd length slice.
var ErrLength = errors.New("encoding/hex: odd length hex string")

// InvalidByteError values describe errors resulting from an invalid byte in a hex string.
type InvalidByteError byte

func (e InvalidByteError) Error() string {
	return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune(e))
}

func DecodedLen(x int) int { return x / 2 }

// Decode decodes src into DecodedLen(len(src)) bytes, returning the actual
// number of bytes written to dst.
//
// If Decode encounters invalid input, it returns an error describing the failure.
func Decode(dst, src []byte) (int, error) {
	if len(src)%2 == 1 {
		return 0, ErrLength
	}

	for i := 0; i < len(src)/2; i++ {
		a, ok := fromHexChar(src[i*2])
		if !ok {
			return 0, InvalidByteError(src[i*2])
		}
		b, ok := fromHexChar(src[i*2+1])
		if !ok {
			return 0, InvalidByteError(src[i*2+1])
		}
		dst[i] = (a << 4) | b
	}

	return len(src) / 2, nil
}

// fromHexChar converts a hex character into its value and a success flag.
func fromHexChar(c byte) (byte, bool) {
	switch {
	case '0' <= c && c <= '9':
		return c - '0', true
	case 'a' <= c && c <= 'f':
		return c - 'a' + 10, true
	case 'A' <= c && c <= 'F':
		return c - 'A' + 10, true
	}

	return 0, false
}

// EncodeToString returns the hexadecimal encoding of src.
func EncodeToString(src []byte) string {
	dst := make([]byte, EncodedLen(len(src)))
	Encode(dst, src)
	return string(dst)
}

// DecodeString returns the bytes represented by the hexadecimal string s.
func DecodeString(s string) ([]byte, error) {
	src := []byte(s)
	dst := make([]byte, DecodedLen(len(src)))
	_, err := Decode(dst, src)
	if err != nil {
		return nil, err
	}
	return dst, nil
}

// Dump returns a string that contains a hex dump of the given data. The format
// of the hex dump matches the output of `hexdump -C` on the command line.
func Dump(data []byte) string {
	var buf bytes.Buffer
	dumper := Dumper(&buf)
	dumper.Write(data)
	dumper.Close()
	return string(buf.Bytes())
}

// Dumper returns a WriteCloser that writes a hex dump of all written data to
// w. The format of the dump matches the output of `hexdump -C` on the command
// line.
func Dumper(w io.Writer) io.WriteCloser {
	return &dumper{w: w}
}

type dumper struct {
	w          io.Writer
	rightChars [18]byte
	buf        [14]byte
	used       int  // number of bytes in the current line
	n          uint // number of bytes, total
}

func toChar(b byte) byte {
	if b < 32 || b > 126 {
		return '.'
	}
	return b
}

func (h *dumper) Write(data []byte) (n int, err error) {
	// Output lines look like:
	// 00000010  2e 2f 30 31 32 33 34 35  36 37 38 39 3a 3b 3c 3d  |./0123456789:;<=|
	// ^ offset                          ^ extra space              ^ ASCII of line.
	for i := range data {
		if h.used == 0 {
			// At the beginning of a line we print the current
			// offset in hex.
			h.buf[0] = byte(h.n >> 24)
			h.buf[1] = byte(h.n >> 16)
			h.buf[2] = byte(h.n >> 8)
			h.buf[3] = byte(h.n)
			Encode(h.buf[4:], h.buf[:4])
			h.buf[12] = ' '
			h.buf[13] = ' '
			_, err = h.w.Write(h.buf[4:])
			if err != nil {
				return
			}
		}
		Encode(h.buf[:], data[i:i+1])
		h.buf[2] = ' '
		l := 3
		if h.used == 7 {
			// There's an additional space after the 8th byte.
			h.buf[3] = ' '
			l = 4
		} else if h.used == 15 {
			// At the end of the line there's an extra space and
			// the bar for the right column.
			h.buf[3] = ' '
			h.buf[4] = '|'
			l = 5
		}
		_, err = h.w.Write(h.buf[:l])
		if err != nil {
			return
		}
		n++
		h.rightChars[h.used] = toChar(data[i])
		h.used++
		h.n++
		if h.used == 16 {
			h.rightChars[16] = '|'
			h.rightChars[17] = '\n'
			_, err = h.w.Write(h.rightChars[:])
			if err != nil {
				return
			}
			h.used = 0
		}
	}
	return
}

func (h *dumper) Close() (err error) {
	// See the comments in Write() for the details of this format.
	if h.used == 0 {
		return
	}
	h.buf[0] = ' '
	h.buf[1] = ' '
	h.buf[2] = ' '
	h.buf[3] = ' '
	h.buf[4] = '|'
	nBytes := h.used
	for h.used < 16 {
		l := 3
		if h.used == 7 {
			l = 4
		} else if h.used == 15 {
			l = 5
		}
		_, err = h.w.Write(h.buf[:l])
		if err != nil {
			return
		}
		h.used++
	}
	h.rightChars[nBytes] = '|'
	h.rightChars[nBytes+1] = '\n'
	_, err = h.w.Write(h.rightChars[:nBytes+2])
	return
}
<|endoftext|>


<commit_before>// Copyright 2011 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.

package template

import (
	"bytes"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"unicode/utf8"
)

// nextJSCtx returns the context that determines whether a slash after the
// given run of tokens starts a regular expression instead of a division
// operator: / or /=.
//
// This assumes that the token run does not include any string tokens, comment
// tokens, regular expression literal tokens, or division operators.
//
// This fails on some valid but nonsensical JavaScript programs like
// "x = ++/foo/i" which is quite different than "x++/foo/i", but is not known to
// fail on any known useful programs. It is based on the draft
// JavaScript 2.0 lexical grammar and requires one token of lookbehind:
// http://www.mozilla.org/js/language/js20-2000-07/rationale/syntax.html
func nextJSCtx(s []byte, preceding jsCtx) jsCtx {
	s = bytes.TrimRight(s, "\t\n\f\r \u2028\u2029")
	if len(s) == 0 {
		return preceding
	}

	// All cases below are in the single-byte UTF-8 group.
	switch c, n := s[len(s)-1], len(s); c {
	case '+', '-':
		// ++ and -- are not regexp preceders, but + and - are whether
		// they are used as infix or prefix operators.
		start := n - 1
		// Count the number of adjacent dashes or pluses.
		for start > 0 && s[start-1] == c {
			start--
		}
		if (n-start)&1 == 1 {
			// Reached for trailing minus signs since "---" is the
			// same as "-- -".
			return jsCtxRegexp
		}
		return jsCtxDivOp
	case '.':
		// Handle "42."
		if n != 1 && '0' <= s[n-2] && s[n-2] <= '9' {
			return jsCtxDivOp
		}
		return jsCtxRegexp
	// Suffixes for all punctuators from section 7.7 of the language spec
	// that only end binary operators not handled above.
	case ',', '<', '>', '=', '*', '%', '&', '|', '^', '?':
		return jsCtxRegexp
	// Suffixes for all punctuators from section 7.7 of the language spec
	// that are prefix operators not handled above.
	case '!', '~':
		return jsCtxRegexp
	// Matches all the punctuators from section 7.7 of the language spec
	// that are open brackets not handled above.
	case '(', '[':
		return jsCtxRegexp
	// Matches all the punctuators from section 7.7 of the language spec
	// that precede expression starts.
	case ':', ';', '{':
		return jsCtxRegexp
	// CAVEAT: the close punctuators ('}', ']', ')') precede div ops and
	// are handled in the default except for '}' which can precede a
	// division op as in
	//    ({ valueOf: function () { return 42 } } / 2
	// which is valid, but, in practice, developers don't divide object
	// literals, so our heuristic works well for code like
	//    function () { ... }  /foo/.test(x) && sideEffect();
	// The ')' punctuator can precede a regular expression as in
	//     if (b) /foo/.test(x) && ...
	// but this is much less likely than
	//     (a + b) / c
	case '}':
		return jsCtxRegexp
	default:
		// Look for an IdentifierName and see if it is a keyword that
		// can precede a regular expression.
		j := n
		for j > 0 && isJSIdentPart(rune(s[j-1])) {
			j--
		}
		if regexpPrecederKeywords[string(s[j:])] {
			return jsCtxRegexp
		}
	}
	// Otherwise is a punctuator not listed above, or
	// a string which precedes a div op, or an identifier
	// which precedes a div op.
	return jsCtxDivOp
}

// regexPrecederKeywords is a set of reserved JS keywords that can precede a
// regular expression in JS source.
var regexpPrecederKeywords = map[string]bool{
	"break":      true,
	"case":       true,
	"continue":   true,
	"delete":     true,
	"do":         true,
	"else":       true,
	"finally":    true,
	"in":         true,
	"instanceof": true,
	"return":     true,
	"throw":      true,
	"try":        true,
	"typeof":     true,
	"void":       true,
}

var jsonMarshalType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()

// indirectToJSONMarshaler returns the value, after dereferencing as many times
// as necessary to reach the base type (or nil) or an implementation of json.Marshal.
func indirectToJSONMarshaler(a interface{}) interface{} {
	v := reflect.ValueOf(a)
	for !v.Type().Implements(jsonMarshalType) && v.Kind() == reflect.Ptr && !v.IsNil() {
		v = v.Elem()
	}
	return v.Interface()
}

// jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has
// neither side-effects nor free variables outside (NaN, Infinity).
func jsValEscaper(args ...interface{}) string {
	var a interface{}
	if len(args) == 1 {
		a = indirectToJSONMarshaler(args[0])
		switch t := a.(type) {
		case JS:
			return string(t)
		case JSStr:
			// TODO: normalize quotes.
			return `"` + string(t) + `"`
		case json.Marshaler:
			// Do not treat as a Stringer.
		case fmt.Stringer:
			a = t.String()
		}
	} else {
		for i, arg := range args {
			args[i] = indirectToJSONMarshaler(arg)
		}
		a = fmt.Sprint(args...)
	}
	// TODO: detect cycles before calling Marshal which loops infinitely on
	// cyclic data. This may be an unacceptable DoS risk.

	b, err := json.Marshal(a)
	if err != nil {
		// Put a space before comment so that if it is flush against
		// a division operator it is not turned into a line comment:
		//     x/{{y}}
		// turning into
		//     x//* error marshalling y:
		//          second line of error message */null
		return fmt.Sprintf(" /* %s */null ", strings.Replace(err.Error(), "*/", "* /", -1))
	}

	// TODO: maybe post-process output to prevent it from containing
	// "<!--", "-->", "<![CDATA[", "]]>", or "</script"
	// in case custom marshallers produce output containing those.

	// TODO: Maybe abbreviate \u00ab to \xab to produce more compact output.
	if len(b) == 0 {
		// In, `x=y/{{.}}*z` a json.Marshaler that produces "" should
		// not cause the output `x=y/*z`.
		return " null "
	}
	first, _ := utf8.DecodeRune(b)
	last, _ := utf8.DecodeLastRune(b)
	var buf bytes.Buffer
	// Prevent IdentifierNames and NumericLiterals from running into
	// keywords: in, instanceof, typeof, void
	pad := isJSIdentPart(first) || isJSIdentPart(last)
	if pad {
		buf.WriteByte(' ')
	}
	written := 0
	// Make sure that json.Marshal escapes codepoints U+2028 & U+2029
	// so it falls within the subset of JSON which is valid JS.
	for i := 0; i < len(b); {
		rune, n := utf8.DecodeRune(b[i:])
		repl := ""
		if rune == 0x2028 {
			repl = `\u2028`
		} else if rune == 0x2029 {
			repl = `\u2029`
		}
		if repl != "" {
			buf.Write(b[written:i])
			buf.WriteString(repl)
			written = i + n
		}
		i += n
	}
	if buf.Len() != 0 {
		buf.Write(b[written:])
		if pad {
			buf.WriteByte(' ')
		}
		b = buf.Bytes()
	}
	return string(b)
}

// jsStrEscaper produces a string that can be included between quotes in
// JavaScript source, in JavaScript embedded in an HTML5 <script> element,
// or in an HTML5 event handler attribute such as onclick.
func jsStrEscaper(args ...interface{}) string {
	s, t := stringify(args...)
	if t == contentTypeJSStr {
		return replace(s, jsStrNormReplacementTable)
	}
	return replace(s, jsStrReplacementTable)
}

// jsRegexpEscaper behaves like jsStrEscaper but escapes regular expression
// specials so the result is treated literally when included in a regular
// expression literal. /foo{{.X}}bar/ matches the string "foo" followed by
// the literal text of {{.X}} followed by the string "bar".
func jsRegexpEscaper(args ...interface{}) string {
	s, _ := stringify(args...)
	s = replace(s, jsRegexpReplacementTable)
	if s == "" {
		// /{{.X}}/ should not produce a line comment when .X == "".
		return "(?:)"
	}
	return s
}

// replace replaces each rune r of s with replacementTable[r], provided that
// r < len(replacementTable). If replacementTable[r] is the empty string then
// no replacement is made.
// It also replaces runes U+2028 and U+2029 with the raw strings `\u2028` and
// `\u2029`.
func replace(s string, replacementTable []string) string {
	var b bytes.Buffer
	written := 0
	for i, r := range s {
		var repl string
		switch {
		case int(r) < len(replacementTable) && replacementTable[r] != "":
			repl = replacementTable[r]
		case r == '\u2028':
			repl = `\u2028`
		case r == '\u2029':
			repl = `\u2029`
		default:
			continue
		}
		b.WriteString(s[written:i])
		b.WriteString(repl)
		written = i + utf8.RuneLen(r)
	}
	if written == 0 {
		return s
	}
	b.WriteString(s[written:])
	return b.String()
}

var jsStrReplacementTable = []string{
	0:    `\0`,
	'\t': `\t`,
	'\n': `\n`,
	'\v': `\x0b`, // "\v" == "v" on IE 6.
	'\f': `\f`,
	'\r': `\r`,
	// Encode HTML specials as hex so the output can be embedded
	// in HTML attributes without further encoding.
	'"':  `\x22`,
	'&':  `\x26`,
	'\'': `\x27`,
	'+':  `\x2b`,
	'/':  `\/`,
	'<':  `\x3c`,
	'>':  `\x3e`,
	'\\': `\\`,
}

// jsStrNormReplacementTable is like jsStrReplacementTable but does not
// overencode existing escapes since this table has no entry for `\`.
var jsStrNormReplacementTable = []string{
	0:    `\0`,
	'\t': `\t`,
	'\n': `\n`,
	'\v': `\x0b`, // "\v" == "v" on IE 6.
	'\f': `\f`,
	'\r': `\r`,
	// Encode HTML specials as hex so the output can be embedded
	// in HTML attributes without further encoding.
	'"':  `\x22`,
	'&':  `\x26`,
	'\'': `\x27`,
	'+':  `\x2b`,
	'/':  `\/`,
	'<':  `\x3c`,
	'>':  `\x3e`,
}

var jsRegexpReplacementTable = []string{
	0:    `\0`,
	'\t': `\t`,
	'\n': `\n`,
	'\v': `\x0b`, // "\v" == "v" on IE 6.
	'\f': `\f`,
	'\r': `\r`,
	// Encode HTML specials as hex so the output can be embedded
	// in HTML attributes without further encoding.
	'"':  `\x22`,
	'$':  `\$`,
	'&':  `\x26`,
	'\'': `\x27`,
	'(':  `\(`,
	')':  `\)`,
	'*':  `\*`,
	'+':  `\x2b`,
	'-':  `\-`,
	'.':  `\.`,
	'/':  `\/`,
	'<':  `\x3c`,
	'>':  `\x3e`,
	'?':  `\?`,
	'[':  `\[`,
	'\\': `\\`,
	']':  `\]`,
	'^':  `\^`,
	'{':  `\{`,
	'|':  `\|`,
	'}':  `\}`,
}

// isJSIdentPart reports whether the given rune is a JS identifier part.
// It does not handle all the non-Latin letters, joiners, and combining marks,
// but it does handle every codepoint that can occur in a numeric literal or
// a keyword.
func isJSIdentPart(r rune) bool {
	switch {
	case r == '$':
		return true
	case '0' <= r && r <= '9':
		return true
	case 'A' <= r && r <= 'Z':
		return true
	case r == '_':
		return true
	case 'a' <= r && r <= 'z':
		return true
	}
	return false
}
<commit_msg>html: fix small typo<commit_after>// Copyright 2011 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.

package template

import (
	"bytes"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"unicode/utf8"
)

// nextJSCtx returns the context that determines whether a slash after the
// given run of tokens starts a regular expression instead of a division
// operator: / or /=.
//
// This assumes that the token run does not include any string tokens, comment
// tokens, regular expression literal tokens, or division operators.
//
// This fails on some valid but nonsensical JavaScript programs like
// "x = ++/foo/i" which is quite different than "x++/foo/i", but is not known to
// fail on any known useful programs. It is based on the draft
// JavaScript 2.0 lexical grammar and requires one token of lookbehind:
// http://www.mozilla.org/js/language/js20-2000-07/rationale/syntax.html
func nextJSCtx(s []byte, preceding jsCtx) jsCtx {
	s = bytes.TrimRight(s, "\t\n\f\r \u2028\u2029")
	if len(s) == 0 {
		return preceding
	}

	// All cases below are in the single-byte UTF-8 group.
	switch c, n := s[len(s)-1], len(s); c {
	case '+', '-':
		// ++ and -- are not regexp preceders, but + and - are whether
		// they are used as infix or prefix operators.
		start := n - 1
		// Count the number of adjacent dashes or pluses.
		for start > 0 && s[start-1] == c {
			start--
		}
		if (n-start)&1 == 1 {
			// Reached for trailing minus signs since "---" is the
			// same as "-- -".
			return jsCtxRegexp
		}
		return jsCtxDivOp
	case '.':
		// Handle "42."
		if n != 1 && '0' <= s[n-2] && s[n-2] <= '9' {
			return jsCtxDivOp
		}
		return jsCtxRegexp
	// Suffixes for all punctuators from section 7.7 of the language spec
	// that only end binary operators not handled above.
	case ',', '<', '>', '=', '*', '%', '&', '|', '^', '?':
		return jsCtxRegexp
	// Suffixes for all punctuators from section 7.7 of the language spec
	// that are prefix operators not handled above.
	case '!', '~':
		return jsCtxRegexp
	// Matches all the punctuators from section 7.7 of the language spec
	// that are open brackets not handled above.
	case '(', '[':
		return jsCtxRegexp
	// Matches all the punctuators from section 7.7 of the language spec
	// that precede expression starts.
	case ':', ';', '{':
		return jsCtxRegexp
	// CAVEAT: the close punctuators ('}', ']', ')') precede div ops and
	// are handled in the default except for '}' which can precede a
	// division op as in
	//    ({ valueOf: function () { return 42 } } / 2
	// which is valid, but, in practice, developers don't divide object
	// literals, so our heuristic works well for code like
	//    function () { ... }  /foo/.test(x) && sideEffect();
	// The ')' punctuator can precede a regular expression as in
	//     if (b) /foo/.test(x) && ...
	// but this is much less likely than
	//     (a + b) / c
	case '}':
		return jsCtxRegexp
	default:
		// Look for an IdentifierName and see if it is a keyword that
		// can precede a regular expression.
		j := n
		for j > 0 && isJSIdentPart(rune(s[j-1])) {
			j--
		}
		if regexpPrecederKeywords[string(s[j:])] {
			return jsCtxRegexp
		}
	}
	// Otherwise is a punctuator not listed above, or
	// a string which precedes a div op, or an identifier
	// which precedes a div op.
	return jsCtxDivOp
}

// regexpPrecederKeywords is a set of reserved JS keywords that can precede a
// regular expression in JS source.
var regexpPrecederKeywords = map[string]bool{
	"break":      true,
	"case":       true,
	"continue":   true,
	"delete":     true,
	"do":         true,
	"else":       true,
	"finally":    true,
	"in":         true,
	"instanceof": true,
	"return":     true,
	"throw":      true,
	"try":        true,
	"typeof":     true,
	"void":       true,
}

var jsonMarshalType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()

// indirectToJSONMarshaler returns the value, after dereferencing as many times
// as necessary to reach the base type (or nil) or an implementation of json.Marshal.
func indirectToJSONMarshaler(a interface{}) interface{} {
	v := reflect.ValueOf(a)
	for !v.Type().Implements(jsonMarshalType) && v.Kind() == reflect.Ptr && !v.IsNil() {
		v = v.Elem()
	}
	return v.Interface()
}

// jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has
// neither side-effects nor free variables outside (NaN, Infinity).
func jsValEscaper(args ...interface{}) string {
	var a interface{}
	if len(args) == 1 {
		a = indirectToJSONMarshaler(args[0])
		switch t := a.(type) {
		case JS:
			return string(t)
		case JSStr:
			// TODO: normalize quotes.
			return `"` + string(t) + `"`
		case json.Marshaler:
			// Do not treat as a Stringer.
		case fmt.Stringer:
			a = t.String()
		}
	} else {
		for i, arg := range args {
			args[i] = indirectToJSONMarshaler(arg)
		}
		a = fmt.Sprint(args...)
	}
	// TODO: detect cycles before calling Marshal which loops infinitely on
	// cyclic data. This may be an unacceptable DoS risk.

	b, err := json.Marshal(a)
	if err != nil {
		// Put a space before comment so that if it is flush against
		// a division operator it is not turned into a line comment:
		//     x/{{y}}
		// turning into
		//     x//* error marshalling y:
		//          second line of error message */null
		return fmt.Sprintf(" /* %s */null ", strings.Replace(err.Error(), "*/", "* /", -1))
	}

	// TODO: maybe post-process output to prevent it from containing
	// "<!--", "-->", "<![CDATA[", "]]>", or "</script"
	// in case custom marshallers produce output containing those.

	// TODO: Maybe abbreviate \u00ab to \xab to produce more compact output.
	if len(b) == 0 {
		// In, `x=y/{{.}}*z` a json.Marshaler that produces "" should
		// not cause the output `x=y/*z`.
		return " null "
	}
	first, _ := utf8.DecodeRune(b)
	last, _ := utf8.DecodeLastRune(b)
	var buf bytes.Buffer
	// Prevent IdentifierNames and NumericLiterals from running into
	// keywords: in, instanceof, typeof, void
	pad := isJSIdentPart(first) || isJSIdentPart(last)
	if pad {
		buf.WriteByte(' ')
	}
	written := 0
	// Make sure that json.Marshal escapes codepoints U+2028 & U+2029
	// so it falls within the subset of JSON which is valid JS.
	for i := 0; i < len(b); {
		rune, n := utf8.DecodeRune(b[i:])
		repl := ""
		if rune == 0x2028 {
			repl = `\u2028`
		} else if rune == 0x2029 {
			repl = `\u2029`
		}
		if repl != "" {
			buf.Write(b[written:i])
			buf.WriteString(repl)
			written = i + n
		}
		i += n
	}
	if buf.Len() != 0 {
		buf.Write(b[written:])
		if pad {
			buf.WriteByte(' ')
		}
		b = buf.Bytes()
	}
	return string(b)
}

// jsStrEscaper produces a string that can be included between quotes in
// JavaScript source, in JavaScript embedded in an HTML5 <script> element,
// or in an HTML5 event handler attribute such as onclick.
func jsStrEscaper(args ...interface{}) string {
	s, t := stringify(args...)
	if t == contentTypeJSStr {
		return replace(s, jsStrNormReplacementTable)
	}
	return replace(s, jsStrReplacementTable)
}

// jsRegexpEscaper behaves like jsStrEscaper but escapes regular expression
// specials so the result is treated literally when included in a regular
// expression literal. /foo{{.X}}bar/ matches the string "foo" followed by
// the literal text of {{.X}} followed by the string "bar".
func jsRegexpEscaper(args ...interface{}) string {
	s, _ := stringify(args...)
	s = replace(s, jsRegexpReplacementTable)
	if s == "" {
		// /{{.X}}/ should not produce a line comment when .X == "".
		return "(?:)"
	}
	return s
}

// replace replaces each rune r of s with replacementTable[r], provided that
// r < len(replacementTable). If replacementTable[r] is the empty string then
// no replacement is made.
// It also replaces runes U+2028 and U+2029 with the raw strings `\u2028` and
// `\u2029`.
func replace(s string, replacementTable []string) string {
	var b bytes.Buffer
	written := 0
	for i, r := range s {
		var repl string
		switch {
		case int(r) < len(replacementTable) && replacementTable[r] != "":
			repl = replacementTable[r]
		case r == '\u2028':
			repl = `\u2028`
		case r == '\u2029':
			repl = `\u2029`
		default:
			continue
		}
		b.WriteString(s[written:i])
		b.WriteString(repl)
		written = i + utf8.RuneLen(r)
	}
	if written == 0 {
		return s
	}
	b.WriteString(s[written:])
	return b.String()
}

var jsStrReplacementTable = []string{
	0:    `\0`,
	'\t': `\t`,
	'\n': `\n`,
	'\v': `\x0b`, // "\v" == "v" on IE 6.
	'\f': `\f`,
	'\r': `\r`,
	// Encode HTML specials as hex so the output can be embedded
	// in HTML attributes without further encoding.
	'"':  `\x22`,
	'&':  `\x26`,
	'\'': `\x27`,
	'+':  `\x2b`,
	'/':  `\/`,
	'<':  `\x3c`,
	'>':  `\x3e`,
	'\\': `\\`,
}

// jsStrNormReplacementTable is like jsStrReplacementTable but does not
// overencode existing escapes since this table has no entry for `\`.
var jsStrNormReplacementTable = []string{
	0:    `\0`,
	'\t': `\t`,
	'\n': `\n`,
	'\v': `\x0b`, // "\v" == "v" on IE 6.
	'\f': `\f`,
	'\r': `\r`,
	// Encode HTML specials as hex so the output can be embedded
	// in HTML attributes without further encoding.
	'"':  `\x22`,
	'&':  `\x26`,
	'\'': `\x27`,
	'+':  `\x2b`,
	'/':  `\/`,
	'<':  `\x3c`,
	'>':  `\x3e`,
}

var jsRegexpReplacementTable = []string{
	0:    `\0`,
	'\t': `\t`,
	'\n': `\n`,
	'\v': `\x0b`, // "\v" == "v" on IE 6.
	'\f': `\f`,
	'\r': `\r`,
	// Encode HTML specials as hex so the output can be embedded
	// in HTML attributes without further encoding.
	'"':  `\x22`,
	'$':  `\$`,
	'&':  `\x26`,
	'\'': `\x27`,
	'(':  `\(`,
	')':  `\)`,
	'*':  `\*`,
	'+':  `\x2b`,
	'-':  `\-`,
	'.':  `\.`,
	'/':  `\/`,
	'<':  `\x3c`,
	'>':  `\x3e`,
	'?':  `\?`,
	'[':  `\[`,
	'\\': `\\`,
	']':  `\]`,
	'^':  `\^`,
	'{':  `\{`,
	'|':  `\|`,
	'}':  `\}`,
}

// isJSIdentPart reports whether the given rune is a JS identifier part.
// It does not handle all the non-Latin letters, joiners, and combining marks,
// but it does handle every codepoint that can occur in a numeric literal or
// a keyword.
func isJSIdentPart(r rune) bool {
	switch {
	case r == '$':
		return true
	case '0' <= r && r <= '9':
		return true
	case 'A' <= r && r <= 'Z':
		return true
	case r == '_':
		return true
	case 'a' <= r && r <= 'z':
		return true
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

// Tests for client.go

package http

import (
	"io/ioutil"
	"os"
	"strings"
	"testing"
)

func TestClient(t *testing.T) {
	// TODO: add a proper test suite.  Current test merely verifies that
	// we can retrieve the Google robots.txt file.

	r, _, err := Get("http://www.google.com/robots.txt")
	var b []byte
	if err == nil {
		b, err = ioutil.ReadAll(r.Body)
		r.Body.Close()
	}
	if err != nil {
		t.Error(err)
	} else if s := string(b); !strings.HasPrefix(s, "User-agent:") {
		t.Errorf("Incorrect page body (did not begin with User-agent): %q", s)
	}
}

func TestClientHead(t *testing.T) {
	r, err := Head("http://www.google.com/robots.txt")
	if err != nil {
		t.Fatal(err)
	}
	if _, ok := r.Header["Last-Modified"]; !ok {
		t.Error("Last-Modified header not found.")
	}
}

type recordingTransport struct {
	req *Request
}

func (t *recordingTransport) Do(req *Request) (resp *Response, err os.Error) {
	t.req = req
	return nil, os.NewError("dummy impl")
}

func TestGetRequestFormat(t *testing.T) {
	tr := &recordingTransport{}
	client := &Client{Transport: tr}
	url := "http://dummy.faketld/"
	client.Get(url) // Note: doesn't hit network
	if tr.req.Method != "GET" {
		t.Errorf("expected method %q; got %q", "GET", tr.req.Method)
	}
	if tr.req.URL.String() != url {
		t.Errorf("expected URL %q; got %q", url, tr.req.URL.String())
	}
	if tr.req.Header == nil {
		t.Errorf("expected non-nil request Header")
	}
}
<commit_msg>http: don't hit external network in client_test.go<commit_after>// Copyright 2009 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.

// Tests for client.go

package http_test

import (
	"fmt"
	. "http"
	"http/httptest"
	"io/ioutil"
	"os"
	"strings"
	"testing"
)

var robotsTxtHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
	w.Header().Set("Last-Modified", "sometime")
	fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")
})

func TestClient(t *testing.T) {
	ts := httptest.NewServer(robotsTxtHandler)
	defer ts.Close()

	r, _, err := Get(ts.URL)
	var b []byte
	if err == nil {
		b, err = ioutil.ReadAll(r.Body)
		r.Body.Close()
	}
	if err != nil {
		t.Error(err)
	} else if s := string(b); !strings.HasPrefix(s, "User-agent:") {
		t.Errorf("Incorrect page body (did not begin with User-agent): %q", s)
	}
}

func TestClientHead(t *testing.T) {
	ts := httptest.NewServer(robotsTxtHandler)
	defer ts.Close()

	r, err := Head(ts.URL)
	if err != nil {
		t.Fatal(err)
	}
	if _, ok := r.Header["Last-Modified"]; !ok {
		t.Error("Last-Modified header not found.")
	}
}

type recordingTransport struct {
	req *Request
}

func (t *recordingTransport) Do(req *Request) (resp *Response, err os.Error) {
	t.req = req
	return nil, os.NewError("dummy impl")
}

func TestGetRequestFormat(t *testing.T) {
	tr := &recordingTransport{}
	client := &Client{Transport: tr}
	url := "http://dummy.faketld/"
	client.Get(url) // Note: doesn't hit network
	if tr.req.Method != "GET" {
		t.Errorf("expected method %q; got %q", "GET", tr.req.Method)
	}
	if tr.req.URL.String() != url {
		t.Errorf("expected URL %q; got %q", url, tr.req.URL.String())
	}
	if tr.req.Header == nil {
		t.Errorf("expected non-nil request Header")
	}
}
<|endoftext|>


<commit_before>package sshego

import (
	"bytes"
	"fmt"
	"github.com/glycerine/greenpack/msgp"
	"io"
	"os"
)

//go:generate greenpack

var hostDbKey = "host-db"

type Filedb struct {
	fd       *os.File
	filepath string
	HostDb   *HostDb `zid:"0"`
}

func (b *Filedb) Close() {
	if b != nil && b.fd != nil {
		b.fd.Close()
		b.fd = nil
	}
}

func NewFiledb(filepath string) (*Filedb, error) {

	if len(filepath) == 0 {
		return nil, fmt.Errorf("filepath must not be empty string")
	}
	if filepath[0] != '/' && filepath[0] != '.' {
		// help back-compat with old prefix style argument
		filepath = "./" + filepath
	}

	b := &Filedb{
		filepath: filepath,
	}
	sz := int64(0)
	if fileExists(filepath) {
		fi, err := os.Stat(filepath)
		if err != nil {
			return nil, err
		}
		sz = fi.Size()
		_ = sz
	}

	// maybe windows doesn't report the size?
	//if sz == 0 {
	//return nil, fmt.Errorf("database file present but empty! '%v'", filepath)
	//}

	// Open the my.db data file in your current directory.
	// It will be created if it doesn't exist.
	fd, err := os.OpenFile(b.filepath, os.O_RDWR|os.O_CREATE, 0600)
	if err != nil {
		wd, _ := os.Getwd()
		// probably already open by another process.
		return nil, fmt.Errorf("error opening Filedb: '%v' "+
			"upon trying to open path '%s' in cwd '%s'", err, filepath, wd)
	}
	if err != nil {
		return nil, err
	}
	defer fd.Close()
	err = msgp.Decode(fd, b)

	if err != nil {
		return nil, err
	}
	p("FILEDB opened successfully '%s'", filepath)

	return b, nil
}

func (b *Filedb) SaveToDisk() error {
	p("Filedb.SaveToDisk is saving to b.filepath='%s'", b.filepath)

	fd, err := os.OpenFile(b.filepath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	fdJson, err := os.OpenFile(b.filepath+".json", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer fdJson.Close()
	defer fd.Close()

	by, err := b.MarshalMsg(nil)
	if err != nil {
		return err
	}
	src := bytes.NewBuffer(by)

	_, err = msgp.CopyToJSON(fdJson, src)
	if err != nil {
		return err
	}
	err = writeFull(fd, by)
	if err != nil {
		return err
	}
	return nil
}

func writeFull(w io.Writer, b []byte) error {
	totw := 0
	n := len(b)
	for totw < n {
		nw, err := w.Write(b[totw:])
		if err != nil {
			panic(err)
			return err
		}
		totw += nw
	}
	return nil
}

func (b *Filedb) storeHostDb(h *HostDb) error {
	b.HostDb = h
	return b.SaveToDisk()
}
<commit_msg>under windows do not mess with the path<commit_after>package sshego

import (
	"bytes"
	"fmt"
	"github.com/glycerine/greenpack/msgp"
	"io"
	"os"
	"runtime"
)

//go:generate greenpack

var hostDbKey = "host-db"

type Filedb struct {
	fd       *os.File
	filepath string
	HostDb   *HostDb `zid:"0"`
}

func (b *Filedb) Close() {
	if b != nil && b.fd != nil {
		b.fd.Close()
		b.fd = nil
	}
}

func NewFiledb(filepath string) (*Filedb, error) {

	if len(filepath) == 0 {
		return nil, fmt.Errorf("filepath must not be empty string")
	}
	if filepath[0] != '/' && filepath[0] != '.' {
		if runtime.GOOS != "windows" {
			// help back-compat with old prefix style argument
			filepath = "./" + filepath
		}
	}

	b := &Filedb{
		filepath: filepath,
	}
	sz := int64(0)
	if fileExists(filepath) {
		fi, err := os.Stat(filepath)
		if err != nil {
			return nil, err
		}
		sz = fi.Size()
		_ = sz
	}

	// maybe windows doesn't report the size?
	//if sz == 0 {
	//return nil, fmt.Errorf("database file present but empty! '%v'", filepath)
	//}

	// Open the my.db data file in your current directory.
	// It will be created if it doesn't exist.
	fd, err := os.OpenFile(b.filepath, os.O_RDWR|os.O_CREATE, 0600)
	if err != nil {
		wd, _ := os.Getwd()
		// probably already open by another process.
		return nil, fmt.Errorf("error opening Filedb: '%v' "+
			"upon trying to open path '%s' in cwd '%s'", err, filepath, wd)
	}
	if err != nil {
		return nil, err
	}
	defer fd.Close()
	err = msgp.Decode(fd, b)

	if err != nil {
		return nil, err
	}
	p("FILEDB opened successfully '%s'", filepath)

	return b, nil
}

func (b *Filedb) SaveToDisk() error {
	p("Filedb.SaveToDisk is saving to b.filepath='%s'", b.filepath)

	fd, err := os.OpenFile(b.filepath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	fdJson, err := os.OpenFile(b.filepath+".json", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer fdJson.Close()
	defer fd.Close()

	by, err := b.MarshalMsg(nil)
	if err != nil {
		return err
	}
	src := bytes.NewBuffer(by)

	_, err = msgp.CopyToJSON(fdJson, src)
	if err != nil {
		return err
	}
	err = writeFull(fd, by)
	if err != nil {
		return err
	}
	return nil
}

func writeFull(w io.Writer, b []byte) error {
	totw := 0
	n := len(b)
	for totw < n {
		nw, err := w.Write(b[totw:])
		if err != nil {
			panic(err)
			return err
		}
		totw += nw
	}
	return nil
}

func (b *Filedb) storeHostDb(h *HostDb) error {
	b.HostDb = h
	return b.SaveToDisk()
}
<|endoftext|>