<commit_before>package etw

import (
	"bytes"
	"crypto/sha1"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"strings"
	"unicode/utf16"
	"unsafe"

	"golang.org/x/sys/windows"
)

// Provider represents an ETW event provider. It is identified by a provider
// name and ID (GUID), which should always have a 1:1 mapping to each other
// (e.g. don't use multiple provider names with the same ID, or vice versa).
type Provider struct {
	ID         *windows.GUID
	handle     providerHandle
	metadata   []byte
	callback   EnableCallback
	index      uint
	enabled    bool
	level      Level
	keywordAny uint64
	keywordAll uint64
}

// String returns the `provider`.ID as a string
func (provider *Provider) String() string {
	data1 := make([]byte, 4)
	binary.BigEndian.PutUint32(data1, provider.ID.Data1)
	data2 := make([]byte, 2)
	binary.BigEndian.PutUint16(data2, provider.ID.Data2)
	data3 := make([]byte, 2)
	binary.BigEndian.PutUint16(data3, provider.ID.Data3)
	return fmt.Sprintf(
		"%s-%s-%s-%s-%s",
		hex.EncodeToString(data1),
		hex.EncodeToString(data2),
		hex.EncodeToString(data3),
		hex.EncodeToString(provider.ID.Data4[:2]),
		hex.EncodeToString(provider.ID.Data4[2:]))
}

type providerHandle windows.Handle

// ProviderState informs the provider EnableCallback what action is being
// performed.
type ProviderState uint32

const (
	// ProviderStateDisable indicates the provider is being disabled.
	ProviderStateDisable ProviderState = iota
	// ProviderStateEnable indicates the provider is being enabled.
	ProviderStateEnable
	// ProviderStateCaptureState indicates the provider is having its current
	// state snap-shotted.
	ProviderStateCaptureState
)

type eventInfoClass uint32

const (
	eventInfoClassProviderBinaryTrackInfo eventInfoClass = iota
	eventInfoClassProviderSetReserved1
	eventInfoClassProviderSetTraits
	eventInfoClassProviderUseDescriptorType
)

// EnableCallback is the form of the callback function that receives provider
// enable/disable notifications from ETW.
type EnableCallback func(*windows.GUID, ProviderState, Level, uint64, uint64, uintptr)

func providerCallback(sourceID *windows.GUID, state ProviderState, level Level, matchAnyKeyword uint64, matchAllKeyword uint64, filterData uintptr, i uintptr) {
	provider := providers.getProvider(uint(i))

	switch state {
	case ProviderStateDisable:
		provider.enabled = false
	case ProviderStateEnable:
		provider.enabled = true
		provider.level = level
		provider.keywordAny = matchAnyKeyword
		provider.keywordAll = matchAllKeyword
	}

	if provider.callback != nil {
		provider.callback(sourceID, state, level, matchAnyKeyword, matchAllKeyword, filterData)
	}
}

// providerCallbackAdapter acts as the first-level callback from the C/ETW side
// for provider notifications. Because Go has trouble with callback arguments of
// different size, it has only pointer-sized arguments, which are then cast to
// the appropriate types when calling providerCallback.
func providerCallbackAdapter(sourceID *windows.GUID, state uintptr, level uintptr, matchAnyKeyword uintptr, matchAllKeyword uintptr, filterData uintptr, i uintptr) uintptr {
	providerCallback(sourceID, ProviderState(state), Level(level), uint64(matchAnyKeyword), uint64(matchAllKeyword), filterData, i)
	return 0
}

// providerIDFromName generates a provider ID based on the provider name. It
// uses the same algorithm as used by .NET's EventSource class, which is based
// on RFC 4122. More information on the algorithm can be found here:
// https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/
// The algorithm is roughly:
// Hash = Sha1(namespace + arg.ToUpper().ToUtf16be())
// Guid = Hash[0..15], with Hash[7] tweaked according to RFC 4122
func providerIDFromName(name string) *windows.GUID {
	buffer := sha1.New()

	namespace := []byte{0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8, 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB}
	buffer.Write(namespace)

	binary.Write(buffer, binary.BigEndian, utf16.Encode([]rune(strings.ToUpper(name))))

	sum := buffer.Sum(nil)
	sum[7] = (sum[7] & 0xf) | 0x50

	return &windows.GUID{
		Data1: binary.LittleEndian.Uint32(sum[0:4]),
		Data2: binary.LittleEndian.Uint16(sum[4:6]),
		Data3: binary.LittleEndian.Uint16(sum[6:8]),
		Data4: [8]byte{sum[8], sum[9], sum[10], sum[11], sum[12], sum[13], sum[14], sum[15]},
	}
}

// NewProvider creates and registers a new ETW provider. The provider ID is
// generated based on the provider name.
func NewProvider(name string, callback EnableCallback) (provider *Provider, err error) {
	return NewProviderWithID(name, providerIDFromName(name), callback)
}

// NewProviderWithID creates and registers a new ETW provider, allowing the
// provider ID to be manually specified. This is most useful when there is an
// existing provider ID that must be used to conform to existing diagnostic
// infrastructure.
func NewProviderWithID(name string, id *windows.GUID, callback EnableCallback) (provider *Provider, err error) {
	providerCallbackOnce.Do(func() {
		globalProviderCallback = windows.NewCallback(providerCallbackAdapter)
	})

	provider = providers.newProvider()
	defer func() {
		if err != nil {
			providers.removeProvider(provider)
		}
	}()
	provider.ID = id
	provider.callback = callback

	if err := eventRegister(provider.ID, globalProviderCallback, uintptr(provider.index), &provider.handle); err != nil {
		return nil, err
	}

	metadata := &bytes.Buffer{}
	binary.Write(metadata, binary.LittleEndian, uint16(0)) // Write empty size for buffer (to update later)
	metadata.WriteString(name)
	metadata.WriteByte(0)                                                   // Null terminator for name
	binary.LittleEndian.PutUint16(metadata.Bytes(), uint16(metadata.Len())) // Update the size at the beginning of the buffer
	provider.metadata = metadata.Bytes()

	if err := eventSetInformation(
		provider.handle,
		eventInfoClassProviderSetTraits,
		uintptr(unsafe.Pointer(&provider.metadata[0])),
		uint32(len(provider.metadata))); err != nil {

		return nil, err
	}

	return provider, nil
}

// Close unregisters the provider.
func (provider *Provider) Close() error {
	providers.removeProvider(provider)
	return eventUnregister(provider.handle)
}

// IsEnabled calls IsEnabledForLevelAndKeywords with LevelAlways and all
// keywords set.
func (provider *Provider) IsEnabled() bool {
	return provider.IsEnabledForLevelAndKeywords(LevelAlways, ^uint64(0))
}

// IsEnabledForLevel calls IsEnabledForLevelAndKeywords with the specified level
// and all keywords set.
func (provider *Provider) IsEnabledForLevel(level Level) bool {
	return provider.IsEnabledForLevelAndKeywords(level, ^uint64(0))
}

// IsEnabledForLevelAndKeywords allows event producer code to check if there are
// any event sessions that are interested in an event, based on the event level
// and keywords. Although this check happens automatically in the ETW
// infrastructure, it can be useful to check if an event will actually be
// consumed before doing expensive work to build the event data.
func (provider *Provider) IsEnabledForLevelAndKeywords(level Level, keywords uint64) bool {
	if !provider.enabled {
		return false
	}

	// ETW automatically sets the level to 255 if it is specified as 0, so we
	// don't need to worry about the level=0 (all events) case.
	if level > provider.level {
		return false
	}

	if keywords != 0 && (keywords&provider.keywordAny == 0 || keywords&provider.keywordAll != provider.keywordAll) {
		return false
	}

	return true
}

// WriteEvent writes a single ETW event from the provider. The event is
// constructed based on the EventOpt and FieldOpt values that are passed as
// opts.
func (provider *Provider) WriteEvent(name string, eventOpts []EventOpt, fieldOpts []FieldOpt) error {
	options := eventOptions{descriptor: newEventDescriptor()}
	em := &eventMetadata{}
	ed := &eventData{}

	// We need to evaluate the EventOpts first since they might change tags, and
	// we write out the tags before evaluating FieldOpts.
	for _, opt := range eventOpts {
		opt(&options)
	}

	if !provider.IsEnabledForLevelAndKeywords(options.descriptor.level, options.descriptor.keyword) {
		return nil
	}

	em.writeEventHeader(name, options.tags)

	for _, opt := range fieldOpts {
		opt(em, ed)
	}

	// Don't pass a data blob if there is no event data. There will always be
	// event metadata (e.g. for the name) so we don't need to do this check for
	// the metadata.
	dataBlobs := [][]byte{}
	if len(ed.bytes()) > 0 {
		dataBlobs = [][]byte{ed.bytes()}
	}

	return provider.writeEventRaw(options.descriptor, nil, nil, [][]byte{em.bytes()}, dataBlobs)
}

// writeEventRaw writes a single ETW event from the provider. This function is
// less abstracted than WriteEvent, and presents a fairly direct interface to
// the event writing functionality. It expects a series of event metadata and
// event data blobs to be passed in, which must conform to the TraceLogging
// schema. The functions on EventMetadata and EventData can help with creating
// these blobs. The blobs of each type are effectively concatenated together by
// the ETW infrastructure.
func (provider *Provider) writeEventRaw(
	descriptor *eventDescriptor,
	activityID *windows.GUID,
	relatedActivityID *windows.GUID,
	metadataBlobs [][]byte,
	dataBlobs [][]byte) error {

	dataDescriptorCount := uint32(1 + len(metadataBlobs) + len(dataBlobs))
	dataDescriptors := make([]eventDataDescriptor, 0, dataDescriptorCount)

	dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeProviderMetadata, provider.metadata))
	for _, blob := range metadataBlobs {
		dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeEventMetadata, blob))
	}
	for _, blob := range dataBlobs {
		dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeUserData, blob))
	}

	return eventWriteTransfer(provider.handle, descriptor, activityID, relatedActivityID, dataDescriptorCount, &dataDescriptors[0])
}
<commit_msg>Fix activity ID not being passed in ETW<commit_after>package etw

import (
	"bytes"
	"crypto/sha1"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"strings"
	"unicode/utf16"
	"unsafe"

	"golang.org/x/sys/windows"
)

// Provider represents an ETW event provider. It is identified by a provider
// name and ID (GUID), which should always have a 1:1 mapping to each other
// (e.g. don't use multiple provider names with the same ID, or vice versa).
type Provider struct {
	ID         *windows.GUID
	handle     providerHandle
	metadata   []byte
	callback   EnableCallback
	index      uint
	enabled    bool
	level      Level
	keywordAny uint64
	keywordAll uint64
}

// String returns the `provider`.ID as a string
func (provider *Provider) String() string {
	data1 := make([]byte, 4)
	binary.BigEndian.PutUint32(data1, provider.ID.Data1)
	data2 := make([]byte, 2)
	binary.BigEndian.PutUint16(data2, provider.ID.Data2)
	data3 := make([]byte, 2)
	binary.BigEndian.PutUint16(data3, provider.ID.Data3)
	return fmt.Sprintf(
		"%s-%s-%s-%s-%s",
		hex.EncodeToString(data1),
		hex.EncodeToString(data2),
		hex.EncodeToString(data3),
		hex.EncodeToString(provider.ID.Data4[:2]),
		hex.EncodeToString(provider.ID.Data4[2:]))
}

type providerHandle windows.Handle

// ProviderState informs the provider EnableCallback what action is being
// performed.
type ProviderState uint32

const (
	// ProviderStateDisable indicates the provider is being disabled.
	ProviderStateDisable ProviderState = iota
	// ProviderStateEnable indicates the provider is being enabled.
	ProviderStateEnable
	// ProviderStateCaptureState indicates the provider is having its current
	// state snap-shotted.
	ProviderStateCaptureState
)

type eventInfoClass uint32

const (
	eventInfoClassProviderBinaryTrackInfo eventInfoClass = iota
	eventInfoClassProviderSetReserved1
	eventInfoClassProviderSetTraits
	eventInfoClassProviderUseDescriptorType
)

