<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 (
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
)

var id3v2Genres = [...]string{
	"Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge",
	"Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B",
	"Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska",
	"Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient",
	"Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical",
	"Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel",
	"Noise", "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative",
	"Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic",
	"Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk",
	"Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta",
	"Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American",
	"Cabaret", "New Wave", "Psychedelic", "Rave", "Showtunes", "Trailer",
	"Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro",
	"Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock",
	"National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival",
	"Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock",
	"Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
	"Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
	"Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
	"Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
	"Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
	"Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall",
	"Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie",
	"Britpop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap",
	"Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian",
	"Christian Rock ", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop",
	"Synthpop",
}

// id3v2Header is a type which represents an ID3v2 tag header.
type id3v2Header struct {
	Version           Format
	Unsynchronisation bool
	ExtendedHeader    bool
	Experimental      bool
	Size              int
}

// readID3v2Header reads the ID3v2 header from the given io.Reader.
func readID3v2Header(r io.Reader) (*id3v2Header, error) {
	b, err := readBytes(r, 10)
	if err != nil {
		return nil, fmt.Errorf("expected to read 10 bytes (ID3v2Header): %v", err)
	}

	if string(b[0:3]) != "ID3" {
		return nil, fmt.Errorf("expected to read \"ID3\"")
	}

	b = b[3:]
	var vers Format
	switch uint(b[0]) {
	case 2:
		vers = ID3v2_2
	case 3:
		vers = ID3v2_3
	case 4:
		vers = ID3v2_4
	case 0, 1:
		fallthrough
	default:
		return nil, fmt.Errorf("ID3 version: %v, expected: 2, 3 or 4", uint(b[0]))
	}

	// NB: We ignore b[1] (the revision) as we don't currently rely on it.
	return &id3v2Header{
		Version:           vers,
		Unsynchronisation: getBit(b[2], 7),
		ExtendedHeader:    getBit(b[2], 6),
		Experimental:      getBit(b[2], 5),
		Size:              get7BitChunkedInt(b[3:7]),
	}, nil
}

// id3v2FrameFlags is a type which represents the flags which can be set on an ID3v2 frame.
type id3v2FrameFlags struct {
	// Message (ID3 2.3.0 and 2.4.0)
	TagAlterPreservation  bool
	FileAlterPreservation bool
	ReadOnly              bool

	// Format (ID3 2.3.0 and 2.4.0)
	Compression   bool
	Encryption    bool
	GroupIdentity bool
	// ID3 2.4.0 only (see http://id3.org/id3v2.4.0-structure sec 4.1)
	Unsynchronisation   bool
	DataLengthIndicator bool
}

func readID3v23FrameFlags(r io.Reader) (*id3v2FrameFlags, error) {
	b, err := readBytes(r, 2)
	if err != nil {
		return nil, err
	}

	msg := b[0]
	fmt := b[1]

	return &id3v2FrameFlags{
		TagAlterPreservation:  getBit(msg, 7),
		FileAlterPreservation: getBit(msg, 6),
		ReadOnly:              getBit(msg, 5),
		Compression:           getBit(fmt, 7),
		Encryption:            getBit(fmt, 6),
		GroupIdentity:         getBit(fmt, 5),
	}, nil
}

func readID3v24FrameFlags(r io.Reader) (*id3v2FrameFlags, error) {
	b, err := readBytes(r, 2)
	if err != nil {
		return nil, err
	}

	msg := b[0]
	fmt := b[1]

	return &id3v2FrameFlags{
		TagAlterPreservation:  getBit(msg, 6),
		FileAlterPreservation: getBit(msg, 5),
		ReadOnly:              getBit(msg, 4),
		GroupIdentity:         getBit(fmt, 6),
		Compression:           getBit(fmt, 3),
		Encryption:            getBit(fmt, 2),
		Unsynchronisation:     getBit(fmt, 1),
		DataLengthIndicator:   getBit(fmt, 0),
	}, nil

}

func readID3v2_2FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) {
	name, err = readString(r, 3)
	if err != nil {
		return
	}
	size, err = readInt(r, 3)
	if err != nil {
		return
	}
	headerSize = 6
	return
}

func readID3v2_3FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) {
	name, err = readString(r, 4)
	if err != nil {
		return
	}
	size, err = readInt(r, 4)
	if err != nil {
		return
	}
	headerSize = 8
	return
}

func readID3v2_4FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) {
	name, err = readString(r, 4)
	if err != nil {
		return
	}
	size, err = read7BitChunkedInt(r, 4)
	if err != nil {
		return
	}
	headerSize = 8
	return
}

// readID3v2Frames reads ID3v2 frames from the given reader using the ID3v2Header.
func readID3v2Frames(r io.Reader, h *id3v2Header) (map[string]interface{}, error) {
	offset := 10 // the size of the header
	result := make(map[string]interface{})

	for offset < h.Size {
		var err error
		var name string
		var size, headerSize int
		var flags *id3v2FrameFlags

		switch h.Version {
		case ID3v2_2:
			name, size, headerSize, err = readID3v2_2FrameHeader(r)

		case ID3v2_3:
			name, size, headerSize, err = readID3v2_3FrameHeader(r)
			if err != nil {
				return nil, err
			}
			flags, err = readID3v23FrameFlags(r)
			headerSize += 2

		case ID3v2_4:
			name, size, headerSize, err = readID3v2_4FrameHeader(r)
			if err != nil {
				return nil, err
			}
			flags, err = readID3v24FrameFlags(r)
			headerSize += 2
		}

		if err != nil {
			return nil, err
		}

		// FIXME: Do we still need this?
		// if size=0, we certainly are in a padding zone. ignore the rest of
		// the tags
		if size == 0 {
			break
		}

		offset += headerSize + size

		// Avoid corrupted padding (see http://id3.org/Compliance%20Issues).
		if !validID3Frame(h.Version, name) && offset > h.Size {
			break
		}

		if flags != nil {
			if flags.Compression {
				_, err = read7BitChunkedInt(r, 4) // read 4
				if err != nil {
					return nil, err
				}
				size -= 4
			}

			if flags.Encryption {
				_, err = readBytes(r, 1) // read 1 byte of encryption method
				if err != nil {
					return nil, err
				}
				size -= 1
			}
		}

		b, err := readBytes(r, size)
		if err != nil {
			return nil, err
		}

		// There can be multiple tag with the same name. Append a number to the
		// name if there is more than one.
		rawName := name
		if _, ok := result[rawName]; ok {
			for i := 0; ok; i++ {
				rawName = name + "_" + strconv.Itoa(i)
				_, ok = result[rawName]
			}
		}

		switch {
		case name == "TXXX" || name == "TXX":
			t, err := readTextWithDescrFrame(b, false, true) // no lang, but enc
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name[0] == 'T':
			txt, err := readTFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = txt

		case name == "UFID" || name == "UFI":
			t, err := readUFID(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name == "WXXX" || name == "WXX":
			t, err := readTextWithDescrFrame(b, false, false) // no lang, no enc
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name[0] == 'W':
			txt, err := readWFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = txt

		case name == "COMM" || name == "USLT":
			t, err := readTextWithDescrFrame(b, true, true) // both lang and enc
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name == "APIC":
			p, err := readAPICFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = p

		case name == "PIC":
			p, err := readPICFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = p

		default:
			result[rawName] = b
		}
	}
	return result, nil
}

type unsynchroniser struct {
	io.Reader
	ff bool
}

// filter io.Reader which skip the Unsynchronisation bytes
func (r *unsynchroniser) Read(p []byte) (int, error) {
	b := make([]byte, 1)
	i := 0
	for i < len(p) {
		if n, err := r.Reader.Read(b); err != nil || n == 0 {
			return i, err
		}
		if r.ff && b[0] == 0x00 {
			r.ff = false
			continue
		}
		p[i] = b[0]
		i++
		r.ff = (b[0] == 0xFF)
	}
	return i, nil
}

// ReadID3v2Tags parses ID3v2.{2,3,4} tags from the io.ReadSeeker into a Metadata, returning
// non-nil error on failure.
func ReadID3v2Tags(r io.ReadSeeker) (Metadata, error) {
	h, err := readID3v2Header(r)
	if err != nil {
		return nil, err
	}

	var ur io.Reader
	ur = r
	if h.Unsynchronisation {
		ur = &unsynchroniser{Reader: r}
	}

	f, err := readID3v2Frames(ur, h)
	if err != nil {
		return nil, err
	}
	return metadataID3v2{header: h, frames: f}, nil
}

//  id3v2genre parse a id3v2 genre tag and expand the numeric genres
func id3v2genre(genre string) string {
	c := true
	for c {
		orig := genre
		re := regexp.MustCompile("(.*[^(]|.* |^)\\(([0-9]+)\\) *(.*)$")
		if match := re.FindStringSubmatch(genre); len(match) > 0 {
			if genreId, err := strconv.Atoi(match[2]); err == nil {
				if genreId < len(id3v2Genres) {
					genre = id3v2Genres[genreId]
					if match[1] != "" {
						genre = strings.TrimSpace(match[1]) + " " + genre
					}
					if match[3] != "" {
						genre = genre + " " + match[3]
					}
				}
			}
		}
		c = (orig != genre)
	}
	return strings.Replace(genre, "((", "(", -1)
}
<commit_msg>Add COM and ULT text frames<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 (
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
)

var id3v2Genres = [...]string{
	"Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge",
	"Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B",
	"Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska",
	"Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient",
	"Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical",
	"Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel",
	"Noise", "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative",
	"Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic",
	"Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk",
	"Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta",
	"Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American",
	"Cabaret", "New Wave", "Psychedelic", "Rave", "Showtunes", "Trailer",
	"Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro",
	"Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock",
	"National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival",
	"Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock",
	"Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
	"Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
	"Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
	"Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
	"Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
	"Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall",
	"Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie",
	"Britpop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap",
	"Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian",
	"Christian Rock ", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop",
	"Synthpop",
}

// id3v2Header is a type which represents an ID3v2 tag header.
type id3v2Header struct {
	Version           Format
	Unsynchronisation bool
	ExtendedHeader    bool
	Experimental      bool
	Size              int
}

// readID3v2Header reads the ID3v2 header from the given io.Reader.
func readID3v2Header(r io.Reader) (*id3v2Header, error) {
	b, err := readBytes(r, 10)
	if err != nil {
		return nil, fmt.Errorf("expected to read 10 bytes (ID3v2Header): %v", err)
	}

	if string(b[0:3]) != "ID3" {
		return nil, fmt.Errorf("expected to read \"ID3\"")
	}

	b = b[3:]
	var vers Format
	switch uint(b[0]) {
	case 2:
		vers = ID3v2_2
	case 3:
		vers = ID3v2_3
	case 4:
		vers = ID3v2_4
	case 0, 1:
		fallthrough
	default:
		return nil, fmt.Errorf("ID3 version: %v, expected: 2, 3 or 4", uint(b[0]))
	}

	// NB: We ignore b[1] (the revision) as we don't currently rely on it.
	return &id3v2Header{
		Version:           vers,
		Unsynchronisation: getBit(b[2], 7),
		ExtendedHeader:    getBit(b[2], 6),
		Experimental:      getBit(b[2], 5),
		Size:              get7BitChunkedInt(b[3:7]),
	}, nil
}

// id3v2FrameFlags is a type which represents the flags which can be set on an ID3v2 frame.
type id3v2FrameFlags struct {
	// Message (ID3 2.3.0 and 2.4.0)
	TagAlterPreservation  bool
	FileAlterPreservation bool
	ReadOnly              bool

	// Format (ID3 2.3.0 and 2.4.0)
	Compression   bool
	Encryption    bool
	GroupIdentity bool
	// ID3 2.4.0 only (see http://id3.org/id3v2.4.0-structure sec 4.1)
	Unsynchronisation   bool
	DataLengthIndicator bool
}

func readID3v23FrameFlags(r io.Reader) (*id3v2FrameFlags, error) {
	b, err := readBytes(r, 2)
	if err != nil {
		return nil, err
	}

	msg := b[0]
	fmt := b[1]

	return &id3v2FrameFlags{
		TagAlterPreservation:  getBit(msg, 7),
		FileAlterPreservation: getBit(msg, 6),
		ReadOnly:              getBit(msg, 5),
		Compression:           getBit(fmt, 7),
		Encryption:            getBit(fmt, 6),
		GroupIdentity:         getBit(fmt, 5),
	}, nil
}

func readID3v24FrameFlags(r io.Reader) (*id3v2FrameFlags, error) {
	b, err := readBytes(r, 2)
	if err != nil {
		return nil, err
	}

	msg := b[0]
	fmt := b[1]

	return &id3v2FrameFlags{
		TagAlterPreservation:  getBit(msg, 6),
		FileAlterPreservation: getBit(msg, 5),
		ReadOnly:              getBit(msg, 4),
		GroupIdentity:         getBit(fmt, 6),
		Compression:           getBit(fmt, 3),
		Encryption:            getBit(fmt, 2),
		Unsynchronisation:     getBit(fmt, 1),
		DataLengthIndicator:   getBit(fmt, 0),
	}, nil

}

func readID3v2_2FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) {
	name, err = readString(r, 3)
	if err != nil {
		return
	}
	size, err = readInt(r, 3)
	if err != nil {
		return
	}
	headerSize = 6
	return
}

