<commit_before>package rootio

import (
	"bytes"
	"compress/zlib"
	"fmt"
	"io"
	"os"

	//"github.com/kr/pretty"
)

// Key is a key (a label) in a ROOT file
//
//  The Key class includes functions to book space on a file,
//   to create I/O buffers, to fill these buffers
//   to compress/uncompress data buffers.
//
//  Before saving (making persistent) an object on a file, a key must
//  be created. The key structure contains all the information to
//  uniquely identify a persistent object on a file.
//  The Key class is used by ROOT:
//    - to write an object in the Current Directory
//    - to write a new ntuple buffer
type Key struct {
	f *File // underlying file

	bytes    int32 // number of bytes for the compressed object+key
	version  int16 // version of the Key struct
	objlen   int32 // length of uncompressed object
	datetime int32 // Date/Time when the object was written
	keylen   int32 // number of bytes for the Key struct
	cycle    int16 // cycle number of the object

	// address of the object on file (points to Key.bytes)
	// this is a redundant information used to cross-check
	// the data base integrity
	seekkey  int64
	seekpdir int64 // pointer to the directory supporting this object

	classname string // object class name
	name      string // name of the object
	title     string // title of the object

	pdat int64 // Pointer to everything after the above.

	read bool
	data []byte
}

func (k *Key) Class() string {
	return k.classname
}

func (k *Key) Name() string {
	return k.name
}

func (k *Key) Title() string {
	return k.title
}

func (k *Key) load() ([]byte, error) {
	if !k.read {
		if int64(cap(k.data)) < int64(k.objlen) {
			k.data = make([]byte, k.objlen)
		}
		_, err := io.ReadFull(k.f, k.data)
		if err != nil {
			return nil, err
		}
		k.read = true
	}
	return k.data, nil
}

// Note: this contains ZL[src][dst] where src and dst are 3 bytes each.
// Won't bother with this for the moment, since we can cross-check against
// objlen.
const ROOT_HDRSIZE = 9