// EnableCallback is the form of the callback function that receives provider
// enable/disable notifications from ETW.
type EnableCallback func(*windows.GUID, ProviderState, Level, uint64, uint64, uintptr)

func providerCallback(sourceID *windows.GUID, state ProviderState, level Level, matchAnyKeyword uint64, matchAllKeyword uint64, filterData uintptr, i uintptr) {
	provider := providers.getProvider(uint(i))

	switch state {
	case ProviderStateDisable:
		provider.enabled = false
	case ProviderStateEnable:
		provider.enabled = true
		provider.level = level
		provider.keywordAny = matchAnyKeyword
		provider.keywordAll = matchAllKeyword
	}

	if provider.callback != nil {
		provider.callback(sourceID, state, level, matchAnyKeyword, matchAllKeyword, filterData)
	}
}

// providerCallbackAdapter acts as the first-level callback from the C/ETW side
// for provider notifications. Because Go has trouble with callback arguments of
// different size, it has only pointer-sized arguments, which are then cast to
// the appropriate types when calling providerCallback.
func providerCallbackAdapter(sourceID *windows.GUID, state uintptr, level uintptr, matchAnyKeyword uintptr, matchAllKeyword uintptr, filterData uintptr, i uintptr) uintptr {
	providerCallback(sourceID, ProviderState(state), Level(level), uint64(matchAnyKeyword), uint64(matchAllKeyword), filterData, i)
	return 0
}

// providerIDFromName generates a provider ID based on the provider name. It
// uses the same algorithm as used by .NET's EventSource class, which is based
// on RFC 4122. More information on the algorithm can be found here:
// https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/
// The algorithm is roughly:
// Hash = Sha1(namespace + arg.ToUpper().ToUtf16be())
// Guid = Hash[0..15], with Hash[7] tweaked according to RFC 4122
func providerIDFromName(name string) *windows.GUID {
	buffer := sha1.New()

	namespace := []byte{0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8, 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB}
	buffer.Write(namespace)

	binary.Write(buffer, binary.BigEndian, utf16.Encode([]rune(strings.ToUpper(name))))

	sum := buffer.Sum(nil)
	sum[7] = (sum[7] & 0xf) | 0x50

	return &windows.GUID{
		Data1: binary.LittleEndian.Uint32(sum[0:4]),
		Data2: binary.LittleEndian.Uint16(sum[4:6]),
		Data3: binary.LittleEndian.Uint16(sum[6:8]),
		Data4: [8]byte{sum[8], sum[9], sum[10], sum[11], sum[12], sum[13], sum[14], sum[15]},
	}
}

// NewProvider creates and registers a new ETW provider. The provider ID is
// generated based on the provider name.
func NewProvider(name string, callback EnableCallback) (provider *Provider, err error) {
	return NewProviderWithID(name, providerIDFromName(name), callback)
}

// NewProviderWithID creates and registers a new ETW provider, allowing the
// provider ID to be manually specified. This is most useful when there is an
// existing provider ID that must be used to conform to existing diagnostic
// infrastructure.
func NewProviderWithID(name string, id *windows.GUID, callback EnableCallback) (provider *Provider, err error) {
	providerCallbackOnce.Do(func() {
		globalProviderCallback = windows.NewCallback(providerCallbackAdapter)
	})

	provider = providers.newProvider()
	defer func() {
		if err != nil {
			providers.removeProvider(provider)
		}
	}()
	provider.ID = id
	provider.callback = callback

	if err := eventRegister(provider.ID, globalProviderCallback, uintptr(provider.index), &provider.handle); err != nil {
		return nil, err
	}

	metadata := &bytes.Buffer{}
	binary.Write(metadata, binary.LittleEndian, uint16(0)) // Write empty size for buffer (to update later)
	metadata.WriteString(name)
	metadata.WriteByte(0)                                                   // Null terminator for name
	binary.LittleEndian.PutUint16(metadata.Bytes(), uint16(metadata.Len())) // Update the size at the beginning of the buffer
	provider.metadata = metadata.Bytes()

	if err := eventSetInformation(
		provider.handle,
		eventInfoClassProviderSetTraits,
		uintptr(unsafe.Pointer(&provider.metadata[0])),
		uint32(len(provider.metadata))); err != nil {

		return nil, err
	}

	return provider, nil
}

// Close unregisters the provider.
func (provider *Provider) Close() error {
	providers.removeProvider(provider)
	return eventUnregister(provider.handle)
}

// IsEnabled calls IsEnabledForLevelAndKeywords with LevelAlways and all
// keywords set.
func (provider *Provider) IsEnabled() bool {
	return provider.IsEnabledForLevelAndKeywords(LevelAlways, ^uint64(0))
}

// IsEnabledForLevel calls IsEnabledForLevelAndKeywords with the specified level
// and all keywords set.
func (provider *Provider) IsEnabledForLevel(level Level) bool {
	return provider.IsEnabledForLevelAndKeywords(level, ^uint64(0))
}

// IsEnabledForLevelAndKeywords allows event producer code to check if there are
// any event sessions that are interested in an event, based on the event level
// and keywords. Although this check happens automatically in the ETW
// infrastructure, it can be useful to check if an event will actually be
// consumed before doing expensive work to build the event data.
func (provider *Provider) IsEnabledForLevelAndKeywords(level Level, keywords uint64) bool {
	if !provider.enabled {
		return false
	}

	// ETW automatically sets the level to 255 if it is specified as 0, so we
	// don't need to worry about the level=0 (all events) case.
	if level > provider.level {
		return false
	}

	if keywords != 0 && (keywords&provider.keywordAny == 0 || keywords&provider.keywordAll != provider.keywordAll) {
		return false
	}

	return true
}

// WriteEvent writes a single ETW event from the provider. The event is
// constructed based on the EventOpt and FieldOpt values that are passed as
// opts.
func (provider *Provider) WriteEvent(name string, eventOpts []EventOpt, fieldOpts []FieldOpt) error {
	options := eventOptions{descriptor: newEventDescriptor()}
	em := &eventMetadata{}
	ed := &eventData{}

	// We need to evaluate the EventOpts first since they might change tags, and
	// we write out the tags before evaluating FieldOpts.
	for _, opt := range eventOpts {
		opt(&options)
	}

	if !provider.IsEnabledForLevelAndKeywords(options.descriptor.level, options.descriptor.keyword) {
		return nil
	}

	em.writeEventHeader(name, options.tags)

	for _, opt := range fieldOpts {
		opt(em, ed)
	}

	// Don't pass a data blob if there is no event data. There will always be
	// event metadata (e.g. for the name) so we don't need to do this check for
	// the metadata.
	dataBlobs := [][]byte{}
	if len(ed.bytes()) > 0 {
		dataBlobs = [][]byte{ed.bytes()}
	}

	return provider.writeEventRaw(options.descriptor, options.activityID, options.relatedActivityID, [][]byte{em.bytes()}, dataBlobs)
}

// writeEventRaw writes a single ETW event from the provider. This function is
// less abstracted than WriteEvent, and presents a fairly direct interface to
// the event writing functionality. It expects a series of event metadata and
// event data blobs to be passed in, which must conform to the TraceLogging
// schema. The functions on EventMetadata and EventData can help with creating
// these blobs. The blobs of each type are effectively concatenated together by
// the ETW infrastructure.
func (provider *Provider) writeEventRaw(
	descriptor *eventDescriptor,
	activityID *windows.GUID,
	relatedActivityID *windows.GUID,
	metadataBlobs [][]byte,
	dataBlobs [][]byte) error {

	dataDescriptorCount := uint32(1 + len(metadataBlobs) + len(dataBlobs))
	dataDescriptors := make([]eventDataDescriptor, 0, dataDescriptorCount)

	dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeProviderMetadata, provider.metadata))
	for _, blob := range metadataBlobs {
		dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeEventMetadata, blob))
	}
	for _, blob := range dataBlobs {
		dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeUserData, blob))
	}

	return eventWriteTransfer(provider.handle, descriptor, activityID, relatedActivityID, dataDescriptorCount, &dataDescriptors[0])
}
<|endoftext|>


<commit_before>package files

import (
	"errors"
	"os"
	"path/filepath"
	"sync"

	"github.com/materials-commons/gohandy/file"
)

// A TreeEntry is passed along the channel to the process function.
type TreeEntry struct {
	Path  string
	Finfo os.FileInfo
}

// ProcessFunc is the signature for a function that processes each file entry.
type ProcessFunc func(done <-chan struct{}, f <-chan TreeEntry, result chan<- string)

// IgnoreFunc is a function definition that tells walkFiles when it should
// ignore files and directory paths.
type IgnoreFunc func(path string, finfo os.FileInfo) bool

// PWalker implements a parallel walker. It holds the options and processing
// methods to use during the walk.
type PWalker struct {
	// NumParallel is the number of go routines to start for processing.
	NumParallel int

	// ProcessFn is the function to use to process entries.
	ProcessFn ProcessFunc

	// IgnoreFn is the method to test if an entry should be ignored. If
	// IgnoreFn is nil then all entries will be process.
	IgnoreFn IgnoreFunc

	// ProcessDirs tells the walker whether directory entries should also
	// be passed to ProcessFn. It defaults to false, so by default directories
	// are not passed to ProcessFn.
	ProcessDirs bool
}

// walkFiles walks a directory tree. It ignores files and paths with permission errors. All other errors
// will cause it to stop walking the tree. For every path segment it will call ignorePathFn and skip that
// file or sub directory if it returns true. Otherwise all regular files are sent along the filesChan
// for further processing.
func (p *PWalker) walkFiles(done <-chan struct{}, root string) (<-chan TreeEntry, <-chan error) {
	// filesChan channel is where we send regular files that aren't ignored.
	filesChan := make(chan TreeEntry)

	// errChan sends errors found while walking the tree.
	errChan := make(chan error, 1)

	// Walk the tree in separate go routine. Send back results and errors in the filesChan and errChan channels.
	go func() {
		defer close(filesChan)
		errChan <- filepath.Walk(root, func(path string, finfo os.FileInfo, err error) error {
			switch {
			case err != nil && os.IsPermission(err):
				// Permission errors are ignored. Just continue walking the tree
				// without processing the file or directory.
				return nil

			case err != nil:
				// All other errors cause walking to stop.
				return err

			case p.IgnoreFn(path, finfo):
				// if ignorePathFn returns true then skip processing this entry.
				if finfo.IsDir() {
					// If entry is a directory, then skip processing that
					// entire sub tree.
					return filepath.SkipDir
				}
				return nil

			case !finfo.Mode().IsRegular() && !finfo.IsDir():
				// Only process regular files. This means the following types
				// are not processed: Symbol Link, Named Pipe, Socket, and
				// device files.
				//
				// Directories are a special case and are handled in the default
				// clause, where regulars files are also handled.
				return nil

			default:
				// Directories aren't being processed, so skip this entry.
				if finfo.IsDir() && !p.ProcessDirs {
					return nil
				}

				// This is an entry we can process, so send it along the fileChan
				exfi, _ := file.ExStatFromFileInfo(finfo, path)
				entry := TreeEntry{
					Path:  path,
					Finfo: exfi,
				}
				select {
				case filesChan <- entry:
				case <-done:
					// if we receive a message on the done channel, then stop
					// walking the tree.
					return errors.New("walk canceled")
				}
				return nil
			}

		})
	}()
	return filesChan, errChan
}