func readID3v2_3FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) {
	name, err = readString(r, 4)
	if err != nil {
		return
	}
	size, err = readInt(r, 4)
	if err != nil {
		return
	}
	headerSize = 8
	return
}

func readID3v2_4FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) {
	name, err = readString(r, 4)
	if err != nil {
		return
	}
	size, err = read7BitChunkedInt(r, 4)
	if err != nil {
		return
	}
	headerSize = 8
	return
}

// readID3v2Frames reads ID3v2 frames from the given reader using the ID3v2Header.
func readID3v2Frames(r io.Reader, h *id3v2Header) (map[string]interface{}, error) {
	offset := 10 // the size of the header
	result := make(map[string]interface{})

	for offset < h.Size {
		var err error
		var name string
		var size, headerSize int
		var flags *id3v2FrameFlags

		switch h.Version {
		case ID3v2_2:
			name, size, headerSize, err = readID3v2_2FrameHeader(r)

		case ID3v2_3:
			name, size, headerSize, err = readID3v2_3FrameHeader(r)
			if err != nil {
				return nil, err
			}
			flags, err = readID3v23FrameFlags(r)
			headerSize += 2

		case ID3v2_4:
			name, size, headerSize, err = readID3v2_4FrameHeader(r)
			if err != nil {
				return nil, err
			}
			flags, err = readID3v24FrameFlags(r)
			headerSize += 2
		}

		if err != nil {
			return nil, err
		}

		// FIXME: Do we still need this?
		// if size=0, we certainly are in a padding zone. ignore the rest of
		// the tags
		if size == 0 {
			break
		}

		offset += headerSize + size

		// Avoid corrupted padding (see http://id3.org/Compliance%20Issues).
		if !validID3Frame(h.Version, name) && offset > h.Size {
			break
		}

		if flags != nil {
			if flags.Compression {
				_, err = read7BitChunkedInt(r, 4) // read 4
				if err != nil {
					return nil, err
				}
				size -= 4
			}

			if flags.Encryption {
				_, err = readBytes(r, 1) // read 1 byte of encryption method
				if err != nil {
					return nil, err
				}
				size -= 1
			}
		}

		b, err := readBytes(r, size)
		if err != nil {
			return nil, err
		}

		// There can be multiple tag with the same name. Append a number to the
		// name if there is more than one.
		rawName := name
		if _, ok := result[rawName]; ok {
			for i := 0; ok; i++ {
				rawName = name + "_" + strconv.Itoa(i)
				_, ok = result[rawName]
			}
		}

		switch {
		case name == "TXXX" || name == "TXX":
			t, err := readTextWithDescrFrame(b, false, true) // no lang, but enc
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name[0] == 'T':
			txt, err := readTFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = txt

		case name == "UFID" || name == "UFI":
			t, err := readUFID(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name == "WXXX" || name == "WXX":
			t, err := readTextWithDescrFrame(b, false, false) // no lang, no enc
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name[0] == 'W':
			txt, err := readWFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = txt

		case name == "COMM" || name == "COM" || name == "USLT" || name == "ULT":
			t, err := readTextWithDescrFrame(b, true, true) // both lang and enc
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name == "APIC":
			p, err := readAPICFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = p

		case name == "PIC":
			p, err := readPICFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = p

		default:
			result[rawName] = b
		}
	}
	return result, nil
}

type unsynchroniser struct {
	io.Reader
	ff bool
}

// filter io.Reader which skip the Unsynchronisation bytes
func (r *unsynchroniser) Read(p []byte) (int, error) {
	b := make([]byte, 1)
	i := 0
	for i < len(p) {
		if n, err := r.Reader.Read(b); err != nil || n == 0 {
			return i, err
		}
		if r.ff && b[0] == 0x00 {
			r.ff = false
			continue
		}
		p[i] = b[0]
		i++
		r.ff = (b[0] == 0xFF)
	}
	return i, nil
}

// ReadID3v2Tags parses ID3v2.{2,3,4} tags from the io.ReadSeeker into a Metadata, returning
// non-nil error on failure.
func ReadID3v2Tags(r io.ReadSeeker) (Metadata, error) {
	h, err := readID3v2Header(r)
	if err != nil {
		return nil, err
	}

	var ur io.Reader
	ur = r
	if h.Unsynchronisation {
		ur = &unsynchroniser{Reader: r}
	}

	f, err := readID3v2Frames(ur, h)
	if err != nil {
		return nil, err
	}
	return metadataID3v2{header: h, frames: f}, nil
}

//  id3v2genre parse a id3v2 genre tag and expand the numeric genres
func id3v2genre(genre string) string {
	c := true
	for c {
		orig := genre
		re := regexp.MustCompile("(.*[^(]|.* |^)\\(([0-9]+)\\) *(.*)$")
		if match := re.FindStringSubmatch(genre); len(match) > 0 {
			if genreId, err := strconv.Atoi(match[2]); err == nil {
				if genreId < len(id3v2Genres) {
					genre = id3v2Genres[genreId]
					if match[1] != "" {
						genre = strings.TrimSpace(match[1]) + " " + genre
					}
					if match[3] != "" {
						genre = genre + " " + match[3]
					}
				}
			}
		}
		c = (orig != genre)
	}
	return strings.Replace(genre, "((", "(", -1)
}
<|endoftext|>


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

package ebiten

import (
	"image"
	"image/color"
	"runtime"
	"sync"

	"github.com/hajimehoshi/ebiten/internal/graphics/opengl"
)

func glContext() *opengl.Context {
	// This is called from finalizers even when the context or the program is not set.
	g, ok := theGraphicsContext.Load().(*graphicsContext)
	if !ok {
		return nil
	}
	if g == nil {
		return nil
	}
	return g.GLContext()
}

type images struct {
	images map[*imageImpl]struct{}
	m      sync.Mutex
}

var theImagesForRestoring = images{
	images: map[*imageImpl]struct{}{},
}

func (i *images) add(img *imageImpl) (*Image, error) {
	i.m.Lock()
	defer i.m.Unlock()
	i.images[img] = struct{}{}
	eimg := &Image{img}
	runtime.SetFinalizer(eimg, theImagesForRestoring.remove)
	return eimg, nil
}

func (i *images) remove(img *Image) {
	if err := img.Dispose(); err != nil {
		panic(err)
	}
	i.m.Lock()
	defer i.m.Unlock()
	delete(i.images, img.impl)
	runtime.SetFinalizer(img, nil)
}

func (i *images) resolveStalePixels(context *opengl.Context) error {
	i.m.Lock()
	defer i.m.Unlock()
	for img := range i.images {
		if err := img.resolveStalePixels(context); err != nil {
			return err
		}
	}
	return nil
}

func (i *images) resetPixelsIfDependingOn(target *Image, context *opengl.Context) error {
	i.m.Lock()
	defer i.m.Unlock()
	for img := range i.images {
		if err := img.resetPixelsIfDependingOn(target.impl, context); err != nil {
			return err
		}
	}
	return nil
}

func (i *images) restore(context *opengl.Context) error {
	i.m.Lock()
	defer i.m.Unlock()
	// Dispose all images first because framebuffer/texture numbers can be reused.
	// If framebuffers/textures are not disposed here, a newly created framebuffer/texture
	// number can be a same number as existing one.
	for img := range i.images {
		if img.isDisposed() {
			continue
		}
		if err := img.restorable.DisposeOnlyImage(); err != nil {
			return err
		}
	}
	imagesWithoutDependency := []*imageImpl{}
	imagesWithDependency := []*imageImpl{}
	for img := range i.images {
		if img.hasDependency() {
			imagesWithDependency = append(imagesWithDependency, img)
		} else {
			imagesWithoutDependency = append(imagesWithoutDependency, img)
		}
	}
	// Images depending on other images should be processed first.
	for _, img := range imagesWithoutDependency {
		if err := img.restore(context); err != nil {
			return err
		}
	}
	for _, img := range imagesWithDependency {
		if err := img.restore(context); err != nil {
			return err
		}
	}
	return nil
}

func (i *images) clearVolatileImages() error {
	i.m.Lock()
	defer i.m.Unlock()
	for img := range i.images {
		if err := img.clearIfVolatile(); err != nil {
			return err
		}
	}
	return nil
}

// Image represents an image.
// The pixel format is alpha-premultiplied.
// Image implements image.Image.
type Image struct {
	impl *imageImpl
}

// Size returns the size of the image.
//
// This function is concurrent-safe.
func (i *Image) Size() (width, height int) {
	return i.impl.restorable.Size()
}

// Clear resets the pixels of the image into 0.
//
// This function is concurrent-safe.
func (i *Image) Clear() error {
	if err := theImagesForRestoring.resetPixelsIfDependingOn(i, glContext()); err != nil {
		return err
	}
	return i.impl.Fill(color.Transparent)
}

// Fill fills the image with a solid color.
//
// This function is concurrent-safe.
func (i *Image) Fill(clr color.Color) error {
	if err := theImagesForRestoring.resetPixelsIfDependingOn(i, glContext()); err != nil {
		return err
	}
	return i.impl.Fill(clr)
}

// DrawImage draws the given image on the receiver image.
//
// This method accepts the options.
// The parts of the given image at the parts of the destination.
// After determining parts to draw, this applies the geometry matrix and the color matrix.
//
// Here are the default values:
//     ImageParts:    (0, 0) - (source width, source height) to (0, 0) - (source width, source height)
//                    (i.e. the whole source image)
//     GeoM:          Identity matrix
//     ColorM:        Identity matrix (that changes no colors)
//     CompositeMode: CompositeModeSourceOver (regular alpha blending)
//
// Note that this function returns immediately and actual drawing is done lazily.
//
// This function is concurrent-safe.
func (i *Image) DrawImage(image *Image, options *DrawImageOptions) error {
	if err := theImagesForRestoring.resetPixelsIfDependingOn(i, glContext()); err != nil {
		return err
	}
	return i.impl.DrawImage(image, options)
}

// Bounds returns the bounds of the image.
//
// This function is concurrent-safe.
func (i *Image) Bounds() image.Rectangle {
	w, h := i.impl.restorable.Size()
	return image.Rect(0, 0, w, h)
}

// ColorModel returns the color model of the image.
//
// This function is concurrent-safe.
func (i *Image) ColorModel() color.Model {
	return color.RGBAModel
}

// At returns the color of the image at (x, y).
//
// This method loads pixels from VRAM to system memory if necessary.
//
// This method can't be called before the main loop (ebiten.Run) starts (as of version 1.4.0-alpha).
//
// This function is concurrent-safe.
func (i *Image) At(x, y int) color.Color {
	return i.impl.At(x, y, glContext())
}

// Dispose disposes the image data. After disposing, the image becomes invalid.
// This is useful to save memory.
//
// The behavior of any functions for a disposed image is undefined.
//
// This function is concurrent-safe.
func (i *Image) Dispose() error {
	if i.impl.isDisposed() {
		return nil
	}
	if err := theImagesForRestoring.resetPixelsIfDependingOn(i, glContext()); err != nil {
		return err
	}
	return i.impl.Dispose()
}

// ReplacePixels replaces the pixels of the image with p.
//
// The given p must represent RGBA pre-multiplied alpha values. len(p) must equal to 4 * (image width) * (image height).
//
// This function may be slow (as for implementation, this calls glTexSubImage2D).
//
// This function is concurrent-safe.
func (i *Image) ReplacePixels(p []uint8) error {
	if err := theImagesForRestoring.resetPixelsIfDependingOn(i, glContext()); err != nil {
		return err
	}
	return i.impl.ReplacePixels(p)
}

// A DrawImageOptions represents options to render an image on an image.
type DrawImageOptions struct {
	ImageParts    ImageParts
	GeoM          GeoM
	ColorM        ColorM
	CompositeMode CompositeMode

	// Deprecated (as of 1.1.0-alpha): Use ImageParts instead.
	Parts []ImagePart
}

// NewImage returns an empty image.
//
// NewImage generates a new texture and a new framebuffer.
//
// This function is concurrent-safe.
func NewImage(width, height int, filter Filter) (*Image, error) {
	img, err := newImageImpl(width, height, filter, false)
	if err != nil {
		return nil, err
	}
	if err := img.Fill(color.Transparent); err != nil {
		return nil, err
	}
	eimg, err := theImagesForRestoring.add(img)
	if err != nil {
		return nil, err
	}
	return eimg, nil
}

// newVolatileImage returns an empty 'volatile' image.
// A volatile image is always cleared at the start of a frame.
//
// This is suitable for offscreen images that pixels are changed often.
//
// Pixels in regular non-volatile images are saved at each end of a frame if the image
// is changed, and restored automatically from the saved pixels on GL context lost.
// On the other hand, pixels in volatile images are not saved.
// Saving pixels is an expensive operation, and it is desirable to avoid it if possible.
//
// This function is concurrent-safe.
func newVolatileImage(width, height int, filter Filter) (*Image, error) {
	img, err := newImageImpl(width, height, filter, true)
	if err != nil {
		return nil, err
	}
	if err := img.Fill(color.Transparent); err != nil {
		return nil, err
	}
	eimg, err := theImagesForRestoring.add(img)
	if err != nil {
		return nil, err
	}
	return eimg, nil
}

// NewImageFromImage creates a new image with the given image (source).
//
// NewImageFromImage generates a new texture and a new framebuffer.
//
// This function is concurrent-safe.
func NewImageFromImage(source image.Image, filter Filter) (*Image, error) {
	img, err := newImageImplFromImage(source, filter)
	if err != nil {
		return nil, err
	}
	eimg, err := theImagesForRestoring.add(img)
	if err != nil {
		return nil, err
	}
	return eimg, nil
}

func newImageWithScreenFramebuffer(width, height int) (*Image, error) {
	img, err := newScreenImageImpl(width, height)
	if err != nil {
		return nil, err
	}
	eimg, err := theImagesForRestoring.add(img)
	if err != nil {
		return nil, err
	}
	return eimg, nil
}
<commit_msg>graphics: Skip resetPixelsIfDependingOn if possible<commit_after>// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ebiten

import (
	"image"
	"image/color"
	"runtime"
	"sync"

	"github.com/hajimehoshi/ebiten/internal/graphics/opengl"
)

func glContext() *opengl.Context {
	// This is called from finalizers even when the context or the program is not set.
	g, ok := theGraphicsContext.Load().(*graphicsContext)
	if !ok {
		return nil
	}
	if g == nil {
		return nil
	}
	return g.GLContext()
}

type images struct {
	images      map[*imageImpl]struct{}
	m           sync.Mutex
	lastChecked *imageImpl
}

var theImagesForRestoring = images{
	images: map[*imageImpl]struct{}{},
}

func (i *images) add(img *imageImpl) (*Image, error) {
	i.m.Lock()
	defer i.m.Unlock()
	i.images[img] = struct{}{}
	eimg := &Image{img}
	runtime.SetFinalizer(eimg, theImagesForRestoring.remove)
	return eimg, nil
}

func (i *images) remove(img *Image) {
	if err := img.Dispose(); err != nil {
		panic(err)
	}
	i.m.Lock()
	defer i.m.Unlock()
	delete(i.images, img.impl)
	runtime.SetFinalizer(img, nil)
}

func (i *images) resolveStalePixels(context *opengl.Context) error {
	i.m.Lock()
	defer i.m.Unlock()
	i.lastChecked = nil
	for img := range i.images {
		if err := img.resolveStalePixels(context); err != nil {
			return err
		}
	}
	return nil
}

func (i *images) resetPixelsIfDependingOn(target *Image, context *opengl.Context) error {
	i.m.Lock()
	defer i.m.Unlock()
	if i.lastChecked == target.impl {
		return nil
	}
	i.lastChecked = target.impl
	for img := range i.images {
		if err := img.resetPixelsIfDependingOn(target.impl, context); err != nil {
			return err
		}
	}
	return nil
}

func (i *images) restore(context *opengl.Context) error {
	i.m.Lock()
	defer i.m.Unlock()
	// Dispose all images first because framebuffer/texture numbers can be reused.
	// If framebuffers/textures are not disposed here, a newly created framebuffer/texture
	// number can be a same number as existing one.
	for img := range i.images {
		if img.isDisposed() {
			continue
		}
		if err := img.restorable.DisposeOnlyImage(); err != nil {
			return err
		}
	}
	imagesWithoutDependency := []*imageImpl{}
	imagesWithDependency := []*imageImpl{}
	for img := range i.images {
		if img.hasDependency() {
			imagesWithDependency = append(imagesWithDependency, img)
		} else {
			imagesWithoutDependency = append(imagesWithoutDependency, img)
		}
	}
	// Images depending on other images should be processed first.
	for _, img := range imagesWithoutDependency {
		if err := img.restore(context); err != nil {
			return err
		}
	}
	for _, img := range imagesWithDependency {
		if err := img.restore(context); err != nil {
			return err
		}
	}
	return nil
}

func (i *images) clearVolatileImages() error {
	i.m.Lock()
	defer i.m.Unlock()
	for img := range i.images {
		if err := img.clearIfVolatile(); err != nil {
			return err
		}
	}
	return nil
}

// Image represents an image.
// The pixel format is alpha-premultiplied.
// Image implements image.Image.
type Image struct {
	impl *imageImpl
}

// Size returns the size of the image.
//
// This function is concurrent-safe.
func (i *Image) Size() (width, height int) {
	return i.impl.restorable.Size()
}

// Clear resets the pixels of the image into 0.
//
// This function is concurrent-safe.
func (i *Image) Clear() error {
	if err := theImagesForRestoring.resetPixelsIfDependingOn(i, glContext()); err != nil {
		return err
	}
	return i.impl.Fill(color.Transparent)
}

// Fill fills the image with a solid color.
//
// This function is concurrent-safe.
func (i *Image) Fill(clr color.Color) error {
	if err := theImagesForRestoring.resetPixelsIfDependingOn(i, glContext()); err != nil {
		return err
	}
	return i.impl.Fill(clr)
}

// DrawImage draws the given image on the receiver image.
//
// This method accepts the options.
// The parts of the given image at the parts of the destination.
// After determining parts to draw, this applies the geometry matrix and the color matrix.
//
// Here are the default values:
//     ImageParts:    (0, 0) - (source width, source height) to (0, 0) - (source width, source height)
//                    (i.e. the whole source image)
//     GeoM:          Identity matrix
//     ColorM:        Identity matrix (that changes no colors)
//     CompositeMode: CompositeModeSourceOver (regular alpha blending)
//
// Note that this function returns immediately and actual drawing is done lazily.
//
// This function is concurrent-safe.
func (i *Image) DrawImage(image *Image, options *DrawImageOptions) error {
	if err := theImagesForRestoring.resetPixelsIfDependingOn(i, glContext()); err != nil {
		return err
	}
	return i.impl.DrawImage(image, options)
}

// Bounds returns the bounds of the image.
//
// This function is concurrent-safe.
func (i *Image) Bounds() image.Rectangle {
	w, h := i.impl.restorable.Size()
	return image.Rect(0, 0, w, h)
}

// ColorModel returns the color model of the image.
//
// This function is concurrent-safe.
func (i *Image) ColorModel() color.Model {
	return color.RGBAModel
}

// At returns the color of the image at (x, y).
//
// This method loads pixels from VRAM to system memory if necessary.
//
// This method can't be called before the main loop (ebiten.Run) starts (as of version 1.4.0-alpha).
//
// This function is concurrent-safe.
func (i *Image) At(x, y int) color.Color {
	return i.impl.At(x, y, glContext())
}

// Dispose disposes the image data. After disposing, the image becomes invalid.
// This is useful to save memory.
//
// The behavior of any functions for a disposed image is undefined.
//
// This function is concurrent-safe.
func (i *Image) Dispose() error {
	if i.impl.isDisposed() {
		return nil
	}
	if err := theImagesForRestoring.resetPixelsIfDependingOn(i, glContext()); err != nil {
		return err
	}
	return i.impl.Dispose()
}

// ReplacePixels replaces the pixels of the image with p.
//
// The given p must represent RGBA pre-multiplied alpha values. len(p) must equal to 4 * (image width) * (image height).
//
// This function may be slow (as for implementation, this calls glTexSubImage2D).
//
// This function is concurrent-safe.
func (i *Image) ReplacePixels(p []uint8) error {
	if err := theImagesForRestoring.resetPixelsIfDependingOn(i, glContext()); err != nil {
		return err
	}
	return i.impl.ReplacePixels(p)
}

// A DrawImageOptions represents options to render an image on an image.
type DrawImageOptions struct {
	ImageParts    ImageParts
	GeoM          GeoM
	ColorM        ColorM
	CompositeMode CompositeMode

	// Deprecated (as of 1.1.0-alpha): Use ImageParts instead.
	Parts []ImagePart
}

// NewImage returns an empty image.
//
// NewImage generates a new texture and a new framebuffer.
//
// This function is concurrent-safe.
func NewImage(width, height int, filter Filter) (*Image, error) {
	img, err := newImageImpl(width, height, filter, false)
	if err != nil {
		return nil, err
	}
	if err := img.Fill(color.Transparent); err != nil {
		return nil, err
	}
	eimg, err := theImagesForRestoring.add(img)
	if err != nil {
		return nil, err
	}
	return eimg, nil
}

// newVolatileImage returns an empty 'volatile' image.
// A volatile image is always cleared at the start of a frame.
//
// This is suitable for offscreen images that pixels are changed often.
//
// Pixels in regular non-volatile images are saved at each end of a frame if the image
// is changed, and restored automatically from the saved pixels on GL context lost.
// On the other hand, pixels in volatile images are not saved.
// Saving pixels is an expensive operation, and it is desirable to avoid it if possible.
//
// This function is concurrent-safe.
func newVolatileImage(width, height int, filter Filter) (*Image, error) {
	img, err := newImageImpl(width, height, filter, true)
	if err != nil {
		return nil, err
	}
	if err := img.Fill(color.Transparent); err != nil {
		return nil, err
	}
	eimg, err := theImagesForRestoring.add(img)
	if err != nil {
		return nil, err
	}
	return eimg, nil
}

// NewImageFromImage creates a new image with the given image (source).
//
// NewImageFromImage generates a new texture and a new framebuffer.
//
// This function is concurrent-safe.
func NewImageFromImage(source image.Image, filter Filter) (*Image, error) {
	img, err := newImageImplFromImage(source, filter)
	if err != nil {
		return nil, err
	}
	eimg, err := theImagesForRestoring.add(img)
	if err != nil {
		return nil, err
	}
	return eimg, nil
}

func newImageWithScreenFramebuffer(width, height int) (*Image, error) {
	img, err := newScreenImageImpl(width, height)
	if err != nil {
		return nil, err
	}
	eimg, err := theImagesForRestoring.add(img)
	if err != nil {
		return nil, err
	}
	return eimg, nil
}
<|endoftext|>


<commit_before>package main

import (
  _ "github.com/lib/pq"
  "database/sql"
  "fmt"
  "log"
  "net/http"
  "net/url"
  "io/ioutil"
  "encoding/json"
  "time"
  "strconv"
  "./config"
  "strings"
)

var db *sql.DB

func init() {
  var err error
  db, err = sql.Open("postgres", "postgres://" + config.DatabaseUser + ":" +
                                                  config.DatabasePassword + "@" +
                                                  config.DatabaseHost + "/" +
                                                  config.DatabaseName + "?sslmode=disable")
  if err != nil {
    log.Fatal(err)
  }

  if err = db.Ping(); err != nil {
    log.Fatal(err)
  }
}

type Task struct {
  id int
  user_id int
  task string
  complete int
}

type Chat struct {
  Id int `json:"id"`
}

type Message struct {
  Id int `json:"message_id"`
  Text string `json:"text"`
  Chat Chat `json:"chat"`
}

type Update struct {
  Id int `json:"update_id"`
  Message Message `json:"message"`
}

type GetUpdates struct {
  Ok bool `json:"ok"`
  UpdateList []Update `json:"result"`
}

func getUpdates(body []byte) (*GetUpdates) {
  var s = new(GetUpdates)
  err := json.Unmarshal(body, &s)
  if err != nil {
    fmt.Printf("%s", err)
  }
  return s
}

func main() {
  ticker := time.NewTicker(time.Millisecond * 1000)

  lastOffset := 0

  for {
    fmt.Println("Getting updates for offset: " + strconv.Itoa(lastOffset + 1))
    getUpdatesUrl := config.TelegramBotUrl + "/getUpdates?offset=" + strconv.Itoa(lastOffset + 1)
    response, err := http.Get(getUpdatesUrl)
    if err != nil {
      fmt.Printf("%s", err)
    } else {
      defer response.Body.Close()
      body, err := ioutil.ReadAll(response.Body)
      if err != nil {
        fmt.Printf("%s", err)
      }

      getUpdatesData := getUpdates([]byte(body))
      updateList := getUpdatesData.UpdateList
      if len(updateList) > 0 {
        lastOffset = updateList[len(updateList) - 1].Id
      }
      for _, update := range updateList {
        processUpdate(update)
      }
    }
    <-ticker.C
  }
}

func processUpdate(update Update) {
  fmt.Println("Update id:", update.Id)
  text := update.Message.Text
  if len(text) == 0 {
    return
  }

  messageSegments := strings.Split(text, " ")
  command := messageSegments[0]

  chatId := update.Message.Chat.Id

  switch command {
  case "/start":
    sendMessage(chatId, "- List todos: `/list`\n" +
                        "- Add todo: `/add <task>`\n" +
                        "- Complete todo: `/complete <todo_id>`\n")
  case "/list":
    rows, err := db.Query("SELECT * FROM tasks WHERE complete = 0")
    if err != nil {
      fmt.Printf("%s", err)
      return
    }
    defer rows.Close()

    todos := make([]*Task, 0)
    for rows.Next() {
      todo := new(Task)
      err := rows.Scan(&todo.id, &todo.user_id, &todo.task, &todo.complete)
      if err != nil {
        fmt.Printf("%s", err)
        return
      }
      todos = append(todos, todo)
    }

    if err = rows.Err(); err != nil {
      return
    }
    message := ""
    for _, todo := range todos {
      message += "(" + strconv.Itoa(todo.id) + ") - " + todo.task + "\n"
    }

    sendMessage(chatId, message)
  case "/add":
    task := strings.Join(messageSegments[1:], " ")
    if task == "" {
      sendMessage(chatId, "Error: No task added")
      return
    }

    fmt.Println("Task:", task)

    _, err := db.Exec("INSERT INTO tasks (user_id, task) VALUES($1, $2)", chatId, task)
    if err != nil {
      sendMessage(chatId, "Error: Invalid input")
      return
    }
    sendMessage(chatId, "Task added successfully!")
  case "/complete":
    taskId := messageSegments[1]
    if taskId == "" {
      sendMessage(chatId, "Error: No task id selected")
      return
    }

    _, err := db.Exec("UPDATE tasks SET complete=1 WHERE id = $1", taskId)
    if err != nil {
      sendMessage(chatId, "Error: Invalid input")
      return
    }
    sendMessage(chatId, "Task completed successfully!")
  default:
    return
  }
}

func sendMessage(chatId int, message string) {

  urlData := make(url.Values)
  urlData.Set("chat_id", strconv.Itoa(chatId))
  urlData.Set("text", message)
  urlData.Set("parse_mode", "Markdown")
  urlData.Set("disable_web_page_preview", "true")

  sendMessageUrl := config.TelegramBotUrl + "/sendMessage"
  response, err := http.PostForm(sendMessageUrl, urlData)
  fmt.Println("Status: ", response.Status)
  if response.Status != "200 OK" {
    urlData.Set("text", "Sorry the request failed with: " + response.Status)
    http.PostForm(sendMessageUrl, urlData)
  }

  if err != nil {
    fmt.Println(err)
  }
}
<commit_msg>Gofmt<commit_after>package main

import (
	"./config"
	"database/sql"
	"encoding/json"
	"fmt"
	_ "github.com/lib/pq"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

var db *sql.DB

func init() {
	var err error
	db, err = sql.Open("postgres", "postgres://"+config.DatabaseUser+":"+
		config.DatabasePassword+"@"+
		config.DatabaseHost+"/"+
		config.DatabaseName+"?sslmode=disable")
	if err != nil {
		log.Fatal(err)
	}

	if err = db.Ping(); err != nil {
		log.Fatal(err)
	}
}

type Task struct {
	id       int
	user_id  int
	task     string
	complete int
}

type Chat struct {
	Id int `json:"id"`
}

type Message struct {
	Id   int    `json:"message_id"`
	Text string `json:"text"`
	Chat Chat   `json:"chat"`
}

type Update struct {
	Id      int     `json:"update_id"`
	Message Message `json:"message"`
}

type GetUpdates struct {
	Ok         bool     `json:"ok"`
	UpdateList []Update `json:"result"`
}

func getUpdates(body []byte) *GetUpdates {
	var s = new(GetUpdates)
	err := json.Unmarshal(body, &s)
	if err != nil {
		fmt.Printf("%s", err)
	}
	return s
}

func main() {
	ticker := time.NewTicker(time.Millisecond * 1000)

	lastOffset := 0

	for {
		fmt.Println("Getting updates for offset: " + strconv.Itoa(lastOffset+1))
		getUpdatesUrl := config.TelegramBotUrl + "/getUpdates?offset=" + strconv.Itoa(lastOffset+1)
		response, err := http.Get(getUpdatesUrl)
		if err != nil {
			fmt.Printf("%s", err)
		} else {
			defer response.Body.Close()
			body, err := ioutil.ReadAll(response.Body)
			if err != nil {
				fmt.Printf("%s", err)
			}

			getUpdatesData := getUpdates([]byte(body))
			updateList := getUpdatesData.UpdateList
			if len(updateList) > 0 {
				lastOffset = updateList[len(updateList)-1].Id
			}
			for _, update := range updateList {
				processUpdate(update)
			}
		}
		<-ticker.C
	}
}

func processUpdate(update Update) {
	fmt.Println("Update id:", update.Id)
	text := update.Message.Text
	if len(text) == 0 {
		return
	}

	messageSegments := strings.Split(text, " ")
	command := messageSegments[0]

	chatId := update.Message.Chat.Id

	switch command {
	case "/start":
		sendMessage(chatId, "- List todos: `/list`\n"+
			"- Add todo: `/add <task>`\n"+
			"- Complete todo: `/complete <todo_id>`\n")
	case "/list":
		rows, err := db.Query("SELECT * FROM tasks WHERE complete = 0")
		if err != nil {
			fmt.Printf("%s", err)
			return
		}
		defer rows.Close()

		todos := make([]*Task, 0)
		for rows.Next() {
			todo := new(Task)
			err := rows.Scan(&todo.id, &todo.user_id, &todo.task, &todo.complete)
			if err != nil {
				fmt.Printf("%s", err)
				return
			}
			todos = append(todos, todo)
		}

		if err = rows.Err(); err != nil {
			return
		}
		message := ""
		for _, todo := range todos {
			message += "(" + strconv.Itoa(todo.id) + ") - " + todo.task + "\n"
		}

		sendMessage(chatId, message)
	case "/add":
		task := strings.Join(messageSegments[1:], " ")
		if task == "" {
			sendMessage(chatId, "Error: No task added")
			return
		}

		fmt.Println("Task:", task)

		_, err := db.Exec("INSERT INTO tasks (user_id, task) VALUES($1, $2)", chatId, task)
		if err != nil {
			sendMessage(chatId, "Error: Invalid input")
			return
		}
		sendMessage(chatId, "Task added successfully!")
	case "/complete":
		taskId := messageSegments[1]
		if taskId == "" {
			sendMessage(chatId, "Error: No task id selected")
			return
		}

		_, err := db.Exec("UPDATE tasks SET complete=1 WHERE id = $1", taskId)
		if err != nil {
			sendMessage(chatId, "Error: Invalid input")
			return
		}
		sendMessage(chatId, "Task completed successfully!")
	default:
		return
	}
}

func sendMessage(chatId int, message string) {

	urlData := make(url.Values)
	urlData.Set("chat_id", strconv.Itoa(chatId))
	urlData.Set("text", message)
	urlData.Set("parse_mode", "Markdown")
	urlData.Set("disable_web_page_preview", "true")

	sendMessageUrl := config.TelegramBotUrl + "/sendMessage"
	response, err := http.PostForm(sendMessageUrl, urlData)
	fmt.Println("Status: ", response.Status)
	if response.Status != "200 OK" {
		urlData.Set("text", "Sorry the request failed with: "+response.Status)
		http.PostForm(sendMessageUrl, urlData)
	}

	if err != nil {
		fmt.Println(err)
	}
}
<|endoftext|>


<commit_before>package bla

import (
	"os"
	"path/filepath"
)

func generateIndex(s *Handler, public string) (err error) {

	var docs []*Doc
	if len(s.sortDocs) > s.Cfg.HomeDocCount {
		docs = s.sortDocs[:s.Cfg.HomeDocCount]
	} else {
		docs = s.sortDocs
	}

	f, err := os.Create(filepath.Join(public, "index.html"))
	if err != nil {
		return
	}
	defer f.Close()

	return s.tpl.ExecuteTemplate(f, "index",
		&mulDocData{s, "", docs})

}

func generateDoc(s *Handler, public string) (err error) {

	for _, doc := range s.docs {
		var f *os.File
		f, err = os.Create(filepath.Join(public, doc.SlugTitle))
		if err != nil {
			return
		}
		defer f.Close()
		err = s.tpl.ExecuteTemplate(f, "single",
			&singleData{s, doc.Title, doc})
		if err != nil {
			return
		}
	}
	return nil
}
<commit_msg>change mod time<commit_after>package bla

import (
	"log"
	"os"
	"path/filepath"
	"time"
)

func generateIndex(s *Handler, public string) (err error) {

	var docs []*Doc
	if len(s.sortDocs) > s.Cfg.HomeDocCount {
		docs = s.sortDocs[:s.Cfg.HomeDocCount]
	} else {
		docs = s.sortDocs
	}

	f, err := os.Create(filepath.Join(public, "index.html"))
	if err != nil {
		return
	}
	defer f.Close()

	return s.tpl.ExecuteTemplate(f, "index",
		&mulDocData{s, "", docs})

}

func generateDoc(s *Handler, public string) (err error) {

	for _, doc := range s.docs {
		var f *os.File
		fp := filepath.Join(public, doc.SlugTitle)
		f, err = os.Create(fp)
		if err != nil {
			return
		}
		defer f.Close()
		err = s.tpl.ExecuteTemplate(f, "single",
			&singleData{s, doc.Title, doc})
		if err != nil {
			return
		}
		err = os.Chtimes(fp, time.Now(), doc.Time)
		if err != nil {
			// not a big deal...
			log.Print(err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/hashicorp/terraform/helper/diff"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/goamz/ec2"
)

func resource_aws_subnet_create(
	s *terraform.ResourceState,
	d *terraform.ResourceDiff,
	meta interface{}) (*terraform.ResourceState, error) {
	p := meta.(*ResourceProvider)
	ec2conn := p.ec2conn

	// Merge the diff so that we have all the proper attributes
	s = s.MergeDiff(d)

	// Create the Subnet
	createOpts := &ec2.CreateSubnet{
		AvailabilityZone: s.Attributes["availability_zone"],
		CidrBlock:        s.Attributes["cidr_block"],
		VpcId:            s.Attributes["vpc_id"],
	}
	log.Printf("[DEBUG] Subnet create config: %#v", createOpts)
	resp, err := ec2conn.CreateSubnet(createOpts)
	if err != nil {
		return nil, fmt.Errorf("Error creating subnet: %s", err)
	}

	// Get the ID and store it
	subnet := &resp.Subnet
	s.ID = subnet.SubnetId
	log.Printf("[INFO] Subnet ID: %s", s.ID)

	// Wait for the Subnet to become available
	log.Printf(
		"[DEBUG] Waiting for subnet (%s) to become available",
		s.ID)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"pending"},
		Target:  "available",
		Refresh: SubnetStateRefreshFunc(ec2conn, s.ID),
		Timeout: 10 * time.Minute,
	}
	subnetRaw, err := stateConf.WaitForState()
	if err != nil {
		return s, fmt.Errorf(
			"Error waiting for subnet (%s) to become available: %s",
			s.ID, err)
	}

	// Update our attributes and return
	return resource_aws_subnet_update_state(s, subnetRaw.(*ec2.Subnet))
}

func resource_aws_subnet_update(
	s *terraform.ResourceState,
	d *terraform.ResourceDiff,
	meta interface{}) (*terraform.ResourceState, error) {
	// This should never be called because we have no update-able
	// attributes
	panic("Update for subnet is not supported")

	return nil, nil
}

func resource_aws_subnet_destroy(
	s *terraform.ResourceState,
	meta interface{}) error {
	p := meta.(*ResourceProvider)
	ec2conn := p.ec2conn

	log.Printf("[INFO] Deleting Subnet: %s", s.ID)
	if _, err := ec2conn.DeleteSubnet(s.ID); err != nil {
		return fmt.Errorf("Error deleting subnet: %s", err)
	}

	return nil
}

func resource_aws_subnet_refresh(
	s *terraform.ResourceState,
	meta interface{}) (*terraform.ResourceState, error) {
	p := meta.(*ResourceProvider)
	ec2conn := p.ec2conn

	subnetRaw, _, err := SubnetStateRefreshFunc(ec2conn, s.ID)()
	if err != nil {
		return s, err
	}
	if subnetRaw == nil {
		return nil, nil
	}

	subnet := subnetRaw.(*ec2.Subnet)
	return resource_aws_subnet_update_state(s, subnet)
}

func resource_aws_subnet_diff(
	s *terraform.ResourceState,
	c *terraform.ResourceConfig,
	meta interface{}) (*terraform.ResourceDiff, error) {
	b := &diff.ResourceBuilder{
		Attrs: map[string]diff.AttrType{
			"availability_zone": diff.AttrTypeCreate,
			"cidr_block":        diff.AttrTypeCreate,
			"vpc_id":            diff.AttrTypeCreate,
		},
	}

	return b.Diff(s, c)
}

func resource_aws_subnet_update_state(
	s *terraform.ResourceState,
	subnet *ec2.Subnet) (*terraform.ResourceState, error) {
	s.Attributes["availability_zone"] = subnet.AvailabilityZone
	s.Attributes["cidr_block"] = subnet.CidrBlock
	s.Attributes["vpc_id"] = subnet.VpcId

	// We belong to a VPC
	s.Dependencies = []terraform.ResourceDependency{
		terraform.ResourceDependency{ID: subnet.VpcId},
	}

	return s, nil
}

// SubnetStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// a Subnet.
func SubnetStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		resp, err := conn.DescribeSubnets([]string{id}, ec2.NewFilter())
		if err != nil {
			if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidSubnetID.NotFound" {
				resp = nil
			} else {
				log.Printf("Error on SubnetStateRefresh: %s", err)
				return nil, "", err
			}
		}

		if resp == nil {
			// Sometimes AWS just has consistency issues and doesn't see
			// our instance yet. Return an empty state.
			return nil, "", nil
		}

		subnet := &resp.Subnets[0]
		return subnet, subnet.State, nil
	}
}
<commit_msg>providers/aws: wait for resource to be gon<commit_after>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/hashicorp/terraform/helper/diff"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/goamz/ec2"
)