// Bytes returns the buffer of bytes corresponding to the Key's value
func (k *Key) Bytes() ([]byte, error) {
	if k.isCompressed() {
		// ... therefore it's compressed
		start := k.seekkey + int64(k.keylen) + ROOT_HDRSIZE
		r := io.NewSectionReader(k.f, start, int64(k.bytes)-int64(k.keylen))
		rc, err := zlib.NewReader(r)
		if err != nil {
			panic(err)
		}

		buf := &bytes.Buffer{}
		_, err = io.Copy(buf, rc)
		if err != nil {
			return nil, err
		}
		return buf.Bytes(), nil
	}
	// ... not compressed
	start := k.seekkey + int64(k.keylen)
	r := io.NewSectionReader(k.f, start, int64(k.bytes))
	buf := &bytes.Buffer{}
	_, err := io.Copy(buf, r)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// Return Basket data associated with this key, if there is any
func (k *Key) AsBasket() *Basket {
	if k.classname != "TBasket" {
		panic("rootio.Key: Key is not a basket!")
	}
	b := &Basket{}
	_, err := k.f.Seek(int64(k.pdat), os.SEEK_SET)
	if err != nil {
		panic(fmt.Errorf("rootio.Key: %v", err))
	}
	err = k.f.readBin(b)
	if err != nil {
		panic(fmt.Errorf("rootio.Key: %v", err))
	}
	return b
}

func (k *Key) isCompressed() bool {
	return k.objlen != k.bytes-k.keylen
}

func (k *Key) Read() error {
	var err error
	f := k.f

	key_offset := f.Tell()

	err = f.readBin(&k.bytes)
	if err != nil {
		return err
	}

	if k.bytes < 0 {
		//fmt.Println("Jumping gap: ", k.bytes)
		k.classname = "[GAP]"
		_, err = f.Seek(int64(-k.bytes)-4, os.SEEK_CUR)
		return err
	}
	err = f.readBin(&k.version)
	if err != nil {
		return err
	}

	err = f.readBin(&k.objlen)
	if err != nil {
		return err
	}

	err = f.readBin(&k.datetime)
	if err != nil {
		return err
	}

	err = f.readInt16(&k.keylen)
	if err != nil {
		return err
	}

	err = f.readBin(&k.cycle)
	if err != nil {
		return err
	}

	if k.version > 1000 {
		err = f.readBin(&k.seekkey)
		if err != nil {
			return err
		}
	} else {
		err = f.readInt32(&k.seekkey)
		if err != nil {
			return err
		}
	}

	if k.version > 1000 {
		err = f.readBin(&k.seekpdir)
		if err != nil {
			return err
		}
	} else {
		err = f.readInt32(&k.seekpdir)
		if err != nil {
			return err
		}
	}

	if k.seekkey == 0 {
		k.seekkey = key_offset
	}
	if k.seekkey != key_offset {
		//pretty.Printf("%+v", k)
		panic(fmt.Errorf("Consistency failure: key offset %v doesn't match actual offset %v",
			k.seekkey, key_offset))
	}

	err = f.readString(&k.classname)
	if err != nil {
		return err
	}

	err = f.readString(&k.name)
	if err != nil {
		return err
	}

	err = f.readString(&k.title)
	if err != nil {
		return err
	}

	k.pdat = f.Tell()

	_, err = f.Seek(int64(key_offset+int64(k.bytes)), os.SEEK_SET)
	return err
}

// testing interfaces

var _ Object = (*Key)(nil)
<commit_msg>key: leverage ROOTUnmarshaler, introduce Key.Value<commit_after>package rootio

import (
	"bytes"
	"compress/zlib"
	"fmt"
	"io"
	"os"

	//"github.com/kr/pretty"
)

// Key is a key (a label) in a ROOT file
//
//  The Key class includes functions to book space on a file,
//   to create I/O buffers, to fill these buffers
//   to compress/uncompress data buffers.
//
//  Before saving (making persistent) an object on a file, a key must
//  be created. The key structure contains all the information to
//  uniquely identify a persistent object on a file.
//  The Key class is used by ROOT:
//    - to write an object in the Current Directory
//    - to write a new ntuple buffer
type Key struct {
	f *File // underlying file

	bytes    int32 // number of bytes for the compressed object+key
	version  int16 // version of the Key struct
	objlen   int32 // length of uncompressed object
	datetime int32 // Date/Time when the object was written
	keylen   int32 // number of bytes for the Key struct
	cycle    int16 // cycle number of the object

	// address of the object on file (points to Key.bytes)
	// this is a redundant information used to cross-check
	// the data base integrity
	seekkey  int64
	seekpdir int64 // pointer to the directory supporting this object

	classname string // object class name
	name      string // name of the object
	title     string // title of the object

	pdat int64 // Pointer to everything after the above.

	read bool
	data []byte
}

func (k *Key) Class() string {
	return k.classname
}

func (k *Key) Name() string {
	return k.name
}

func (k *Key) Title() string {
	return k.title
}

func (k *Key) load() ([]byte, error) {
	if !k.read {
		if int64(cap(k.data)) < int64(k.objlen) {
			k.data = make([]byte, k.objlen)
		}
		_, err := io.ReadFull(k.f, k.data)
		if err != nil {
			return nil, err
		}
		k.read = true
	}
	return k.data, nil
}

// Note: this contains ZL[src][dst] where src and dst are 3 bytes each.
// Won't bother with this for the moment, since we can cross-check against
// objlen.
const ROOT_HDRSIZE = 9

// Bytes returns the buffer of bytes corresponding to the Key's value
func (k *Key) Bytes() ([]byte, error) {
	if k.isCompressed() {
		// ... therefore it's compressed
		start := k.seekkey + int64(k.keylen) + ROOT_HDRSIZE
		r := io.NewSectionReader(k.f, start, int64(k.bytes)-int64(k.keylen))
		rc, err := zlib.NewReader(r)
		if err != nil {
			panic(err)
		}

		buf := &bytes.Buffer{}
		_, err = io.Copy(buf, rc)
		if err != nil {
			return nil, err
		}
		return buf.Bytes(), nil
	}
	// ... not compressed
	start := k.seekkey + int64(k.keylen)
	r := io.NewSectionReader(k.f, start, int64(k.bytes))
	buf := &bytes.Buffer{}
	_, err := io.Copy(buf, r)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// Value returns the data corresponding to the Key's value
func (k *Key) Value() interface{} {
	var v interface{}

	factory := Factory.Get(k.Class())
	if factory == nil {
		panic(fmt.Errorf("key[%v]: no factory for type [%s]\n", k.Name(), k.Class()))
	}

	vv := factory()
	if vv, ok := vv.Interface().(ROOTUnmarshaler); ok {
		data, err := k.Bytes()
		if err != nil {
			panic(fmt.Errorf("key[%v]: %v", k.Name(), err))
		}
		err = vv.UnmarshalROOT(data)
		if err != nil {
			panic(err)
		}
		v = vv
	} else {
		panic(fmt.Errorf(
			"key[%v]: type [%s] does not satisfy the ROOTUnmarshaler interface",
			k.Name(), k.Class(),
		))
	}

	// FIXME: hack.
	if vv, ok := v.(*Tree); ok {
		vv.f = k.f
	}
	return v
}

// Return Basket data associated with this key, if there is any
func (k *Key) AsBasket() *Basket {
	if k.classname != "TBasket" {
		panic("rootio.Key: Key is not a basket!")
	}
	b := &Basket{}
	_, err := k.f.Seek(int64(k.pdat), os.SEEK_SET)
	if err != nil {
		panic(fmt.Errorf("rootio.Key: %v", err))
	}
	err = k.f.readBin(b)
	if err != nil {
		panic(fmt.Errorf("rootio.Key: %v", err))
	}
	return b
}

func (k *Key) isCompressed() bool {
	return k.objlen != k.bytes-k.keylen
}

func (k *Key) Read() error {
	var err error
	f := k.f

	key_offset := f.Tell()

	err = f.readBin(&k.bytes)
	if err != nil {
		return err
	}

	if k.bytes < 0 {
		//fmt.Println("Jumping gap: ", k.bytes)
		k.classname = "[GAP]"
		_, err = f.Seek(int64(-k.bytes)-4, os.SEEK_CUR)
		return err
	}
	err = f.readBin(&k.version)
	if err != nil {
		return err
	}

	err = f.readBin(&k.objlen)
	if err != nil {
		return err
	}

	err = f.readBin(&k.datetime)
	if err != nil {
		return err
	}

	err = f.readInt16(&k.keylen)
	if err != nil {
		return err
	}

	err = f.readBin(&k.cycle)
	if err != nil {
		return err
	}

	if k.version > 1000 {
		err = f.readBin(&k.seekkey)
		if err != nil {
			return err
		}
	} else {
		err = f.readInt32(&k.seekkey)
		if err != nil {
			return err
		}
	}

	if k.version > 1000 {
		err = f.readBin(&k.seekpdir)
		if err != nil {
			return err
		}
	} else {
		err = f.readInt32(&k.seekpdir)
		if err != nil {
			return err
		}
	}

	if k.seekkey == 0 {
		k.seekkey = key_offset
	}
	if k.seekkey != key_offset {
		//pretty.Printf("%+v", k)
		panic(fmt.Errorf("Consistency failure: key offset %v doesn't match actual offset %v",
			k.seekkey, key_offset))
	}

	err = f.readString(&k.classname)
	if err != nil {
		return err
	}

	err = f.readString(&k.name)
	if err != nil {
		return err
	}

	err = f.readString(&k.title)
	if err != nil {
		return err
	}

	k.pdat = f.Tell()

	_, err = f.Seek(int64(key_offset+int64(k.bytes)), os.SEEK_SET)
	return err
}

// testing interfaces

var _ Object = (*Key)(nil)
<|endoftext|>


<commit_before>package main

import "fmt"
import "github.com/stianeikeland/go-rpio"
import "os"
import "time"

var pin = rpio.Pin(4)

func main() {
	fmt.Println("Opening rpio access")

	var err = rpio.Open()

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	defer rpio.Close()

	fmt.Println("Pin as output")
	pin.Output()

	fmt.Println("Number of blinks?")
	var num int
	_, err = fmt.Scanf("%d", &num)
	if err != nil {
		fmt.Println("not a number")
	} else {
		fmt.Print(num)
		fmt.Println(" is a number")
	}

	for i := 0; i < num; i++ {
		fmt.Println("Toggle")
		pin.Toggle()
		time.Sleep(time.Second)
	}
}
<commit_msg>Moved input to command line parameters<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"time"

	"github.com/stianeikeland/go-rpio"
)

var pin = rpio.Pin(4)

func main() {
	fmt.Println("Parsing parameters")
	num := flag.Int("num", 0, "number of blinks")
	flag.Parse()

	fmt.Println("Opening rpio access")

	var err = rpio.Open()

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	defer rpio.Close()

	fmt.Println("Pin as output")
	pin.Output()

	for i := 0; i < num; i++ {
		fmt.Println("Toggle")
		pin.Toggle()
		time.Sleep(time.Second)
	}
}
<|endoftext|>


<commit_before>// package mmw provides a generic http.Handler middleware for go-metrics.
package mmw

import (
	"net/http"
	"time"

	"github.com/armon/go-metrics"
)

type metricsHandler struct {
	handler  http.Handler
	mw       *Middleware
	timeKey  []string
	countKey []string
}

// Middleware is simple wrapper around go-metrics.
type Middleware struct {
	sink *metrics.Metrics
}

// New creates a new Middleware
func New(sink *metrics.Metrics) *Middleware {
	return &Middleware{sink: sink}
}

// HandlerWrapper wraps Handler and returns an http.Handler. Useful for chains of middleware like
// https://github.com/justinas/alice
func (mw *Middleware) HandlerWrapper(key ...string) func(http.Handler) http.Handler {
	return func(h http.Handler) http.Handler {
		return mw.Handler(h, key...)
	}
}

// Handler creates a new metrics handler that implements http.Handler. This wraps
// a handler
func (mw *Middleware) Handler(handler http.Handler, key ...string) *metricsHandler {
	m := &metricsHandler{
		handler: handler,
		mw:      mw,
	}

	m.timeKey = make([]string, len(key)+1)
	copy(m.timeKey, key)
	m.timeKey[len(key)] = "time"

	m.countKey = make([]string, len(key)+1)
	copy(m.countKey, key)
	m.countKey[len(key)] = "count"

	return m
}

// ServeHTTP wraps a handler and records timing and increments a counter
func (m *metricsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	now := time.Now()
	m.handler.ServeHTTP(w, r)
	m.mw.sink.AddSample(m.timeKey, float32(time.Since(now).Seconds()*1000))
	m.mw.sink.IncrCounter(m.countKey, 1)
}
<commit_msg>use MeasureSince<commit_after>// package mmw provides a generic http.Handler middleware for go-metrics.
package mmw

import (
	"net/http"
	"time"

	"github.com/armon/go-metrics"
)

type metricsHandler struct {
	handler  http.Handler
	mw       *Middleware
	timeKey  []string
	countKey []string
}

// Middleware is simple wrapper around go-metrics.
type Middleware struct {
	sink *metrics.Metrics
}

// New creates a new Middleware
func New(sink *metrics.Metrics) *Middleware {
	return &Middleware{sink: sink}
}

// HandlerWrapper wraps Handler and returns an http.Handler. Useful for chains of middleware like
// https://github.com/justinas/alice
func (mw *Middleware) HandlerWrapper(key ...string) func(http.Handler) http.Handler {
	return func(h http.Handler) http.Handler {
		return mw.Handler(h, key...)
	}
}

// Handler creates a new metrics handler that implements http.Handler. This wraps
// a handler
func (mw *Middleware) Handler(handler http.Handler, key ...string) *metricsHandler {
	m := &metricsHandler{
		handler: handler,
		mw:      mw,
	}

	m.timeKey = make([]string, len(key)+1)
	copy(m.timeKey, key)
	m.timeKey[len(key)] = "time"

	m.countKey = make([]string, len(key)+1)
	copy(m.countKey, key)
	m.countKey[len(key)] = "count"

	return m
}

// ServeHTTP wraps a handler and records timing and increments a counter
func (m *metricsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	now := time.Now()
	m.handler.ServeHTTP(w, r)
	m.mw.sink.MeasureSince(m.timeKey, now)
	m.mw.sink.IncrCounter(m.countKey, 1)
}
<|endoftext|>


<commit_before>// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tag

import (
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
)

var atomTypes = map[int]string{
	0:  "uint8",
	1:  "text",
	13: "jpeg",
	14: "png",
	21: "uint8",
}

// NB: atoms does not include "----", this is handled separately
var atoms = atomNames(map[string]string{
	"\xa9alb": "album",
	"\xa9art": "artist",
	"\xa9ART": "artist",
	"aART":    "album_artist",
	"\xa9day": "year",
	"\xa9nam": "title",
	"\xa9gen": "genre",
	"trkn":    "track",
	"\xa9wrt": "composer",
	"\xa9too": "encoder",
	"cprt":    "copyright",
	"covr":    "picture",
	"\xa9grp": "grouping",
	"keyw":    "keyword",
	"\xa9lyr": "lyrics",
	"\xa9cmt": "comment",
	"tmpo":    "tempo",
	"cpil":    "compilation",
	"disk":    "disc",
})

type atomNames map[string]string

func (f atomNames) Name(n string) []string {
	res := make([]string, 1)
	for k, v := range f {
		if v == n {
			res = append(res, k)
		}
	}
	return res
}

// metadataMP4 is the implementation of Metadata for MP4 tag (atom) data.
type metadataMP4 struct {
	fileType FileType
	data     map[string]interface{}
}

// ReadAtoms reads MP4 metadata atoms from the io.ReadSeeker into a Metadata, returning
// non-nil error if there was a problem.
func ReadAtoms(r io.ReadSeeker) (Metadata, error) {
	m := metadataMP4{
		data: make(map[string]interface{}),
		fileType: UnknownFileType,
	}
	err := m.readAtoms(r)
	return m, err
}

func (m metadataMP4) readAtoms(r io.ReadSeeker) error {
	for {
		name, size, err := readAtomHeader(r)
		if err != nil {
			if err == io.EOF {
				return nil
			}
			return err
		}

		switch name {
		case "meta":
			// next_item_id (int32)
			_, err := readBytes(r, 4)
			if err != nil {
				return err
			}
			fallthrough

		case "moov", "udta", "ilst":
			return m.readAtoms(r)
		}

		_, ok := atoms[name]
		if name == "----" {
			name, size, err = readCustomAtom(r, size)
			if err != nil {
				return err
			}

			if name != "----" {
				ok = true
			}
		}

		if !ok {
			_, err := r.Seek(int64(size-8), os.SEEK_CUR)
			if err != nil {
				return err
			}
			continue
		}

		err = m.readAtomData(r, name, size-8)
		if err != nil {
			return err
		}
	}
}

func (m metadataMP4) readAtomData(r io.ReadSeeker, name string, size uint32) error {
	b, err := readBytes(r, int(size))
	if err != nil {
		return err
	}

	// "data" + size (4 bytes each)
	b = b[8:]

	class := getInt(b[1:4])
	contentType, ok := atomTypes[class]
	if !ok {
		return fmt.Errorf("invalid content type: %v (%x) (%x)", class, b[1:4], b)
	}

	// 4: atom version (1 byte) + atom flags (3 bytes)
	// 4: NULL (usually locale indicator)
	b = b[8:]

	if name == "trkn" || name == "disk" {
		m.data[name] = int(b[3])
		m.data[name+"_count"] = int(b[5])
		return nil
	}

	var data interface{}
	switch contentType {
	case "text":
		data = string(b)

	case "uint8":
		data = getInt(b[:1])

	case "jpeg", "png":
		data = &Picture{
			Ext:      contentType,
			MIMEType: "image/" + contentType,
			Data:     b,
		}
	}
	m.data[name] = data

	return nil
}

func readAtomHeader(r io.ReadSeeker) (name string, size uint32, err error) {
	err = binary.Read(r, binary.BigEndian, &size)
	if err != nil {
		return
	}
	name, err = readString(r, 4)
	return
}

// Generic atom.
// Should have 3 sub atoms : mean, name and data.
// We check that mean is "com.apple.iTunes" and we use the subname as
// the name, and move to the data atom.
// If anything goes wrong, we jump at the end of the "----" atom.
func readCustomAtom(r io.ReadSeeker, size uint32) (string, uint32, error) {
	subNames := make(map[string]string)
	var dataSize uint32

	for size > 8 {
		subName, subSize, err := readAtomHeader(r)
		if err != nil {
			return "", 0, err
		}

		// Remove the size of the atom from the size counter
		size -= subSize

		switch subName {
		case "mean", "name":
			b, err := readBytes(r, int(subSize-8))
			if err != nil {
				return "", 0, err
			}
			subNames[subName] = string(b[4:])

		case "data":
			// Found the "data" atom, rewind
			dataSize = subSize + 8 // will need to re-read "data" + size (4 + 4)
			_, err := r.Seek(-8, os.SEEK_CUR)
			if err != nil {
				return "", 0, err
			}
		}
	}

	// there should remain only the header size
	if size != 8 {
		err := errors.New("---- atom out of bounds")
		return "", 0, err
	}

	if subNames["mean"] != "com.apple.iTunes" || subNames["name"] == "" || dataSize == 0 {
		return "----", 0, nil
	}
	return subNames["name"], dataSize, nil
}

func (metadataMP4) Format() Format     { return MP4 }
func (m metadataMP4) FileType() FileType { return m.fileType }

func (m metadataMP4) Raw() map[string]interface{} { return m.data }

func (m metadataMP4) getString(n []string) string {
	for _, k := range n {
		if x, ok := m.data[k]; ok {
			return x.(string)
		}
	}
	return ""
}

func (m metadataMP4) getInt(n []string) int {
	for _, k := range n {
		if x, ok := m.data[k]; ok {
			return x.(int)
		}
	}
	return 0
}

func (m metadataMP4) Title() string {
	return m.getString(atoms.Name("title"))
}

func (m metadataMP4) Artist() string {
	return m.getString(atoms.Name("artist"))
}

func (m metadataMP4) Album() string {
	return m.getString(atoms.Name("album"))
}

func (m metadataMP4) AlbumArtist() string {
	return m.getString(atoms.Name("album_artist"))
}

func (m metadataMP4) Composer() string {
	return m.getString(atoms.Name("composer"))
}

func (m metadataMP4) Genre() string {
	return m.getString(atoms.Name("genre"))
}

func (m metadataMP4) Year() int {
	date := m.getString(atoms.Name("year"))
	if len(date) >= 4 {
		year, _ := strconv.Atoi(date[:4])
		return year
	}
	return 0
}

func (m metadataMP4) Track() (int, int) {
	x := m.getInt([]string{"trkn"})
	if n, ok := m.data["trkn_count"]; ok {
		return x, n.(int)
	}
	return x, 0
}

func (m metadataMP4) Disc() (int, int) {
	x := m.getInt([]string{"disk"})
	if n, ok := m.data["disk_count"]; ok {
		return x, n.(int)
	}
	return x, 0
}

func (m metadataMP4) Lyrics() string {
	t, ok := m.data["\xa9lyr"]
	if !ok {
		return ""
	}
	return t.(string)
}

func (m metadataMP4) Picture() *Picture {
	v, ok := m.data["covr"]
	if !ok {
		return nil
	}
	return v.(*Picture)
}
<commit_msg>Fix panic when picture isn't properly parsed.<commit_after>// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tag

import (
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
)

var atomTypes = map[int]string{
	0:  "uint8",
	1:  "text",
	13: "jpeg",
	14: "png",
	21: "uint8",
}

// NB: atoms does not include "----", this is handled separately
var atoms = atomNames(map[string]string{
	"\xa9alb": "album",
	"\xa9art": "artist",
	"\xa9ART": "artist",
	"aART":    "album_artist",
	"\xa9day": "year",
	"\xa9nam": "title",
	"\xa9gen": "genre",
	"trkn":    "track",
	"\xa9wrt": "composer",
	"\xa9too": "encoder",
	"cprt":    "copyright",
	"covr":    "picture",
	"\xa9grp": "grouping",
	"keyw":    "keyword",
	"\xa9lyr": "lyrics",
	"\xa9cmt": "comment",
	"tmpo":    "tempo",
	"cpil":    "compilation",
	"disk":    "disc",
})

type atomNames map[string]string

func (f atomNames) Name(n string) []string {
	res := make([]string, 1)
	for k, v := range f {
		if v == n {
			res = append(res, k)
		}
	}
	return res
}

// metadataMP4 is the implementation of Metadata for MP4 tag (atom) data.
type metadataMP4 struct {
	fileType FileType
	data     map[string]interface{}
}

// ReadAtoms reads MP4 metadata atoms from the io.ReadSeeker into a Metadata, returning
// non-nil error if there was a problem.
func ReadAtoms(r io.ReadSeeker) (Metadata, error) {
	m := metadataMP4{
		data: make(map[string]interface{}),
		fileType: UnknownFileType,
	}
	err := m.readAtoms(r)
	return m, err
}

func (m metadataMP4) readAtoms(r io.ReadSeeker) error {
	for {
		name, size, err := readAtomHeader(r)
		if err != nil {
			if err == io.EOF {
				return nil
			}
			return err
		}

		switch name {
		case "meta":
			// next_item_id (int32)
			_, err := readBytes(r, 4)
			if err != nil {
				return err
			}
			fallthrough

		case "moov", "udta", "ilst":
			return m.readAtoms(r)
		}

		_, ok := atoms[name]
		if name == "----" {
			name, size, err = readCustomAtom(r, size)
			if err != nil {
				return err
			}

			if name != "----" {
				ok = true
			}
		}

		if !ok {
			_, err := r.Seek(int64(size-8), os.SEEK_CUR)
			if err != nil {
				return err
			}
			continue
		}

		err = m.readAtomData(r, name, size-8)
		if err != nil {
			return err
		}
	}
}

func (m metadataMP4) readAtomData(r io.ReadSeeker, name string, size uint32) error {
	b, err := readBytes(r, int(size))
	if err != nil {
		return err
	}

	// "data" + size (4 bytes each)
	b = b[8:]

	class := getInt(b[1:4])
	contentType, ok := atomTypes[class]
	if !ok {
		return fmt.Errorf("invalid content type: %v (%x) (%x)", class, b[1:4], b)
	}

	// 4: atom version (1 byte) + atom flags (3 bytes)
	// 4: NULL (usually locale indicator)
	b = b[8:]

	if name == "trkn" || name == "disk" {
		m.data[name] = int(b[3])
		m.data[name+"_count"] = int(b[5])
		return nil
	}

	var data interface{}
	switch contentType {
	case "text":
		data = string(b)

	case "uint8":
		data = getInt(b[:1])

	case "jpeg", "png":
		data = &Picture{
			Ext:      contentType,
			MIMEType: "image/" + contentType,
			Data:     b,
		}
	}
	m.data[name] = data

	return nil
}

func readAtomHeader(r io.ReadSeeker) (name string, size uint32, err error) {
	err = binary.Read(r, binary.BigEndian, &size)
	if err != nil {
		return
	}
	name, err = readString(r, 4)
	return
}

// Generic atom.
// Should have 3 sub atoms : mean, name and data.
// We check that mean is "com.apple.iTunes" and we use the subname as
// the name, and move to the data atom.
// If anything goes wrong, we jump at the end of the "----" atom.
func readCustomAtom(r io.ReadSeeker, size uint32) (string, uint32, error) {
	subNames := make(map[string]string)
	var dataSize uint32

	for size > 8 {
		subName, subSize, err := readAtomHeader(r)
		if err != nil {
			return "", 0, err
		}

		// Remove the size of the atom from the size counter
		size -= subSize

		switch subName {
		case "mean", "name":
			b, err := readBytes(r, int(subSize-8))
			if err != nil {
				return "", 0, err
			}
			subNames[subName] = string(b[4:])

		case "data":
			// Found the "data" atom, rewind
			dataSize = subSize + 8 // will need to re-read "data" + size (4 + 4)
			_, err := r.Seek(-8, os.SEEK_CUR)
			if err != nil {
				return "", 0, err
			}
		}
	}

	// there should remain only the header size
	if size != 8 {
		err := errors.New("---- atom out of bounds")
		return "", 0, err
	}

	if subNames["mean"] != "com.apple.iTunes" || subNames["name"] == "" || dataSize == 0 {
		return "----", 0, nil
	}
	return subNames["name"], dataSize, nil
}

func (metadataMP4) Format() Format     { return MP4 }
func (m metadataMP4) FileType() FileType { return m.fileType }

func (m metadataMP4) Raw() map[string]interface{} { return m.data }

func (m metadataMP4) getString(n []string) string {
	for _, k := range n {
		if x, ok := m.data[k]; ok {
			return x.(string)
		}
	}
	return ""
}

func (m metadataMP4) getInt(n []string) int {
	for _, k := range n {
		if x, ok := m.data[k]; ok {
			return x.(int)
		}
	}
	return 0
}

func (m metadataMP4) Title() string {
	return m.getString(atoms.Name("title"))
}

func (m metadataMP4) Artist() string {
	return m.getString(atoms.Name("artist"))
}

func (m metadataMP4) Album() string {
	return m.getString(atoms.Name("album"))
}

func (m metadataMP4) AlbumArtist() string {
	return m.getString(atoms.Name("album_artist"))
}

func (m metadataMP4) Composer() string {
	return m.getString(atoms.Name("composer"))
}

func (m metadataMP4) Genre() string {
	return m.getString(atoms.Name("genre"))
}

func (m metadataMP4) Year() int {
	date := m.getString(atoms.Name("year"))
	if len(date) >= 4 {
		year, _ := strconv.Atoi(date[:4])
		return year
	}
	return 0
}

func (m metadataMP4) Track() (int, int) {
	x := m.getInt([]string{"trkn"})
	if n, ok := m.data["trkn_count"]; ok {
		return x, n.(int)
	}
	return x, 0
}

func (m metadataMP4) Disc() (int, int) {
	x := m.getInt([]string{"disk"})
	if n, ok := m.data["disk_count"]; ok {
		return x, n.(int)
	}
	return x, 0
}

func (m metadataMP4) Lyrics() string {
	t, ok := m.data["\xa9lyr"]
	if !ok {
		return ""
	}
	return t.(string)
}

func (m metadataMP4) Picture() *Picture {
	v, ok := m.data["covr"]
	if !ok {
		return nil
	}
	p, _ := v.(*Picture)
	return p
}
<|endoftext|>


<commit_before>// Copyright 2014 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package mux

import (
	"context"
	"net/http"
	"path"

	"github.com/issue9/mux/internal/entry"
	"github.com/issue9/mux/internal/method"
)

// 初始化时，默认的路由项数量大小，在一定的情况下，可以减少后期的内存多次分配操作
const defaultEntriesSize = 1000

// 两个默认处理函数
var (
	defaultNotFound = func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	defaultMethodNotAllowed = func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
	}
)