// PWalk will walk a tree and process files in it in parallel. fn is the process function. "n" go routines will
// be started with fn for processing. The ignorePathFn determines whether a file or directory path should be
// ignored. ignorePathFn can be nil. If it is nil then no files or paths are ignored.
func (p *PWalker) PWalk(root string) (<-chan string, <-chan error) {
	done := make(chan struct{})
	defer close(done)

	if p.IgnoreFn == nil {
		p.IgnoreFn = neverIgnore
	}

	if p.NumParallel < 1 {
		p.NumParallel = 3
	}

	filesChan, errChan := p.walkFiles(done, root)

	// results holds the results of processing each entry. Its value is
	// ignored and only exists so that PWalk will block until all
	// processing has completed.
	results := make(chan string)

	// Setup a WaitGroup equal to the number of routines processing
	// file entries.
	var wg sync.WaitGroup
	wg.Add(p.NumParallel)

	// Start "n" fn routines to process files. When each one completes
	// processing all entries along its channel signal done.
	for i := 0; i < p.NumParallel; i++ {
		go func() {
			p.ProcessFn(done, filesChan, results)
			wg.Done()
		}()
	}

	go func() {
		wg.Wait()
		close(results)
	}()
	for r := range results {
		var _ = r
	}
	return results, errChan
}

// neverIgnore is the default method for checking if an entry should be ignored.
// It never ignores an entry.
func neverIgnore(_ string, _ os.FileInfo) bool {
	return false
}
<commit_msg>Add func IgnoreDotFiles and make it the default function. This function will ignore all files that start with a "." when uploading files and directories. Also exported the NeverIgnore function in case we ever want to setup an upload that never ignores files.<commit_after>package files

import (
	"errors"
	"os"
	"path/filepath"
	"sync"

	"strings"

	"github.com/materials-commons/gohandy/file"
)

// A TreeEntry is passed along the channel to the process function.
type TreeEntry struct {
	Path  string
	Finfo os.FileInfo
}

// ProcessFunc is the signature for a function that processes each file entry.
type ProcessFunc func(done <-chan struct{}, f <-chan TreeEntry, result chan<- string)

// IgnoreFunc is a function definition that tells walkFiles when it should
// ignore files and directory paths.
type IgnoreFunc func(path string, finfo os.FileInfo) bool

// PWalker implements a parallel walker. It holds the options and processing
// methods to use during the walk.
type PWalker struct {
	// NumParallel is the number of go routines to start for processing.
	NumParallel int

	// ProcessFn is the function to use to process entries.
	ProcessFn ProcessFunc

	// IgnoreFn is the method to test if an entry should be ignored. If
	// IgnoreFn is nil then all entries will be process.
	IgnoreFn IgnoreFunc

	// ProcessDirs tells the walker whether directory entries should also
	// be passed to ProcessFn. It defaults to false, so by default directories
	// are not passed to ProcessFn.
	ProcessDirs bool
}

// walkFiles walks a directory tree. It ignores files and paths with permission errors. All other errors
// will cause it to stop walking the tree. For every path segment it will call ignorePathFn and skip that
// file or sub directory if it returns true. Otherwise all regular files are sent along the filesChan
// for further processing.
func (p *PWalker) walkFiles(done <-chan struct{}, root string) (<-chan TreeEntry, <-chan error) {
	// filesChan channel is where we send regular files that aren't ignored.
	filesChan := make(chan TreeEntry)

	// errChan sends errors found while walking the tree.
	errChan := make(chan error, 1)

	// Walk the tree in separate go routine. Send back results and errors in the filesChan and errChan channels.
	go func() {
		defer close(filesChan)
		errChan <- filepath.Walk(root, func(path string, finfo os.FileInfo, err error) error {
			switch {
			case err != nil && os.IsPermission(err):
				// Permission errors are ignored. Just continue walking the tree
				// without processing the file or directory.
				return nil

			case err != nil:
				// All other errors cause walking to stop.
				return err

			case p.IgnoreFn(path, finfo):
				// if ignorePathFn returns true then skip processing this entry.
				if finfo.IsDir() {
					// If entry is a directory, then skip processing that
					// entire sub tree.
					return filepath.SkipDir
				}
				return nil

			case !finfo.Mode().IsRegular() && !finfo.IsDir():
				// Only process regular files. This means the following types
				// are not processed: Symbol Link, Named Pipe, Socket, and
				// device files.
				//
				// Directories are a special case and are handled in the default
				// clause, where regulars files are also handled.
				return nil

			default:
				// Directories aren't being processed, so skip this entry.
				if finfo.IsDir() && !p.ProcessDirs {
					return nil
				}

				// This is an entry we can process, so send it along the fileChan
				exfi, _ := file.ExStatFromFileInfo(finfo, path)
				entry := TreeEntry{
					Path:  path,
					Finfo: exfi,
				}
				select {
				case filesChan <- entry:
				case <-done:
					// if we receive a message on the done channel, then stop
					// walking the tree.
					return errors.New("walk canceled")
				}
				return nil
			}

		})
	}()
	return filesChan, errChan
}

// PWalk will walk a tree and process files in it in parallel. fn is the process function. "n" go routines will
// be started with fn for processing. The ignorePathFn determines whether a file or directory path should be
// ignored. ignorePathFn can be nil. If it is nil then no files or paths are ignored.
func (p *PWalker) PWalk(root string) (<-chan string, <-chan error) {
	done := make(chan struct{})
	defer close(done)

	if p.IgnoreFn == nil {
		p.IgnoreFn = IgnoreDotFiles
	}

	if p.NumParallel < 1 {
		p.NumParallel = 3
	}

	filesChan, errChan := p.walkFiles(done, root)

	// results holds the results of processing each entry. Its value is
	// ignored and only exists so that PWalk will block until all
	// processing has completed.
	results := make(chan string)

	// Setup a WaitGroup equal to the number of routines processing
	// file entries.
	var wg sync.WaitGroup
	wg.Add(p.NumParallel)

	// Start "n" fn routines to process files. When each one completes
	// processing all entries along its channel signal done.
	for i := 0; i < p.NumParallel; i++ {
		go func() {
			p.ProcessFn(done, filesChan, results)
			wg.Done()
		}()
	}

	go func() {
		wg.Wait()
		close(results)
	}()
	for r := range results {
		var _ = r
	}
	return results, errChan
}

// NeverIgnore never ignores an entry.
func NeverIgnore(_ string, _ os.FileInfo) bool {
	return false
}

// IgnoreDotFiles returns true if the name starts with a ".".
// This is the default method for checking if an entry should be ignored.
func IgnoreDotFiles(_ string, fileInfo os.FileInfo) bool {
	if strings.HasPrefix(fileInfo.Name(), ".") {
		return true
	}

	return false
}
<|endoftext|>


<commit_before>package ics

import (
	"errors"
	"io"
)

type parser struct {
	l *lexer
	t token
}

func newParser(r io.Reader) *parser {
	return &parser{l: newLexer(r)}
}

func (p *parser) GetComponent() (c component, err error) {
	n, err := p.readName()
	if err != nil {
		return nil, err
	}
	switch n {
	case beginc:
		return p.readBeginComponent()
	case endc:
		return p.readEndComponent()
	case calscalec:
		return p.readCalScaleComponent()
	case methodc:
		return p.readMethodComponent()
	case prodidc:
		return p.readProductIDComponent()
	case versionc:
		return p.readVersionComponent()
	case attachc:
		return p.readAttachComponent()
	case categoriesc:
		return p.readCategoriesComponent()
	case classc:
		return p.readClassComponent()
	case commentc:
		return p.readCommentComponent()
	case descriptionc:
		return p.readDescriptionComponent()
	case geoc:
		return p.readGeoComponent()
	case locationc:
		return p.readLocationComponent()
	case percentcompletec:
		return p.readPercentCompleteComponent()
	case priorityc:
		return p.readPriorityComponent()
	case resourcesc:
		return p.readResourcesComponent()
	case statusc:
		return p.readStatusComponent()
	case summaryc:
		return p.readSummaryComponent()
	case completedc:
		return p.readCompletedComponent()
	case dtendc:
		return p.readDateTimeEndComponent()
	case duec:
		return p.readDateTimeDueComponent()
	case dtstartc:
		return p.readDateTimeStartComponent()
	case durationc:
		return p.readDurationComponent()
	case freebusyc:
		return p.readFreeBusyTimeComponent()
	case transpc:
		return p.readTimeTransparencyComponent()
	case tzidc:
		return p.readTimezoneIDComponent()
	case tznamec:
		return p.readTimezoneNameComponent()
	case tzoffsetfromc:
		return p.readTimezoneOffsetFromComponent()
	case tzoffsettoc:
		return p.readTimezoneOffsetToComponent()
	case tzurlc:
		return p.readTimezoneURLComponent()
	case attendeec:
		return p.readAttendeeComponent()
	case contactc:
		return p.readContactComponent()
	case organizerc:
		return p.readOrganizerComponent()
	case recuridc:
		return p.readRecurrenceIDComponent()
	case relatedc:
		return p.readRelatedToComponent()
	case urlc:
		return p.readURLComponent()
	case uidc:
		return p.readUIDComponent()
	case exdatec:
		return p.readExceptionDateComponent()
	case rdatec:
		return p.readRecurrenceDateComponent()
	case rrulec:
		return p.readRecurrenceRuleComponent()
	case actionc:
		return p.readActionComponent()
	case repeatc:
		return p.readRepeatComponent()
	case triggerc:
		return p.readTriggerComponent()
	case createdc:
		return p.readCreateComponent()
	case dtstampc:
		return p.readDateStampComponent()
	case lastmodc:
		return p.readLastModifiedComponent()
	case seqc:
		return p.readSequenceComponent()
	case rstatusc:
		return p.readRequestStatusComponent()
	default:
		return p.readUnknownComponent(p.t.data)
	}
}

func (p *parser) readName() (string, error) {
	if p.t.typ != tokenName {
		var err error
		p.t, err = p.l.GetToken()
		if err != nil {
			return "", err
		} else if p.t.typ != tokenName {
			return "", ErrInvalidToken
		}
	}
	return p.t.data, nil

}

func (p *parser) readAttributes(accepted ...string) (as map[string]attribute, err error) {
	as = make(map[string]attribute)
	var (
		a   attribute
		all bool
	)
	if len(accepted) == 1 && accepted[0] == "*" {
		all = true
	}
	for {
		if p.t.typ != tokenParamName {
			p.t, err = p.l.GetToken()
			if err != nil {
				return nil, err
			}
			if p.t.typ == tokenValue {
				return as, nil
			}
		}
		pt := p.t
		vs := make([]token, 0, 1)
		for {
			t, err := p.l.GetToken()
			if err != nil {
				return nil, err
			}
			if t.typ != tokenParamValue && t.typ != tokenParamQValue {
				p.t = t
				break
			}
			vs = append(vs, t)
		}
		switch pt.data {
		case altrepparam:
			a, err = newAltRepParam(vs)
		case cnparam:
			a, err = newCommonNameParam(vs)
		case cutypeparam:
			a, err = newCalendarUserTypeParam(vs)
		case delfromparam:
			a, err = newDelegatorsParam(vs)
		case deltoparam:
			a, err = newDelegateeParam(vs)
		case dirparam:
			a, err = newDirectoryEntryRefParam(vs)
		case encodingparam:
			a, err = newEncodingParam(vs)
		case fmttypeparam:
			a, err = newFmtTypeParam(vs)
		case fbtypeparam:
			a, err = newFreeBusyParam(vs)
		case languageparam:
			a, err = newLanguageParam(vs)
		case memberparam:
			a, err = newMemberParam(vs)
		case partstatparam:
			a, err = newParticipationStatusParam(vs)
		case rangeparam:
			a, err = newRangeParam(vs)
		case trigrelparam:
			a, err = newAlarmTriggerRelationshipParam(vs)
		case reltypeparam:
			a, err = newRelationshipTypeParam(vs)
		case roleparam:
			a, err = newParticipationRoleParam(vs)
		case rsvpparam:
			a, err = newRSVPExpectationParam(vs)
		case sentbyparam:
			a, err = newSentByParam(vs)
		case tzidparam:
			a, err = newTimezoneIDParam(vs)
		case valuetypeparam:
			a, err = newValueParam(vs)
		default:
			a, err = newUnknownParam(vs)
		}
		if err != nil {
			return nil, err
		}
		for _, pn := range accepted {
			if all || pn == pt.data {
				if _, ok := as[pn]; ok {
					return nil, ErrDuplicateParam
				}
				as[pn] = a
			}
		}
		if p.t.typ != tokenParamName {
			return as, nil
		}
	}
}