func resource_aws_subnet_create(
	s *terraform.ResourceState,
	d *terraform.ResourceDiff,
	meta interface{}) (*terraform.ResourceState, error) {
	p := meta.(*ResourceProvider)
	ec2conn := p.ec2conn

	// Merge the diff so that we have all the proper attributes
	s = s.MergeDiff(d)

	// Create the Subnet
	createOpts := &ec2.CreateSubnet{
		AvailabilityZone: s.Attributes["availability_zone"],
		CidrBlock:        s.Attributes["cidr_block"],
		VpcId:            s.Attributes["vpc_id"],
	}
	log.Printf("[DEBUG] Subnet create config: %#v", createOpts)
	resp, err := ec2conn.CreateSubnet(createOpts)
	if err != nil {
		return nil, fmt.Errorf("Error creating subnet: %s", err)
	}

	// Get the ID and store it
	subnet := &resp.Subnet
	s.ID = subnet.SubnetId
	log.Printf("[INFO] Subnet ID: %s", s.ID)

	// Wait for the Subnet to become available
	log.Printf(
		"[DEBUG] Waiting for subnet (%s) to become available",
		s.ID)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"pending"},
		Target:  "available",
		Refresh: SubnetStateRefreshFunc(ec2conn, s.ID),
		Timeout: 10 * time.Minute,
	}
	subnetRaw, err := stateConf.WaitForState()
	if err != nil {
		return s, fmt.Errorf(
			"Error waiting for subnet (%s) to become available: %s",
			s.ID, err)
	}

	// Update our attributes and return
	return resource_aws_subnet_update_state(s, subnetRaw.(*ec2.Subnet))
}