// Mux 提供了强大的路由匹配功能，可以处理正则路径和按请求方法进行匹配。
//
// 用法如下：
//  m := mux.New()
//  m.Get("/abc/h1", h1).
//    Post("/abc/h2", h2).
//    Add("/api/{version:\\d+}",h3, http.MethodGet, http.MethodPost) // 只匹配 GET 和 POST
//  http.ListenAndServe(m)
type Mux struct {
	entries          *entry.Entries
	skipCleanPath    bool             // 是否不对提交的路径作处理。
	notFound         http.HandlerFunc // 404 的处理方式
	methodNotAllowed http.HandlerFunc // 405 的处理方式
}

// New 声明一个新的 Mux。
//
// disableOptions 是否禁用自动生成 OPTIONS 功能；
// skipCleanPath 是否忽略对访问路径作处理，比如 "//api" ==> "/api"；
// notFound 404 页面的处理方式，为 nil 时会调用 http.Error 进行处理；
// methodNotAllowed 405 页面的处理方式，为 nil 时会调用 http.Error 进行处理。
func New(disableOptions, skipCleanPath bool, notFound, methodNotAllowed http.HandlerFunc) *Mux {
	if notFound == nil {
		notFound = defaultNotFound
	}
	if methodNotAllowed == nil {
		methodNotAllowed = defaultMethodNotAllowed
	}

	return &Mux{
		entries:          entry.NewEntries(disableOptions, defaultEntriesSize),
		skipCleanPath:    skipCleanPath,
		notFound:         notFound,
		methodNotAllowed: methodNotAllowed,
	}
}

// Clean 清除所有的路由项
func (mux *Mux) Clean() *Mux {
	mux.entries.Clean("")
	return mux
}

// Remove 移除指定的路由项。
//
// 当未指定 methods 时，将删除所有 method 匹配的项。
// 指定错误的 methods 值，将自动忽略该值。
func (mux *Mux) Remove(pattern string, methods ...string) {
	mux.entries.Remove(pattern, methods...)
}

// Add 添加一条路由数据。
//
// pattern 为路由匹配模式，可以是正则匹配也可以是字符串匹配；
// methods 参数应该只能为 method.Default 中的字符串，若不指定，默认为所有。
// 当 h 或是 pattern 为空时，将触发 panic。
func (mux *Mux) Add(pattern string, h http.Handler, methods ...string) error {
	return mux.entries.Add(pattern, h, methods...)
}

// Options 将 OPTIONS 请求方法的报头 allow 值固定为指定的值。
//
// 若无特殊需求，不用调用此方法，系统会自动计算符合当前路由的请求方法列表。
// 如果想实现对处理方法的自定义，可以显示地调用 Add 方法:
//  Mux.Add("/api/1", handle, http.MethodOptions)
func (mux *Mux) Options(pattern string, allow string) *Mux {
	ety, err := mux.entries.Entry(pattern)
	if err != nil {
		panic(err)
	}
	ety.SetAllow(allow)

	return mux
}

func (mux *Mux) add(pattern string, h http.Handler, methods ...string) *Mux {
	if err := mux.Add(pattern, h, methods...); err != nil {
		panic(err)
	}
	return mux
}

// Get 相当于 Mux.Add(pattern, h, "GET") 的简易写法
func (mux *Mux) Get(pattern string, h http.Handler) *Mux {
	return mux.add(pattern, h, http.MethodGet)
}

// Post 相当于 Mux.Add(pattern, h, "POST") 的简易写法
func (mux *Mux) Post(pattern string, h http.Handler) *Mux {
	return mux.add(pattern, h, http.MethodPost)
}

// Delete 相当于 Mux.Add(pattern, h, "DELETE") 的简易写法
func (mux *Mux) Delete(pattern string, h http.Handler) *Mux {
	return mux.add(pattern, h, http.MethodDelete)
}

// Put 相当于 Mux.Add(pattern, h, "PUT") 的简易写法
func (mux *Mux) Put(pattern string, h http.Handler) *Mux {
	return mux.add(pattern, h, http.MethodPut)
}

// Patch 相当于 Mux.Add(pattern, h, "PATCH") 的简易写法
func (mux *Mux) Patch(pattern string, h http.Handler) *Mux {
	return mux.add(pattern, h, http.MethodPatch)
}

// Any 相当于 Mux.Add(pattern, h) 的简易写法
func (mux *Mux) Any(pattern string, h http.Handler) *Mux {
	return mux.add(pattern, h, method.Default...)
}

// AddFunc 功能同 Mux.Add()，但是将第二个参数从 http.Handler 换成了 http.HandlerFunc
func (mux *Mux) AddFunc(pattern string, fun http.HandlerFunc, methods ...string) error {
	return mux.Add(pattern, fun, methods...)
}

func (mux *Mux) addFunc(pattern string, fun http.HandlerFunc, methods ...string) *Mux {
	return mux.add(pattern, fun, methods...)
}

// GetFunc 相当于 Mux.AddFunc(pattern, func, "GET") 的简易写法
func (mux *Mux) GetFunc(pattern string, fun http.HandlerFunc) *Mux {
	return mux.addFunc(pattern, fun, http.MethodGet)
}

// PutFunc 相当于 Mux.AddFunc(pattern, func, "PUT") 的简易写法
func (mux *Mux) PutFunc(pattern string, fun http.HandlerFunc) *Mux {
	return mux.addFunc(pattern, fun, http.MethodPut)
}

// PostFunc 相当于 Mux.AddFunc(pattern, func, "POST") 的简易写法
func (mux *Mux) PostFunc(pattern string, fun http.HandlerFunc) *Mux {
	return mux.addFunc(pattern, fun, http.MethodPost)
}

// DeleteFunc 相当于 Mux.AddFunc(pattern, func, "DELETE") 的简易写法
func (mux *Mux) DeleteFunc(pattern string, fun http.HandlerFunc) *Mux {
	return mux.addFunc(pattern, fun, http.MethodDelete)
}

// PatchFunc 相当于 Mux.AddFunc(pattern, func, "PATCH") 的简易写法
func (mux *Mux) PatchFunc(pattern string, fun http.HandlerFunc) *Mux {
	return mux.addFunc(pattern, fun, http.MethodPatch)
}

// AnyFunc 相当于 Mux.AddFunc(pattern, func) 的简易写法
func (mux *Mux) AnyFunc(pattern string, fun http.HandlerFunc) *Mux {
	return mux.addFunc(pattern, fun, method.Default...)
}

func (mux *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p := r.URL.Path
	if !mux.skipCleanPath {
		p = cleanPath(p)
	}

	e, params := mux.entries.Match(p)
	if e == nil {
		mux.notFound(w, r)
		return
	}

	h := e.Handler(r.Method)
	if h == nil {
		mux.methodNotAllowed(w, r)
		return
	}

	if len(params) > 0 {
		ctx := context.WithValue(r.Context(), contextKeyParams, Params(params))
		r = r.WithContext(ctx)
	}

	h.ServeHTTP(w, r)
}

// MethodIsSuppotred 检测请求方法当前包是否支持
func MethodIsSuppotred(m string) bool {
	return method.IsSupported(m)
}

// SupportedMethods 返回所有支持的请求方法
func SupportedMethods() []string {
	return method.Supported
}

// 清除路径中的怪异符号
func cleanPath(p string) string {
	if p == "" {
		return "/"
	}

	if p[0] != '/' {
		p = "/" + p
	}

	pp := path.Clean(p)
	if pp == "/" {
		return pp
	}

	// path.Clean 会去掉最后的 / 符号，所以原来有 / 的，需要加回去
	if p[len(p)-1] == '/' {
		pp += "/"
	}
	return pp
}
<commit_msg>统一接口类型<commit_after>// Copyright 2014 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package mux

import (
	"context"
	"net/http"
	"path"

	"github.com/issue9/mux/internal/entry"
	"github.com/issue9/mux/internal/method"
)

// 初始化时，默认的路由项数量大小，在一定的情况下，可以减少后期的内存多次分配操作
const defaultEntriesSize = 1000

// 两个默认处理函数
var (
	defaultNotFound = func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	defaultMethodNotAllowed = func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
	}
)

// Mux 提供了强大的路由匹配功能，可以处理正则路径和按请求方法进行匹配。
//
// 用法如下：
//  m := mux.New()
//  m.Get("/abc/h1", h1).
//    Post("/abc/h2", h2).
//    Add("/api/{version:\\d+}",h3, http.MethodGet, http.MethodPost) // 只匹配 GET 和 POST
//  http.ListenAndServe(m)
type Mux struct {
	entries          *entry.Entries
	skipCleanPath    bool             // 是否不对提交的路径作处理。
	notFound         http.HandlerFunc // 404 的处理方式
	methodNotAllowed http.HandlerFunc // 405 的处理方式
}

// New 声明一个新的 Mux。
//
// disableOptions 是否禁用自动生成 OPTIONS 功能；
// skipCleanPath 是否忽略对访问路径作处理，比如 "//api" ==> "/api"；
// notFound 404 页面的处理方式，为 nil 时会调用 http.Error 进行处理；
// methodNotAllowed 405 页面的处理方式，为 nil 时会调用 http.Error 进行处理。
func New(disableOptions, skipCleanPath bool, notFound, methodNotAllowed http.HandlerFunc) *Mux {
	if notFound == nil {
		notFound = defaultNotFound
	}
	if methodNotAllowed == nil {
		methodNotAllowed = defaultMethodNotAllowed
	}

	return &Mux{
		entries:          entry.NewEntries(disableOptions, defaultEntriesSize),
		skipCleanPath:    skipCleanPath,
		notFound:         notFound,
		methodNotAllowed: methodNotAllowed,
	}
}

// Clean 清除所有的路由项
func (mux *Mux) Clean() *Mux {
	mux.entries.Clean("")
	return mux
}

// Remove 移除指定的路由项。
//
// 当未指定 methods 时，将删除所有 method 匹配的项。
// 指定错误的 methods 值，将自动忽略该值。
func (mux *Mux) Remove(pattern string, methods ...string) *Mux {
	mux.entries.Remove(pattern, methods...)
	return mux
}

// Add 添加一条路由数据。
//
// pattern 为路由匹配模式，可以是正则匹配也可以是字符串匹配；
// methods 参数应该只能为 method.Default 中的字符串，若不指定，默认为所有。
// 当 h 或是 pattern 为空时，将触发 panic。
func (mux *Mux) Add(pattern string, h http.Handler, methods ...string) error {
	return mux.entries.Add(pattern, h, methods...)
}