func (p *parser) readValue() (v string, err error) {
	if p.t.typ != tokenValue {
		_, err = p.readAttributes()
		if err == nil && p.t.typ != tokenValue {
			err = ErrInvalidToken
		}
	}
	return p.t.data, err
}

// Errors

var (
	ErrInvalidToken   = errors.New("received invalid token")
	ErrDuplicateParam = errors.New("duplicate parameter")
)
<commit_msg>corrected error in new wildcard attributes<commit_after>package ics

import (
	"errors"
	"io"
)

type parser struct {
	l *lexer
	t token
}

func newParser(r io.Reader) *parser {
	return &parser{l: newLexer(r)}
}

func (p *parser) GetComponent() (c component, err error) {
	n, err := p.readName()
	if err != nil {
		return nil, err
	}
	switch n {
	case beginc:
		return p.readBeginComponent()
	case endc:
		return p.readEndComponent()
	case calscalec:
		return p.readCalScaleComponent()
	case methodc:
		return p.readMethodComponent()
	case prodidc:
		return p.readProductIDComponent()
	case versionc:
		return p.readVersionComponent()
	case attachc:
		return p.readAttachComponent()
	case categoriesc:
		return p.readCategoriesComponent()
	case classc:
		return p.readClassComponent()
	case commentc:
		return p.readCommentComponent()
	case descriptionc:
		return p.readDescriptionComponent()
	case geoc:
		return p.readGeoComponent()
	case locationc:
		return p.readLocationComponent()
	case percentcompletec:
		return p.readPercentCompleteComponent()
	case priorityc:
		return p.readPriorityComponent()
	case resourcesc:
		return p.readResourcesComponent()
	case statusc:
		return p.readStatusComponent()
	case summaryc:
		return p.readSummaryComponent()
	case completedc:
		return p.readCompletedComponent()
	case dtendc:
		return p.readDateTimeEndComponent()
	case duec:
		return p.readDateTimeDueComponent()
	case dtstartc:
		return p.readDateTimeStartComponent()
	case durationc:
		return p.readDurationComponent()
	case freebusyc:
		return p.readFreeBusyTimeComponent()
	case transpc:
		return p.readTimeTransparencyComponent()
	case tzidc:
		return p.readTimezoneIDComponent()
	case tznamec:
		return p.readTimezoneNameComponent()
	case tzoffsetfromc:
		return p.readTimezoneOffsetFromComponent()
	case tzoffsettoc:
		return p.readTimezoneOffsetToComponent()
	case tzurlc:
		return p.readTimezoneURLComponent()
	case attendeec:
		return p.readAttendeeComponent()
	case contactc:
		return p.readContactComponent()
	case organizerc:
		return p.readOrganizerComponent()
	case recuridc:
		return p.readRecurrenceIDComponent()
	case relatedc:
		return p.readRelatedToComponent()
	case urlc:
		return p.readURLComponent()
	case uidc:
		return p.readUIDComponent()
	case exdatec:
		return p.readExceptionDateComponent()
	case rdatec:
		return p.readRecurrenceDateComponent()
	case rrulec:
		return p.readRecurrenceRuleComponent()
	case actionc:
		return p.readActionComponent()
	case repeatc:
		return p.readRepeatComponent()
	case triggerc:
		return p.readTriggerComponent()
	case createdc:
		return p.readCreateComponent()
	case dtstampc:
		return p.readDateStampComponent()
	case lastmodc:
		return p.readLastModifiedComponent()
	case seqc:
		return p.readSequenceComponent()
	case rstatusc:
		return p.readRequestStatusComponent()
	default:
		return p.readUnknownComponent(p.t.data)
	}
}

func (p *parser) readName() (string, error) {
	if p.t.typ != tokenName {
		var err error
		p.t, err = p.l.GetToken()
		if err != nil {
			return "", err
		} else if p.t.typ != tokenName {
			return "", ErrInvalidToken
		}
	}
	return p.t.data, nil

}

func (p *parser) readAttributes(accepted ...string) (as map[string]attribute, err error) {
	as = make(map[string]attribute)
	var (
		a   attribute
		all bool
	)
	if len(accepted) == 1 && accepted[0] == "*" {
		all = true
	}
	for {
		if p.t.typ != tokenParamName {
			p.t, err = p.l.GetToken()
			if err != nil {
				return nil, err
			}
			if p.t.typ == tokenValue {
				return as, nil
			}
		}
		pt := p.t
		vs := make([]token, 0, 1)
		for {
			t, err := p.l.GetToken()
			if err != nil {
				return nil, err
			}
			if t.typ != tokenParamValue && t.typ != tokenParamQValue {
				p.t = t
				break
			}
			vs = append(vs, t)
		}
		switch pt.data {
		case altrepparam:
			a, err = newAltRepParam(vs)
		case cnparam:
			a, err = newCommonNameParam(vs)
		case cutypeparam:
			a, err = newCalendarUserTypeParam(vs)
		case delfromparam:
			a, err = newDelegatorsParam(vs)
		case deltoparam:
			a, err = newDelegateeParam(vs)
		case dirparam:
			a, err = newDirectoryEntryRefParam(vs)
		case encodingparam:
			a, err = newEncodingParam(vs)
		case fmttypeparam:
			a, err = newFmtTypeParam(vs)
		case fbtypeparam:
			a, err = newFreeBusyParam(vs)
		case languageparam:
			a, err = newLanguageParam(vs)
		case memberparam:
			a, err = newMemberParam(vs)
		case partstatparam:
			a, err = newParticipationStatusParam(vs)
		case rangeparam:
			a, err = newRangeParam(vs)
		case trigrelparam:
			a, err = newAlarmTriggerRelationshipParam(vs)
		case reltypeparam:
			a, err = newRelationshipTypeParam(vs)
		case roleparam:
			a, err = newParticipationRoleParam(vs)
		case rsvpparam:
			a, err = newRSVPExpectationParam(vs)
		case sentbyparam:
			a, err = newSentByParam(vs)
		case tzidparam:
			a, err = newTimezoneIDParam(vs)
		case valuetypeparam:
			a, err = newValueParam(vs)
		default:
			a, err = newUnknownParam(vs)
		}
		if err != nil {
			return nil, err
		}
		for _, pn := range accepted {
			if all || pn == pt.data {
				if _, ok := as[pt.data]; ok {
					return nil, ErrDuplicateParam
				}
				as[pt.data] = a
			}
		}
		if p.t.typ != tokenParamName {
			return as, nil
		}
	}
}

func (p *parser) readValue() (v string, err error) {
	if p.t.typ != tokenValue {
		_, err = p.readAttributes()
		if err == nil && p.t.typ != tokenValue {
			err = ErrInvalidToken
		}
	}
	return p.t.data, err
}

// Errors

var (
	ErrInvalidToken   = errors.New("received invalid token")
	ErrDuplicateParam = errors.New("duplicate parameter")
)
<|endoftext|>


<commit_before>package dxf

import (
	"errors"
	"fmt"
	"github.com/yofu/dxf/color"
	"github.com/yofu/dxf/drawing"
	"github.com/yofu/dxf/entity"
	"github.com/yofu/dxf/header"
	"github.com/yofu/dxf/table"
	"strconv"
	"strings"
)

func SetFloat(data [2]string, f func(float64)) error {
	val, err := strconv.ParseFloat(strings.TrimSpace(data[1]), 64)
	if err != nil {
		return fmt.Errorf("code %s: %s", data[0], err.Error())
	}
	f(val)
	return nil
}

// HEADER
func ParseHeader(d *drawing.Drawing, line int, data [][2]string) error {
	h := d.Sections[drawing.HEADER].(*header.Header)
	var name string
	var err error
	for _, dt := range data {
		switch dt[0] {
		case "9":
			name = dt[1]
		case "1":
			switch name {
			case "$ACADVER":
				h.Version = dt[1]
			}
		case "10":
			switch name {
			case "$INSBASE":
				err = SetFloat(dt, func(val float64) { h.InsBase[0] = val })
			case "$EXTMIN":
				err = SetFloat(dt, func(val float64) { h.ExtMin[0] = val })
			case "$EXTMAX":
				err = SetFloat(dt, func(val float64) { h.ExtMax[0] = val })
			}
		case "20":
			switch name {
			case "$INSBASE":
				err = SetFloat(dt, func(val float64) { h.InsBase[1] = val })
			case "$EXTMIN":
				err = SetFloat(dt, func(val float64) { h.ExtMin[1] = val })
			case "$EXTMAX":
				err = SetFloat(dt, func(val float64) { h.ExtMax[1] = val })
			}
		case "30":
			switch name {
			case "$INSBASE":
				err = SetFloat(dt, func(val float64) { h.InsBase[2] = val })
			case "$EXTMIN":
				err = SetFloat(dt, func(val float64) { h.ExtMin[2] = val })
			case "$EXTMAX":
				err = SetFloat(dt, func(val float64) { h.ExtMax[2] = val })
			}
		}
		if err != nil {
			return err
		}
	}
	return nil
}

// CLASSES
func ParseClasses(d *drawing.Drawing, line int, data [][2]string) error {
	return nil
}

// TABLES
func ParseTables(d *drawing.Drawing, line int, data [][2]string) error {
	parsers := []func(*drawing.Drawing, [][2]string) (table.SymbolTable, error) {
		ParseVport,
		ParseLtype,
		ParseLayer,
		ParseStyle,
		ParseView,
		ParseUCS,
		ParseAppID,
		ParseDimStyle,
		ParseBlockRecord,
	}
	tmpdata := make([][2]string, 0)
	setparser := false
	var parser func(*drawing.Drawing, [][2]string) (table.SymbolTable, error)
	var ind int
	for i, dt := range data {
		if setparser {
			if dt[0] != "2" {
				return fmt.Errorf("line %d: invalid group code: %s", line + 2*i, dt[0])
			}
			ind = int(table.TableTypeValue(strings.ToUpper(dt[1])))
			if ind < 0 {
				return fmt.Errorf("line %d: unknown table type: %s", line + 2*i, dt[1])
			}
			parser = parsers[ind]
			setparser = false
		} else {
			if dt[0] == "0" {
				switch strings.ToUpper(dt[1]) {
				case "TABLE":
					setparser = true
				case "ENDTAB":
					if len(tmpdata) > 0 {
						err := ParseTable(d, tmpdata, ind, parser)
						if err != nil {
							return err
						}
						tmpdata = make([][2]string, 0)
					}
				default:
					tmpdata = append(tmpdata, dt)
				}
			} else {
				tmpdata = append(tmpdata, dt)
			}
		}
	}
	if len(tmpdata) > 0 {
		err := ParseTable(d, tmpdata, ind, parser)
		if err != nil {
			return fmt.Errorf("line %d: %s", line + 2*len(data), err.Error())
		}
		tmpdata = make([][2]string, 0)
	}
	return nil
}

func ParseTable(d *drawing.Drawing, data [][2]string, index int, parser func(*drawing.Drawing, [][2]string)(table.SymbolTable, error)) error {
	t := d.Sections[drawing.TABLES].(table.Tables)[index]
	t.Clear()
	tmpdata := make([][2]string, 0)
	for _, dt := range data {
		switch dt[0] {
		case "0":
			if len(tmpdata) > 0 {
				st, err := parser(d, tmpdata)
				if err != nil {
					return err
				}
				if st != nil {
					t.Add(st)
				}
				tmpdata = make([][2]string, 0)
			}
		default:
			tmpdata = append(tmpdata, dt)
		}
	}
	if len(tmpdata) > 0 {
		st, err := parser(d, tmpdata)
		if err != nil {
			return err
		}
		if st != nil {
			t.Add(st)
		}
		tmpdata = make([][2]string, 0)
	}
	return nil
}

func ParseVport(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	return nil, nil
}