func resource_aws_subnet_update(
	s *terraform.ResourceState,
	d *terraform.ResourceDiff,
	meta interface{}) (*terraform.ResourceState, error) {
	// This should never be called because we have no update-able
	// attributes
	panic("Update for subnet is not supported")

	return nil, nil
}

func resource_aws_subnet_destroy(
	s *terraform.ResourceState,
	meta interface{}) error {
	p := meta.(*ResourceProvider)
	ec2conn := p.ec2conn

	log.Printf("[INFO] Deleting Subnet: %s", s.ID)
	if _, err := ec2conn.DeleteSubnet(s.ID); err != nil {
		return fmt.Errorf("Error deleting subnet: %s", err)
	}

	// Wait for the Subnet to actually delete
	log.Printf("[DEBUG] Waiting for subnet (%s) to delete", s.ID)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"available", "pending"},
		Target:  "",
		Refresh: SubnetStateRefreshFunc(ec2conn, s.ID),
		Timeout: 10 * time.Minute,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf(
			"Error waiting for subnet (%s) to destroy",
			s.ID, err)
	}

	return nil
}

func resource_aws_subnet_refresh(
	s *terraform.ResourceState,
	meta interface{}) (*terraform.ResourceState, error) {
	p := meta.(*ResourceProvider)
	ec2conn := p.ec2conn

	subnetRaw, _, err := SubnetStateRefreshFunc(ec2conn, s.ID)()
	if err != nil {
		return s, err
	}
	if subnetRaw == nil {
		return nil, nil
	}

	subnet := subnetRaw.(*ec2.Subnet)
	return resource_aws_subnet_update_state(s, subnet)
}