// Options 将 OPTIONS 请求方法的报头 allow 值固定为指定的值。
//
// 若无特殊需求，不用调用此方法，系统会自动计算符合当前路由的请求方法列表。
// 如果想实现对处理方法的自定义，可以显示地调用 Add 方法:
//  Mux.Add("/api/1", handle, http.MethodOptions)
func (mux *Mux) Options(pattern string, allow string) *Mux {
	ety, err := mux.entries.Entry(pattern)
	if err != nil {
		panic(err)
	}
	ety.SetAllow(allow)

	return mux
}

func (mux *Mux) add(pattern string, h http.Handler, methods ...string) *Mux {
	if err := mux.Add(pattern, h, methods...); err != nil {
		panic(err)
	}
	return mux
}

// Get 相当于 Mux.Add(pattern, h, "GET") 的简易写法
func (mux *Mux) Get(pattern string, h http.Handler) *Mux {
	return mux.add(pattern, h, http.MethodGet)
}

// Post 相当于 Mux.Add(pattern, h, "POST") 的简易写法
func (mux *Mux) Post(pattern string, h http.Handler) *Mux {
	return mux.add(pattern, h, http.MethodPost)
}

// Delete 相当于 Mux.Add(pattern, h, "DELETE") 的简易写法
func (mux *Mux) Delete(pattern string, h http.Handler) *Mux {
	return mux.add(pattern, h, http.MethodDelete)
}

// Put 相当于 Mux.Add(pattern, h, "PUT") 的简易写法
func (mux *Mux) Put(pattern string, h http.Handler) *Mux {
	return mux.add(pattern, h, http.MethodPut)
}

// Patch 相当于 Mux.Add(pattern, h, "PATCH") 的简易写法
func (mux *Mux) Patch(pattern string, h http.Handler) *Mux {
	return mux.add(pattern, h, http.MethodPatch)
}

// Any 相当于 Mux.Add(pattern, h) 的简易写法
func (mux *Mux) Any(pattern string, h http.Handler) *Mux {
	return mux.add(pattern, h, method.Default...)
}

// AddFunc 功能同 Mux.Add()，但是将第二个参数从 http.Handler 换成了 http.HandlerFunc
func (mux *Mux) AddFunc(pattern string, fun http.HandlerFunc, methods ...string) error {
	return mux.Add(pattern, fun, methods...)
}

func (mux *Mux) addFunc(pattern string, fun http.HandlerFunc, methods ...string) *Mux {
	return mux.add(pattern, fun, methods...)
}

// GetFunc 相当于 Mux.AddFunc(pattern, func, "GET") 的简易写法
func (mux *Mux) GetFunc(pattern string, fun http.HandlerFunc) *Mux {
	return mux.addFunc(pattern, fun, http.MethodGet)
}

// PutFunc 相当于 Mux.AddFunc(pattern, func, "PUT") 的简易写法
func (mux *Mux) PutFunc(pattern string, fun http.HandlerFunc) *Mux {
	return mux.addFunc(pattern, fun, http.MethodPut)
}

// PostFunc 相当于 Mux.AddFunc(pattern, func, "POST") 的简易写法
func (mux *Mux) PostFunc(pattern string, fun http.HandlerFunc) *Mux {
	return mux.addFunc(pattern, fun, http.MethodPost)
}

// DeleteFunc 相当于 Mux.AddFunc(pattern, func, "DELETE") 的简易写法
func (mux *Mux) DeleteFunc(pattern string, fun http.HandlerFunc) *Mux {
	return mux.addFunc(pattern, fun, http.MethodDelete)
}

// PatchFunc 相当于 Mux.AddFunc(pattern, func, "PATCH") 的简易写法
func (mux *Mux) PatchFunc(pattern string, fun http.HandlerFunc) *Mux {
	return mux.addFunc(pattern, fun, http.MethodPatch)
}

// AnyFunc 相当于 Mux.AddFunc(pattern, func) 的简易写法
func (mux *Mux) AnyFunc(pattern string, fun http.HandlerFunc) *Mux {
	return mux.addFunc(pattern, fun, method.Default...)
}

func (mux *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p := r.URL.Path
	if !mux.skipCleanPath {
		p = cleanPath(p)
	}

	e, params := mux.entries.Match(p)
	if e == nil {
		mux.notFound(w, r)
		return
	}

	h := e.Handler(r.Method)
	if h == nil {
		mux.methodNotAllowed(w, r)
		return
	}

	if len(params) > 0 {
		ctx := context.WithValue(r.Context(), contextKeyParams, Params(params))
		r = r.WithContext(ctx)
	}

	h.ServeHTTP(w, r)
}

// MethodIsSuppotred 检测请求方法当前包是否支持
func MethodIsSuppotred(m string) bool {
	return method.IsSupported(m)
}

// SupportedMethods 返回所有支持的请求方法
func SupportedMethods() []string {
	return method.Supported
}

// 清除路径中的怪异符号
func cleanPath(p string) string {
	if p == "" {
		return "/"
	}

	if p[0] != '/' {
		p = "/" + p
	}

	pp := path.Clean(p)
	if pp == "/" {
		return pp
	}

	// path.Clean 会去掉最后的 / 符号，所以原来有 / 的，需要加回去
	if p[len(p)-1] == '/' {
		pp += "/"
	}
	return pp
}
<|endoftext|>


<commit_before>package main

import (
    "bufio"
    "compress/gzip"
    "fmt"
    "os"
    "io"
)

const (
    tagStructEnd = 0 // No name. Single zero byte.
    tagInt8 = 1 // A single signed byte (8 bits)
    tagInt16 = 2 // A signed short (16 bits, big endian)
    tagInt32 = 3 // A signed int (32 bits, big endian)
    tagInt64 = 4 // A signed long (64 bits, big endian)
    tagFloat32 = 5 // A floating point value (32 bits, big endian, IEEE 754-2008, binary32)
    tagFloat64 = 6 // A floating point value (64 bits, big endian, IEEE 754-2008, binary64)
    tagByteArray = 7 // { TAG_Int length; An array of bytes of unspecified format. The length of this array is <length> bytes }
    tagString = 8 // { TAG_Short length; An array of bytes defining a string in UTF-8 format. The length of this array is <length> bytes }
    tagList = 9 // { TAG_Byte tagId; TAG_Int length; A sequential list of Tags (not Named Tags), of type <typeId>. The length of this array is <length> Tags. } Notes: All tags share the same type.
    tagStruct = 10 // { A sequential list of Named Tags. This array keeps going until a TAG_End is found.; TAG_End end } Notes: If there's a nested TAG_Compound within this tag, that one will also have a TAG_End, so simply reading until the next TAG_End will not work. The names of the named tags have to be unique within each TAG_Compound The order of the tags is not guaranteed.
)

const (
    trace = false
)

func main() {
    var file, fileErr = os.Open("c.4.4.dat", os.O_RDONLY, 0666) // x: 76, y: 69
    if fileErr != nil {
        fmt.Println(fileErr)
        return
    }

    var r, rErr = gzip.NewReader(file)
    if rErr != nil {
        fmt.Println(rErr)
        return
    }

    var br = bufio.NewReader(r)

    for {
        var typeId, name, err = readTag(br)
        if err != nil {
            break
        }

        switch typeId {
        case tagStruct:
            if trace {
                fmt.Printf("%s struct start\n", name)
            }
        case tagStructEnd:
            if trace {
                fmt.Printf("struct end\n")
            }
        case tagByteArray:
            var bytes, err2 = readBytes(br)
            if err2 != nil {
                fmt.Println(err2)
            }
            if trace {
                fmt.Printf("%s bytes(%d) %v\n", name, len(bytes), bytes)
            }
            if name == "Blocks" {
                processBlocks(bytes)
            }
        case tagInt8:
            var number, err2 = readInt8(br)
            if err2 != nil {
                fmt.Println(err2)
            }
            if trace {
                fmt.Printf("%s int8 %v\n", name, number)
            }
        case tagInt16:
            var number, err2 = readInt16(br)
            if err2 != nil {
                fmt.Println(err2)
            }
            if trace {
                fmt.Printf("%s int16 %v\n", name, number)
            }
        case tagInt32:
            var number, err2 = readInt32(br)
            if err2 != nil {
                fmt.Println(err2)
            }
            if trace {
                fmt.Printf("%s int32 %v\n", name, number)
            }
        case tagInt64:
            var number, err2 = readInt64(br)
            if err2 != nil {
                fmt.Println(err2)
            }
            if trace {
                fmt.Printf("%s int64 %v\n", name, number)
            }
        case tagList:
            var itemTypeId, length, err2 = readListHeader(br)
            if err2 != nil {
                fmt.Println(err2)
            }
            switch itemTypeId {
                case tagInt8:
                    if trace {
                        fmt.Printf("%s list int8 (%d)\n", name, length)
                    }
                    for i := 0; i < length; i++ {
                        var v, err3 = readInt8(br)
                        if err3 != nil {
                            fmt.Println(err3)
                        }
                        if trace {
                            fmt.Println("  ", v)
                        }
                    }
                default:
                    fmt.Printf("list todo(%n) %n\n", itemTypeId, length)
            }
        default:
            fmt.Printf("%s todo(%d)\n", name, typeId)
        }
    }
    fmt.Println()
}

func readTag(r *bufio.Reader) (byte, string, os.Error) {
    var typeId, err = r.ReadByte()
    if err != nil || typeId == 0 {
        return typeId, "", err
    }

    var name, err2 = readString(r)
    if err2 != nil {
        return typeId, name, err2
    }

    return typeId, name, nil
}

func readListHeader(r *bufio.Reader) (itemTypeId byte, length int, err os.Error) {
    length = 0

    itemTypeId, err = r.ReadByte()
    if err == nil {
        length, err = readInt32(r)
    }

    return
}

func readString(r *bufio.Reader) (string, os.Error) {
    var length, err1 = readInt16(r)
    if err1 != nil {
        return "", err1
    }

    var bytes = make([]byte, length)
    var _, err2 = io.ReadFull(r, bytes)
    return string(bytes), err2
}

func readBytes(r *bufio.Reader) ([]byte, os.Error) {
    var length, err1 = readInt32(r)
    if err1 != nil {
        return nil, err1
    }

    var bytes = make([]byte, length)
    var _, err2 = io.ReadFull(r, bytes)
    return bytes, err2
}

func readInt8(r *bufio.Reader) (int, os.Error) {
    return readIntN(r, 1)
}

func readInt16(r *bufio.Reader) (int, os.Error) {
    return readIntN(r, 2)
}

func readInt32(r *bufio.Reader) (int, os.Error) {
    return readIntN(r, 4)
}

func readInt64(r *bufio.Reader) (int, os.Error) {
    return readIntN(r, 8)
}

func readIntN(r *bufio.Reader, n int) (int, os.Error) {
    var a int = 0

    for i := 0; i < n; i++ {
        var b, err = r.ReadByte()
        if err != nil {
            return a, err
        }
        a = a << 8 + int(b)
    }

    return a, nil
}

type Blocks []byte

func (b *Blocks) Get(x, y, z int) byte {
    return (*b)[y + (z*128 + (x * 128 * 16))]
}

func (b *Blocks) IsEmpty(x, y, z int) bool {
    if x < 0 || y < 0 || z < 0 || x >= 16 || y >= 16 || z >= 16 {
        return true
    }
    return isEmptyBlockId(b.Get(x, y, z))
}

func isEmptyBlockId(blockId byte) bool {
    return blockId == 0 || blockId == 1
}

func processBlocks(bytes []byte) {
    var blocks = Blocks(bytes)

    for i := 0; i < len(blocks); i += 128 {
        var x, z = (i / 128) / 16, (i / 128) % 16

        var column = blocks[i:i+128]
        fmt.Printf("# %x, %x:\n", x, z)
        for y, blockId := range column {
            if !isEmptyBlockId(blockId) {
                if blocks.IsEmpty(x, y-1, z) {
                    printFace(v{x, y, z}, v{x+1, y, z}, v{x+1, y, z+1}, v{x, y, z+1})
                }

                if blocks.IsEmpty(x, y+1, z) {
                    printFace(v{x, y+1, z}, v{x, y+1, z+1}, v{x+1, y+1, z+1}, v{x+1, y+1, z})
                }

                if blocks.IsEmpty(x-1, y, z) {
                    printFace(v{x, y, z}, v{x, y, z+1}, v{x, y+1, z+1}, v{x, y+1, z})
                }

                if blocks.IsEmpty(x+1, y, z) {
                    printFace(v{x+1, y, z}, v{x+1, y+1, z}, v{x+1, y+1, z+1}, v{x+1, y, z+1})
                }

                if blocks.IsEmpty(x, y, z-1) {
                    printFace(v{x, y, z}, v{x, y+1, z}, v{x+1, y+1, z}, v{x+1, y, z})
                }

                if blocks.IsEmpty(x, y, z+1) {
                    printFace(v{x, y, z+1}, v{x+1, y, z+1}, v{x+1, y+1, z+1}, v{x, y+1, z+1})
                }
            }
        }
        fmt.Println()
    }
}

func printVertex(x, y, z int) {
    fmt.Printf("v %v %v %v\n", float64(x)*0.05, float64(y)*0.05, float64(z)*0.05)
}

func printFaceN(vertexes... int) {
    fmt.Printf("f")
    for _, vertex := range vertexes {
        fmt.Printf(" %v", vertex)
    }
    fmt.Println()
}

type v struct { x, y, z int }

func printFace(vertexes... v) {
    for _, vertex := range vertexes {
        printVertex(vertex.x, vertex.y, vertex.z)
    }
    fmt.Printf("f")
    for i, _ := range vertexes {
        fmt.Printf(" -%d", len(vertexes) - i)
    }
    fmt.Println()
}
<commit_msg>Read .dat file or entire directories<commit_after>package main

import (
    "bufio"
    "compress/gzip"
    "flag"
    "fmt"
    "io"
    "os"
    "path"
)