func ParseLtype(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	var name, desc string
	var lengths []float64
	ind := 0
	for _, dt := range data {
		switch dt[0] {
		case "2":
			name = dt[1]
		case "3":
			desc = dt[1]
		case "73":
			l, err := strconv.ParseInt(strings.TrimSpace(dt[1]), 10, 64)
			if err != nil {
				return nil, err
			}
			lengths = make([]float64, int(l))
		case "49":
			if ind >= len(lengths) {
				return nil, fmt.Errorf("ltype too long")
			}
			val, err := strconv.ParseFloat(dt[1], 64)
			if err != nil {
				return nil, err
			}
			lengths[ind] = val
			ind++
		}
	}
	return table.NewLineType(name, desc, lengths...), nil
}

func ParseLayer(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	var name string
	var flag int
	var col color.ColorNumber
	var lt *table.LineType
	var lw int
	for _, dt := range data {
		switch dt[0] {
		case "2":
			name = dt[1]
		case "70":
			val, err := strconv.ParseInt(strings.TrimSpace(dt[1]), 10, 64)
			if err != nil {
				return nil, err
			}
			flag = int(val)
		case "62":
			val, err := strconv.ParseInt(strings.TrimSpace(dt[1]), 10, 64)
			if err != nil {
				return nil, err
			}
			col = color.ColorNumber(val)
		case "6":
			l, err := d.LineType(dt[1])
			if err != nil {
				return nil, err
			}
			lt = l
		case "370":
			val, err := strconv.ParseInt(strings.TrimSpace(dt[1]), 10, 64)
			if err != nil {
				return nil, err
			}
			lw = int(val)
		case "390":
			// plotstyle
		}
	}
	l := table.NewLayer(name, col, lt)
	l.SetFlag(flag)
	l.SetLineWidth(lw)
	return l, nil
}

func ParseStyle(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	var name, font, bigfont string
	for _, dt := range data {
		switch dt[0] {
		case "2":
			name = dt[1]
		case "3":
			font = dt[1]
		case "4":
			bigfont = dt[1]
		}
	}
	s := table.NewStyle(name)
	s.FontName = font
	s.BigFontName = bigfont
	return s, nil
}

func ParseView(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	return nil, nil
}

func ParseUCS(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	return nil, nil
}

func ParseAppID(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	return nil, nil
}

func ParseDimStyle(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	return nil, nil
}

func ParseBlockRecord(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	return nil, nil
}

// BLOCKS
func ParseBlocks(d *drawing.Drawing, line int, data [][2]string) error {
	return nil
}

// ENTITIES
func ParseEntities(d *drawing.Drawing, line int, data [][2]string) error {
	tmpdata := make([][2]string, 0)
	for i, dt := range data {
		if dt[0] == "0" {
			if len(tmpdata) > 0 {
				e, err := ParseEntity(d, tmpdata)
				if err != nil {
					return fmt.Errorf("line %d: %s", line + 2*i, err.Error())
				}
				d.AddEntity(e)
				tmpdata = make([][2]string, 0)
			}
		}
		tmpdata = append(tmpdata, dt)
	}
	if len(tmpdata) > 0 {
		e, err := ParseEntity(d, tmpdata)
		if err != nil {
			return fmt.Errorf("line %d: %s", line + 2*len(data), err.Error())
		}
		d.AddEntity(e)
		tmpdata = make([][2]string, 0)
	}
	return nil
}

func ParseEntity(d *drawing.Drawing, data [][2]string) (entity.Entity, error) {
	if len(data) < 1 {
		return nil, fmt.Errorf("no data")
	}
	if data[0][0] != "0" {
		return nil, fmt.Errorf("invalid group code: %d", data[0][0])
	}
	f, err := ParseEntityFunc(data[0][1])
	if err != nil {
		return nil, err
	}
	return f(d, data)
}

func ParseEntityFunc(t string) (func(*drawing.Drawing, [][2]string)(entity.Entity, error), error) {
	switch t {
	case "LINE":
		return ParseLine, nil
	// case "3DFACE":
	// 	return Parse3DFace, nil
	// case "LWPOLYLINE":
	// 	return ParseLwPolyline, nil
	// case "CIRCLE":
	// 	return ParseCircle, nil
	// case "POLYLINE":
	// 	return ParsePolyline, nil
	// case "VERTEX":
	// 	return ParseVertex, nil
	// case "POINT":
	// 	return ParsePoint, nil
	// case "TEXT":
	// 	return ParseText, nil
	default:
		return nil, errors.New("unknown entity type")
	}
}

func ParseLine(d *drawing.Drawing, data [][2]string) (entity.Entity, error) {
	l := entity.NewLine()
	var err error
	for _, dt := range data {
		switch dt[0] {
		default:
			continue
		case "8":
			if layer, exists := d.Layers[dt[1]]; exists {
				l.SetLayer(layer)
			} else {
				err = fmt.Errorf("unknown layer: %s", dt[1])
			}
		case "10":
			err = SetFloat(dt, func(val float64) { l.Start[0] = val })
		case "20":
			err = SetFloat(dt, func(val float64) { l.Start[1] = val })
		case "30":
			err = SetFloat(dt, func(val float64) { l.Start[2] = val })
		case "11":
			err = SetFloat(dt, func(val float64) { l.End[0] = val })
		case "21":
			err = SetFloat(dt, func(val float64) { l.End[1] = val })
		case "31":
			err = SetFloat(dt, func(val float64) { l.End[2] = val })
		}
		if err != nil {
			return l, err
		}
	}
	return l, nil
}

// OBJECTS
func ParseObjects(d *drawing.Drawing, line int, data [][2]string) error {
	return nil
}
<commit_msg>implement ParseAppID<commit_after>package dxf

import (
	"errors"
	"fmt"
	"github.com/yofu/dxf/color"
	"github.com/yofu/dxf/drawing"
	"github.com/yofu/dxf/entity"
	"github.com/yofu/dxf/header"
	"github.com/yofu/dxf/table"
	"strconv"
	"strings"
)

func SetFloat(data [2]string, f func(float64)) error {
	val, err := strconv.ParseFloat(strings.TrimSpace(data[1]), 64)
	if err != nil {
		return fmt.Errorf("code %s: %s", data[0], err.Error())
	}
	f(val)
	return nil
}

// HEADER
func ParseHeader(d *drawing.Drawing, line int, data [][2]string) error {
	h := d.Sections[drawing.HEADER].(*header.Header)
	var name string
	var err error
	for _, dt := range data {
		switch dt[0] {
		case "9":
			name = dt[1]
		case "1":
			switch name {
			case "$ACADVER":
				h.Version = dt[1]
			}
		case "10":
			switch name {
			case "$INSBASE":
				err = SetFloat(dt, func(val float64) { h.InsBase[0] = val })
			case "$EXTMIN":
				err = SetFloat(dt, func(val float64) { h.ExtMin[0] = val })
			case "$EXTMAX":
				err = SetFloat(dt, func(val float64) { h.ExtMax[0] = val })
			}
		case "20":
			switch name {
			case "$INSBASE":
				err = SetFloat(dt, func(val float64) { h.InsBase[1] = val })
			case "$EXTMIN":
				err = SetFloat(dt, func(val float64) { h.ExtMin[1] = val })
			case "$EXTMAX":
				err = SetFloat(dt, func(val float64) { h.ExtMax[1] = val })
			}
		case "30":
			switch name {
			case "$INSBASE":
				err = SetFloat(dt, func(val float64) { h.InsBase[2] = val })
			case "$EXTMIN":
				err = SetFloat(dt, func(val float64) { h.ExtMin[2] = val })
			case "$EXTMAX":
				err = SetFloat(dt, func(val float64) { h.ExtMax[2] = val })
			}
		}
		if err != nil {
			return err
		}
	}
	return nil
}

// CLASSES
func ParseClasses(d *drawing.Drawing, line int, data [][2]string) error {
	return nil
}

// TABLES
func ParseTables(d *drawing.Drawing, line int, data [][2]string) error {
	parsers := []func(*drawing.Drawing, [][2]string) (table.SymbolTable, error) {
		ParseVport,
		ParseLtype,
		ParseLayer,
		ParseStyle,
		ParseView,
		ParseUCS,
		ParseAppID,
		ParseDimStyle,
		ParseBlockRecord,
	}
	tmpdata := make([][2]string, 0)
	setparser := false
	var parser func(*drawing.Drawing, [][2]string) (table.SymbolTable, error)
	var ind int
	for i, dt := range data {
		if setparser {
			if dt[0] != "2" {
				return fmt.Errorf("line %d: invalid group code: %s", line + 2*i, dt[0])
			}
			ind = int(table.TableTypeValue(strings.ToUpper(dt[1])))
			if ind < 0 {
				return fmt.Errorf("line %d: unknown table type: %s", line + 2*i, dt[1])
			}
			parser = parsers[ind]
			setparser = false
		} else {
			if dt[0] == "0" {
				switch strings.ToUpper(dt[1]) {
				case "TABLE":
					setparser = true
				case "ENDTAB":
					if len(tmpdata) > 0 {
						err := ParseTable(d, tmpdata, ind, parser)
						if err != nil {
							return err
						}
						tmpdata = make([][2]string, 0)
					}
				default:
					tmpdata = append(tmpdata, dt)
				}
			} else {
				tmpdata = append(tmpdata, dt)
			}
		}
	}
	if len(tmpdata) > 0 {
		err := ParseTable(d, tmpdata, ind, parser)
		if err != nil {
			return fmt.Errorf("line %d: %s", line + 2*len(data), err.Error())
		}
		tmpdata = make([][2]string, 0)
	}
	return nil
}

func ParseTable(d *drawing.Drawing, data [][2]string, index int, parser func(*drawing.Drawing, [][2]string)(table.SymbolTable, error)) error {
	t := d.Sections[drawing.TABLES].(table.Tables)[index]
	t.Clear()
	tmpdata := make([][2]string, 0)
	for _, dt := range data {
		switch dt[0] {
		case "0":
			if len(tmpdata) > 0 {
				st, err := parser(d, tmpdata)
				if err != nil {
					return err
				}
				if st != nil {
					t.Add(st)
				}
				tmpdata = make([][2]string, 0)
			}
		default:
			tmpdata = append(tmpdata, dt)
		}
	}
	if len(tmpdata) > 0 {
		st, err := parser(d, tmpdata)
		if err != nil {
			return err
		}
		if st != nil {
			t.Add(st)
		}
		tmpdata = make([][2]string, 0)
	}
	return nil
}

func ParseVport(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	return nil, nil
}

func ParseLtype(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	var name, desc string
	var lengths []float64
	ind := 0
	for _, dt := range data {
		switch dt[0] {
		case "2":
			name = dt[1]
		case "3":
			desc = dt[1]
		case "73":
			l, err := strconv.ParseInt(strings.TrimSpace(dt[1]), 10, 64)
			if err != nil {
				return nil, err
			}
			lengths = make([]float64, int(l))
		case "49":
			if ind >= len(lengths) {
				return nil, fmt.Errorf("ltype too long")
			}
			val, err := strconv.ParseFloat(dt[1], 64)
			if err != nil {
				return nil, err
			}
			lengths[ind] = val
			ind++
		}
	}
	return table.NewLineType(name, desc, lengths...), nil
}

func ParseLayer(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	var name string
	var flag int
	var col color.ColorNumber
	var lt *table.LineType
	var lw int
	for _, dt := range data {
		switch dt[0] {
		case "2":
			name = dt[1]
		case "70":
			val, err := strconv.ParseInt(strings.TrimSpace(dt[1]), 10, 64)
			if err != nil {
				return nil, err
			}
			flag = int(val)
		case "62":
			val, err := strconv.ParseInt(strings.TrimSpace(dt[1]), 10, 64)
			if err != nil {
				return nil, err
			}
			col = color.ColorNumber(val)
		case "6":
			l, err := d.LineType(dt[1])
			if err != nil {
				return nil, err
			}
			lt = l
		case "370":
			val, err := strconv.ParseInt(strings.TrimSpace(dt[1]), 10, 64)
			if err != nil {
				return nil, err
			}
			lw = int(val)
		case "390":
			// plotstyle
		}
	}
	l := table.NewLayer(name, col, lt)
	l.SetFlag(flag)
	l.SetLineWidth(lw)
	return l, nil
}