func resource_aws_subnet_diff(
	s *terraform.ResourceState,
	c *terraform.ResourceConfig,
	meta interface{}) (*terraform.ResourceDiff, error) {
	b := &diff.ResourceBuilder{
		Attrs: map[string]diff.AttrType{
			"availability_zone": diff.AttrTypeCreate,
			"cidr_block":        diff.AttrTypeCreate,
			"vpc_id":            diff.AttrTypeCreate,
		},
	}

	return b.Diff(s, c)
}

func resource_aws_subnet_update_state(
	s *terraform.ResourceState,
	subnet *ec2.Subnet) (*terraform.ResourceState, error) {
	s.Attributes["availability_zone"] = subnet.AvailabilityZone
	s.Attributes["cidr_block"] = subnet.CidrBlock
	s.Attributes["vpc_id"] = subnet.VpcId

	// We belong to a VPC
	s.Dependencies = []terraform.ResourceDependency{
		terraform.ResourceDependency{ID: subnet.VpcId},
	}

	return s, nil
}

// SubnetStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// a Subnet.
func SubnetStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		resp, err := conn.DescribeSubnets([]string{id}, ec2.NewFilter())
		if err != nil {
			if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidSubnetID.NotFound" {
				resp = nil
			} else {
				log.Printf("Error on SubnetStateRefresh: %s", err)
				return nil, "", err
			}
		}

		if resp == nil {
			// Sometimes AWS just has consistency issues and doesn't see
			// our instance yet. Return an empty state.
			return nil, "", nil
		}

		subnet := &resp.Subnets[0]
		return subnet, subnet.State, nil
	}
}
<|endoftext|>


<commit_before>package sdl

import (
	"fmt"
	"reflect"
	"strings"
	"testing"
)

// TODO: SysWMInfo
// TODO: RendererInfo
// TODO: AudioCVT
func TestStructABI(t *testing.T) {
	var tests = []struct {
		gStruct interface{}
		cStruct interface{}
	}{
		{AudioSpec{}, cAudioSpec{}},
		{DisplayMode{}, cDisplayMode{}},
		{Palette{}, cPalette{}},
		{PixelFormat{}, cPixelFormat{}},
		{Surface{}, cSurface{}},
		{Version{}, cVersion{}},
		{WindowEvent{}, cWindowEvent{}},

		// EVENTS
		{KeyDownEvent{}, cKeyboardEvent{}},
		{KeyUpEvent{}, cKeyboardEvent{}},
		{MouseButtonEvent{}, cMouseButtonEvent{}},
		{MouseMotionEvent{}, cMouseMotionEvent{}},
		{MouseWheelEvent{}, cMouseWheelEvent{}},
		{TextEditingEvent{}, cTextEditingEvent{}},
		{TextInputEvent{}, cTextInputEvent{}},
		{JoyAxisEvent{}, cJoyAxisEvent{}},
		{JoyBallEvent{}, cJoyBallEvent{}},
		{JoyHatEvent{}, cJoyHatEvent{}},
		{JoyButtonEvent{}, cJoyButtonEvent{}},
		{ControllerAxisEvent{}, cControllerAxisEvent{}},
		{ControllerButtonEvent{}, cControllerButtonEvent{}},
		{ControllerDeviceEvent{}, cControllerDeviceEvent{}},
		{TouchFingerEvent{}, cTouchFingerEvent{}},
		{MultiGestureEvent{}, cMultiGestureEvent{}},
		{DollarGestureEvent{}, cDollarGestureEvent{}},
		{DropEvent{}, cDropEvent{}},
		{UserEvent{}, cUserEvent{}},
		{SysWMEvent{}, cSysWMEvent{}},
	}

	for _, test := range tests {
		testABI(t, test.gStruct, test.cStruct)
	}
}

// TODO: mixer.MusicType
// TODO: mixer.Fading
func TestTypeABI(t *testing.T) {
	var tests = []struct {
		gType interface{}
		cType interface{}
	}{
		{AudioStatus(0), cAudioStatus(0)},
		{ErrorCode(0), cErrorCode(0)},
		{RendererFlip(0), cRendererFlip(0)},
	}

	for _, test := range tests {
		testABI(t, test.gType, test.cType)
	}
}

func testABI(t *testing.T, a, b interface{}) {
	ta, tb := reflect.TypeOf(a), reflect.TypeOf(b)
	if ta.Size() != tb.Size() || edgeCases(a, b) {
		t.Errorf("type size mismatch: %s(%d) != %s(%d)",
			ta.Name(), ta.Size(), tb.Name(), tb.Size())
		t.Fail()
	}
	if ta.Kind() != tb.Kind() {
		t.Errorf("type kind mismatch: %s=%s != %s=%s",
			ta.Name(), ta.Kind(), tb.Name(), tb.Kind())
		t.Fail()
	}

	if ta.Kind() == reflect.Struct {
		for i := 0; i < ta.NumField(); i++ {
			f := ta.Field(i)
			if f.Name == "_" {
				// ignore padding fields
				continue
			}
			if err := verifyField(t, f, tb); err != nil {
				dumpStructFormat(t, ta)
				dumpStructFormat(t, tb)
				t.Error(err)
			}
		}
	}
}

// @note: add checks by SDL version for known edge-cases
func edgeCases(a, b interface{}) bool {
	if !VERSION_ATLEAST(2, 0, 4) {
		if _, ok := a.(MouseWheelEvent); !ok {
			return true
		}
	}

	return false
}

func dumpStructFormat(t *testing.T, s reflect.Type) {
	const dFormat = "%5d %30s %8d %8d"
	var sFormat = strings.Replace(dFormat, "d", "s", -1)

	t.Logf(sFormat, "index", "name", "offset", "size")
	for i := 0; i < s.NumField(); i++ {
		f := s.Field(i)
		t.Logf(dFormat, i, s.Name()+"."+f.Name, f.Offset, f.Type.Size())
	}
}

func verifyField(t *testing.T, gField reflect.StructField, cType reflect.Type) error {
	var cField = searchField(cType, gField.Name)
	if cField == nil {
		return fmt.Errorf("field not found: %q", gField.Name)
	}
	gOffset, gSize := gField.Offset, gField.Type.Size()
	cOffset, cSize := cField.Offset, cField.Type.Size()
	if cOffset != gOffset || cSize != gSize {
		return fmt.Errorf("field offset/size mismatch %s(%d, %d) != %s(%d, %d)",
			gField.Name, gOffset, gSize,
			cField.Name, cOffset, cSize)
	}
	return nil
}

func searchField(t reflect.Type, gName string) *reflect.StructField {
	for i := 0; i < t.NumField(); i++ {
		var f = t.Field(i)
		// convert c field_name to fieldname
		var cName = strings.Replace(f.Name, "_", "", -1)
		if strings.EqualFold(gName, cName) {
			return &f
		}
	}
	return nil
}
<commit_msg>sdl: struct_test: implement new technique for testing some struct ABIs<commit_after>package sdl

import (
	"fmt"
	"reflect"
	"strings"
	"testing"
)

type EdgeCaseFunc func(t *testing.T, a, b interface{})

func structMatch(t *testing.T, ta, tb reflect.Type) {
	structMatchGeneric(t, ta, tb, ta.Size(), tb.Size())
}

func structMatchFixed(t *testing.T, ta, tb reflect.Type, szb uintptr) {
	structMatchGeneric(t, ta, tb, ta.Size(), szb)
}

func structMatchGeneric(t *testing.T, ta, tb reflect.Type, sza, szb uintptr) {
	if sza != szb {
		t.Errorf("type size mismatch: %s(%d) != %s(%d)",
			ta.Name(), ta.Size(), tb.Name(), tb.Size())
		t.Fail()
	}

	if ta.Kind() != tb.Kind() {
		t.Errorf("type kind mismatch: %s=%s != %s=%s",
			ta.Name(), ta.Kind(), tb.Name(), tb.Kind())
		t.Fail()
	}
}