const (
    tagStructEnd = 0 // No name. Single zero byte.
    tagInt8 = 1 // A single signed byte (8 bits)
    tagInt16 = 2 // A signed short (16 bits, big endian)
    tagInt32 = 3 // A signed int (32 bits, big endian)
    tagInt64 = 4 // A signed long (64 bits, big endian)
    tagFloat32 = 5 // A floating point value (32 bits, big endian, IEEE 754-2008, binary32)
    tagFloat64 = 6 // A floating point value (64 bits, big endian, IEEE 754-2008, binary64)
    tagByteArray = 7 // { TAG_Int length; An array of bytes of unspecified format. The length of this array is <length> bytes }
    tagString = 8 // { TAG_Short length; An array of bytes defining a string in UTF-8 format. The length of this array is <length> bytes }
    tagList = 9 // { TAG_Byte tagId; TAG_Int length; A sequential list of Tags (not Named Tags), of type <typeId>. The length of this array is <length> Tags. } Notes: All tags share the same type.
    tagStruct = 10 // { A sequential list of Named Tags. This array keeps going until a TAG_End is found.; TAG_End end } Notes: If there's a nested TAG_Compound within this tag, that one will also have a TAG_End, so simply reading until the next TAG_End will not work. The names of the named tags have to be unique within each TAG_Compound The order of the tags is not guaranteed.
)

const (
    trace = false
)

var (
    out *bufio.Writer
)

func main() {
    var outFile, outErr = os.Open("a.obj", os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0666)
    if outErr != nil {
        fmt.Fprintln(os.Stderr, outErr)
        return
    }
    defer outFile.Close()
    var bufErr os.Error
    out, bufErr = bufio.NewWriterSize(outFile, 1024 * 1024)
    if bufErr != nil {
        fmt.Fprintln(os.Stderr, bufErr)
        return
    }

    flag.Parse()
    for i := 0; i < flag.NArg(); i++ {
        var filepath = flag.Arg(i)
        var fi, err = os.Stat(filepath)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
        }

        switch {
        case fi.IsRegular():
            processFile(filepath)
        case fi.IsDirectory():
            var errors = make(chan os.Error, 5)
            var done = make(chan bool)
            go func(){
                for error := range errors {
                    fmt.Fprintln(os.Stderr, error)
                }
                done <- true
            }()
            path.Walk(filepath, &visitor{}, errors)
            close(errors)
            <-done
        }
    }
}

type visitor struct {}

func (v *visitor) VisitDir(dir string, f *os.FileInfo) bool {
    return true
}

func (v *visitor) VisitFile(file string, f *os.FileInfo) {
    var _, name = path.Split(file)
    var match, err = path.Match("c.*.dat", name)
    if match && err == nil {
        processFile(file)
    }
}

func processFile(filename string) bool {
    fmt.Fprintln(out, "#", filename)
    fmt.Fprintln(os.Stderr, filename)
    var file, fileErr = os.Open(filename, os.O_RDONLY, 0666)
    if fileErr != nil {
        fmt.Println(fileErr)
        return true
    }

    var r, rErr = gzip.NewReader(file)
    if rErr != nil {
        fmt.Println(rErr)
        return true
    }

    var xPos, zPos int

    var abort = false

    var br = bufio.NewReader(r)

    for {
        var typeId, name, err = readTag(br)
        if err != nil {
            break
        }

        switch typeId {
        case tagStruct:
            if trace {
                fmt.Printf("%s struct start\n", name)
            }
        case tagStructEnd:
            if trace {
                fmt.Printf("struct end\n")
            }
        case tagByteArray:
            var bytes, err2 = readBytes(br)
            if err2 != nil {
                fmt.Println(err2)
                return true
            }
            if trace {
                fmt.Printf("%s bytes(%d) %v\n", name, len(bytes), bytes)
            }
            if name == "Blocks" {
                processBlocks(bytes, xPos, zPos)
            }
        case tagInt8:
            var number, err2 = readInt8(br)
            if err2 != nil {
                fmt.Println(err2)
                return true
            }
            if trace {
                fmt.Printf("%s int8 %v\n", name, number)
            }
        case tagInt16:
            var number, err2 = readInt16(br)
            if err2 != nil {
                fmt.Println(err2)
                return true
            }
            if trace {
                fmt.Printf("%s int16 %v\n", name, number)
            }
        case tagInt32:
            var number, err2 = readInt32(br)
            if err2 != nil {
                fmt.Println(err2)
                return true
            }
            if trace {
                fmt.Printf("%s int32 %v\n", name, number)
            }

            if name == "xPos" {
                xPos = number
            }
            if name == "zPos" {
                zPos = number
            }
        case tagInt64:
            var number, err2 = readInt64(br)
            if err2 != nil {
                fmt.Println(err2)
                return true
            }
            if trace {
                fmt.Printf("%s int64 %v\n", name, number)
            }
        case tagFloat32:
            var number, err2 = readInt32(br) // TODO(jw): read floats not ints
            if err2 != nil {
                fmt.Println(err2)
                return true
            }
            if trace {
                fmt.Printf("%s int32 %v\n", name, number)
            }
        case tagFloat64:
            var number, err2 = readInt64(br) // TODO(jw): read floats not ints
            if err2 != nil {
                fmt.Println(err2)
                return true
            }
            if trace {
                fmt.Printf("%s int64 %v\n", name, number)
            }
        case tagString:
            var s, err2 = readString(br)
            if err2 != nil {
                fmt.Println(err2)
                return true
            }
            if trace {
                fmt.Printf("%s string \"%s\"", name, s)
            }
        case tagList:
            var itemTypeId, length, err2 = readListHeader(br)
            if err2 != nil {
                fmt.Println(err2)
                return true
            }
            switch itemTypeId {
                case tagInt8:
                    if trace {
                        fmt.Printf("%s list int8 (%d)\n", name, length)
                    }
                    for i := 0; i < length; i++ {
                        var v, err3 = readInt8(br)
                        if err3 != nil {
                            fmt.Println(err3)
                            return true
                        }
                        if trace {
                            fmt.Println("  ", v)
                        }
                    }
                case tagFloat32:
                    if trace {
                        fmt.Printf("%s list float64 (%d)\n", name, length)
                    }
                    for i := 0; i < length; i++ {
                        var v, err3 = readInt32(br) // TODO(jw) read float32 instead of int32
                        if err3 != nil {
                            fmt.Println(err3)
                            return true
                        }
                        if trace {
                            fmt.Println("  ", v)
                        }
                    }
                case tagFloat64:
                    if trace {
                        fmt.Printf("%s list float64 (%d)\n", name, length)
                    }
                    for i := 0; i < length; i++ {
                        var v, err3 = readInt64(br) // TODO(jw) read float64 instead of int64
                        if err3 != nil {
                            fmt.Println(err3)
                            return true
                        }
                        if trace {
                            fmt.Println("  ", v)
                        }
                    }
                case tagStruct:
                    if trace {
                        fmt.Printf("%s list struct (%d)\n", name, length)
                    }
                    abort = true
                default:
                    fmt.Printf("# %s list todo(%v) %v\n", name, itemTypeId, length)
            }
        default:
            fmt.Printf("# %s todo(%d)\n", name, typeId)
        }
    }
    fmt.Fprintln(out)

    return abort
}

func readTag(r *bufio.Reader) (byte, string, os.Error) {
    var typeId, err = r.ReadByte()
    if err != nil || typeId == 0 {
        return typeId, "", err
    }

    var name, err2 = readString(r)
    if err2 != nil {
        return typeId, name, err2
    }

    return typeId, name, nil
}

func readListHeader(r *bufio.Reader) (itemTypeId byte, length int, err os.Error) {
    length = 0

    itemTypeId, err = r.ReadByte()
    if err == nil {
        length, err = readInt32(r)
    }

    return
}

func readString(r *bufio.Reader) (string, os.Error) {
    var length, err1 = readInt16(r)
    if err1 != nil {
        return "", err1
    }

    var bytes = make([]byte, length)
    var _, err2 = io.ReadFull(r, bytes)
    return string(bytes), err2
}

func readBytes(r *bufio.Reader) ([]byte, os.Error) {
    var length, err1 = readInt32(r)
    if err1 != nil {
        return nil, err1
    }

    var bytes = make([]byte, length)
    var _, err2 = io.ReadFull(r, bytes)
    return bytes, err2
}

func readInt8(r *bufio.Reader) (int, os.Error) {
    return readIntN(r, 1)
}

func readInt16(r *bufio.Reader) (int, os.Error) {
    return readIntN(r, 2)
}

func readInt32(r *bufio.Reader) (int, os.Error) {
    return readIntN(r, 4)
}

func readInt64(r *bufio.Reader) (int, os.Error) {
    return readIntN(r, 8)
}

func readIntN(r *bufio.Reader, n int) (int, os.Error) {
    var a int = 0

    for i := 0; i < n; i++ {
        var b, err = r.ReadByte()
        if err != nil {
            return a, err
        }
        a = a << 8 + int(b)
    }

    return a, nil
}

type Blocks []byte

func (b *Blocks) Get(x, y, z int) byte {
    return (*b)[y + (z*128 + (x * 128 * 16))]
}

func (b *Blocks) IsEmpty(x, y, z int) bool {
    if x < 0 || y < 0 || z < 0 || x >= 16 || y >= 128 || z >= 16 {
        return true
    }
    return isEmptyBlockId(b.Get(x, y, z))
}

func (b *Blocks) IsBoundary(x, y, z int, blockId byte) bool {
    if x < 0 || y < 0 || z < 0 || x >= 16 || y >= 128 || z >= 16 {
        return blockId != 0
    }

    var otherBlockId = b.Get(x, y, z)

    if blockId == 1 && otherBlockId == 0 {
        return true
    }

    return (blockId != 0 && blockId != 1) && (otherBlockId == 0 || blockId == 1)
}

func isEmptyBlockId(blockId byte) bool {
    return blockId == 0 || blockId == 1
}

func processBlocks(bytes []byte, xPos, zPos int) {
    var blocks = Blocks(bytes)

    fmt.Fprintf(out, "# %v,%v\n", xPos, zPos)

    xPos = xPos * 16
    zPos = zPos * 16

    var faces = 0

    for i := 0; i < len(blocks); i += 128 {
        var x, z = (i / 128) / 16, (i / 128) % 16

        var column = blocks[i:i+128]
        for y, blockId := range column {
            if y < 62 { continue }
            if blocks.IsBoundary(x, y-1, z, blockId) {
                printFace(v{xPos+x, y, zPos+z}, v{xPos+x+1, y, zPos+z}, v{xPos+x+1, y, zPos+z+1}, v{xPos+x, y, zPos+z+1})
                faces++
            }

            if blocks.IsBoundary(x, y+1, z, blockId) {
                printFace(v{xPos+x, y+1, zPos+z}, v{xPos+x, y+1, zPos+z+1}, v{xPos+x+1, y+1, zPos+z+1}, v{xPos+x+1, y+1, zPos+z})
                faces++
            }

            if blocks.IsBoundary(x-1, y, z, blockId) {
                printFace(v{xPos+x, y, zPos+z}, v{xPos+x, y, zPos+z+1}, v{xPos+x, y+1, zPos+z+1}, v{xPos+x, y+1, zPos+z})
                faces++
            }

            if blocks.IsBoundary(x+1, y, z, blockId) {
                printFace(v{xPos+x+1, y, zPos+z}, v{xPos+x+1, y+1, zPos+z}, v{xPos+x+1, y+1, zPos+z+1}, v{xPos+x+1, y, zPos+z+1})
                faces++
            }

            if blocks.IsBoundary(x, y, z-1, blockId) {
                printFace(v{xPos+x, y, zPos+z}, v{xPos+x, y+1, zPos+z}, v{xPos+x+1, y+1, zPos+z}, v{xPos+x+1, y, zPos+z})
                faces++
            }

            if blocks.IsBoundary(x, y, z+1, blockId) {
                printFace(v{xPos+x, y, zPos+z+1}, v{xPos+x+1, y, zPos+z+1}, v{xPos+x+1, y+1, zPos+z+1}, v{xPos+x, y+1, zPos+z+1})
                faces++
            }
        }
        fmt.Fprintln(out)
    }
    fmt.Fprintln(os.Stderr, "Faces:", faces)
}

func printVertex(x, y, z int) {
    fmt.Fprintf(out, "v %.2f %.2f %.2f\n", float64(x)*0.05, float64(y)*0.05, float64(z)*0.05)
}

func printFaceN(vertexes... int) {
    fmt.Fprintf(out, "f")
    for _, vertex := range vertexes {
        fmt.Fprintf(out, " %v", vertex)
    }
    fmt.Fprintln(out)
}

type v struct { x, y, z int }

func printFace(vertexes... v) {
    for _, vertex := range vertexes {
        printVertex(vertex.x, vertex.y, vertex.z)
    }
    fmt.Fprintf(out, "f")
    for i, _ := range vertexes {
        fmt.Fprintf(out, " -%d", len(vertexes) - i)
    }
    fmt.Fprintln(out)
}
<|endoftext|>


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

// Package owm implements a wrapper for the OpenWeatherMap API.
//
// Read more about the OpenWeatherMap API here: http://openweathermap.org/api.
package owm

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"strconv"
)

type weatherLine struct {
	Main        string
	Description string
}

type forecastLine struct {
	Dt   int
	Main struct {
		Temp      float64
		TempMin   float64 `json:"temp_min"`
		TempMax   float64 `json:"temp_max"`
		Pressure  float64
		SeaLevel  float64 `json:"sea_level"`
		GrndLevel float64 `json:"grnd_level"`
		Humidity  int
	}
	Weather []weatherLine
	Wind    struct {
		Speed float64
		Deg   float64
		Gust  float64
	}
	Clouds struct {
		All int
	}
	Sys struct {
		Pod string
	}
	DtTxt string `json:"dt_txt"`
}