func ParseStyle(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	var name, font, bigfont string
	for _, dt := range data {
		switch dt[0] {
		case "2":
			name = dt[1]
		case "3":
			font = dt[1]
		case "4":
			bigfont = dt[1]
		}
	}
	s := table.NewStyle(name)
	s.FontName = font
	s.BigFontName = bigfont
	return s, nil
}

func ParseView(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	return nil, nil
}

func ParseUCS(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	return nil, nil
}

func ParseAppID(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	var name string
	for _, dt := range data {
		switch dt[0] {
		case "2":
			name = dt[1]
		}
	}
	a := table.NewAppID(name)
	return a, nil
}

func ParseDimStyle(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	return nil, nil
}

func ParseBlockRecord(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) {
	return nil, nil
}

// BLOCKS
func ParseBlocks(d *drawing.Drawing, line int, data [][2]string) error {
	return nil
}

// ENTITIES
func ParseEntities(d *drawing.Drawing, line int, data [][2]string) error {
	tmpdata := make([][2]string, 0)
	for i, dt := range data {
		if dt[0] == "0" {
			if len(tmpdata) > 0 {
				e, err := ParseEntity(d, tmpdata)
				if err != nil {
					return fmt.Errorf("line %d: %s", line + 2*i, err.Error())
				}
				d.AddEntity(e)
				tmpdata = make([][2]string, 0)
			}
		}
		tmpdata = append(tmpdata, dt)
	}
	if len(tmpdata) > 0 {
		e, err := ParseEntity(d, tmpdata)
		if err != nil {
			return fmt.Errorf("line %d: %s", line + 2*len(data), err.Error())
		}
		d.AddEntity(e)
		tmpdata = make([][2]string, 0)
	}
	return nil
}

func ParseEntity(d *drawing.Drawing, data [][2]string) (entity.Entity, error) {
	if len(data) < 1 {
		return nil, fmt.Errorf("no data")
	}
	if data[0][0] != "0" {
		return nil, fmt.Errorf("invalid group code: %d", data[0][0])
	}
	f, err := ParseEntityFunc(data[0][1])
	if err != nil {
		return nil, err
	}
	return f(d, data)
}

func ParseEntityFunc(t string) (func(*drawing.Drawing, [][2]string)(entity.Entity, error), error) {
	switch t {
	case "LINE":
		return ParseLine, nil
	// case "3DFACE":
	// 	return Parse3DFace, nil
	// case "LWPOLYLINE":
	// 	return ParseLwPolyline, nil
	// case "CIRCLE":
	// 	return ParseCircle, nil
	// case "POLYLINE":
	// 	return ParsePolyline, nil
	// case "VERTEX":
	// 	return ParseVertex, nil
	// case "POINT":
	// 	return ParsePoint, nil
	// case "TEXT":
	// 	return ParseText, nil
	default:
		return nil, errors.New("unknown entity type")
	}
}

func ParseLine(d *drawing.Drawing, data [][2]string) (entity.Entity, error) {
	l := entity.NewLine()
	var err error
	for _, dt := range data {
		switch dt[0] {
		default:
			continue
		case "8":
			if layer, exists := d.Layers[dt[1]]; exists {
				l.SetLayer(layer)
			} else {
				err = fmt.Errorf("unknown layer: %s", dt[1])
			}
		case "10":
			err = SetFloat(dt, func(val float64) { l.Start[0] = val })
		case "20":
			err = SetFloat(dt, func(val float64) { l.Start[1] = val })
		case "30":
			err = SetFloat(dt, func(val float64) { l.Start[2] = val })
		case "11":
			err = SetFloat(dt, func(val float64) { l.End[0] = val })
		case "21":
			err = SetFloat(dt, func(val float64) { l.End[1] = val })
		case "31":
			err = SetFloat(dt, func(val float64) { l.End[2] = val })
		}
		if err != nil {
			return l, err
		}
	}
	return l, nil
}

// OBJECTS
func ParseObjects(d *drawing.Drawing, line int, data [][2]string) error {
	return nil
}
<|endoftext|>


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

package kvstore

import (
	"context"
	"fmt"
	"time"

	"github.com/cilium/cilium/pkg/lock"
	uuidfactor "github.com/cilium/cilium/pkg/uuid"

	"github.com/pborman/uuid"
	"github.com/sirupsen/logrus"
)

var (
	kvstoreLocks = pathLocks{lockPaths: map[string]uuid.UUID{}}

	// staleLockTimeout is the timeout after which waiting for a believed
	// other local lock user for the same key is given up on and etcd is
	// asked directly. It is still highly unlikely that concurrent access
	// occurs as only one consumer will manage to acquire the newly
	// released lock. The only possibility of concurrent access is if a
	// consumer is *still* holding the lock but this is highly unlikely
	// given the duration of this timeout.
	staleLockTimeout = time.Duration(30) * time.Second
)

type kvLocker interface {
	Unlock() error
}

// getLockPath returns the lock path representation of the given path.
func getLockPath(path string) string {
	return path + ".lock"
}

type pathLocks struct {
	mutex     lock.RWMutex
	lockPaths map[string]uuid.UUID
}

func (pl *pathLocks) lock(ctx context.Context, path string) (id uuid.UUID, err error) {
	id = uuidfactor.NewUUID()
	started := time.Now()

	for {
		pl.mutex.Lock()

		if _, ok := pl.lockPaths[path]; !ok {
			pl.lockPaths[path] = id
			pl.mutex.Unlock()
			return
		}

		if time.Since(started) > staleLockTimeout {
			log.WithField("path", path).Warning("Timeout while waiting for lock, forcefully unlocking...")
			delete(pl.lockPaths, path)
			pl.mutex.Unlock()

			// The lock was forcefully released, restart a new
			// timeout period as we will attempt to acquire the
			// local lock again
			started = time.Now()
			continue
		}

		pl.mutex.Unlock()

		select {
		case <-time.After(time.Duration(10) * time.Millisecond):
		case <-ctx.Done():
			err = fmt.Errorf("lock was cancelled: %s", ctx.Err())
			return
		}
	}
}

func (pl *pathLocks) unlock(path string, id uuid.UUID) {
	pl.mutex.Lock()
	if owner, ok := pl.lockPaths[path]; ok && uuid.Equal(owner, id) {
		delete(pl.lockPaths, path)
	}
	pl.mutex.Unlock()
}

// Lock is a lock return by LockPath
type Lock struct {
	path   string
	id     uuid.UUID
	kvLock kvLocker
}

// LockPath locks the specified path. The key for the lock is not the path
// provided itself but the path with a suffix of ".lock" appended. The lock
// returned also contains a patch specific local Mutex which will be held.
//
// It is required to call Unlock() on the returned Lock to unlock
func LockPath(ctx context.Context, path string) (l *Lock, err error) {
	id, err := kvstoreLocks.lock(ctx, path)
	if err != nil {
		return nil, err
	}

	lock, err := Client().LockPath(ctx, path)
	if err != nil {
		kvstoreLocks.unlock(path, id)
		Trace("Failed to lock", err, logrus.Fields{fieldKey: path})
		err = fmt.Errorf("error while locking path %s: %s", path, err)
		return nil, err
	}

	Trace("Successful lock", err, logrus.Fields{fieldKey: path})
	return &Lock{kvLock: lock, path: path, id: id}, err
}

// Unlock unlocks a lock
func (l *Lock) Unlock() error {
	if l == nil {
		return nil
	}

	// Unlock kvstore mutex first
	err := l.kvLock.Unlock()

	// unlock local lock even if kvstore cannot be unlocked
	kvstoreLocks.unlock(l.path, l.id)
	Trace("Unlocked", nil, logrus.Fields{fieldKey: l.path})

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

package kvstore

import (
	"context"
	"fmt"
	"time"

	"github.com/cilium/cilium/pkg/lock"
	uuidfactor "github.com/cilium/cilium/pkg/uuid"

	"github.com/pborman/uuid"
	"github.com/sirupsen/logrus"
)

var (
	kvstoreLocks = pathLocks{lockPaths: map[string]uuid.UUID{}}

	// staleLockTimeout is the timeout after which waiting for a believed
	// other local lock user for the same key is given up on and etcd is
	// asked directly. It is still highly unlikely that concurrent access
	// occurs as only one consumer will manage to acquire the newly
	// released lock. The only possibility of concurrent access is if a
	// consumer is *still* holding the lock but this is highly unlikely
	// given the duration of this timeout.
	staleLockTimeout = time.Duration(30) * time.Second
)

type kvLocker interface {
	Unlock() error
}

// getLockPath returns the lock path representation of the given path.
func getLockPath(path string) string {
	return path + ".lock"
}

type pathLocks struct {
	mutex     lock.RWMutex
	lockPaths map[string]uuid.UUID
}

func (pl *pathLocks) lock(ctx context.Context, path string) (id uuid.UUID, err error) {
	id = uuidfactor.NewUUID()
	started := time.Now()

	for {
		pl.mutex.Lock()

		if _, ok := pl.lockPaths[path]; !ok {
			pl.lockPaths[path] = id
			pl.mutex.Unlock()
			return
		}

		if time.Since(started) > staleLockTimeout {
			log.WithField("path", path).Warning("Timeout while waiting for lock, forcefully unlocking...")
			delete(pl.lockPaths, path)
			pl.mutex.Unlock()

			// The lock was forcefully released, restart a new
			// timeout period as we will attempt to acquire the
			// local lock again
			started = time.Now()
			continue
		}

		pl.mutex.Unlock()

		select {
		case <-time.After(time.Duration(10) * time.Millisecond):
		case <-ctx.Done():
			err = fmt.Errorf("lock was cancelled: %s", ctx.Err())
			return
		}
	}
}

func (pl *pathLocks) unlock(path string, id uuid.UUID) {
	pl.mutex.Lock()
	if owner, ok := pl.lockPaths[path]; ok && uuid.Equal(owner, id) {
		delete(pl.lockPaths, path)
	}
	pl.mutex.Unlock()
}

// Lock is a lock return by LockPath
type Lock struct {
	path   string
	id     uuid.UUID
	kvLock kvLocker
}

// LockPath locks the specified path. The key for the lock is not the path
// provided itself but the path with a suffix of ".lock" appended. The lock
// returned also contains a patch specific local Mutex which will be held.
//
// It is required to call Unlock() on the returned Lock to unlock
func LockPath(ctx context.Context, path string) (l *Lock, err error) {
	id, err := kvstoreLocks.lock(ctx, path)
	if err != nil {
		return nil, err
	}

	lock, err := Client().LockPath(ctx, path)
	if err != nil {
		kvstoreLocks.unlock(path, id)
		Trace("Failed to lock", err, logrus.Fields{fieldKey: path})
		err = fmt.Errorf("error while locking path %s: %s", path, err)
		return nil, err
	}

	Trace("Successful lock", err, logrus.Fields{fieldKey: path})
	return &Lock{kvLock: lock, path: path, id: id}, err
}

// Unlock unlocks a lock
func (l *Lock) Unlock() error {
	if l == nil {
		return nil
	}

	// Unlock kvstore mutex first
	err := l.kvLock.Unlock()
	if err != nil {
		log.WithError(err).WithField("path", l.path).Error("Unable to unlock kvstore lock")
	}

	// unlock local lock even if kvstore cannot be unlocked
	kvstoreLocks.unlock(l.path, l.id)
	Trace("Unlocked", nil, logrus.Fields{fieldKey: l.path})

	return err
}
<|endoftext|>


<commit_before>package lang

import (
	"bufio"
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"github.com/dockpit/pit/contract"
)

var CaseEX = regexp.MustCompile(`^'(.*)'$`)
var ResourceEX = regexp.MustCompile(`^- (.*)`)
var VariableEX = regexp.MustCompile(`(\(.*?\))`)
var ValidHTTPMethods = []string{"GET", "POST", "PUT"} //@todo add more

func UnexpectedDirError(fi os.FileInfo) error {
	return fmt.Errorf("Parser encountered an unexpected directory: %s, expected a resource directory (starting with `- `), or a case directory formatted as `'case name'`", fi.Name())
}

func UnexpectedFileError(fi os.FileInfo) error {
	return fmt.Errorf("Parser encountered a file without an extension: '%s', only 'given', 'when', 'then' or 'while' is allowed", fi.Name())
}

func UnexpectedStateLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'given' file '%s', with an invalid line: \n%s\n expected format \"<state provider name> '<state name>'\"", fpath, line)
}

func UnexpectedLinkLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'while' file '%s' with an unexpected line: %s, expected format \"<service id> '<case name>'\"", fpath, line)
}

func UnexpectedLinkLineCaseNameError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'while' file '%s' with a invalid casename: \"%s\", expected single-quoted name: e.g 'name of the case'", fpath, line)
}

func UnexpectedHeaderLineError(fpath, giv string) error {
	return fmt.Errorf("File '%s' has an unexpected header line: '%s', expected format 'Header-Key: Value'", fpath, giv)
}

func UnexpectedResponseLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'then' file '%s' with an unexpected first line: %s, expected format '<HTTP Status Code> <Status Text>'", fpath, line)
}

func UnexpectedResponseLineCodeError(fpath, giv string, err error) error {
	return fmt.Errorf("Parser encountered a 'then' file '%s' with an unexpected status code: %s, expected a number. (%s)", fpath, giv, err)
}

func UnexpectedRequestLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'when' file '%s' with an unexpected first line: %s, expected format '<HTTP method> <path>'", fpath, line)
}

func UnexpectedRequestLineMethodError(fpath, giv string) error {
	return fmt.Errorf("Parser encountered a 'when' file '%s' with an unexpected HTTP Method in the first line: '%s', expected one of: %s", fpath, giv, ValidHTTPMethods)
}

func UnexpectedRequestLinePathError(fpath, giv string) error {
	return fmt.Errorf("Parser encountered a 'when' file '%s' with an unexpected path in the first line: '%s', expected absolute path (starting with '/')", fpath, giv)
}

type Node struct {
	Pattern  string
	Children []*Node
}

func NewNode() *Node {
	return &Node{
		Pattern:  "/",
		Children: []*Node{},
	}
}

func (n *Node) Append(nn *Node, part string) {
	n.Children = append(n.Children, nn)

	nn.Pattern = path.Join(n.Pattern, part)
}

//
//
//
type Parser struct {
	Dir string

	data  *contract.ContractData
	nodes map[string]*Node
	cases map[string]string

	currentNode     *Node
	currentResource *contract.ResourceData
	currentCase     *contract.CaseData
}

func NewParser(dir string) *Parser {
	p := &Parser{
		Dir: dir,
	}
	p.reset()
	return p
}

func (p *Parser) reset() {
	root := NewNode()
	p.data = &contract.ContractData{}
	p.nodes = map[string]*Node{".": root}
	p.cases = map[string]string{}
	p.currentNode = root
	p.currentResource = nil
	p.currentCase = nil
}

func (p *Parser) ParseHTTPMessage(r io.ReadCloser, fpath string) (string, http.Header, string, error) {

	inbody := false
	rline := ""
	hlines := []string{}
	body := ""
	headers := make(http.Header)

	s := bufio.NewScanner(r)
	for s.Scan() {
		if !inbody {
			//first line should be the request line
			if rline == "" {
				rline = s.Text()
				continue
			}

			//empty line indicates that we will be parsing the body
			if s.Text() == "" {
				inbody = true
				continue
			}

			//add as headers
			hlines = append(hlines, s.Text())
		} else {
			//res is assumed to be body
			body += s.Text()
		}
	}

	//check/parse header format
	for _, h := range hlines {
		hp := strings.SplitN(h, ":", 2)
		if len(hp) != 2 {
			return rline, headers, body, UnexpectedHeaderLineError(fpath, h)
		}

		headers.Add(http.CanonicalHeaderKey(hp[0]), strings.TrimSpace(hp[1]))
	}

	return rline, headers, body, nil
}

// parses a when file loosely based on the http request spec
func (p *Parser) ParseWhen(r io.ReadCloser, fpath string) (*contract.When, error) {
	w := &contract.When{}

	rline, headers, body, err := p.ParseHTTPMessage(r, fpath)
	if err != nil {
		return nil, err
	}

	//request line should be 2 parts seperated by a space
	rlinep := strings.SplitN(rline, " ", 2)
	if len(rlinep) != 2 {
		return nil, UnexpectedRequestLineError(fpath, rline)
	}

	//check method format
	method := ""
	for _, m := range ValidHTTPMethods {
		if m == rlinep[0] {
			method = m
			break
		}
	}

	if method == "" {
		return nil, UnexpectedRequestLineMethodError(fpath, rlinep[0])
	}
	w.Method = method

	//check path
	if !path.IsAbs(rlinep[1]) {
		return nil, UnexpectedRequestLinePathError(fpath, rlinep[1])
	}
	w.Path = rlinep[1]
	w.Headers = headers
	w.Body = body

	return w, nil
}

// parses a then file loosely based on the format of a standard http message
func (p *Parser) ParseThen(r io.ReadCloser, fpath string) (*contract.Then, error) {
	t := &contract.Then{}

	//parse as a standard http message
	rline, headers, body, err := p.ParseHTTPMessage(r, fpath)
	if err != nil {
		return nil, err
	}

	//response line should be 2 parts seperated by a space
	rlinep := strings.SplitN(rline, " ", 2)
	if len(rlinep) != 2 {
		return nil, UnexpectedResponseLineError(fpath, rline)
	}

	//first one should be parseable as int
	code, err := strconv.Atoi(rlinep[0])
	if err != nil {
		return nil, UnexpectedResponseLineCodeError(fpath, rlinep[0], err)
	}

	t.StatusCode = code
	t.Status = rlinep[1]
	t.Headers = headers
	t.Body = body
	return t, nil
}

// parses a 'while' file
func (p *Parser) ParseWhile(r io.ReadCloser, fpath string) ([]contract.While, error) {
	ws := []contract.While{}

	s := bufio.NewScanner(r)
	for s.Scan() {

		//dont mind empty lines
		if strings.TrimSpace(s.Text()) == "" {
			continue
		}

		//every non-empty line should have space seperated link
		wp := strings.SplitN(s.Text(), " ", 2)
		if len(wp) != 2 {
			return ws, UnexpectedLinkLineError(fpath, s.Text())
		}

		cname := p.ToCaseName(wp[1])
		if cname == "" {
			return ws, UnexpectedLinkLineCaseNameError(fpath, wp[1])
		}

		//create while for case
		ws = append(ws, contract.While{
			ID:       wp[0],
			CaseName: cname,
		})
	}

	return ws, nil
}

// parses a 'given' file
func (p *Parser) ParseGiven(r io.ReadCloser, fpath string) (map[string]contract.Given, error) {
	gs := make(map[string]contract.Given)

	s := bufio.NewScanner(r)
	for s.Scan() {

		//dont mind empty lines
		if strings.TrimSpace(s.Text()) == "" {
			continue
		}

		//every non-empty line should have space seperated link
		gp := strings.SplitN(s.Text(), ":", 2)
		if len(gp) != 2 {
			return gs, UnexpectedStateLineError(fpath, s.Text())
		}

		//extract provider name
		pname := strings.TrimSpace(gp[0])
		if pname == "" {
			return gs, UnexpectedStateLineError(fpath, s.Text())
		}

		//extract state name as case name
		sname := p.ToCaseName(strings.TrimSpace(gp[1]))
		if sname == "" {
			return gs, UnexpectedStateLineError(fpath, s.Text())
		}

		//set given
		gs[pname] = contract.Given{
			Name: sname,
		}
	}

	return gs, nil
}

//
// Returns wether a given basename of a file path denotes a resource
func (p *Parser) ToResourcePatternPart(basename string) string {
	m := ResourceEX.FindStringSubmatch(basename)
	if m == nil {
		return ""
	}

	//replace variables by sinatra style vars
	res := VariableEX.ReplaceAllFunc([]byte(m[1]), func(in []byte) []byte {
		v := in[1 : len(in)-1]
		return append([]byte(":"), v...)
	})

	return string(res)
}

//
// Returns wether a given basename of a file path denotes a case example
func (p *Parser) ToCaseName(basename string) string {
	m := CaseEX.FindStringSubmatch(basename)
	if m == nil {
		return ""
	}

	return m[1]
}

func (p *Parser) visit(fpath string, fi os.FileInfo, err error) error {

	//cancel walk if something went wrong
	if err != nil {
		return err
	}

	//skip root
	if fpath == p.Dir {
		return nil
	}

	//care only about relative path
	rel, err := filepath.Rel(p.Dir, fpath)
	if err != nil {
		return err
	}

	//directories are expected to be either resources or cases
	if fi.IsDir() {
		if part := p.ToResourcePatternPart(filepath.Base(rel)); part != "" {
			var parent *Node
			var ok bool

			//create and add new resource node
			p.currentNode = NewNode()
			p.nodes[rel] = p.currentNode

			//get parent node
			if parent, ok = p.nodes[filepath.Dir(rel)]; !ok {
				return fmt.Errorf("No parent node found for '%s'", fpath)
			}

			//apprent to parent
			parent.Append(p.currentNode, part)

			//use node structure to create and append resource to contract
			p.currentResource = &contract.ResourceData{
				Pattern: p.currentNode.Pattern,
				Cases:   []*contract.CaseData{},
			}

			// add to contract data
			p.data.Resources = append(p.data.Resources, p.currentResource)

		} else if cname := p.ToCaseName(filepath.Base(rel)); cname != "" {

			//get current resource (if any)
			res := p.currentResource
			if res == nil {
				return fmt.Errorf("Case folder '%s' (%s) is outside a resource", cname, fpath)
			}

			//case name must be unique
			if ex, ok := p.cases[cname]; ok {
				return fmt.Errorf("Case with name '%s' (%s) already exists in '%s'", cname, fpath, filepath.Dir(ex))
			}

			//create the case from available data
			p.currentCase = &contract.CaseData{
				Name: cname,
				When: contract.When{},
				Then: contract.Then{},
			}

			//and append to resource
			res.Cases = append(res.Cases, p.currentCase)
			p.cases[cname] = fpath
		} else {
			return UnexpectedDirError(fi)
		}
	} else {

		//files without extension have to be either when/then/given/while
		if filepath.Ext(fpath) == "" {

			f, err := os.Open(fpath)
			if err != nil {
				return err
			}
			defer f.Close()

			//'keywords;
			if filepath.Base(fpath) == "given" {
				given, err := p.ParseGiven(f, fpath)
				if err != nil {
					return err
				}

				p.currentCase.Given = given
			} else if filepath.Base(fpath) == "when" {
				when, err := p.ParseWhen(f, fpath)
				if err != nil {
					return err
				}

				p.currentCase.When = *when
			} else if filepath.Base(fpath) == "then" {
				then, err := p.ParseThen(f, fpath)
				if err != nil {
					return err
				}

				fmt.Println("AAAAAA", p.currentCase, then)
				p.currentCase.Then = *then
			} else if filepath.Base(fpath) == "while" {
				whiles, err := p.ParseWhile(f, fpath)
				if err != nil {
					return err
				}

				p.currentCase.While = whiles
			} else {
				return UnexpectedFileError(fi)
			}

		} else {
			//@todo parse data files
		}

	}

	return nil
}

func (p *Parser) Parse() (*contract.ContractData, error) {

	//reset parser afterwards
	defer p.reset()

	//walk nodes
	err := filepath.Walk(p.Dir, p.visit)
	if err != nil {
		return nil, err
	}

	//return result
	return p.data, nil
}
<commit_msg>debugging...<commit_after>package lang

import (
	"bufio"
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"github.com/dockpit/pit/contract"
)

var CaseEX = regexp.MustCompile(`^'(.*)'$`)
var ResourceEX = regexp.MustCompile(`^- (.*)`)
var VariableEX = regexp.MustCompile(`(\(.*?\))`)
var ValidHTTPMethods = []string{"GET", "POST", "PUT"} //@todo add more