func mouseWheelEventEdgeCase(t *testing.T, a, b interface{}) {
	ta, tb := reflect.TypeOf(a), reflect.TypeOf(b)

	if VERSION_ATLEAST(2, 0, 4) {
		structMatchFixed(t, ta, tb, 28)
	} else {
		structMatchFixed(t, ta, tb, 24)
	}
}

// TODO: SysWMInfo
// TODO: RendererInfo
// TODO: AudioCVT
func TestStructABI(t *testing.T) {
	var tests = []struct {
		gStruct interface{}
		cStruct interface{}
		isEdgeCase EdgeCaseFunc
	}{
		{AudioSpec{}, cAudioSpec{}, nil},
		{DisplayMode{}, cDisplayMode{}, nil},
		{Palette{}, cPalette{}, nil},
		{PixelFormat{}, cPixelFormat{}, nil},
		{Surface{}, cSurface{}, nil},
		{Version{}, cVersion{}, nil},
		{WindowEvent{}, cWindowEvent{}, nil},

		// EVENTS
		{KeyDownEvent{}, cKeyboardEvent{}, nil},
		{KeyUpEvent{}, cKeyboardEvent{}, nil},
		{MouseButtonEvent{}, cMouseButtonEvent{}, nil},
		{MouseMotionEvent{}, cMouseMotionEvent{}, nil},
		{MouseWheelEvent{}, cMouseWheelEvent{}, mouseWheelEventEdgeCase},
		{TextEditingEvent{}, cTextEditingEvent{}, nil},
		{TextInputEvent{}, cTextInputEvent{}, nil},
		{JoyAxisEvent{}, cJoyAxisEvent{}, nil},
		{JoyBallEvent{}, cJoyBallEvent{}, nil},
		{JoyHatEvent{}, cJoyHatEvent{}, nil},
		{JoyButtonEvent{}, cJoyButtonEvent{}, nil},
		{ControllerAxisEvent{}, cControllerAxisEvent{}, nil},
		{ControllerButtonEvent{}, cControllerButtonEvent{}, nil},
		{ControllerDeviceEvent{}, cControllerDeviceEvent{}, nil},
		{TouchFingerEvent{}, cTouchFingerEvent{}, nil},
		{MultiGestureEvent{}, cMultiGestureEvent{}, nil},
		{DollarGestureEvent{}, cDollarGestureEvent{}, nil},
		{DropEvent{}, cDropEvent{}, nil},
		{UserEvent{}, cUserEvent{}, nil},
		{SysWMEvent{}, cSysWMEvent{}, nil},
	}

	for _, test := range tests {
		testABI(t, test.gStruct, test.cStruct, test.isEdgeCase)
	}
}

// TODO: mixer.MusicType
// TODO: mixer.Fading
func TestTypeABI(t *testing.T) {
	var tests = []struct {
		gType interface{}
		cType interface{}
	}{
		{AudioStatus(0), cAudioStatus(0)},
		{ErrorCode(0), cErrorCode(0)},
		{RendererFlip(0), cRendererFlip(0)},
	}

	for _, test := range tests {
		testABI(t, test.gType, test.cType, nil)
	}
}

func testABI(t *testing.T, a, b interface{}, f EdgeCaseFunc) {
	ta, tb := reflect.TypeOf(a), reflect.TypeOf(b)

	if f != nil {
		f(t, a, b)
	} else {
		structMatch(t, ta, tb)
	}

	if ta.Kind() == reflect.Struct {
		for i := 0; i < ta.NumField(); i++ {
			f := ta.Field(i)
			if f.Name == "_" {
				// ignore padding fields
				continue
			}
			if err := verifyField(t, f, tb); err != nil {
				dumpStructFormat(t, ta)
				dumpStructFormat(t, tb)
				t.Error(err)
			}
		}
	}
}

func dumpStructFormat(t *testing.T, s reflect.Type) {
	const dFormat = "%5d %30s %8d %8d"
	var sFormat = strings.Replace(dFormat, "d", "s", -1)

	t.Logf(sFormat, "index", "name", "offset", "size")
	for i := 0; i < s.NumField(); i++ {
		f := s.Field(i)
		t.Logf(dFormat, i, s.Name()+"."+f.Name, f.Offset, f.Type.Size())
	}
}

func verifyField(t *testing.T, gField reflect.StructField, cType reflect.Type) error {
	var cField = searchField(cType, gField.Name)
	if cField == nil {
		return fmt.Errorf("field not found: %q", gField.Name)
	}
	gOffset, gSize := gField.Offset, gField.Type.Size()
	cOffset, cSize := cField.Offset, cField.Type.Size()
	if cOffset != gOffset || cSize != gSize {
		return fmt.Errorf("field offset/size mismatch %s(%d, %d) != %s(%d, %d)",
			gField.Name, gOffset, gSize,
			cField.Name, cOffset, cSize)
	}
	return nil
}