// Weather represents the current weather at a specific location.
//
// It is returned by the WeatherBy* methods of Client.
type Weather struct {
	Coord struct {
		Lat float64
		Lon float64
	}
	Sys struct {
		Country string
		Sunrise int
		Sunset  int
	}
	Weather []weatherLine
	Main    struct {
		Temp      float64
		Humidity  int
		TempMin   float64 `json:"temp_min"`
		TempMax   float64 `json:"temp_max"`
		Pressure  float64
		SeaLevel  float64 `json:"sea_level"`
		GrndLevel float64 `json:"grnd_level"`
	}
	Wind struct {
		Speed float64
		Deg   float64
		Gust  float64
	}
	Clouds struct {
		All int
	}
	Dt   int
	Id   int
	Name string
	Cod  int
}

// WeatherSet represents the current weather at a set of specific locations.
//
// It is returned by the WeatherSetBy* methods of Client.
type WeatherSet struct {
	Cnt     int
	Weather []Weather `json:"list"`
}

// Forecast represents the weather forecast for a specific location.
//
// It is returned by the ForecastBy* methods of Client.
type Forecast struct {
	Cod  int
	City struct {
		Id    int
		Name  string
		Coord struct {
			Lon float64
			Lat float64
		}
		Country string
	}
	Cnt          int
	ForecastLine []forecastLine
}

// Client represents an OpenWeatherMap API client.
type Client struct {
	key     string
	baseURL string
}

// NewClient returns a new Client given an API key.
//
// Pass an empty string as the key argument to use the client without an API key.
func NewClient(key string) *Client {
	return &Client{key, "http://api.openweathermap.org/data/2.5/"}
}

func (c *Client) data(url string) (data []byte, err error) {
	res, err := http.Get(url)
	if err != nil {
		err = errors.New("owm: error while getting weather data")
		return
	}
	data, err = ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		err = errors.New("owm: error while getting weather data")
		return
	}
	return
}

func (c *Client) weather(url string) (w Weather, err error) {
	if c.key != "" {
		url += "&APPID=" + c.key
	}
	data, err := c.data(url)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
		return
	}
	err = json.Unmarshal(data, &w)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
		return
	}
	return
}

// WeatherByName decodes the current weather given the city name and the units.
func (c *Client) WeatherByName(name string, units string) (w Weather, err error) {
	w, err = c.weather(c.baseURL +
		"weather" +
		"?q=" + name +
		"&units=" + units)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
	}
	return
}

// WeatherById decodes the current weather given the city id and the units.
func (c *Client) WeatherById(id int, units string) (w Weather, err error) {
	w, err = c.weather(c.baseURL +
		"weather" +
		"?id=" + strconv.Itoa(id) +
		"&units=" + units)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
	}
	return
}

// WeatherByCoord decodes the current weather given the city coordinates and the units.
func (c *Client) WeatherByCoord(lat float64, lon float64, units string) (w Weather, err error) {
	w, err = c.weather(c.baseURL +
		"weather" +
		"?lat=" + strconv.FormatFloat(lat, 'f', 2, 64) +
		"&lon=" + strconv.FormatFloat(lon, 'f', 2, 64) +
		"&units=" + units)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
	}
	return
}

func (c *Client) weatherSet(url string) (ws WeatherSet, err error) {
	if c.key != "" {
		url += "&APPID=" + c.key
	}
	data, err := c.data(url)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
		return
	}
	err = json.Unmarshal(data, &ws)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
		return
	}
	return
}

// WeatherSetByIds decodes the current weather given a slice of city ids and the units.
func (c *Client) WeatherSetByIds(id []int, units string) (ws WeatherSet, err error) {
	var ids string
	for i := range id {
		ids += strconv.Itoa(id[i]) + ","
	}
	ws, err = c.weatherSet(c.baseURL +
		"group" +
		"?id=" + ids[:len(ids)-1] +
		"&units=" + units)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
	}
	return
}
<commit_msg>added struct fields<commit_after>// Copyright 2014 Vasco Costa. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package owm implements a wrapper for the OpenWeatherMap API.
//
// Read more about the OpenWeatherMap API here: http://openweathermap.org/api.
package owm

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"strconv"
)

type weatherLine struct {
	Id int
	Main        string
	Description string
	Icon string
}

type forecastLine struct {
	Dt   int
	Main struct {
		Temp      float64
		TempMin   float64 `json:"temp_min"`
		TempMax   float64 `json:"temp_max"`
		Pressure  float64
		SeaLevel  float64 `json:"sea_level"`
		GrndLevel float64 `json:"grnd_level"`
		Humidity  int
	}
	Weather []weatherLine
	Wind    struct {
		Speed float64
		Deg   float64
		Gust  float64
	}
	Clouds struct {
		All int
	}
	Sys struct {
		Pod string
	}
	DtTxt string `json:"dt_txt"`
}

// Weather represents the current weather at a specific location.
//
// It is returned by the WeatherBy* methods of Client.
type Weather struct {
	Coord struct {
		Lat float64
		Lon float64
	}
	Sys struct {
		Type int
		Id int
		Message int
		Country string
		Sunrise int
		Sunset  int
	}
	Weather []weatherLine
	Base string
	Main    struct {
		Temp      float64
		Humidity  int
		TempMin   float64 `json:"temp_min"`
		TempMax   float64 `json:"temp_max"`
		Pressure  float64
		SeaLevel  float64 `json:"sea_level"`
		GrndLevel float64 `json:"grnd_level"`
	}
	Wind struct {
		Speed float64
		Deg   float64
		Gust  float64
	}
	Clouds struct {
		All int
	}
	Dt   int
	Id   int
	Name string
	Cod  int
}

// WeatherSet represents the current weather at a set of specific locations.
//
// It is returned by the WeatherSetBy* methods of Client.
type WeatherSet struct {
	Cnt     int
	Weather []Weather `json:"list"`
}

// Forecast represents the weather forecast for a specific location.
//
// It is returned by the ForecastBy* methods of Client.
type Forecast struct {
	Cod  int
	City struct {
		Id    int
		Name  string
		Coord struct {
			Lon float64
			Lat float64
		}
		Country string
	}
	Cnt          int
	ForecastLine []forecastLine
}

// Client represents an OpenWeatherMap API client.
type Client struct {
	key     string
	baseURL string
}

// NewClient returns a new Client given an API key.
//
// Pass an empty string as the key argument to use the client without an API key.
func NewClient(key string) *Client {
	return &Client{key, "http://api.openweathermap.org/data/2.5/"}
}

func (c *Client) data(url string) (data []byte, err error) {
	res, err := http.Get(url)
	if err != nil {
		err = errors.New("owm: error while getting weather data")
		return
	}
	data, err = ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		err = errors.New("owm: error while getting weather data")
		return
	}
	return
}

func (c *Client) weather(url string) (w Weather, err error) {
	if c.key != "" {
		url += "&APPID=" + c.key
	}
	data, err := c.data(url)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
		return
	}
	err = json.Unmarshal(data, &w)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
		return
	}
	return
}

// WeatherByName decodes the current weather given the city name and the units.
func (c *Client) WeatherByName(name string, units string) (w Weather, err error) {
	w, err = c.weather(c.baseURL +
		"weather" +
		"?q=" + name +
		"&units=" + units)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
	}
	return
}

// WeatherById decodes the current weather given the city id and the units.
func (c *Client) WeatherById(id int, units string) (w Weather, err error) {
	w, err = c.weather(c.baseURL +
		"weather" +
		"?id=" + strconv.Itoa(id) +
		"&units=" + units)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
	}
	return
}

// WeatherByCoord decodes the current weather given the city coordinates and the units.
func (c *Client) WeatherByCoord(lat float64, lon float64, units string) (w Weather, err error) {
	w, err = c.weather(c.baseURL +
		"weather" +
		"?lat=" + strconv.FormatFloat(lat, 'f', 2, 64) +
		"&lon=" + strconv.FormatFloat(lon, 'f', 2, 64) +
		"&units=" + units)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
	}
	return
}

func (c *Client) weatherSet(url string) (ws WeatherSet, err error) {
	if c.key != "" {
		url += "&APPID=" + c.key
	}
	data, err := c.data(url)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
		return
	}
	err = json.Unmarshal(data, &ws)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
		return
	}
	return
}

// WeatherSetByIds decodes the current weather given a slice of city ids and the units.
func (c *Client) WeatherSetByIds(id []int, units string) (ws WeatherSet, err error) {
	var ids string
	for i := range id {
		ids += strconv.Itoa(id[i]) + ","
	}
	ws, err = c.weatherSet(c.baseURL +
		"group" +
		"?id=" + ids[:len(ids)-1] +
		"&units=" + units)
	if err != nil {
		err = errors.New("owm: error while decoding weather")
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go.net/websocket"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"sync"
)

var _ = fmt.Printf

type PadServer struct {
	docs map[string]*Doc
	mu   sync.Mutex
}

type Doc struct {
	diffs []Diff
	mu sync.Mutex
	listeners []chan Diff
}

type Diff string

// PAD SERVER

func MakePadServer() *PadServer {
	ps := &PadServer{}
	ps.docs = make(map[string]*Doc)
	return ps
}

// DOC

func NewDoc() *Doc {
	doc := &Doc{}
	doc.diffs = make([]Diff, 1)
	doc.listeners = make([]chan Diff, 0)
	return doc
}

func (doc *Doc) getDiff(id int) chan Diff {
	doc.mu.Lock()
	defer doc.mu.Unlock()
	c := make(chan Diff)
	if (id < len(doc.diffs)) {
		go func() {c <- doc.diffs[id]}()
	} else {
		doc.listeners = append(doc.listeners, c)
	}
	return c
}

func (doc *Doc) putDiff(diff Diff) {
	doc.mu.Lock()
	defer doc.mu.Unlock()
	for _, c := range doc.listeners {
		c <- diff
	}
	doc.listeners = make([]chan Diff, 0)
	doc.diffs = append(doc.diffs, diff)
}

// HANDLERS

func (ps *PadServer) diffPutter(w http.ResponseWriter, r *http.Request) {
	ps.mu.Lock()
	defer ps.mu.Unlock()
	docID := r.PostFormValue("doc-id")
	diff := Diff(r.PostFormValue("diff"))
	doc, ok := ps.docs[docID]
	if !ok {
		ps.docs[docID] = NewDoc()
		doc = ps.docs[docID]
	}
	doc.putDiff(diff)
}

func (ps *PadServer) diffGetter(ws *websocket.Conn) {
	var docID string
	websocket.Message.Receive(ws, &docID)
	var msg string
	websocket.Message.Receive(ws, &msg)
	nextDiff, _ := strconv.Atoi(msg)

	ps.mu.Lock()
	doc, ok := ps.docs[docID]
	if !ok {
		ps.docs[docID] = NewDoc()
		doc = ps.docs[docID]
	}
	ps.mu.Unlock()

	for {
		c := doc.getDiff(nextDiff)
		diff := <- c
		err := websocket.Message.Send(ws, string(diff))
		if err != nil {
			return
		}
		nextDiff += 1
	}
}

func (ps *PadServer) docHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Content-Type", "text/html")
	if html, err := ioutil.ReadFile("./index.html"); err == nil {
		w.Write(html)
	} else {
		panic("unknown file")
	}
}

func (ps *PadServer) Start() {
	http.HandleFunc("/diffs/put", ps.diffPutter)
	http.Handle("/diffs/get", websocket.Handler(ps.diffGetter))
	http.HandleFunc("/docs/", ps.docHandler)
	http.ListenAndServe(":8080", nil)
}

func main() {
	ps := MakePadServer()
	ps.Start()
}
<commit_msg>buffer channel rather than spin off goroutine<commit_after>package main

import (
	"code.google.com/p/go.net/websocket"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"sync"
)

var _ = fmt.Printf

type PadServer struct {
	docs map[string]*Doc
	mu   sync.Mutex
}

type Doc struct {
	diffs []Diff
	mu sync.Mutex
	listeners []chan Diff
}

type Diff string

// PAD SERVER

func MakePadServer() *PadServer {
	ps := &PadServer{}
	ps.docs = make(map[string]*Doc)
	return ps
}

// DOC

func NewDoc() *Doc {
	doc := &Doc{}
	doc.diffs = make([]Diff, 1)
	doc.listeners = make([]chan Diff, 0)
	return doc
}

func (doc *Doc) getDiff(id int) chan Diff {
	doc.mu.Lock()
	defer doc.mu.Unlock()
	c := make(chan Diff, 1)
	if (id < len(doc.diffs)) {
		c <- doc.diffs[id]
	} else {
		doc.listeners = append(doc.listeners, c)
	}
	return c
}

func (doc *Doc) putDiff(diff Diff) {
	doc.mu.Lock()
	defer doc.mu.Unlock()
	for _, c := range doc.listeners {
		c <- diff
	}
	doc.listeners = make([]chan Diff, 0)
	doc.diffs = append(doc.diffs, diff)
}

// HANDLERS

func (ps *PadServer) diffPutter(w http.ResponseWriter, r *http.Request) {
	ps.mu.Lock()
	defer ps.mu.Unlock()
	docID := r.PostFormValue("doc-id")
	diff := Diff(r.PostFormValue("diff"))
	doc, ok := ps.docs[docID]
	if !ok {
		ps.docs[docID] = NewDoc()
		doc = ps.docs[docID]
	}
	doc.putDiff(diff)
}

func (ps *PadServer) diffGetter(ws *websocket.Conn) {
	var docID string
	websocket.Message.Receive(ws, &docID)
	var msg string
	websocket.Message.Receive(ws, &msg)
	nextDiff, _ := strconv.Atoi(msg)

	ps.mu.Lock()
	doc, ok := ps.docs[docID]
	if !ok {
		ps.docs[docID] = NewDoc()
		doc = ps.docs[docID]
	}
	ps.mu.Unlock()

	for {
		c := doc.getDiff(nextDiff)
		diff := <- c
		err := websocket.Message.Send(ws, string(diff))
		if err != nil {
			return
		}
		nextDiff += 1
	}
}