func UnexpectedDirError(fi os.FileInfo) error {
	return fmt.Errorf("Parser encountered an unexpected directory: %s, expected a resource directory (starting with `- `), or a case directory formatted as `'case name'`", fi.Name())
}

func UnexpectedFileError(fi os.FileInfo) error {
	return fmt.Errorf("Parser encountered a file without an extension: '%s', only 'given', 'when', 'then' or 'while' is allowed", fi.Name())
}

func UnexpectedStateLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'given' file '%s', with an invalid line: \n%s\n expected format \"<state provider name> '<state name>'\"", fpath, line)
}

func UnexpectedLinkLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'while' file '%s' with an unexpected line: %s, expected format \"<service id> '<case name>'\"", fpath, line)
}

func UnexpectedLinkLineCaseNameError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'while' file '%s' with a invalid casename: \"%s\", expected single-quoted name: e.g 'name of the case'", fpath, line)
}

func UnexpectedHeaderLineError(fpath, giv string) error {
	return fmt.Errorf("File '%s' has an unexpected header line: '%s', expected format 'Header-Key: Value'", fpath, giv)
}

func UnexpectedResponseLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'then' file '%s' with an unexpected first line: %s, expected format '<HTTP Status Code> <Status Text>'", fpath, line)
}

func UnexpectedResponseLineCodeError(fpath, giv string, err error) error {
	return fmt.Errorf("Parser encountered a 'then' file '%s' with an unexpected status code: %s, expected a number. (%s)", fpath, giv, err)
}

func UnexpectedRequestLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'when' file '%s' with an unexpected first line: %s, expected format '<HTTP method> <path>'", fpath, line)
}

func UnexpectedRequestLineMethodError(fpath, giv string) error {
	return fmt.Errorf("Parser encountered a 'when' file '%s' with an unexpected HTTP Method in the first line: '%s', expected one of: %s", fpath, giv, ValidHTTPMethods)
}

func UnexpectedRequestLinePathError(fpath, giv string) error {
	return fmt.Errorf("Parser encountered a 'when' file '%s' with an unexpected path in the first line: '%s', expected absolute path (starting with '/')", fpath, giv)
}

type Node struct {
	Pattern  string
	Children []*Node
}

func NewNode() *Node {
	return &Node{
		Pattern:  "/",
		Children: []*Node{},
	}
}

func (n *Node) Append(nn *Node, part string) {
	n.Children = append(n.Children, nn)

	nn.Pattern = path.Join(n.Pattern, part)
}

//
//
//
type Parser struct {
	Dir string

	data  *contract.ContractData
	nodes map[string]*Node
	cases map[string]string

	currentNode     *Node
	currentResource *contract.ResourceData
	currentCase     *contract.CaseData
}

func NewParser(dir string) *Parser {
	p := &Parser{
		Dir: dir,
	}
	p.reset()
	return p
}

func (p *Parser) reset() {
	root := NewNode()
	p.data = &contract.ContractData{}
	p.nodes = map[string]*Node{".": root}
	p.cases = map[string]string{}
	p.currentNode = root
	p.currentResource = nil
	p.currentCase = nil
}

func (p *Parser) ParseHTTPMessage(r io.ReadCloser, fpath string) (string, http.Header, string, error) {

	inbody := false
	rline := ""
	hlines := []string{}
	body := ""
	headers := make(http.Header)

	s := bufio.NewScanner(r)
	for s.Scan() {
		if !inbody {
			//first line should be the request line
			if rline == "" {
				rline = s.Text()
				continue
			}

			//empty line indicates that we will be parsing the body
			if s.Text() == "" {
				inbody = true
				continue
			}

			//add as headers
			hlines = append(hlines, s.Text())
		} else {
			//res is assumed to be body
			body += s.Text()
		}
	}

	//check/parse header format
	for _, h := range hlines {
		hp := strings.SplitN(h, ":", 2)
		if len(hp) != 2 {
			return rline, headers, body, UnexpectedHeaderLineError(fpath, h)
		}

		headers.Add(http.CanonicalHeaderKey(hp[0]), strings.TrimSpace(hp[1]))
	}

	return rline, headers, body, nil
}

// parses a when file loosely based on the http request spec
func (p *Parser) ParseWhen(r io.ReadCloser, fpath string) (*contract.When, error) {
	w := &contract.When{}

	rline, headers, body, err := p.ParseHTTPMessage(r, fpath)
	if err != nil {
		return nil, err
	}

	//request line should be 2 parts seperated by a space
	rlinep := strings.SplitN(rline, " ", 2)
	if len(rlinep) != 2 {
		return nil, UnexpectedRequestLineError(fpath, rline)
	}

	//check method format
	method := ""
	for _, m := range ValidHTTPMethods {
		if m == rlinep[0] {
			method = m
			break
		}
	}

	if method == "" {
		return nil, UnexpectedRequestLineMethodError(fpath, rlinep[0])
	}
	w.Method = method

	//check path
	if !path.IsAbs(rlinep[1]) {
		return nil, UnexpectedRequestLinePathError(fpath, rlinep[1])
	}
	w.Path = rlinep[1]
	w.Headers = headers
	w.Body = body

	return w, nil
}

// parses a then file loosely based on the format of a standard http message
func (p *Parser) ParseThen(r io.ReadCloser, fpath string) (*contract.Then, error) {
	t := &contract.Then{}

	//parse as a standard http message
	rline, headers, body, err := p.ParseHTTPMessage(r, fpath)
	if err != nil {
		return nil, err
	}

	//response line should be 2 parts seperated by a space
	rlinep := strings.SplitN(rline, " ", 2)
	if len(rlinep) != 2 {
		return nil, UnexpectedResponseLineError(fpath, rline)
	}

	//first one should be parseable as int
	code, err := strconv.Atoi(rlinep[0])
	if err != nil {
		return nil, UnexpectedResponseLineCodeError(fpath, rlinep[0], err)
	}

	t.StatusCode = code
	t.Status = rlinep[1]
	t.Headers = headers
	t.Body = body
	return t, nil
}

// parses a 'while' file
func (p *Parser) ParseWhile(r io.ReadCloser, fpath string) ([]contract.While, error) {
	ws := []contract.While{}

	s := bufio.NewScanner(r)
	for s.Scan() {

		//dont mind empty lines
		if strings.TrimSpace(s.Text()) == "" {
			continue
		}

		//every non-empty line should have space seperated link
		wp := strings.SplitN(s.Text(), " ", 2)
		if len(wp) != 2 {
			return ws, UnexpectedLinkLineError(fpath, s.Text())
		}

		cname := p.ToCaseName(wp[1])
		if cname == "" {
			return ws, UnexpectedLinkLineCaseNameError(fpath, wp[1])
		}

		//create while for case
		ws = append(ws, contract.While{
			ID:       wp[0],
			CaseName: cname,
		})
	}

	return ws, nil
}

// parses a 'given' file
func (p *Parser) ParseGiven(r io.ReadCloser, fpath string) (map[string]contract.Given, error) {
	gs := make(map[string]contract.Given)

	s := bufio.NewScanner(r)
	for s.Scan() {

		//dont mind empty lines
		if strings.TrimSpace(s.Text()) == "" {
			continue
		}

		//every non-empty line should have space seperated link
		gp := strings.SplitN(s.Text(), ":", 2)
		if len(gp) != 2 {
			return gs, UnexpectedStateLineError(fpath, s.Text())
		}

		//extract provider name
		pname := strings.TrimSpace(gp[0])
		if pname == "" {
			return gs, UnexpectedStateLineError(fpath, s.Text())
		}

		//extract state name as case name
		sname := p.ToCaseName(strings.TrimSpace(gp[1]))
		if sname == "" {
			return gs, UnexpectedStateLineError(fpath, s.Text())
		}

		//set given
		gs[pname] = contract.Given{
			Name: sname,
		}
	}

	return gs, nil
}

//
// Returns wether a given basename of a file path denotes a resource
func (p *Parser) ToResourcePatternPart(basename string) string {
	m := ResourceEX.FindStringSubmatch(basename)
	if m == nil {
		return ""
	}

	//replace variables by sinatra style vars
	res := VariableEX.ReplaceAllFunc([]byte(m[1]), func(in []byte) []byte {
		v := in[1 : len(in)-1]
		return append([]byte(":"), v...)
	})

	return string(res)
}

//
// Returns wether a given basename of a file path denotes a case example
func (p *Parser) ToCaseName(basename string) string {
	m := CaseEX.FindStringSubmatch(basename)
	if m == nil {
		return ""
	}

	return m[1]
}

func (p *Parser) visit(fpath string, fi os.FileInfo, err error) error {

	//cancel walk if something went wrong
	if err != nil {
		return err
	}

	//skip root
	if fpath == p.Dir {
		return nil
	}

	//care only about relative path
	rel, err := filepath.Rel(p.Dir, fpath)
	if err != nil {
		return err
	}

	//directories are expected to be either resources or cases
	if fi.IsDir() {
		if part := p.ToResourcePatternPart(filepath.Base(rel)); part != "" {
			var parent *Node
			var ok bool

			//create and add new resource node
			p.currentNode = NewNode()
			p.nodes[rel] = p.currentNode

			//get parent node
			if parent, ok = p.nodes[filepath.Dir(rel)]; !ok {
				return fmt.Errorf("No parent node found for '%s'", fpath)
			}

			//apprent to parent
			parent.Append(p.currentNode, part)

			//use node structure to create and append resource to contract
			p.currentResource = &contract.ResourceData{
				Pattern: p.currentNode.Pattern,
				Cases:   []*contract.CaseData{},
			}

			// add to contract data
			p.data.Resources = append(p.data.Resources, p.currentResource)

		} else if cname := p.ToCaseName(filepath.Base(rel)); cname != "" {

			//get current resource (if any)
			res := p.currentResource
			if res == nil {
				return fmt.Errorf("Case folder '%s' (%s) is outside a resource", cname, fpath)
			}

			//case name must be unique
			if ex, ok := p.cases[cname]; ok {
				return fmt.Errorf("Case with name '%s' (%s) already exists in '%s'", cname, fpath, filepath.Dir(ex))
			}

			//create the case from available data
			p.currentCase = &contract.CaseData{
				Name: cname,
				When: contract.When{},
				Then: contract.Then{},
			}

			//and append to resource
			res.Cases = append(res.Cases, p.currentCase)
			p.cases[cname] = fpath
		} else {
			return UnexpectedDirError(fi)
		}
	} else {

		//files without extension have to be either when/then/given/while
		if filepath.Ext(fpath) == "" {

			f, err := os.Open(fpath)
			if err != nil {
				return err
			}
			defer f.Close()

			//'keywords;
			if filepath.Base(fpath) == "given" {
				given, err := p.ParseGiven(f, fpath)
				if err != nil {
					return err
				}

				p.currentCase.Given = given
			} else if filepath.Base(fpath) == "when" {
				when, err := p.ParseWhen(f, fpath)
				if err != nil {
					return err
				}

				p.currentCase.When = *when
			} else if filepath.Base(fpath) == "then" {
				then, err := p.ParseThen(f, fpath)
				if err != nil {
					return err
				}

				if p.currentCase == nil {
					return fmt.Errorf("Race condition? cancelling:", p.currentCase)
				}

				p.currentCase.Then = *then
			} else if filepath.Base(fpath) == "while" {
				whiles, err := p.ParseWhile(f, fpath)
				if err != nil {
					return err
				}

				p.currentCase.While = whiles
			} else {
				return UnexpectedFileError(fi)
			}

		} else {
			//@todo parse data files
		}

	}

	return nil
}

func (p *Parser) Parse() (*contract.ContractData, error) {

	//reset parser afterwards
	defer p.reset()

	//walk nodes
	err := filepath.Walk(p.Dir, p.visit)
	if err != nil {
		return nil, err
	}

	//return result
	return p.data, nil
}
<|endoftext|>