func searchField(t reflect.Type, gName string) *reflect.StructField {
	for i := 0; i < t.NumField(); i++ {
		var f = t.Field(i)
		// convert c field_name to fieldname
		var cName = strings.Replace(f.Name, "_", "", -1)
		if strings.EqualFold(gName, cName) {
			return &f
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package server

import (
	"errors"

	"github.com/emersion/imap/common"
	"github.com/emersion/imap/commands"
	"github.com/emersion/imap/responses"
)

type Select struct {
	commands.Select
}

func (cmd *Select) Handle(conn *Conn) error {
	if conn.User == nil {
		return errors.New("Not authenticated")
	}

	mbox, err := conn.User.GetMailbox(cmd.Mailbox)
	if err != nil {
		return err
	}

	items := []string{"MESSAGES", "RECENT", "UNSEEN", "UIDNEXT", "UIDVALIDITY"}
	status, err := mbox.Status(items)
	if err != nil {
		return err
	}

	conn.Mailbox = mbox
	conn.MailboxReadOnly = cmd.ReadOnly

	flags := make([]interface{}, len(status.Flags))
	for i, f := range status.Flags {
		flags[i] = f
	}
	res := common.NewUntaggedResp([]interface{}{"FLAGS", flags})
	if err := res.WriteTo(conn.Writer); err != nil {
		return err
	}

	res = common.NewUntaggedResp([]interface{}{status.Messages, "EXISTS"})
	if err := res.WriteTo(conn.Writer); err != nil {
		return err
	}

	res = common.NewUntaggedResp([]interface{}{status.Recent, "RECENT"})
	if err := res.WriteTo(conn.Writer); err != nil {
		return err
	}

	statusRes := &common.StatusResp{
		Tag: "*",
		Type: common.OK,
		Code: "UNSEEN",
		Arguments: []interface{}{status.Unseen},
	}
	if err := statusRes.WriteTo(conn.Writer); err != nil {
		return err
	}

	flags = make([]interface{}, len(status.PermanentFlags))
	for i, f := range status.PermanentFlags {
		flags[i] = f
	}
	statusRes = &common.StatusResp{
		Tag: "*",
		Type: common.OK,
		Code: "PERMANENTFLAGS",
		Arguments: []interface{}{flags},
	}
	if err := statusRes.WriteTo(conn.Writer); err != nil {
		return err
	}

	statusRes = &common.StatusResp{
		Tag: "*",
		Type: common.OK,
		Code: "UIDNEXT",
		Arguments: []interface{}{status.UidNext},
	}
	if err := statusRes.WriteTo(conn.Writer); err != nil {
		return err
	}

	statusRes = &common.StatusResp{
		Tag: "*",
		Type: common.OK,
		Code: "UIDVALIDITY",
		Arguments: []interface{}{status.UidValidity},
	}
	if err := statusRes.WriteTo(conn.Writer); err != nil {
		return err
	}

	return nil
}

type List struct {
	commands.List
}

func (cmd *List) Handle(conn *Conn) error {
	if conn.User == nil {
		return errors.New("Not authenticated")
	}

	done := make(chan error)
	defer close(done)

	ch := make(chan *common.MailboxInfo)
	res := responses.List{Mailboxes: ch}

	go (func () {
		done <- res.WriteTo(conn.Writer)
	})()

	mailboxes, err := conn.User.ListMailboxes()
	if err != nil {
		close(ch)
		return err
	}

	for _, mbox := range mailboxes {
		// TODO: filter mailboxes with cmd.Reference and cmd.Mailbox

		info, err := mbox.Info()
		if err != nil {
			close(ch)
			return err
		}

		ch <- info
	}

	close(ch)

	return <-done
}

type Status struct {
	commands.Status
}

func (cmd *Status) Handle(conn *Conn) error {
	if conn.User == nil {
		return errors.New("Not authenticated")
	}

	mbox, err := conn.User.GetMailbox(cmd.Mailbox)
	if err != nil {
		return err
	}

	status, err := mbox.Status(cmd.Items)
	if err != nil {
		return err
	}

	res := responses.Status{Mailbox: status}
	return res.WriteTo(conn.Writer)
}
<commit_msg>Opens mailbox in read-only mode if backend doesn't open it in read-write mode<commit_after>package server

import (
	"errors"

	"github.com/emersion/imap/common"
	"github.com/emersion/imap/commands"
	"github.com/emersion/imap/responses"
)

type Select struct {
	commands.Select
}

func (cmd *Select) Handle(conn *Conn) error {
	if conn.User == nil {
		return errors.New("Not authenticated")
	}

	mbox, err := conn.User.GetMailbox(cmd.Mailbox)
	if err != nil {
		return err
	}

	items := []string{"MESSAGES", "RECENT", "UNSEEN", "UIDNEXT", "UIDVALIDITY"}
	status, err := mbox.Status(items)
	if err != nil {
		return err
	}

	conn.Mailbox = mbox
	conn.MailboxReadOnly = cmd.ReadOnly || status.ReadOnly

	flags := make([]interface{}, len(status.Flags))
	for i, f := range status.Flags {
		flags[i] = f
	}
	res := common.NewUntaggedResp([]interface{}{"FLAGS", flags})
	if err := res.WriteTo(conn.Writer); err != nil {
		return err
	}

	res = common.NewUntaggedResp([]interface{}{status.Messages, "EXISTS"})
	if err := res.WriteTo(conn.Writer); err != nil {
		return err
	}

	res = common.NewUntaggedResp([]interface{}{status.Recent, "RECENT"})
	if err := res.WriteTo(conn.Writer); err != nil {
		return err
	}

	statusRes := &common.StatusResp{
		Tag: "*",
		Type: common.OK,
		Code: "UNSEEN",
		Arguments: []interface{}{status.Unseen},
	}
	if err := statusRes.WriteTo(conn.Writer); err != nil {
		return err
	}

	flags = make([]interface{}, len(status.PermanentFlags))
	for i, f := range status.PermanentFlags {
		flags[i] = f
	}
	statusRes = &common.StatusResp{
		Tag: "*",
		Type: common.OK,
		Code: "PERMANENTFLAGS",
		Arguments: []interface{}{flags},
	}
	if err := statusRes.WriteTo(conn.Writer); err != nil {
		return err
	}

	statusRes = &common.StatusResp{
		Tag: "*",
		Type: common.OK,
		Code: "UIDNEXT",
		Arguments: []interface{}{status.UidNext},
	}
	if err := statusRes.WriteTo(conn.Writer); err != nil {
		return err
	}

	statusRes = &common.StatusResp{
		Tag: "*",
		Type: common.OK,
		Code: "UIDVALIDITY",
		Arguments: []interface{}{status.UidValidity},
	}
	if err := statusRes.WriteTo(conn.Writer); err != nil {
		return err
	}

	return nil
}

type List struct {
	commands.List
}

func (cmd *List) Handle(conn *Conn) error {
	if conn.User == nil {
		return errors.New("Not authenticated")
	}

	done := make(chan error)
	defer close(done)

	ch := make(chan *common.MailboxInfo)
	res := responses.List{Mailboxes: ch}

	go (func () {
		done <- res.WriteTo(conn.Writer)
	})()

	mailboxes, err := conn.User.ListMailboxes()
	if err != nil {
		close(ch)
		return err
	}

	for _, mbox := range mailboxes {
		// TODO: filter mailboxes with cmd.Reference and cmd.Mailbox

		info, err := mbox.Info()
		if err != nil {
			close(ch)
			return err
		}

		ch <- info
	}

	close(ch)

	return <-done
}

type Status struct {
	commands.Status
}

func (cmd *Status) Handle(conn *Conn) error {
	if conn.User == nil {
		return errors.New("Not authenticated")
	}

	mbox, err := conn.User.GetMailbox(cmd.Mailbox)
	if err != nil {
		return err
	}

	status, err := mbox.Status(cmd.Items)
	if err != nil {
		return err
	}

	res := responses.Status{Mailbox: status}
	return res.WriteTo(conn.Writer)
}
<|endoftext|>


<commit_before>package core

type (
	// EnvVariable defines `env_variables` db table.
	EnvVariable struct {
		ID           uint       `gorm:"primary_key;auto_increment;not null" json:"id"`
		Key          string     `gorm:"not null" json:"key"`
		Value        string     `gorm:"not null" json:"value"`
		Secret       bool       `gorm:"not null,default:false" json:"secret"`
		RepositoryID uint       `gorm:"not null" json:"repositoryID"`
		Repository   Repository `json:"repository"`
		Timestamp
	}

	// EnvVariableStore defines operations on environment variables
	// in datastore.
	EnvVariableStore interface {
		// Find returns env variable from datastore.
		Find(uint) (*EnvVariable, error)

		// List returns list of environment variables from the datastore.
		List(uint) ([]*EnvVariable, error)

		// Create persists a new env variable to the datastore.
		Create(*EnvVariable) error

		// Update persists updated env variable to the datastore.
		Update(*EnvVariable) error

		// Delete deletes env variable from the datastore.
		Delete(*EnvVariable) error
	}
)
<commit_msg>fix(env): change column type for env variables<commit_after>package core

type (
	// EnvVariable defines `env_variables` db table.
	EnvVariable struct {
		ID           uint       `gorm:"primary_key;auto_increment;not null" json:"id"`
		Key          string     `gorm:"not null" json:"key"`
		Value        string     `gorm:"not null" sql:"type:text" json:"value"`
		Secret       bool       `gorm:"not null,default:false" json:"secret"`
		RepositoryID uint       `gorm:"not null" json:"repositoryID"`
		Repository   Repository `json:"repository"`
		Timestamp
	}

	// EnvVariableStore defines operations on environment variables
	// in datastore.
	EnvVariableStore interface {
		// Find returns env variable from datastore.
		Find(uint) (*EnvVariable, error)

		// List returns list of environment variables from the datastore.
		List(uint) ([]*EnvVariable, error)

		// Create persists a new env variable to the datastore.
		Create(*EnvVariable) error

		// Update persists updated env variable to the datastore.
		Update(*EnvVariable) error

		// Delete deletes env variable from the datastore.
		Delete(*EnvVariable) error
	}
)
<|endoftext|>


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

package platform

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/coreos/coreos-cloudinit/config"
	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/satori/go.uuid"
	"github.com/coreos/mantle/Godeps/_workspace/src/golang.org/x/crypto/ssh"
	"github.com/coreos/mantle/platform/local"
	"github.com/coreos/mantle/util"
)

type QEMUOptions struct {
	DiskImage string
}

type qemuCluster struct {
	*local.LocalCluster
	machines map[string]*qemuMachine
	conf     QEMUOptions
}

type qemuMachine struct {
	qc          *qemuCluster
	id          string
	qemu        util.Cmd
	configDrive *local.ConfigDrive
	netif       *local.Interface
	sshClient   *ssh.Client
}

func NewQemuCluster(conf QEMUOptions) (Cluster, error) {
	lc, err := local.NewLocalCluster()
	if err != nil {
		return nil, err
	}

	qc := &qemuCluster{
		LocalCluster: lc,
		machines:     make(map[string]*qemuMachine),
		conf:         conf,
	}
	return Cluster(qc), nil
}

func (qc *qemuCluster) Machines() []Machine {
	machines := make([]Machine, 0, len(qc.machines))
	for _, m := range qc.machines {
		machines = append(machines, m)
	}
	return machines
}

func (qc *qemuCluster) Destroy() error {
	for _, qm := range qc.machines {
		qm.Destroy()
	}
	return qc.LocalCluster.Destroy()
}

func (qc *qemuCluster) NewMachine(cfg string) (Machine, error) {
	id := uuid.NewV4()

	// hacky solution for cloud config ip substitution
	// NOTE: escaping is not supported
	netif := qc.Dnsmasq.GetInterface("br0")
	ip := strings.Split(netif.DHCPv4[0].String(), "/")[0]
	cfg = strings.Replace(cfg, "$public_ipv4", ip, -1)
	cfg = strings.Replace(cfg, "$private_ipv4", ip, -1)

	cloudConfig, err := config.NewCloudConfig(cfg)
	if err != nil {
		return nil, err
	}

	if err = qc.SSHAgent.UpdateConfig(cloudConfig); err != nil {
		return nil, err
	}

	if cloudConfig.Hostname == "" {
		cloudConfig.Hostname = id.String()[:8]
	}

	configDrive, err := local.NewConfigDrive(cloudConfig)
	if err != nil {
		return nil, err
	}

	qm := &qemuMachine{
		qc:          qc,
		id:          id.String(),
		configDrive: configDrive,
		netif:       netif,
	}

	disk, err := setupDisk(qc.conf.DiskImage)
	if err != nil {
		return nil, err
	}
	defer disk.Close()

	tap, err := qc.NewTap("br0")
	if err != nil {
		return nil, err
	}
	defer tap.Close()

	qmMac := qm.netif.HardwareAddr.String()
	qmCfg := qm.configDrive.Directory
	qm.qemu = qm.qc.NewCommand(
		"qemu-system-x86_64",
		"-machine", "accel=kvm",
		"-cpu", "host",
		"-smp", "2",
		"-m", "1024",
		"-uuid", qm.id,
		"-display", "none",
		"-add-fd", "fd=3,set=1",
		"-drive", "file=/dev/fdset/1,media=disk,if=virtio,format=raw",
		"-netdev", "tap,id=tap,fd=4",
		"-device", "virtio-net,netdev=tap,mac="+qmMac,
		"-fsdev", "local,id=cfg,security_model=none,readonly,path="+qmCfg,
		"-device", "virtio-9p-pci,fsdev=cfg,mount_tag=config-2")

	cmd := qm.qemu.(*local.NsCmd)
	cmd.Stderr = os.Stderr
	cmd.ExtraFiles = append(cmd.ExtraFiles, disk)     // fd=3
	cmd.ExtraFiles = append(cmd.ExtraFiles, tap.File) // fd=4

	if err = qm.qemu.Start(); err != nil {
		return nil, err
	}

	// Allow a few authentication failures in case setup is slow.
	sshchecker := func() error {
		qm.sshClient, err = qm.qc.SSHAgent.NewClient(qm.IP())
		if err != nil {
			return err
		}
		return nil
	}

	if err := util.Retry(sshRetries, sshTimeout, sshchecker); err != nil {
		return nil, err
	}

	if err != nil {
		qm.Destroy()
		return nil, err
	}

	out, err := qm.SSH("grep ^ID= /etc/os-release")
	if err != nil {
		qm.Destroy()
		return nil, err
	}

	if !bytes.Equal(out, []byte("ID=coreos")) {
		qm.Destroy()
		return nil, fmt.Errorf("Unexpected SSH output: %s", out)
	}

	qc.machines[qm.ID()] = qm

	return Machine(qm), nil
}

// Copy the base image to a new nameless temporary file.
// cp is used since it supports sparse and reflink.
func setupDisk(imageFile string) (*os.File, error) {
	dstFile, err := ioutil.TempFile("", "mantle-qemu")
	if err != nil {
		return nil, err
	}
	dstFileName := dstFile.Name()
	defer os.Remove(dstFileName)
	dstFile.Close()

	cp := exec.Command("cp", "--force",
		"--sparse=always", "--reflink=auto",
		imageFile, dstFileName)
	cp.Stdout = os.Stdout
	cp.Stderr = os.Stderr

	if err := cp.Run(); err != nil {
		return nil, err
	}

	return os.OpenFile(dstFileName, os.O_RDWR, 0)
}

func (m *qemuMachine) ID() string {
	return m.id
}

func (m *qemuMachine) IP() string {
	return m.netif.DHCPv4[0].IP.String()
}

func (m *qemuMachine) PrivateIP() string {
	return m.netif.DHCPv4[0].IP.String()
}

func (qm *qemuMachine) SSHSession() (*ssh.Session, error) {
	session, err := qm.sshClient.NewSession()
	if err != nil {
		return nil, err
	}

	return session, nil
}

func (qm *qemuMachine) SSH(cmd string) ([]byte, error) {
	session, err := qm.SSHSession()
	if err != nil {
		return []byte{}, err
	}
	defer session.Close()

	session.Stderr = os.Stderr
	out, err := session.Output(cmd)
	out = bytes.TrimSpace(out)
	return out, err
}

func (qm *qemuMachine) StartJournal() error {
	s, err := qm.SSHSession()
	if err != nil {
		return fmt.Errorf("SSH session failed: %v", err)
	}

	s.Stdout = os.Stdout
	s.Stderr = os.Stderr
	go func() {
		s.Run("journalctl -f")
		s.Close()
	}()

	return nil
}

func (qm *qemuMachine) Destroy() error {
	if qm.sshClient != nil {
		qm.sshClient.Close()
	}
	err := qm.qemu.Kill()

	if qm.configDrive != nil {
		err2 := qm.configDrive.Destroy()
		if err == nil && err2 != nil {
			err = err2
		}
	}

	delete(qm.qc.machines, qm.ID())
	return err
}
<commit_msg>platform: use a lock to prevent races around qemu cluster<commit_after>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package platform

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"sync"

	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/coreos/coreos-cloudinit/config"
	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/satori/go.uuid"
	"github.com/coreos/mantle/Godeps/_workspace/src/golang.org/x/crypto/ssh"
	"github.com/coreos/mantle/platform/local"
	"github.com/coreos/mantle/util"
)

type QEMUOptions struct {
	DiskImage string
}

type qemuCluster struct {
	mu sync.Mutex
	*local.LocalCluster
	machines map[string]*qemuMachine
	conf     QEMUOptions
}

type qemuMachine struct {
	qc          *qemuCluster
	id          string
	qemu        util.Cmd
	configDrive *local.ConfigDrive
	netif       *local.Interface
	sshClient   *ssh.Client
}

func NewQemuCluster(conf QEMUOptions) (Cluster, error) {
	lc, err := local.NewLocalCluster()
	if err != nil {
		return nil, err
	}

	qc := &qemuCluster{
		LocalCluster: lc,
		machines:     make(map[string]*qemuMachine),
		conf:         conf,
	}
	return Cluster(qc), nil
}

func (qc *qemuCluster) Machines() []Machine {
	machines := make([]Machine, 0, len(qc.machines))
	qc.mu.Lock()
	defer qc.mu.Unlock()
	for _, m := range qc.machines {
		machines = append(machines, m)
	}
	return machines
}

func (qc *qemuCluster) Destroy() error {
	qc.mu.Lock()
	defer qc.mu.Unlock()
	for _, qm := range qc.machines {
		qm.Destroy()
	}
	return qc.LocalCluster.Destroy()
}

func (qc *qemuCluster) NewMachine(cfg string) (Machine, error) {
	id := uuid.NewV4()

	// hacky solution for cloud config ip substitution
	// NOTE: escaping is not supported
	qc.mu.Lock()
	netif := qc.Dnsmasq.GetInterface("br0")
	ip := strings.Split(netif.DHCPv4[0].String(), "/")[0]

	cfg = strings.Replace(cfg, "$public_ipv4", ip, -1)
	cfg = strings.Replace(cfg, "$private_ipv4", ip, -1)

	cloudConfig, err := config.NewCloudConfig(cfg)
	if err != nil {
		qc.mu.Unlock()
		return nil, err
	}

	if err = qc.SSHAgent.UpdateConfig(cloudConfig); err != nil {
		qc.mu.Unlock()
		return nil, err
	}

	if cloudConfig.Hostname == "" {
		cloudConfig.Hostname = id.String()[:8]
	}

	qc.mu.Unlock()

	configDrive, err := local.NewConfigDrive(cloudConfig)
	if err != nil {
		return nil, err
	}

	qm := &qemuMachine{
		qc:          qc,
		id:          id.String(),
		configDrive: configDrive,
		netif:       netif,
	}

	disk, err := setupDisk(qc.conf.DiskImage)
	if err != nil {
		return nil, err
	}
	defer disk.Close()

	qc.mu.Lock()

	tap, err := qc.NewTap("br0")
	if err != nil {
		qc.mu.Unlock()
		return nil, err
	}
	defer tap.Close()

	qmMac := qm.netif.HardwareAddr.String()
	qmCfg := qm.configDrive.Directory
	qm.qemu = qm.qc.NewCommand(
		"qemu-system-x86_64",
		"-machine", "accel=kvm",
		"-cpu", "host",
		"-smp", "2",
		"-m", "1024",
		"-uuid", qm.id,
		"-display", "none",
		"-add-fd", "fd=3,set=1",
		"-drive", "file=/dev/fdset/1,media=disk,if=virtio,format=raw",
		"-netdev", "tap,id=tap,fd=4",
		"-device", "virtio-net,netdev=tap,mac="+qmMac,
		"-fsdev", "local,id=cfg,security_model=none,readonly,path="+qmCfg,
		"-device", "virtio-9p-pci,fsdev=cfg,mount_tag=config-2")

	qc.mu.Unlock()

	cmd := qm.qemu.(*local.NsCmd)
	cmd.Stderr = os.Stderr
	cmd.ExtraFiles = append(cmd.ExtraFiles, disk)     // fd=3
	cmd.ExtraFiles = append(cmd.ExtraFiles, tap.File) // fd=4

	if err = qm.qemu.Start(); err != nil {
		return nil, err
	}

	// Allow a few authentication failures in case setup is slow.
	sshchecker := func() error {
		qm.qc.mu.Lock()
		defer qm.qc.mu.Unlock()
		qm.sshClient, err = qm.qc.SSHAgent.NewClient(qm.IP())
		if err != nil {
			return err
		}
		return nil
	}

	if err := util.Retry(sshRetries, sshTimeout, sshchecker); err != nil {
		return nil, err
	}

	if err != nil {
		qm.Destroy()
		return nil, err
	}

	out, err := qm.SSH("grep ^ID= /etc/os-release")
	if err != nil {
		qm.Destroy()
		return nil, err
	}

	if !bytes.Equal(out, []byte("ID=coreos")) {
		qm.Destroy()
		return nil, fmt.Errorf("Unexpected SSH output: %s", out)
	}

	qc.mu.Lock()
	qc.machines[qm.ID()] = qm
	qc.mu.Unlock()

	return Machine(qm), nil
}

// Copy the base image to a new nameless temporary file.
// cp is used since it supports sparse and reflink.
func setupDisk(imageFile string) (*os.File, error) {
	dstFile, err := ioutil.TempFile("", "mantle-qemu")
	if err != nil {
		return nil, err
	}
	dstFileName := dstFile.Name()
	defer os.Remove(dstFileName)
	dstFile.Close()

	cp := exec.Command("cp", "--force",
		"--sparse=always", "--reflink=auto",
		imageFile, dstFileName)
	cp.Stdout = os.Stdout
	cp.Stderr = os.Stderr

	if err := cp.Run(); err != nil {
		return nil, err
	}

	return os.OpenFile(dstFileName, os.O_RDWR, 0)
}

func (m *qemuMachine) ID() string {
	return m.id
}

func (m *qemuMachine) IP() string {
	return m.netif.DHCPv4[0].IP.String()
}

func (m *qemuMachine) PrivateIP() string {
	return m.netif.DHCPv4[0].IP.String()
}

func (qm *qemuMachine) SSHSession() (*ssh.Session, error) {
	session, err := qm.sshClient.NewSession()
	if err != nil {
		return nil, err
	}

	return session, nil
}

func (qm *qemuMachine) SSH(cmd string) ([]byte, error) {
	session, err := qm.SSHSession()
	if err != nil {
		return []byte{}, err
	}
	defer session.Close()

	session.Stderr = os.Stderr
	out, err := session.Output(cmd)
	out = bytes.TrimSpace(out)
	return out, err
}

func (qm *qemuMachine) StartJournal() error {
	s, err := qm.SSHSession()
	if err != nil {
		return fmt.Errorf("SSH session failed: %v", err)
	}

	s.Stdout = os.Stdout
	s.Stderr = os.Stderr
	go func() {
		s.Run("journalctl -f")
		s.Close()
	}()

	return nil
}

func (qm *qemuMachine) Destroy() error {
	if qm.sshClient != nil {
		qm.sshClient.Close()
	}
	err := qm.qemu.Kill()

	if qm.configDrive != nil {
		err2 := qm.configDrive.Destroy()
		if err == nil && err2 != nil {
			err = err2
		}
	}

	// ugh.
	qm.qc.mu.Lock()
	defer qm.qc.mu.Unlock()
	delete(qm.qc.machines, qm.ID())
	return err
}
<|endoftext|>


<commit_before>package plugins

import (
	"reflect"
	"runtime"
	"time"
)

type PluginHandler struct {
	plugins       plugins
	closers       map[string]chan<- struct{}
	subscriptions map[Event]plugins
	selects       []reflect.SelectCase
}

type plugins map[string]Plugin

func NewHandler() *PluginHandler {
	return &PluginHandler{
		plugins:       make(plugins),
		closers:       make(map[string]chan<- struct{}),
		subscriptions: make(map[Event]plugins),
		selects:       make([]reflect.SelectCase, 0, 16),
	}
}

type packet struct {
	Event Event
	Args  Args
}

func (ph *PluginHandler) Handle(pl Plugin) {
	ph.plugins[pl.Name()] = pl

	for _, sub := range pl.Subscribes() {
		if ph.subscriptions[sub] == nil {
			ph.subscriptions[sub] = make(plugins)
		}
		ph.subscriptions[sub][pl.Name()] = pl
	}

	f, ch, closer := generatefunc(pl)
	ph.closers[pl.Name()] = closer

	go f()

	rch := reflect.ValueOf(ch)
	ph.selects = append(ph.selects,
		reflect.SelectCase{
			Dir:  reflect.SelectRecv,
			Chan: rch,
		})

}

func (ph *PluginHandler) HandleAll(pls ...Plugin) {
	for _, pl := range pls {
		ph.Handle(pl)
	}
}

func generatefunc(pl Plugin) (func(), <-chan packet, chan<- struct{}) {
	ch, closer := make(chan packet), make(chan struct{})
	return func() {
		defer close(ch)

		if len(pl.Provides()) == 0 {
			return //Nothing is provided, so nothing to listen for.
		}

		for {
			select {
			case <-closer:
				return
			default:
				event, args, err := pl.Recieve() //Probably blocking. Hopefully not for to long.
				if err != nil {
					switch err {
					case Unblocking:
						time.Sleep(500 * time.Millisecond)
					case NotImplemented:
					default:
						return
					}
					continue
				}
				ch <- packet{event, args}
			}
			runtime.Gosched() // Allow something else to run if tight loop.
		}
	}, ch, closer
}

func (ph *PluginHandler) Unload(pl Plugin) {
	close(ph.closers[pl.Name()])
	delete(ph.closers, pl.Name())

	for _, sub := range pl.Subscribes() {
		delete(ph.subscriptions[sub], pl.Name())
	}

	delete(ph.plugins, pl.Name())
}

func (ph *PluginHandler) ListenAndServe() error {

	for len(ph.plugins) > 0 {
		if chosen, recv, ok := reflect.Select(ph.selects); ok {
			pck := recv.Interface().(packet)
			ph.Dispatch(pck.Event, pck.Args)
		} else {
			//Not ok
			//Removing select case from list
			if chosen == len(ph.selects)-1 {
				ph.selects = ph.selects[:len(ph.selects)-1]
			} else {
				ph.selects = append(ph.selects[:chosen], ph.selects[chosen+1:]...)
			}
		}
	}

	return nil
}

func (ph PluginHandler) Dispatch(e Event, args Args) error {
	return ph.dispatch("local", e, args)
}

func (ph PluginHandler) dispatch(identifier string, e Event, args Args) (err error) {
	for _, plugin := range ph.subscriptions[e] {
		go func(plugin Plugin) {
			tmp := plugin.Send(e, args)
			if err == nil && tmp != NotImplemented {
				err = tmp
			}
		}(plugin)
	}
	return
}
<commit_msg>Changed the name of the handler to be shorter since PluginHandler is redundant for a handler in the plugins package.<commit_after>package plugins

import (
	"reflect"
	"runtime"
	"time"
)

type Handler struct {
	plugins       plugins
	closers       map[string]chan<- struct{}
	subscriptions map[Event]plugins
	selects       []reflect.SelectCase
}

type plugins map[string]Plugin

func NewHandler() *Handler {
	return &Handler{
		plugins:       make(plugins),
		closers:       make(map[string]chan<- struct{}),
		subscriptions: make(map[Event]plugins),
		selects:       make([]reflect.SelectCase, 0, 16),
	}
}

type packet struct {
	Event Event
	Args  Args
}

func (ph *Handler) Handle(pl Plugin) {
	ph.plugins[pl.Name()] = pl

	for _, sub := range pl.Subscribes() {
		if ph.subscriptions[sub] == nil {
			ph.subscriptions[sub] = make(plugins)
		}
		ph.subscriptions[sub][pl.Name()] = pl
	}

	f, ch, closer := generatefunc(pl)
	ph.closers[pl.Name()] = closer

	go f()

	rch := reflect.ValueOf(ch)
	ph.selects = append(ph.selects,
		reflect.SelectCase{
			Dir:  reflect.SelectRecv,
			Chan: rch,
		})

}

func (ph *Handler) HandleAll(pls ...Plugin) {
	for _, pl := range pls {
		ph.Handle(pl)
	}
}

func generatefunc(pl Plugin) (func(), <-chan packet, chan<- struct{}) {
	ch, closer := make(chan packet), make(chan struct{})
	return func() {
		defer close(ch)

		if len(pl.Provides()) == 0 {
			return //Nothing is provided, so nothing to listen for.
		}

		for {
			select {
			case <-closer:
				return
			default:
				event, args, err := pl.Recieve() //Probably blocking. Hopefully not for to long.
				if err != nil {
					switch err {
					case Unblocking:
						time.Sleep(500 * time.Millisecond)
					case NotImplemented:
					default:
						return
					}
					continue
				}
				ch <- packet{event, args}
			}
			runtime.Gosched() // Allow something else to run if tight loop.
		}
	}, ch, closer
}

func (ph *Handler) Unload(pl Plugin) {
	close(ph.closers[pl.Name()])
	delete(ph.closers, pl.Name())

	for _, sub := range pl.Subscribes() {
		delete(ph.subscriptions[sub], pl.Name())
	}

	delete(ph.plugins, pl.Name())
}

func (ph *Handler) ListenAndServe() error {

	for len(ph.plugins) > 0 {
		if chosen, recv, ok := reflect.Select(ph.selects); ok {
			pck := recv.Interface().(packet)
			ph.Dispatch(pck.Event, pck.Args)
		} else {
			//Not ok
			//Removing select case from list
			if chosen == len(ph.selects)-1 {
				ph.selects = ph.selects[:len(ph.selects)-1]
			} else {
				ph.selects = append(ph.selects[:chosen], ph.selects[chosen+1:]...)
			}
		}
	}

	return nil
}

func (ph Handler) Dispatch(e Event, args Args) error {
	return ph.dispatch("local", e, args)
}

func (ph Handler) dispatch(identifier string, e Event, args Args) (err error) {
	for _, plugin := range ph.subscriptions[e] {
		go func(plugin Plugin) {
			tmp := plugin.Send(e, args)
			if err == nil && tmp != NotImplemented {
				err = tmp
			}
		}(plugin)
	}
	return
}
<|endoftext|>


<commit_before>package cast

func Int16(v interface{}) (int16, error) {

	switch value := v.(type) {
	case uint8:
		return int16(value), nil
	case int8:
		return int16(value), nil
	case int16:
		return int16(value), nil
	case int16er:
		return value.Int16()
	default:
		return 0, internalCannotCastComplainer{expectedType:"int16", actualType:typeof(value)}
	}
}

// MustInt16 is like Int16, expect panic()s on an error.
func MustInt16(v interface{}) int16 {

	x, err := Int16(v)
	if nil != err {
		panic(err)
	}

	return x
}

type int16er interface {
	Int16() (int16, error)
}
<commit_msg>improved cast.Int16()<commit_after>package cast

// Int16 will return an int16 when `v` is of type int16, int8, uint8 or has a method:
//
//	type interface {
//		Int16() (int16, error)
//	}
//
// ... that returns successfully, or has a method:
//
//	type interface {
//		Int8() (int8, error)
//	}
//
// ... that returns successfully, or has a method:
//
//	type interface {
//		Uint8() (uint8, error)
//	}
//
// Else it will return an error.
func Int16(v interface{}) (int16, error) {

	switch value := v.(type) {
	case int16:
		return int16(value), nil
	case int16er:
		return value.Int16()
	case int8:
		return int16(value), nil
	case int8er:
		return func()(int16, error){
			value, err := value.Int8()
			if nil != err {
				return 0, err
			}
			return int16(value), nil
		}()
	case uint8:
		return int16(value), nil
	case uint8er:
		return func()(int16, error){
			value, err := value.Uint8()
			if nil != err {
				return 0, err
			}
			return int16(value), nil
		}()
	default:
		return 0, internalCannotCastComplainer{expectedType:"int16", actualType:typeof(value)}
	}
}

// MustInt16 is like Int16, expect panic()s on an error.
func MustInt16(v interface{}) int16 {

	x, err := Int16(v)
	if nil != err {
		panic(err)
	}

	return x
}

type int16er interface {
	Int16() (int16, error)
}
<|endoftext|>