func (ps *PadServer) docHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Content-Type", "text/html")
	if html, err := ioutil.ReadFile("./index.html"); err == nil {
		w.Write(html)
	} else {
		panic("unknown file")
	}
}

func (ps *PadServer) Start() {
	http.HandleFunc("/diffs/put", ps.diffPutter)
	http.Handle("/diffs/get", websocket.Handler(ps.diffGetter))
	http.HandleFunc("/docs/", ps.docHandler)
	http.ListenAndServe(":8080", nil)
}

func main() {
	ps := MakePadServer()
	ps.Start()
}
<|endoftext|>


<commit_before>package speed

// pcpString defines a string that also has a memory offset containing
// the location where it will be written
type pcpString struct {
	val    string
	offset int
}

// newpcpString creates a new instance of a pcpString from a raw string
func newpcpString(s string) *pcpString {
	return &pcpString{s, 0}
}

func (s *pcpString) String() string { return s.val }
<commit_msg>pcp: remove String() as pcpString is internal<commit_after>package speed

// pcpString defines a string that also has a memory offset containing
// the location where it will be written
type pcpString struct {
	val    string
	offset int
}

// newpcpString creates a new instance of a pcpString from a raw string
func newpcpString(s string) *pcpString {
	return &pcpString{s, 0}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"sync"

	"github.com/Masterminds/vcs"
	"github.com/whitecypher/vgo/lib/native"
	"gopkg.in/yaml.v2"
)

// Version compatibility string e.g. "~1.0.0" or "1.*"
type Version string

// NewPkg creates and initializes a Pkg
func NewPkg(name string) *Pkg {
	return &Pkg{
		Name: name,
	}
}

// Pkg ...
type Pkg struct {
	sync.Mutex `yaml:"-"`

	meta         *build.Package `yaml:"-"`
	repo         vcs.Repo       `yaml:"-"`
	parent       *Pkg           `yaml:"-"`
	hasManifest  bool           `yaml:"-"`
	manifestFile string         `yaml:"-"`
	installed    bool           `yaml:"-"`
	path         string         `yaml:"-"`
	installPath  string         `yaml:"-"`

	Name         string  `yaml:"pkg,omitempty"`
	Version      Version `yaml:"ver,omitempty"`
	Reference    string  `yaml:"ref,omitempty"`
	Dependencies []*Pkg  `yaml:"deps,omitempty"`
	URL          string  `yaml:"url,omitempty"`
}

// Meta get the package meta data using the go/build internal package profiler
func (p *Pkg) Meta() *build.Package {
	if p.meta != nil {
		return p.meta
	}
	m, err := build.Import(p.FQN(), cwd, build.ImportMode(0))
	if err != nil {
		Logf("Unable to import package %s with error %s", p.FQN(), err.Error())
	}
	p.meta = m
	return m
}

// FQN resolves the fully qualified package name. This is the equivalent to the name that go uses dependant on it's context.
func (p Pkg) FQN() string {
	// if p.IsInGoPath() && p.parent != nil {
	// 	return filepath.Join(p.parent.FQN(), "vendor", p.Name)
	// }
	if p.Name == "" {
		return "."
	}
	return p.Name
}

// Root returns the topmost package (typically this is the application package)
func (p *Pkg) Root() *Pkg {
	if p.parent == nil {
		return p
	}
	return p.parent.Root()
}

// IsInGoPath returns whether project and all vendored packages are contained in the $GOPATH
func (p Pkg) IsInGoPath() bool {
	if p.parent != nil {
		return p.parent.IsInGoPath()
	}
	return strings.HasPrefix(p.path, gosrcpath)
}

func resolveImportsRecursive(path string, imports []string) []string {
	r := []string{}
	for _, i := range imports {
		// Skip native packages
		if native.IsNative(i) {
			continue
		}
		// check subpackages for dependencies
		if strings.HasPrefix(i, path) { //  && !strings.HasPrefix(i, filepath.Join(path, "vendor"))
			m, err := build.Import(i, cwd, build.ImportMode(0))
			if err != nil {
				fmt.Println(err.Error())
			} else {
				r = append(r, resolveImportsRecursive(i, m.Imports)...)
			}
		}
		name := repoName(i)
		if name == path {
			continue
		}
		r = append(r, name)
	}
	// return only unique imports
	u := []string{}
	m := map[string]bool{}
	for _, i := range r {
		if _, ok := m[i]; ok {
			continue
		}
		m[i] = true
		u = append(u, i)
	}
	sort.Strings(u)
	return u
}

// Init ...
func (p *Pkg) Init(meta *build.Package) {
	p.Lock()
	p.path = meta.Dir
	if p.IsInGoPath() {
		p.Name = repoName(meta.ImportPath)
	}
	p.Unlock()

	// i := resolveImportsRecursive(p.Name, meta.Imports)
	// js, _ := json.MarshalIndent(i, "", "  ")
	// fmt.Println(string(js))
	// os.Exit(0)

	wg := sync.WaitGroup{}
	for _, i := range resolveImportsRecursive(p.Name, meta.Imports) {
		name := repoName(i)
		// Skip subpackages
		if strings.HasPrefix(name, p.Name) {
			continue
		}

		// Reuse packages already added to the project
		dep := p.Find(name)
		if dep == nil {
			fmt.Println("adding", name)
			dep = NewPkg(name)
			dep.parent = p
			p.Lock()
			p.Dependencies = append(p.Dependencies, dep)
			p.Unlock()
		} else {
			// check the version compatibility. We might need to create a broken diamond here.
		}
		wg.Add(1)
		go func() {
			dep.Install()
			wg.Done()
		}()
	}
	wg.Wait()
	// p.InstallDeps()
}

// LoadManifest ...
func (p *Pkg) LoadManifest() error {
	p.hasManifest = false
	if len(p.manifestFile) == 0 {
		p.manifestFile = "vgo.yaml"
	}
	fmt.Println("Loading manifest", filepath.Join(p.path, p.manifestFile))
	data, err := ioutil.ReadFile(filepath.Join(p.path, p.manifestFile))
	if err != nil {
		fmt.Println("Unable to load manifest with error", err.Error())
		return err
	}
	p.Lock()
	err = yaml.Unmarshal(data, p)
	p.Unlock()
	if err != nil {
		fmt.Println("Unable to unmarshal manifest with error", err.Error())
		return err
	}
	p.hasManifest = true
	p.updateDepsParents()
	return nil
}

// updateDepsParents resolves the parent (caller) pkg for all dependencies recursively
func (p *Pkg) updateDepsParents() {
	for _, d := range p.Dependencies {
		d.Lock()
		d.parent = p
		d.Unlock()
		d.updateDepsParents()
	}
}

// Find looks for a package in it's dependencies or parents dependencies recursively
func (p Pkg) Find(name string) *Pkg {
	fmt.Println("find", name)
	for _, d := range p.Dependencies {
		fmt.Println("find - check against", d.Name)
		if (*d).Name == name {
			return d
		}
	}
	if p.parent != nil {
		return (*p.parent).Find(name)
	}
	return nil
}

// SaveManifest ...
func (p Pkg) SaveManifest() error {
	data, err := yaml.Marshal(p)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filepath.Join(p.path, p.manifestFile), data, os.FileMode(0644))
	if err != nil {
		return err
	}
	return nil
}

// Install the package
func (p *Pkg) Install() error {
	if p.parent == nil {
		// don't touch the current working directory
		return nil
	}
	repo, err := p.VCS()
	if repo == nil {
		fmt.Println(err)
		return fmt.Errorf("Could not resolve repo for %s with error %s", p.Name, err)
	}
	p.Lock()
	p.installed = repo.CheckLocal()
	p.path = repo.LocalPath()
	if !p.installed {
		Logf("Installing %s", p.Name)
		err = repo.Get()
		if err != nil {
			Logf("Failed to install %s with error %s", p.Name, err.Error())
		}
	}
	p.Unlock()
	p.Checkout()
	p.LoadManifest()
	if !p.hasManifest {
		p.Init(p.Meta())
	}
	return err
}

// InstallDeps install package dependencies
func (p *Pkg) InstallDeps() (err error) {
	wg := sync.WaitGroup{}
	for _, dep := range p.Dependencies {
		d := dep
		wg.Add(1)
		go func() {
			err = d.Install()
			if err != nil {
				Logf("Package %s could not be installed with error", err.Error())
			}
			wg.Done()
		}()

	}
	wg.Wait()
	return
}

// RepoPath path to the package
func (p Pkg) RepoPath() string {
	dir := p.installPath
	if len(dir) == 0 {
		dir = installPath
	}
	return path.Join(dir, p.Name)
}

// Checkout switches the package version to the commit nearest maching the Compat string
func (p *Pkg) Checkout() error {
	if p.parent == nil {
		// don't touch the current working directory
		return nil
	}
	repo, err := p.VCS()
	if err != nil {
		return err
	}
	p.Lock()
	version := p.Version
	if p.Reference != "" {
		version = Version(p.Reference)
	}
	p.installed = repo.CheckLocal()
	if p.installed {
		if v, err := repo.Version(); err == nil && p.Reference == v {
			p.Unlock()
			Logf("%s OK %s", p.Reference, p.Name)
			return nil
		}
		err = repo.UpdateVersion(string(version))
		if err != nil {
			p.Unlock()
			return err
		}
	}
	p.Reference, err = repo.Version()
	p.path = repo.LocalPath()
	p.Unlock()
	Logf("Switching %s to %s", p.Name, version)
	p.LoadManifest()
	if !p.hasManifest {
		p.Init(p.Meta())
	}
	return err
}

// VCS resolves the vcs.Repo for the Pkg
func (p *Pkg) VCS() (repo vcs.Repo, err error) {
	p.Lock()
	defer p.Unlock()
	if p.repo != nil {
		repo = p.repo
		return
	}
	repoType := p.RepoType()
	repoURL := p.RepoURL()
	repoPath := p.RepoPath()
	switch repoType {
	case vcs.Git:
		repo, err = vcs.NewGitRepo(repoURL, repoPath)
	case vcs.Bzr:
		repo, err = vcs.NewBzrRepo(repoURL, repoPath)
	case vcs.Hg:
		repo, err = vcs.NewHgRepo(repoURL, repoPath)
	case vcs.Svn:
		repo, err = vcs.NewSvnRepo(repoURL, repoPath)
	}
	p.repo = repo
	return
}

// RepoURL creates the repo url from the package import path
func (p Pkg) RepoURL() string {
	if p.URL != "" {
		return p.URL
	}
	// If it's already installed in vendor or gopath, grab the url from there
	repo := repoFromPath(p.RepoPath(), filepath.Join(gopath, "src", p.Name))
	if repo != nil {
		return repo.Remote()
	}
	// Fallback to resolving the path from the package import path
	// Add more cases as needed/requested
	parts := strings.Split(p.Name, "/")
	switch parts[0] {
	case "github.com":
		return fmt.Sprintf("git@github.com:%s.git", strings.Join(parts[1:3], "/"))
	}
	return ""
}

// RepoType attempts to resolve the repository type of the package by it's name
func (p Pkg) RepoType() vcs.Type {
	// If it's already installed in vendor or gopath, grab the type from there
	repo := repoFromPath(p.RepoPath(), filepath.Join(gopath, "src", p.Name))
	if repo != nil {
		return repo.Vcs()
	}
	// Fallback to resolving the type from the package import path
	// Add more cases as needed/requested
	parts := strings.Split(p.Name, "/")
	switch parts[0] {
	case "github.com":
		return vcs.Git
	}
	return vcs.NoVCS
}

// MarshalYAML implements yaml.Marsheler to prevent duplicate storage of nested packages with vgo.yaml
func (p Pkg) MarshalYAML() (interface{}, error) {
	copy := p
	if copy.hasManifest && copy.parent != nil {
		copy.Dependencies = []*Pkg{}
	}
	return copy, nil
}

// repoFromPath attempts to resolve the vcs.Repo from any of the given paths in sequence.
func repoFromPath(paths ...string) vcs.Repo {
	for _, path := range paths {
		repoType, err := vcs.DetectVcsFromFS(path)
		if err != nil {
			continue
		}
		var repo vcs.Repo
		switch repoType {
		case vcs.Git:
			repo, err = vcs.NewGitRepo("", path)
		case vcs.Bzr:
			repo, err = vcs.NewBzrRepo("", path)
		case vcs.Hg:
			repo, err = vcs.NewHgRepo("", path)
		case vcs.Svn:
			repo, err = vcs.NewSvnRepo("", path)
		}
		if err != nil {
			continue
		}
		return repo
	}
	return nil
}
<commit_msg>add root fqn to package import path<commit_after>package main

import (
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"sync"

	"github.com/Masterminds/vcs"
	"github.com/whitecypher/vgo/lib/native"
	"gopkg.in/yaml.v2"
)

// Version compatibility string e.g. "~1.0.0" or "1.*"
type Version string

// NewPkg creates and initializes a Pkg
func NewPkg(name string) *Pkg {
	return &Pkg{
		Name: name,
	}
}

// Pkg ...
type Pkg struct {
	sync.Mutex `yaml:"-"`

	meta         *build.Package `yaml:"-"`
	repo         vcs.Repo       `yaml:"-"`
	parent       *Pkg           `yaml:"-"`
	hasManifest  bool           `yaml:"-"`
	manifestFile string         `yaml:"-"`
	installed    bool           `yaml:"-"`
	path         string         `yaml:"-"`
	installPath  string         `yaml:"-"`

	Name         string  `yaml:"pkg,omitempty"`
	Version      Version `yaml:"ver,omitempty"`
	Reference    string  `yaml:"ref,omitempty"`
	Dependencies []*Pkg  `yaml:"deps,omitempty"`
	URL          string  `yaml:"url,omitempty"`
}

// Meta get the package meta data using the go/build internal package profiler
func (p *Pkg) Meta() *build.Package {
	if p.meta != nil {
		return p.meta
	}
	m, err := build.Import(p.FQN(), cwd, build.ImportMode(0))
	if err != nil {
		Logf("Unable to import package %s with error %s", p.FQN(), err.Error())
	}
	p.meta = m
	return m
}

// FQN resolves the fully qualified package name. This is the equivalent to the name that go uses dependant on it's context.
func (p Pkg) FQN() string {
	root := &p
	for root.parent != nil {
		root = root.parent
	}
	if p.IsInGoPath() && root != &p {
		return filepath.Join(root.FQN(), "vendor", p.Name)
	}
	if p.Name == "" {
		return "."
	}
	return p.Name
}

// Root returns the topmost package (typically this is the application package)
func (p *Pkg) Root() *Pkg {
	if p.parent == nil {
		return p
	}
	return p.parent.Root()
}

// IsInGoPath returns whether project and all vendored packages are contained in the $GOPATH
func (p Pkg) IsInGoPath() bool {
	if p.parent != nil {
		return p.parent.IsInGoPath()
	}
	return strings.HasPrefix(p.path, gosrcpath)
}

func resolveImportsRecursive(path string, imports []string) []string {
	r := []string{}
	for _, i := range imports {
		// Skip native packages
		if native.IsNative(i) {
			continue
		}
		// check subpackages for dependencies
		if strings.HasPrefix(i, path) { //  && !strings.HasPrefix(i, filepath.Join(path, "vendor"))
			m, err := build.Import(i, cwd, build.ImportMode(0))
			if err != nil {
				fmt.Println(err.Error())
			} else {
				r = append(r, resolveImportsRecursive(i, m.Imports)...)
			}
		}
		name := repoName(i)
		if name == path {
			continue
		}
		r = append(r, name)
	}
	// return only unique imports
	u := []string{}
	m := map[string]bool{}
	for _, i := range r {
		if _, ok := m[i]; ok {
			continue
		}
		m[i] = true
		u = append(u, i)
	}
	sort.Strings(u)
	return u
}

// Init ...
func (p *Pkg) Init(meta *build.Package) {
	p.Lock()
	p.path = meta.Dir
	if p.IsInGoPath() {
		p.Name = repoName(meta.ImportPath)
	}
	p.Unlock()

	// i := resolveImportsRecursive(p.Name, meta.Imports)
	// js, _ := json.MarshalIndent(i, "", "  ")
	// fmt.Println(string(js))
	// os.Exit(0)

	wg := sync.WaitGroup{}
	for _, i := range resolveImportsRecursive(p.Name, meta.Imports) {
		name := repoName(i)
		// Skip subpackages
		if strings.HasPrefix(name, p.Name) {
			continue
		}

		// Reuse packages already added to the project
		dep := p.Find(name)
		if dep == nil {
			fmt.Println("adding", name)
			dep = NewPkg(name)
			dep.parent = p
			p.Lock()
			p.Dependencies = append(p.Dependencies, dep)
			p.Unlock()
		} else {
			// check the version compatibility. We might need to create a broken diamond here.
		}
		wg.Add(1)
		go func() {
			dep.Install()
			wg.Done()
		}()
	}
	wg.Wait()
	// p.InstallDeps()
}

// LoadManifest ...
func (p *Pkg) LoadManifest() error {
	p.hasManifest = false
	if len(p.manifestFile) == 0 {
		p.manifestFile = "vgo.yaml"
	}
	fmt.Println("Loading manifest", filepath.Join(p.path, p.manifestFile))
	data, err := ioutil.ReadFile(filepath.Join(p.path, p.manifestFile))
	if err != nil {
		fmt.Println("Unable to load manifest with error", err.Error())
		return err
	}
	p.Lock()
	err = yaml.Unmarshal(data, p)
	p.Unlock()
	if err != nil {
		fmt.Println("Unable to unmarshal manifest with error", err.Error())
		return err
	}
	p.hasManifest = true
	p.updateDepsParents()
	return nil
}

// updateDepsParents resolves the parent (caller) pkg for all dependencies recursively
func (p *Pkg) updateDepsParents() {
	for _, d := range p.Dependencies {
		d.Lock()
		d.parent = p
		d.Unlock()
		d.updateDepsParents()
	}
}

// Find looks for a package in it's dependencies or parents dependencies recursively
func (p Pkg) Find(name string) *Pkg {
	fmt.Println("find", name)
	for _, d := range p.Dependencies {
		fmt.Println("find - check against", d.Name)
		if (*d).Name == name {
			return d
		}
	}
	if p.parent != nil {
		return (*p.parent).Find(name)
	}
	return nil
}

// SaveManifest ...
func (p Pkg) SaveManifest() error {
	data, err := yaml.Marshal(p)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filepath.Join(p.path, p.manifestFile), data, os.FileMode(0644))
	if err != nil {
		return err
	}
	return nil
}

// Install the package
func (p *Pkg) Install() error {
	if p.parent == nil {
		// don't touch the current working directory
		return nil
	}
	repo, err := p.VCS()
	if repo == nil {
		fmt.Println(err)
		return fmt.Errorf("Could not resolve repo for %s with error %s", p.Name, err)
	}
	p.Lock()
	p.installed = repo.CheckLocal()
	p.path = repo.LocalPath()
	if !p.installed {
		Logf("Installing %s", p.Name)
		err = repo.Get()
		if err != nil {
			Logf("Failed to install %s with error %s", p.Name, err.Error())
		}
	}
	p.Unlock()
	p.Checkout()
	p.LoadManifest()
	if !p.hasManifest {
		p.Init(p.Meta())
	}
	return err
}

// InstallDeps install package dependencies
func (p *Pkg) InstallDeps() (err error) {
	wg := sync.WaitGroup{}
	for _, dep := range p.Dependencies {
		d := dep
		wg.Add(1)
		go func() {
			err = d.Install()
			if err != nil {
				Logf("Package %s could not be installed with error", err.Error())
			}
			wg.Done()
		}()

	}
	wg.Wait()
	return
}

// RepoPath path to the package
func (p Pkg) RepoPath() string {
	dir := p.installPath
	if len(dir) == 0 {
		dir = installPath
	}
	return path.Join(dir, p.Name)
}

// Checkout switches the package version to the commit nearest maching the Compat string
func (p *Pkg) Checkout() error {
	if p.parent == nil {
		// don't touch the current working directory
		return nil
	}
	repo, err := p.VCS()
	if err != nil {
		return err
	}
	p.Lock()
	version := p.Version
	if p.Reference != "" {
		version = Version(p.Reference)
	}
	p.installed = repo.CheckLocal()
	if p.installed {
		if v, err := repo.Version(); err == nil && p.Reference == v {
			p.Unlock()
			Logf("%s OK %s", p.Reference, p.Name)
			return nil
		}
		err = repo.UpdateVersion(string(version))
		if err != nil {
			p.Unlock()
			return err
		}
	}
	p.Reference, err = repo.Version()
	p.path = repo.LocalPath()
	p.Unlock()
	Logf("Switching %s to %s", p.Name, version)
	p.LoadManifest()
	if !p.hasManifest {
		p.Init(p.Meta())
	}
	return err
}

// VCS resolves the vcs.Repo for the Pkg
func (p *Pkg) VCS() (repo vcs.Repo, err error) {
	p.Lock()
	defer p.Unlock()
	if p.repo != nil {
		repo = p.repo
		return
	}
	repoType := p.RepoType()
	repoURL := p.RepoURL()
	repoPath := p.RepoPath()
	switch repoType {
	case vcs.Git:
		repo, err = vcs.NewGitRepo(repoURL, repoPath)
	case vcs.Bzr:
		repo, err = vcs.NewBzrRepo(repoURL, repoPath)
	case vcs.Hg:
		repo, err = vcs.NewHgRepo(repoURL, repoPath)
	case vcs.Svn:
		repo, err = vcs.NewSvnRepo(repoURL, repoPath)
	}
	p.repo = repo
	return
}

// RepoURL creates the repo url from the package import path
func (p Pkg) RepoURL() string {
	if p.URL != "" {
		return p.URL
	}
	// If it's already installed in vendor or gopath, grab the url from there
	repo := repoFromPath(p.RepoPath(), filepath.Join(gopath, "src", p.Name))
	if repo != nil {
		return repo.Remote()
	}
	// Fallback to resolving the path from the package import path
	// Add more cases as needed/requested
	parts := strings.Split(p.Name, "/")
	switch parts[0] {
	case "github.com":
		return fmt.Sprintf("git@github.com:%s.git", strings.Join(parts[1:3], "/"))
	}
	return ""
}

// RepoType attempts to resolve the repository type of the package by it's name
func (p Pkg) RepoType() vcs.Type {
	// If it's already installed in vendor or gopath, grab the type from there
	repo := repoFromPath(p.RepoPath(), filepath.Join(gopath, "src", p.Name))
	if repo != nil {
		return repo.Vcs()
	}
	// Fallback to resolving the type from the package import path
	// Add more cases as needed/requested
	parts := strings.Split(p.Name, "/")
	switch parts[0] {
	case "github.com":
		return vcs.Git
	}
	return vcs.NoVCS
}

// MarshalYAML implements yaml.Marsheler to prevent duplicate storage of nested packages with vgo.yaml
func (p Pkg) MarshalYAML() (interface{}, error) {
	copy := p
	if copy.hasManifest && copy.parent != nil {
		copy.Dependencies = []*Pkg{}
	}
	return copy, nil
}

// repoFromPath attempts to resolve the vcs.Repo from any of the given paths in sequence.
func repoFromPath(paths ...string) vcs.Repo {
	for _, path := range paths {
		repoType, err := vcs.DetectVcsFromFS(path)
		if err != nil {
			continue
		}
		var repo vcs.Repo
		switch repoType {
		case vcs.Git:
			repo, err = vcs.NewGitRepo("", path)
		case vcs.Bzr:
			repo, err = vcs.NewBzrRepo("", path)
		case vcs.Hg:
			repo, err = vcs.NewHgRepo("", path)
		case vcs.Svn:
			repo, err = vcs.NewSvnRepo("", path)
		}
		if err != nil {
			continue
		}
		return repo
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import "flag"
import "fmt"
import "log"
import "os"
import "os/exec"
import "path"
import "strings"
import "time"

/*

# the plan

qst: run things quickly

qst - detects the current project type and runs it
qst hello.rb - runs `ruby hello.rb`
qst hello.go - compiles & runs hello.go

qst -watch hello.go - watches changes to hello.go and recompiles and runs it on changes

*/

var mappings = map[string]func(string) string{
	".go": func(name string) string {
		return fmt.Sprintf("go build %s && ./%s", name, strings.TrimSuffix(name, path.Ext(name)))
	},
}

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s <file>\n", os.Args[0])
		flag.PrintDefaults()
	}

	flag.Parse()
	args := flag.Args()

	if len(args) < 1 {
		flag.Usage()
		os.Exit(1)
	}

	file := os.Args[1]
	if !isFile(file) {
		fmt.Fprintf(os.Stderr, "Error: %s is not a file.\n", file)
		os.Exit(1)
	}

	ext := path.Ext(file)
	fn, found := mappings[ext]
	if found {
		runAndWatch(file, fn(file))
	} else {
		fmt.Fprintf(os.Stderr, "Error: No command defined for %s files", ext)
		os.Exit(1)
	}
}

func runAndWatch(file string, cmd string) {
	// run command, if file changes (mtime) restart command
	// for now: run command until it exits, wait a bit, run again
	runShellCmd(cmd)
	time.Sleep(1 * time.Second)
	runAndWatch(file, cmd)
}

func runShellCmd(cmd string) {
	log.Printf("Running: `%s'", cmd)
	output, err := exec.Command("sh", "-c", cmd).CombinedOutput()
	os.Stderr.Write(output)
	log.Printf("Error running command: %s\n", err.Error())
}

func isFile(file string) bool {
	info, err := os.Stat(file)
	return err == nil && !info.IsDir()
}
<commit_msg>rerun if mtime changes.<commit_after>package main

import "flag"
import "fmt"
import "log"
import "os"
import "os/exec"
import "path"
import "strings"
import "time"

/*

# the plan

qst: run things quickly

qst - detects the current project type and runs it
qst hello.rb - runs `ruby hello.rb`
qst hello.go - compiles & runs hello.go

qst -watch hello.go - watches changes to hello.go and recompiles and runs it on changes

*/

var mappings = map[string]func(string) string{
	".go": func(name string) string {
		return fmt.Sprintf("go build %s && ./%s", name, strings.TrimSuffix(name, path.Ext(name)))
	},
}

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s <file>\n", os.Args[0])
		flag.PrintDefaults()
	}

	flag.Parse()
	args := flag.Args()

	if len(args) < 1 {
		flag.Usage()
		os.Exit(1)
	}

	file := os.Args[1]
	if !isFile(file) {
		fmt.Fprintf(os.Stderr, "Error: %s is not a file.\n", file)
		os.Exit(1)
	}

	ext := path.Ext(file)
	fn, found := mappings[ext]
	if found {
		runAndWatch(file, fn(file))
	} else {
		fmt.Fprintf(os.Stderr, "Error: No command defined for %s files", ext)
		os.Exit(1)
	}
}

func runAndWatch(file string, cmd string) {
	// run command, if file changes (mtime) restart command
	lastMtime := time.Unix(0, 0)
	for {
		info, err := os.Stat(file)
		if err != nil {
			log.Fatalf("Error: %s disappeared, exiting.", file)
		}

		mtime := info.ModTime()
		if mtime.After(lastMtime) {
			log.Printf("%s changed, rerunning", file)
			runShellCmd(cmd)
		}

		lastMtime = mtime
		time.Sleep(1 * time.Second)
	}
}

func runShellCmd(cmd string) {
	log.Printf("Running: `%s'", cmd)
	output, err := exec.Command("sh", "-c", cmd).CombinedOutput()
	os.Stderr.Write(output)
	log.Printf("Error running command: %s\n", err.Error())
}

func isFile(file string) bool {
	info, err := os.Stat(file)
	return err == nil && !info.IsDir()
}
<|endoftext|>