<commit_before>package irc

import (
	"log"
	"strconv"
)

type Channel struct {
	flags     ChannelModeSet
	lists     map[ChannelMode]*UserMaskSet
	key       Text
	members   MemberSet
	name      Name
	server    *Server
	topic     Text
	userLimit uint64
}

// NewChannel creates a new channel from a `Server` and a `name`
// string, which must be unique on the server.
func NewChannel(s *Server, name Name) *Channel {
	channel := &Channel{
		flags: make(ChannelModeSet),
		lists: map[ChannelMode]*UserMaskSet{
			BanMask:    NewUserMaskSet(),
			ExceptMask: NewUserMaskSet(),
			InviteMask: NewUserMaskSet(),
		},
		members: make(MemberSet),
		name:    name,
		server:  s,
	}

	s.channels.Add(channel)

	return channel
}

func (channel *Channel) IsEmpty() bool {
	return len(channel.members) == 0
}

func (channel *Channel) Names(client *Client) {
	client.RplNamReply(channel)
	client.RplEndOfNames(channel)
}

func (channel *Channel) ClientIsOperator(client *Client) bool {
	return client.flags[Operator] || channel.members.HasMode(client, ChannelOperator)
}

func (channel *Channel) Nicks(target *Client) []string {
	isMultiPrefix := (target != nil) && target.capabilities[MultiPrefix]
	nicks := make([]string, len(channel.members))
	i := 0
	for client, modes := range channel.members {
		if isMultiPrefix {
			if modes[ChannelOperator] {
				nicks[i] += "@"
			}
			if modes[Voice] {
				nicks[i] += "+"
			}
		} else {
			if modes[ChannelOperator] {
				nicks[i] += "@"
			} else if modes[Voice] {
				nicks[i] += "+"
			}
		}
		nicks[i] += client.Nick().String()
		i += 1
	}
	return nicks
}

func (channel *Channel) Id() Name {
	return channel.name
}

func (channel *Channel) Nick() Name {
	return channel.name
}

func (channel *Channel) String() string {
	return channel.Id().String()
}

// <mode> <mode params>
func (channel *Channel) ModeString(client *Client) (str string) {
	isMember := client.flags[Operator] || channel.members.Has(client)
	showKey := isMember && (channel.key != "")
	showUserLimit := channel.userLimit > 0

	// flags with args
	if showKey {
		str += Key.String()
	}
	if showUserLimit {
		str += UserLimit.String()
	}

	// flags
	for mode := range channel.flags {
		str += mode.String()
	}

	if len(str) > 0 {
		str = "+" + str
	}

	// args for flags with args: The order must match above to keep
	// positional arguments in place.
	if showKey {
		str += " " + channel.key.String()
	}
	if showUserLimit {
		str += " " + strconv.FormatUint(channel.userLimit, 10)
	}

	return
}

func (channel *Channel) IsFull() bool {
	return (channel.userLimit > 0) &&
		(uint64(len(channel.members)) >= channel.userLimit)
}

func (channel *Channel) CheckKey(key Text) bool {
	return (channel.key == "") || (channel.key == key)
}

func (channel *Channel) Join(client *Client, key Text) {
	if channel.members.Has(client) {
		// already joined, no message?
		return
	}

	if channel.IsFull() {
		client.ErrChannelIsFull(channel)
		return
	}

	if !channel.CheckKey(key) {
		client.ErrBadChannelKey(channel)
		return
	}

	isInvited := channel.lists[InviteMask].Match(client.UserHost())
	if channel.flags[InviteOnly] && !isInvited {
		client.ErrInviteOnlyChan(channel)
		return
	}

	if channel.lists[BanMask].Match(client.UserHost()) &&
		!isInvited &&
		!channel.lists[ExceptMask].Match(client.UserHost()) {
		client.ErrBannedFromChan(channel)
		return
	}

	client.channels.Add(channel)
	channel.members.Add(client)
	if !channel.flags[Persistent] && (len(channel.members) == 1) {
		channel.members[client][ChannelCreator] = true
		channel.members[client][ChannelOperator] = true
	}

	reply := RplJoin(client, channel)
	for member := range channel.members {
		member.Reply(reply)
	}
	channel.GetTopic(client)
	channel.Names(client)
}

func (channel *Channel) Part(client *Client, message Text) {
	if !channel.members.Has(client) {
		client.ErrNotOnChannel(channel)
		return
	}

	reply := RplPart(client, channel, message)
	for member := range channel.members {
		member.Reply(reply)
	}
	channel.Quit(client)
}

func (channel *Channel) GetTopic(client *Client) {
	if !channel.members.Has(client) {
		client.ErrNotOnChannel(channel)
		return
	}

	if channel.topic == "" {
		// clients appear not to expect this
		//replier.Reply(RplNoTopic(channel))
		return
	}

	client.RplTopic(channel)
}

func (channel *Channel) SetTopic(client *Client, topic Text) {
	if !(client.flags[Operator] || channel.members.Has(client)) {
		client.ErrNotOnChannel(channel)
		return
	}

	if channel.flags[OpOnlyTopic] && !channel.ClientIsOperator(client) {
		client.ErrChanOPrivIsNeeded(channel)
		return
	}

	channel.topic = topic

	reply := RplTopicMsg(client, channel)
	for member := range channel.members {
		member.Reply(reply)
	}

	if err := channel.Persist(); err != nil {
		log.Println("Channel.Persist:", channel, err)
	}
}

func (channel *Channel) CanSpeak(client *Client) bool {
	if client.flags[Operator] {
		return true
	}
	if channel.flags[NoOutside] && !channel.members.Has(client) {
		return false
	}
	if channel.flags[Moderated] && !(channel.members.HasMode(client, Voice) ||
		channel.members.HasMode(client, ChannelOperator)) {
		return false
	}
	return true
}

func (channel *Channel) PrivMsg(client *Client, message Text) {
	if !channel.CanSpeak(client) {
		client.ErrCannotSendToChan(channel)
		return
	}
	reply := RplPrivMsg(client, channel, message)
	for member := range channel.members {
		if member == client {
			continue
		}
		member.Reply(reply)
	}
}

func (channel *Channel) applyModeFlag(client *Client, mode ChannelMode,
	op ModeOp) bool {
	if !channel.ClientIsOperator(client) {
		client.ErrChanOPrivIsNeeded(channel)
		return false
	}

	switch op {
	case Add:
		if channel.flags[mode] {
			return false
		}
		channel.flags[mode] = true
		return true

	case Remove:
		if !channel.flags[mode] {
			return false
		}
		delete(channel.flags, mode)
		return true
	}
	return false
}

func (channel *Channel) applyModeMember(client *Client, mode ChannelMode,
	op ModeOp, nick Name) bool {
	if !channel.ClientIsOperator(client) {
		client.ErrChanOPrivIsNeeded(channel)
		return false
	}

	if nick == "" {
		client.ErrNeedMoreParams("MODE")
		return false
	}

	target := channel.server.clients.Get(nick)
	if target == nil {
		client.ErrNoSuchNick(nick)
		return false
	}

	if !channel.members.Has(target) {
		client.ErrUserNotInChannel(channel, target)
		return false
	}

	switch op {
	case Add:
		if channel.members[target][mode] {
			return false
		}
		channel.members[target][mode] = true
		return true

	case Remove:
		if !channel.members[target][mode] {
			return false
		}
		channel.members[target][mode] = false
		return true
	}
	return false
}

func (channel *Channel) ShowMaskList(client *Client, mode ChannelMode) {
	for lmask := range channel.lists[mode].masks {
		client.RplMaskList(mode, channel, lmask)
	}
	client.RplEndOfMaskList(mode, channel)
}

func (channel *Channel) applyModeMask(client *Client, mode ChannelMode, op ModeOp,
	mask Name) bool {
	list := channel.lists[mode]
	if list == nil {
		// This should never happen, but better safe than panicky.
		return false
	}

	if (op == List) || (mask == "") {
		channel.ShowMaskList(client, mode)
		return false
	}

	if !channel.ClientIsOperator(client) {
		client.ErrChanOPrivIsNeeded(channel)
		return false
	}

	if op == Add {
		return list.Add(mask)
	}

	if op == Remove {
		return list.Remove(mask)
	}

	return false
}

func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) bool {
	switch change.mode {
	case BanMask, ExceptMask, InviteMask:
		return channel.applyModeMask(client, change.mode, change.op,
			NewName(change.arg))

	case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Persistent, Private:
		return channel.applyModeFlag(client, change.mode, change.op)

	case Key:
		if !channel.ClientIsOperator(client) {
			client.ErrChanOPrivIsNeeded(channel)
			return false
		}

		switch change.op {
		case Add:
			if change.arg == "" {
				client.ErrNeedMoreParams("MODE")
				return false
			}
			key := NewText(change.arg)
			if key == channel.key {
				return false
			}

			channel.key = key
			return true

		case Remove:
			channel.key = ""
			return true
		}

	case UserLimit:
		limit, err := strconv.ParseUint(change.arg, 10, 64)
		if err != nil {
			client.ErrNeedMoreParams("MODE")
			return false
		}
		if (limit == 0) || (limit == channel.userLimit) {
			return false
		}

		channel.userLimit = limit
		return true

	case ChannelOperator, Voice:
		return channel.applyModeMember(client, change.mode, change.op,
			NewName(change.arg))

	default:
		client.ErrUnknownMode(change.mode, channel)
	}
	return false
}

func (channel *Channel) Mode(client *Client, changes ChannelModeChanges) {
	if len(changes) == 0 {
		client.RplChannelModeIs(channel)
		return
	}

	applied := make(ChannelModeChanges, 0)
	for _, change := range changes {
		if channel.applyMode(client, change) {
			applied = append(applied, change)
		}
	}

	if len(applied) > 0 {
		reply := RplChannelMode(client, channel, applied)
		for member := range channel.members {
			member.Reply(reply)
		}

		if err := channel.Persist(); err != nil {
			log.Println("Channel.Persist:", channel, err)
		}
	}
}

func (channel *Channel) Persist() (err error) {
	if channel.flags[Persistent] {
		_, err = channel.server.db.Exec(`
            INSERT OR REPLACE INTO channel
              (name, flags, key, topic, user_limit, ban_list, except_list,
               invite_list)
              VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
			channel.name.String(), channel.flags.String(), channel.key.String(),
			channel.topic.String(), channel.userLimit, channel.lists[BanMask].String(),
			channel.lists[ExceptMask].String(), channel.lists[InviteMask].String())
	} else {
		_, err = channel.server.db.Exec(`
            DELETE FROM channel WHERE name = ?`, channel.name.String())
	}
	return
}

func (channel *Channel) Notice(client *Client, message Text) {
	if !channel.CanSpeak(client) {
		client.ErrCannotSendToChan(channel)
		return
	}
	reply := RplNotice(client, channel, message)
	for member := range channel.members {
		if member == client {
			continue
		}
		member.Reply(reply)
	}
}

func (channel *Channel) Quit(client *Client) {
	channel.members.Remove(client)
	client.channels.Remove(channel)

	if !channel.flags[Persistent] && channel.IsEmpty() {
		channel.server.channels.Remove(channel)
	}
}

func (channel *Channel) Kick(client *Client, target *Client, comment Text) {
	if !(client.flags[Operator] || channel.members.Has(client)) {
		client.ErrNotOnChannel(channel)
		return
	}
	if !channel.ClientIsOperator(client) {
		client.ErrChanOPrivIsNeeded(channel)
		return
	}
	if !channel.members.Has(target) {
		client.ErrUserNotInChannel(channel, target)
		return
	}

	reply := RplKick(channel, client, target, comment)
	for member := range channel.members {
		member.Reply(reply)
	}
	channel.Quit(target)
}

func (channel *Channel) Invite(invitee *Client, inviter *Client) {
	if channel.flags[InviteOnly] && !channel.ClientIsOperator(inviter) {
		inviter.ErrChanOPrivIsNeeded(channel)
		return
	}

	if !channel.members.Has(inviter) {
		inviter.ErrNotOnChannel(channel)
		return
	}

	if channel.flags[InviteOnly] {
		channel.lists[InviteMask].Add(invitee.UserHost())
		if err := channel.Persist(); err != nil {
			log.Println("Channel.Persist:", channel, err)
		}
	}

	inviter.RplInviting(invitee, channel.name)
	invitee.Reply(RplInviteMsg(inviter, invitee, channel.name))
	if invitee.flags[Away] {
		inviter.RplAway(invitee)
	}
}
<commit_msg>this is related to https://github.com/fluffle/goirc/pull/71: common irc server behaviour is to reply with an empty +<commit_after>package irc

import (
	"log"
	"strconv"
)

type Channel struct {
	flags     ChannelModeSet
	lists     map[ChannelMode]*UserMaskSet
	key       Text
	members   MemberSet
	name      Name
	server    *Server
	topic     Text
	userLimit uint64
}

// NewChannel creates a new channel from a `Server` and a `name`
// string, which must be unique on the server.
func NewChannel(s *Server, name Name) *Channel {
	channel := &Channel{
		flags: make(ChannelModeSet),
		lists: map[ChannelMode]*UserMaskSet{
			BanMask:    NewUserMaskSet(),
			ExceptMask: NewUserMaskSet(),
			InviteMask: NewUserMaskSet(),
		},
		members: make(MemberSet),
		name:    name,
		server:  s,
	}

	s.channels.Add(channel)

	return channel
}

func (channel *Channel) IsEmpty() bool {
	return len(channel.members) == 0
}

func (channel *Channel) Names(client *Client) {
	client.RplNamReply(channel)
	client.RplEndOfNames(channel)
}

func (channel *Channel) ClientIsOperator(client *Client) bool {
	return client.flags[Operator] || channel.members.HasMode(client, ChannelOperator)
}

func (channel *Channel) Nicks(target *Client) []string {
	isMultiPrefix := (target != nil) && target.capabilities[MultiPrefix]
	nicks := make([]string, len(channel.members))
	i := 0
	for client, modes := range channel.members {
		if isMultiPrefix {
			if modes[ChannelOperator] {
				nicks[i] += "@"
			}
			if modes[Voice] {
				nicks[i] += "+"
			}
		} else {
			if modes[ChannelOperator] {
				nicks[i] += "@"
			} else if modes[Voice] {
				nicks[i] += "+"
			}
		}
		nicks[i] += client.Nick().String()
		i += 1
	}
	return nicks
}

func (channel *Channel) Id() Name {
	return channel.name
}

func (channel *Channel) Nick() Name {
	return channel.name
}

func (channel *Channel) String() string {
	return channel.Id().String()
}

// <mode> <mode params>
func (channel *Channel) ModeString(client *Client) (str string) {
	isMember := client.flags[Operator] || channel.members.Has(client)
	showKey := isMember && (channel.key != "")
	showUserLimit := channel.userLimit > 0

	// flags with args
	if showKey {
		str += Key.String()
	}
	if showUserLimit {
		str += UserLimit.String()
	}

	// flags
	for mode := range channel.flags {
		str += mode.String()
	}

	str = "+" + str

	// args for flags with args: The order must match above to keep
	// positional arguments in place.
	if showKey {
		str += " " + channel.key.String()
	}
	if showUserLimit {
		str += " " + strconv.FormatUint(channel.userLimit, 10)
	}

	return
}

func (channel *Channel) IsFull() bool {
	return (channel.userLimit > 0) &&
		(uint64(len(channel.members)) >= channel.userLimit)
}

func (channel *Channel) CheckKey(key Text) bool {
	return (channel.key == "") || (channel.key == key)
}

func (channel *Channel) Join(client *Client, key Text) {
	if channel.members.Has(client) {
		// already joined, no message?
		return
	}

	if channel.IsFull() {
		client.ErrChannelIsFull(channel)
		return
	}

	if !channel.CheckKey(key) {
		client.ErrBadChannelKey(channel)
		return
	}

	isInvited := channel.lists[InviteMask].Match(client.UserHost())
	if channel.flags[InviteOnly] && !isInvited {
		client.ErrInviteOnlyChan(channel)
		return
	}

	if channel.lists[BanMask].Match(client.UserHost()) &&
		!isInvited &&
		!channel.lists[ExceptMask].Match(client.UserHost()) {
		client.ErrBannedFromChan(channel)
		return
	}

	client.channels.Add(channel)
	channel.members.Add(client)
	if !channel.flags[Persistent] && (len(channel.members) == 1) {
		channel.members[client][ChannelCreator] = true
		channel.members[client][ChannelOperator] = true
	}

	reply := RplJoin(client, channel)
	for member := range channel.members {
		member.Reply(reply)
	}
	channel.GetTopic(client)
	channel.Names(client)
}

func (channel *Channel) Part(client *Client, message Text) {
	if !channel.members.Has(client) {
		client.ErrNotOnChannel(channel)
		return
	}

	reply := RplPart(client, channel, message)
	for member := range channel.members {
		member.Reply(reply)
	}
	channel.Quit(client)
}

func (channel *Channel) GetTopic(client *Client) {
	if !channel.members.Has(client) {
		client.ErrNotOnChannel(channel)
		return
	}

	if channel.topic == "" {
		// clients appear not to expect this
		//replier.Reply(RplNoTopic(channel))
		return
	}

	client.RplTopic(channel)
}

func (channel *Channel) SetTopic(client *Client, topic Text) {
	if !(client.flags[Operator] || channel.members.Has(client)) {
		client.ErrNotOnChannel(channel)
		return
	}

	if channel.flags[OpOnlyTopic] && !channel.ClientIsOperator(client) {
		client.ErrChanOPrivIsNeeded(channel)
		return
	}

	channel.topic = topic

	reply := RplTopicMsg(client, channel)
	for member := range channel.members {
		member.Reply(reply)
	}

	if err := channel.Persist(); err != nil {
		log.Println("Channel.Persist:", channel, err)
	}
}

func (channel *Channel) CanSpeak(client *Client) bool {
	if client.flags[Operator] {
		return true
	}
	if channel.flags[NoOutside] && !channel.members.Has(client) {
		return false
	}
	if channel.flags[Moderated] && !(channel.members.HasMode(client, Voice) ||
		channel.members.HasMode(client, ChannelOperator)) {
		return false
	}
	return true
}

func (channel *Channel) PrivMsg(client *Client, message Text) {
	if !channel.CanSpeak(client) {
		client.ErrCannotSendToChan(channel)
		return
	}
	reply := RplPrivMsg(client, channel, message)
	for member := range channel.members {
		if member == client {
			continue
		}
		member.Reply(reply)
	}
}

func (channel *Channel) applyModeFlag(client *Client, mode ChannelMode,
	op ModeOp) bool {
	if !channel.ClientIsOperator(client) {
		client.ErrChanOPrivIsNeeded(channel)
		return false
	}

	switch op {
	case Add:
		if channel.flags[mode] {
			return false
		}
		channel.flags[mode] = true
		return true

	case Remove:
		if !channel.flags[mode] {
			return false
		}
		delete(channel.flags, mode)
		return true
	}
	return false
}

func (channel *Channel) applyModeMember(client *Client, mode ChannelMode,
	op ModeOp, nick Name) bool {
	if !channel.ClientIsOperator(client) {
		client.ErrChanOPrivIsNeeded(channel)
		return false
	}

	if nick == "" {
		client.ErrNeedMoreParams("MODE")
		return false
	}

	target := channel.server.clients.Get(nick)
	if target == nil {
		client.ErrNoSuchNick(nick)
		return false
	}

	if !channel.members.Has(target) {
		client.ErrUserNotInChannel(channel, target)
		return false
	}

	switch op {
	case Add:
		if channel.members[target][mode] {
			return false
		}
		channel.members[target][mode] = true
		return true

	case Remove:
		if !channel.members[target][mode] {
			return false
		}
		channel.members[target][mode] = false
		return true
	}
	return false
}

func (channel *Channel) ShowMaskList(client *Client, mode ChannelMode) {
	for lmask := range channel.lists[mode].masks {
		client.RplMaskList(mode, channel, lmask)
	}
	client.RplEndOfMaskList(mode, channel)
}

func (channel *Channel) applyModeMask(client *Client, mode ChannelMode, op ModeOp,
	mask Name) bool {
	list := channel.lists[mode]
	if list == nil {
		// This should never happen, but better safe than panicky.
		return false
	}

	if (op == List) || (mask == "") {
		channel.ShowMaskList(client, mode)
		return false
	}

	if !channel.ClientIsOperator(client) {
		client.ErrChanOPrivIsNeeded(channel)
		return false
	}

	if op == Add {
		return list.Add(mask)
	}

	if op == Remove {
		return list.Remove(mask)
	}

	return false
}

func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) bool {
	switch change.mode {
	case BanMask, ExceptMask, InviteMask:
		return channel.applyModeMask(client, change.mode, change.op,
			NewName(change.arg))

	case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Persistent, Private:
		return channel.applyModeFlag(client, change.mode, change.op)

	case Key:
		if !channel.ClientIsOperator(client) {
			client.ErrChanOPrivIsNeeded(channel)
			return false
		}

		switch change.op {
		case Add:
			if change.arg == "" {
				client.ErrNeedMoreParams("MODE")
				return false
			}
			key := NewText(change.arg)
			if key == channel.key {
				return false
			}

			channel.key = key
			return true

		case Remove:
			channel.key = ""
			return true
		}

	case UserLimit:
		limit, err := strconv.ParseUint(change.arg, 10, 64)
		if err != nil {
			client.ErrNeedMoreParams("MODE")
			return false
		}
		if (limit == 0) || (limit == channel.userLimit) {
			return false
		}

		channel.userLimit = limit
		return true

	case ChannelOperator, Voice:
		return channel.applyModeMember(client, change.mode, change.op,
			NewName(change.arg))

	default:
		client.ErrUnknownMode(change.mode, channel)
	}
	return false
}

func (channel *Channel) Mode(client *Client, changes ChannelModeChanges) {
	if len(changes) == 0 {
		client.RplChannelModeIs(channel)
		return
	}

	applied := make(ChannelModeChanges, 0)
	for _, change := range changes {
		if channel.applyMode(client, change) {
			applied = append(applied, change)
		}
	}

	if len(applied) > 0 {
		reply := RplChannelMode(client, channel, applied)
		for member := range channel.members {
			member.Reply(reply)
		}

		if err := channel.Persist(); err != nil {
			log.Println("Channel.Persist:", channel, err)
		}
	}
}

func (channel *Channel) Persist() (err error) {
	if channel.flags[Persistent] {
		_, err = channel.server.db.Exec(`
            INSERT OR REPLACE INTO channel
              (name, flags, key, topic, user_limit, ban_list, except_list,
               invite_list)
              VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
			channel.name.String(), channel.flags.String(), channel.key.String(),
			channel.topic.String(), channel.userLimit, channel.lists[BanMask].String(),
			channel.lists[ExceptMask].String(), channel.lists[InviteMask].String())
	} else {
		_, err = channel.server.db.Exec(`
            DELETE FROM channel WHERE name = ?`, channel.name.String())
	}
	return
}

func (channel *Channel) Notice(client *Client, message Text) {
	if !channel.CanSpeak(client) {
		client.ErrCannotSendToChan(channel)
		return
	}
	reply := RplNotice(client, channel, message)
	for member := range channel.members {
		if member == client {
			continue
		}
		member.Reply(reply)
	}
}

func (channel *Channel) Quit(client *Client) {
	channel.members.Remove(client)
	client.channels.Remove(channel)

	if !channel.flags[Persistent] && channel.IsEmpty() {
		channel.server.channels.Remove(channel)
	}
}

func (channel *Channel) Kick(client *Client, target *Client, comment Text) {
	if !(client.flags[Operator] || channel.members.Has(client)) {
		client.ErrNotOnChannel(channel)
		return
	}
	if !channel.ClientIsOperator(client) {
		client.ErrChanOPrivIsNeeded(channel)
		return
	}
	if !channel.members.Has(target) {
		client.ErrUserNotInChannel(channel, target)
		return
	}

	reply := RplKick(channel, client, target, comment)
	for member := range channel.members {
		member.Reply(reply)
	}
	channel.Quit(target)
}

func (channel *Channel) Invite(invitee *Client, inviter *Client) {
	if channel.flags[InviteOnly] && !channel.ClientIsOperator(inviter) {
		inviter.ErrChanOPrivIsNeeded(channel)
		return
	}

	if !channel.members.Has(inviter) {
		inviter.ErrNotOnChannel(channel)
		return
	}

	if channel.flags[InviteOnly] {
		channel.lists[InviteMask].Add(invitee.UserHost())
		if err := channel.Persist(); err != nil {
			log.Println("Channel.Persist:", channel, err)
		}
	}

	inviter.RplInviting(invitee, channel.name)
	invitee.Reply(RplInviteMsg(inviter, invitee, channel.name))
	if invitee.flags[Away] {
		inviter.RplAway(invitee)
	}
}
<|endoftext|>


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

package org

import (
	"fmt"
	"net/http"

	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/modules/context"
	"code.gitea.io/gitea/modules/convert"
	api "code.gitea.io/gitea/modules/structs"
	"code.gitea.io/gitea/routers/api/v1/user"
	"code.gitea.io/gitea/routers/api/v1/utils"
)

func listUserOrgs(ctx *context.APIContext, u *models.User, all bool) {
	if err := u.GetOrganizations(&models.SearchOrganizationsOptions{
		ListOptions: utils.GetListOptions(ctx),
		All:         all,
	}); err != nil {
		ctx.Error(http.StatusInternalServerError, "GetOrganizations", err)
		return
	}

	apiOrgs := make([]*api.Organization, len(u.Orgs))
	for i := range u.Orgs {
		apiOrgs[i] = convert.ToOrganization(u.Orgs[i])
	}
	ctx.JSON(http.StatusOK, &apiOrgs)
}

// ListMyOrgs list all my orgs
func ListMyOrgs(ctx *context.APIContext) {
	// swagger:operation GET /user/orgs organization orgListCurrentUserOrgs
	// ---
	// summary: List the current user's organizations
	// produces:
	// - application/json
	// parameters:
	// - name: page
	//   in: query
	//   description: page number of results to return (1-based)
	//   type: integer
	// - name: limit
	//   in: query
	//   description: page size of results
	//   type: integer
	// responses:
	//   "200":
	//     "$ref": "#/responses/OrganizationList"

	listUserOrgs(ctx, ctx.User, true)
}

// ListUserOrgs list user's orgs
func ListUserOrgs(ctx *context.APIContext) {
	// swagger:operation GET /users/{username}/orgs organization orgListUserOrgs
	// ---
	// summary: List a user's organizations
	// produces:
	// - application/json
	// parameters:
	// - name: username
	//   in: path
	//   description: username of user
	//   type: string
	//   required: true
	// - name: page
	//   in: query
	//   description: page number of results to return (1-based)
	//   type: integer
	// - name: limit
	//   in: query
	//   description: page size of results
	//   type: integer
	// responses:
	//   "200":
	//     "$ref": "#/responses/OrganizationList"

	u := user.GetUserByParams(ctx)
	if ctx.Written() {
		return
	}
	listUserOrgs(ctx, u, ctx.User.IsAdmin)
}

// GetAll return list of all public organizations
func GetAll(ctx *context.APIContext) {
	// swagger:operation Get /orgs organization orgGetAll
	// ---
	// summary: Get list of organizations
	// produces:
	// - application/json
	// parameters:
	// - name: page
	//   in: query
	//   description: page number of results to return (1-based)
	//   type: integer
	// - name: limit
	//   in: query
	//   description: page size of results
	//   type: integer
	// responses:
	//   "200":
	//     "$ref": "#/responses/OrganizationList"

	vMode := []api.VisibleType{api.VisibleTypePublic}
	if ctx.IsSigned {
		vMode = append(vMode, api.VisibleTypeLimited)
		if ctx.User.IsAdmin {
			vMode = append(vMode, api.VisibleTypePrivate)
		}
	}

	listOptions := utils.GetListOptions(ctx)

	publicOrgs, maxResults, err := models.SearchUsers(&models.SearchUserOptions{
		ListOptions: listOptions,
		Type:        models.UserTypeOrganization,
		OrderBy:     models.SearchOrderByAlphabetically,
		Visible:     vMode,
	})
	if err != nil {
		ctx.Error(http.StatusInternalServerError, "SearchOrganizations", err)
		return
	}
	orgs := make([]*api.Organization, len(publicOrgs))
	for i := range publicOrgs {
		orgs[i] = convert.ToOrganization(publicOrgs[i])
	}

	ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
	ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults))
	ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
	ctx.JSON(http.StatusOK, &orgs)
}

// Create api for create organization
func Create(ctx *context.APIContext, form api.CreateOrgOption) {
	// swagger:operation POST /orgs organization orgCreate
	// ---
	// summary: Create an organization
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - name: organization
	//   in: body
	//   required: true
	//   schema: { "$ref": "#/definitions/CreateOrgOption" }
	// responses:
	//   "201":
	//     "$ref": "#/responses/Organization"
	//   "403":
	//     "$ref": "#/responses/forbidden"
	//   "422":
	//     "$ref": "#/responses/validationError"

	if !ctx.User.CanCreateOrganization() {
		ctx.Error(http.StatusForbidden, "Create organization not allowed", nil)
		return
	}

	visibility := api.VisibleTypePublic
	if form.Visibility != "" {
		visibility = api.VisibilityModes[form.Visibility]
	}

	org := &models.User{
		Name:                      form.UserName,
		FullName:                  form.FullName,
		Description:               form.Description,
		Website:                   form.Website,
		Location:                  form.Location,
		IsActive:                  true,
		Type:                      models.UserTypeOrganization,
		Visibility:                visibility,
		RepoAdminChangeTeamAccess: form.RepoAdminChangeTeamAccess,
	}
	if err := models.CreateOrganization(org, ctx.User); err != nil {
		if models.IsErrUserAlreadyExist(err) ||
			models.IsErrNameReserved(err) ||
			models.IsErrNameCharsNotAllowed(err) ||
			models.IsErrNamePatternNotAllowed(err) {
			ctx.Error(http.StatusUnprocessableEntity, "", err)
		} else {
			ctx.Error(http.StatusInternalServerError, "CreateOrganization", err)
		}
		return
	}

	ctx.JSON(http.StatusCreated, convert.ToOrganization(org))
}

// Get get an organization
func Get(ctx *context.APIContext) {
	// swagger:operation GET /orgs/{org} organization orgGet
	// ---
	// summary: Get an organization
	// produces:
	// - application/json
	// parameters:
	// - name: org
	//   in: path
	//   description: name of the organization to get
	//   type: string
	//   required: true
	// responses:
	//   "200":
	//     "$ref": "#/responses/Organization"

	if !models.HasOrgVisible(ctx.Org.Organization, ctx.User) {
		ctx.NotFound("HasOrgVisible", nil)
		return
	}
	ctx.JSON(http.StatusOK, convert.ToOrganization(ctx.Org.Organization))
}

// Edit change an organization's information
func Edit(ctx *context.APIContext, form api.EditOrgOption) {
	// swagger:operation PATCH /orgs/{org} organization orgEdit
	// ---
	// summary: Edit an organization
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - name: org
	//   in: path
	//   description: name of the organization to edit
	//   type: string
	//   required: true
	// - name: body
	//   in: body
	//   required: true
	//   schema:
	//     "$ref": "#/definitions/EditOrgOption"
	// responses:
	//   "200":
	//     "$ref": "#/responses/Organization"

	org := ctx.Org.Organization
	org.FullName = form.FullName
	org.Description = form.Description
	org.Website = form.Website
	org.Location = form.Location
	if form.Visibility != "" {
		org.Visibility = api.VisibilityModes[form.Visibility]
	}
	if err := models.UpdateUserCols(org, "full_name", "description", "website", "location", "visibility"); err != nil {
		ctx.Error(http.StatusInternalServerError, "EditOrganization", err)
		return
	}

	ctx.JSON(http.StatusOK, convert.ToOrganization(org))
}

//Delete an organization
func Delete(ctx *context.APIContext) {
	// swagger:operation DELETE /orgs/{org} organization orgDelete
	// ---
	// summary: Delete an organization
	// produces:
	// - application/json
	// parameters:
	// - name: org
	//   in: path
	//   description: organization that is to be deleted
	//   type: string
	//   required: true
	// responses:
	//   "204":
	//     "$ref": "#/responses/empty"

	if err := models.DeleteOrganization(ctx.Org.Organization); err != nil {
		ctx.Error(http.StatusInternalServerError, "DeleteOrganization", err)
		return
	}
	ctx.Status(http.StatusNoContent)
}
<commit_msg>Fix ListUserOrgs (#12910)<commit_after>// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package org

import (
	"fmt"
	"net/http"

	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/modules/context"
	"code.gitea.io/gitea/modules/convert"
	api "code.gitea.io/gitea/modules/structs"
	"code.gitea.io/gitea/routers/api/v1/user"
	"code.gitea.io/gitea/routers/api/v1/utils"
)

func listUserOrgs(ctx *context.APIContext, u *models.User, all bool) {
	if err := u.GetOrganizations(&models.SearchOrganizationsOptions{
		ListOptions: utils.GetListOptions(ctx),
		All:         all,
	}); err != nil {
		ctx.Error(http.StatusInternalServerError, "GetOrganizations", err)
		return
	}

	apiOrgs := make([]*api.Organization, len(u.Orgs))
	for i := range u.Orgs {
		apiOrgs[i] = convert.ToOrganization(u.Orgs[i])
	}
	ctx.JSON(http.StatusOK, &apiOrgs)
}

// ListMyOrgs list all my orgs
func ListMyOrgs(ctx *context.APIContext) {
	// swagger:operation GET /user/orgs organization orgListCurrentUserOrgs
	// ---
	// summary: List the current user's organizations
	// produces:
	// - application/json
	// parameters:
	// - name: page
	//   in: query
	//   description: page number of results to return (1-based)
	//   type: integer
	// - name: limit
	//   in: query
	//   description: page size of results
	//   type: integer
	// responses:
	//   "200":
	//     "$ref": "#/responses/OrganizationList"

	listUserOrgs(ctx, ctx.User, true)
}

// ListUserOrgs list user's orgs
func ListUserOrgs(ctx *context.APIContext) {
	// swagger:operation GET /users/{username}/orgs organization orgListUserOrgs
	// ---
	// summary: List a user's organizations
	// produces:
	// - application/json
	// parameters:
	// - name: username
	//   in: path
	//   description: username of user
	//   type: string
	//   required: true
	// - name: page
	//   in: query
	//   description: page number of results to return (1-based)
	//   type: integer
	// - name: limit
	//   in: query
	//   description: page size of results
	//   type: integer
	// responses:
	//   "200":
	//     "$ref": "#/responses/OrganizationList"

	u := user.GetUserByParams(ctx)
	if ctx.Written() {
		return
	}
	listUserOrgs(ctx, u, ctx.User != nil && (ctx.User.IsAdmin || ctx.User.ID == u.ID))
}

// GetAll return list of all public organizations
func GetAll(ctx *context.APIContext) {
	// swagger:operation Get /orgs organization orgGetAll
	// ---
	// summary: Get list of organizations
	// produces:
	// - application/json
	// parameters:
	// - name: page
	//   in: query
	//   description: page number of results to return (1-based)
	//   type: integer
	// - name: limit
	//   in: query
	//   description: page size of results
	//   type: integer
	// responses:
	//   "200":
	//     "$ref": "#/responses/OrganizationList"

	vMode := []api.VisibleType{api.VisibleTypePublic}
	if ctx.IsSigned {
		vMode = append(vMode, api.VisibleTypeLimited)
		if ctx.User.IsAdmin {
			vMode = append(vMode, api.VisibleTypePrivate)
		}
	}

	listOptions := utils.GetListOptions(ctx)

	publicOrgs, maxResults, err := models.SearchUsers(&models.SearchUserOptions{
		ListOptions: listOptions,
		Type:        models.UserTypeOrganization,
		OrderBy:     models.SearchOrderByAlphabetically,
		Visible:     vMode,
	})
	if err != nil {
		ctx.Error(http.StatusInternalServerError, "SearchOrganizations", err)
		return
	}
	orgs := make([]*api.Organization, len(publicOrgs))
	for i := range publicOrgs {
		orgs[i] = convert.ToOrganization(publicOrgs[i])
	}

	ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
	ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults))
	ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
	ctx.JSON(http.StatusOK, &orgs)
}

// Create api for create organization
func Create(ctx *context.APIContext, form api.CreateOrgOption) {
	// swagger:operation POST /orgs organization orgCreate
	// ---
	// summary: Create an organization
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - name: organization
	//   in: body
	//   required: true
	//   schema: { "$ref": "#/definitions/CreateOrgOption" }
	// responses:
	//   "201":
	//     "$ref": "#/responses/Organization"
	//   "403":
	//     "$ref": "#/responses/forbidden"
	//   "422":
	//     "$ref": "#/responses/validationError"

	if !ctx.User.CanCreateOrganization() {
		ctx.Error(http.StatusForbidden, "Create organization not allowed", nil)
		return
	}

	visibility := api.VisibleTypePublic
	if form.Visibility != "" {
		visibility = api.VisibilityModes[form.Visibility]
	}

	org := &models.User{
		Name:                      form.UserName,
		FullName:                  form.FullName,
		Description:               form.Description,
		Website:                   form.Website,
		Location:                  form.Location,
		IsActive:                  true,
		Type:                      models.UserTypeOrganization,
		Visibility:                visibility,
		RepoAdminChangeTeamAccess: form.RepoAdminChangeTeamAccess,
	}
	if err := models.CreateOrganization(org, ctx.User); err != nil {
		if models.IsErrUserAlreadyExist(err) ||
			models.IsErrNameReserved(err) ||
			models.IsErrNameCharsNotAllowed(err) ||
			models.IsErrNamePatternNotAllowed(err) {
			ctx.Error(http.StatusUnprocessableEntity, "", err)
		} else {
			ctx.Error(http.StatusInternalServerError, "CreateOrganization", err)
		}
		return
	}

	ctx.JSON(http.StatusCreated, convert.ToOrganization(org))
}

// Get get an organization
func Get(ctx *context.APIContext) {
	// swagger:operation GET /orgs/{org} organization orgGet
	// ---
	// summary: Get an organization
	// produces:
	// - application/json
	// parameters:
	// - name: org
	//   in: path
	//   description: name of the organization to get
	//   type: string
	//   required: true
	// responses:
	//   "200":
	//     "$ref": "#/responses/Organization"

	if !models.HasOrgVisible(ctx.Org.Organization, ctx.User) {
		ctx.NotFound("HasOrgVisible", nil)
		return
	}
	ctx.JSON(http.StatusOK, convert.ToOrganization(ctx.Org.Organization))
}

// Edit change an organization's information
func Edit(ctx *context.APIContext, form api.EditOrgOption) {
	// swagger:operation PATCH /orgs/{org} organization orgEdit
	// ---
	// summary: Edit an organization
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - name: org
	//   in: path
	//   description: name of the organization to edit
	//   type: string
	//   required: true
	// - name: body
	//   in: body
	//   required: true
	//   schema:
	//     "$ref": "#/definitions/EditOrgOption"
	// responses:
	//   "200":
	//     "$ref": "#/responses/Organization"

	org := ctx.Org.Organization
	org.FullName = form.FullName
	org.Description = form.Description
	org.Website = form.Website
	org.Location = form.Location
	if form.Visibility != "" {
		org.Visibility = api.VisibilityModes[form.Visibility]
	}
	if err := models.UpdateUserCols(org, "full_name", "description", "website", "location", "visibility"); err != nil {
		ctx.Error(http.StatusInternalServerError, "EditOrganization", err)
		return
	}

	ctx.JSON(http.StatusOK, convert.ToOrganization(org))
}

//Delete an organization
func Delete(ctx *context.APIContext) {
	// swagger:operation DELETE /orgs/{org} organization orgDelete
	// ---
	// summary: Delete an organization
	// produces:
	// - application/json
	// parameters:
	// - name: org
	//   in: path
	//   description: organization that is to be deleted
	//   type: string
	//   required: true
	// responses:
	//   "204":
	//     "$ref": "#/responses/empty"

	if err := models.DeleteOrganization(ctx.Org.Organization); err != nil {
		ctx.Error(http.StatusInternalServerError, "DeleteOrganization", err)
		return
	}
	ctx.Status(http.StatusNoContent)
}
<|endoftext|>


<commit_before>package images

import (
	"Fantasia/system"
	"image"
	"image/color"
	"image/gif"
	"image/png"
	"io"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"strings"

	"github.com/bwmarrin/discordgo"
)

// util.go : provides various utility functions
var urlRegex = regexp.MustCompile(`(http|ftp|https):\/\/([\w\-_]+(?:(?:\.[\w\-_]+)+))([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?`)

// ImageSuffixes is a list of image suffixes
var ImageSuffixes = []string{
	"png",
	"jpeg",
	"jpg",
	"gif",
}

// ReplyImage replies to the sender with the given image
func ReplyImage(ctx *system.Context, img image.Image) {
	rd, wr := io.Pipe()
	go func() {
		png.Encode(wr, img)
		wr.Close()
	}()
	ctx.Ses.DG.ChannelFileSend(ctx.Msg.ChannelID, "image.png", rd)
}

// ReplyGif replies to the sender with the given gif
func ReplyGif(ctx *system.Context, g *gif.GIF) {
	rd, wr := io.Pipe()
	go func() {
		err := gif.EncodeAll(wr, g)
		if err != nil {
			ctx.ReplyError("error encoding gif: ", err)
		}
		wr.Close()
	}()
	_, err := ctx.Ses.DG.ChannelFileSend(ctx.Msg.ChannelID, "image.gif", rd)
	if err != nil {
		ctx.ReplyError("Error uploading gif: ", err)
	}
}

// CompressGif attempts to compress a Gif's images
func CompressGif(src *gif.GIF) *gif.GIF {
	dst := &gif.GIF{
		Image:    make([]*image.Paletted, len(src.Image)),
		Disposal: make([]byte, len(src.Disposal)),
		Delay:    src.Delay,
	}
	// Set disposal to none.
	for i := 0; i < len(dst.Disposal); i++ {
		dst.Disposal[i] = 1
	}

	diff := func(a, b uint32) bool {
		if a >= b-500 && a <= b+500 {
			return false
		}
		return true
	}

	var frame = src.Image[0]
	dst.Image[0] = frame
	for i := 1; i < len(src.Image); i++ {
		cp := src.Image[i]
		np := image.NewPaletted(src.Image[i].Bounds(), cp.Palette)
		np.Palette[0] = color.RGBA{0, 0, 0, 0} // Insert an alpha pixel into the palette

		w, h := cp.Bounds().Dx(), cp.Bounds().Dy()
		for y := 0; y < h; y++ {
			for x := 0; x < w; x++ {
				pidx := y*cp.Stride + x*1
				// Compare the colours of frame(last image) and cp(current paletted),
				// if they are different, add the pixel to new paletted (np)
				r, g, b, a := cp.Palette[cp.Pix[pidx]].RGBA()
				r1, g1, b1, a1 := frame.Palette[frame.Pix[pidx]].RGBA()

				if diff(r, r1) || diff(g, g1) || diff(b, b1) || diff(a, a1) {
					np.Pix[pidx] = cp.Pix[pidx]
				} else {
					np.Pix[pidx] = uint8(np.Palette.Index(color.RGBA{0, 0, 0, 0}))
				}
			}
		}

		dst.Image[i] = np
		frame = cp // Update last frame
	}

	return dst
}

// PullImages requests images from the user, or retrieves them from the cache.
//    amount    : amount of messages to retrieve
//    channelID : channelID to pull images from
//    message   : message user supplied. If images are present, pull them from here first.
//                otherwise fall back to the image cache.
func (m *Module) PullImages(limit int, channelID string, message *discordgo.Message) ([]image.Image, error) {
	images := make([]image.Image, 0, limit)

	// Pull images from message issuing command.
	tmp, err := PullImagesFromMessage(limit, message)
	if err != nil {
		return images, err
	}
	images = append(images, tmp...)

	// If we have satisfied the message limit, return
	if len(images) >= limit {
		return images, nil
	}

	// Else continue searching the cache for images
	tmp, err = PullImagesFromCache(m.ImgCache, limit-len(images), channelID)
	if err != nil {
		return images, err
	}
	images = append(images, tmp...)

	return images, nil
}

// PullImagesFromMessage retrieves images from a message
func PullImagesFromMessage(limit int, msg *discordgo.Message) ([]image.Image, error) {
	URLs := ImageURLsInMessage(msg)
	images := []image.Image{}

	for _, v := range URLs {
		img, err := ImageFromURL(v)
		if err != nil {
			return images, err
		}
		images = append(images, img)

		limit--
		if limit == 0 {
			break
		}
	}

	return images, nil
}

// ImageFromURL gets an image from a URL
//    URL : URL to perform an HTTP get reqeust to
func ImageFromURL(URL string) (image.Image, error) {
	resp, err := http.Get(URL)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	img, _, err := image.Decode(resp.Body)
	return img, err
}

// ImageURLsInMessage returns a list of image URLs in a message
// Images are obtained from embed images, and thumbnails, and attachments.
//    msg : message to obtain the URLs from
func ImageURLsInMessage(msg *discordgo.Message) []string {
	URLs := []string{}

	for _, a := range msg.Attachments {
		URLs = append(URLs, a.URL)
	}

	for _, embed := range msg.Embeds {
		if embed.Image != nil && embed.Image.ProxyURL != "" {
			URLs = append(URLs, embed.Image.ProxyURL)
		}
		if embed.Thumbnail != nil && embed.Thumbnail.ProxyURL != "" {
			URLs = append(URLs, embed.Thumbnail.ProxyURL)
		}
	}

	if len(msg.Embeds) == 0 {
		for _, u := range urlRegex.FindAllString(msg.Content, -1) {
			if IsImageURL(u) {
				URLs = append(URLs, u)
			}
		}
	}

	return URLs
}

// IsImageURL guesses if a URL is an image
func IsImageURL(path string) bool {
	t, err := url.Parse(path)
	if err != nil {
		log.Println("error parsing URL")
	} else {
		if HasImageSuffix(strings.ToLower(t.Path)) {
			return true
		}
	}
	return false
}

// PullImagesFromCache ...
func PullImagesFromCache(cache *MessageCache, limit int, channelID string) ([]image.Image, error) {
	images := make([]image.Image, 0, limit)

	messages, err := cache.Messages(channelID)
	if err != nil {
		return nil, err
	}

	for i := len(messages) - 1; i >= 0; i-- {
		v := messages[i]
		tmp, err := PullImagesFromMessage(limit, v)
		if err != nil {
			return nil, err
		}
		images = append(images, tmp...)

		// Decrement the image limit
		limit -= len(images)
		if limit <= 0 {
			break
		}
	}

	return images, nil
}

// HasImage returns if a message has an image
func HasImage(msg *discordgo.Message) bool {

	for _, embed := range msg.Embeds {
		if embed.Image != nil && embed.Image.URL != "" {
			return true
		}
		if embed.Thumbnail != nil && embed.Thumbnail.URL != "" {
			return true
		}
	}

	for _, v := range msg.Attachments {
		if HasImageSuffix(v.Filename) {
			return true
		}
	}

	for _, u := range urlRegex.FindAllString(msg.Content, -1) {
		if IsImageURL(u) {
			return true
		}
	}

	return false
}

// HasImageSuffix returns true if an image suffix is present
func HasImageSuffix(filename string) bool {
	for _, v := range ImageSuffixes {
		if strings.HasSuffix(filename, v) {
			return true
		}
	}
	return false
}
<commit_msg>Added distance parameter to CompressGif<commit_after>package images

import (
	"Fantasia/system"
	"image"
	"image/color"
	"image/gif"
	"image/png"
	"io"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"strings"

	"github.com/bwmarrin/discordgo"
)

// util.go : provides various utility functions
var urlRegex = regexp.MustCompile(`(http|ftp|https):\/\/([\w\-_]+(?:(?:\.[\w\-_]+)+))([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?`)

// ImageSuffixes is a list of image suffixes
var ImageSuffixes = []string{
	"png",
	"jpeg",
	"jpg",
	"gif",
}

// ReplyImage replies to the sender with the given image
func ReplyImage(ctx *system.Context, img image.Image) {
	rd, wr := io.Pipe()
	go func() {
		png.Encode(wr, img)
		wr.Close()
	}()
	ctx.Ses.DG.ChannelFileSend(ctx.Msg.ChannelID, "image.png", rd)
}

// ReplyGif replies to the sender with the given gif
func ReplyGif(ctx *system.Context, g *gif.GIF) {
	rd, wr := io.Pipe()
	go func() {
		err := gif.EncodeAll(wr, g)
		if err != nil {
			ctx.ReplyError("error encoding gif: ", err)
		}
		wr.Close()
	}()
	_, err := ctx.Ses.DG.ChannelFileSend(ctx.Msg.ChannelID, "image.gif", rd)
	if err != nil {
		ctx.ReplyError("Error uploading gif: ", err)
	}
}

// CompressGif attempts to compress a Gif's images
//    src      : source gif to compress
//    distance : allowed color distance for colors to be considered equal
func CompressGif(src *gif.GIF, distance uint32) *gif.GIF {
	dst := &gif.GIF{
		Image:    make([]*image.Paletted, len(src.Image)),
		Disposal: make([]byte, len(src.Disposal)),
		Delay:    src.Delay,
	}
	// Set disposal to none.
	for i := 0; i < len(dst.Disposal); i++ {
		dst.Disposal[i] = 1
	}

	diff := func(a, b uint32) bool {
		if a >= b-distance && a <= b+distance {
			return false
		}
		return true
	}

	var frame = src.Image[0]
	dst.Image[0] = frame
	for i := 1; i < len(src.Image); i++ {
		cp := src.Image[i]
		np := image.NewPaletted(src.Image[i].Bounds(), cp.Palette)
		np.Palette[0] = color.RGBA{0, 0, 0, 0} // Insert an alpha pixel into the palette

		w, h := cp.Bounds().Dx(), cp.Bounds().Dy()
		for y := 0; y < h; y++ {
			for x := 0; x < w; x++ {
				pidx := y*cp.Stride + x*1
				// Compare the colours of frame(last image) and cp(current paletted),
				// if they are different, add the pixel to new paletted (np)
				r, g, b, a := cp.Palette[cp.Pix[pidx]].RGBA()
				r1, g1, b1, a1 := frame.Palette[frame.Pix[pidx]].RGBA()

				if diff(r, r1) || diff(g, g1) || diff(b, b1) || diff(a, a1) {
					np.Pix[pidx] = cp.Pix[pidx]
				} else {
					np.Pix[pidx] = uint8(np.Palette.Index(color.RGBA{0, 0, 0, 0}))
				}
			}
		}

		dst.Image[i] = np
		frame = cp // Update last frame
	}

	return dst
}

// PullImages requests images from the user, or retrieves them from the cache.
//    amount    : amount of messages to retrieve
//    channelID : channelID to pull images from
//    message   : message user supplied. If images are present, pull them from here first.
//                otherwise fall back to the image cache.
func (m *Module) PullImages(limit int, channelID string, message *discordgo.Message) ([]image.Image, error) {
	images := make([]image.Image, 0, limit)

	// Pull images from message issuing command.
	tmp, err := PullImagesFromMessage(limit, message)
	if err != nil {
		return images, err
	}
	images = append(images, tmp...)

	// If we have satisfied the message limit, return
	if len(images) >= limit {
		return images, nil
	}

	// Else continue searching the cache for images
	tmp, err = PullImagesFromCache(m.ImgCache, limit-len(images), channelID)
	if err != nil {
		return images, err
	}
	images = append(images, tmp...)

	return images, nil
}

// PullImagesFromMessage retrieves images from a message
func PullImagesFromMessage(limit int, msg *discordgo.Message) ([]image.Image, error) {
	URLs := ImageURLsInMessage(msg)
	images := []image.Image{}

	for _, v := range URLs {
		img, err := ImageFromURL(v)
		if err != nil {
			return images, err
		}
		images = append(images, img)

		limit--
		if limit == 0 {
			break
		}
	}

	return images, nil
}

// ImageFromURL gets an image from a URL
//    URL : URL to perform an HTTP get reqeust to
func ImageFromURL(URL string) (image.Image, error) {
	resp, err := http.Get(URL)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	img, _, err := image.Decode(resp.Body)
	return img, err
}

// ImageURLsInMessage returns a list of image URLs in a message
// Images are obtained from embed images, and thumbnails, and attachments.
//    msg : message to obtain the URLs from
func ImageURLsInMessage(msg *discordgo.Message) []string {
	URLs := []string{}

	for _, a := range msg.Attachments {
		URLs = append(URLs, a.URL)
	}

	for _, embed := range msg.Embeds {
		if embed.Image != nil && embed.Image.ProxyURL != "" {
			URLs = append(URLs, embed.Image.ProxyURL)
		}
		if embed.Thumbnail != nil && embed.Thumbnail.ProxyURL != "" {
			URLs = append(URLs, embed.Thumbnail.ProxyURL)
		}
	}

	if len(msg.Embeds) == 0 {
		for _, u := range urlRegex.FindAllString(msg.Content, -1) {
			if IsImageURL(u) {
				URLs = append(URLs, u)
			}
		}
	}

	return URLs
}

// IsImageURL guesses if a URL is an image
func IsImageURL(path string) bool {
	t, err := url.Parse(path)
	if err != nil {
		log.Println("error parsing URL")
	} else {
		if HasImageSuffix(strings.ToLower(t.Path)) {
			return true
		}
	}
	return false
}

// PullImagesFromCache ...
func PullImagesFromCache(cache *MessageCache, limit int, channelID string) ([]image.Image, error) {
	images := make([]image.Image, 0, limit)

	messages, err := cache.Messages(channelID)
	if err != nil {
		return nil, err
	}

	for i := len(messages) - 1; i >= 0; i-- {
		v := messages[i]
		tmp, err := PullImagesFromMessage(limit, v)
		if err != nil {
			return nil, err
		}
		images = append(images, tmp...)

		// Decrement the image limit
		limit -= len(images)
		if limit <= 0 {
			break
		}
	}

	return images, nil
}

// HasImage returns if a message has an image
func HasImage(msg *discordgo.Message) bool {

	for _, embed := range msg.Embeds {
		if embed.Image != nil && embed.Image.URL != "" {
			return true
		}
		if embed.Thumbnail != nil && embed.Thumbnail.URL != "" {
			return true
		}
	}

	for _, v := range msg.Attachments {
		if HasImageSuffix(v.Filename) {
			return true
		}
	}

	for _, u := range urlRegex.FindAllString(msg.Content, -1) {
		if IsImageURL(u) {
			return true
		}
	}

	return false
}

// HasImageSuffix returns true if an image suffix is present
func HasImageSuffix(filename string) bool {
	for _, v := range ImageSuffixes {
		if strings.HasSuffix(filename, v) {
			return true
		}
	}
	return false
}
<|endoftext|>


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

package mvs

import (
	"reflect"
	"strings"
	"testing"

	"cmd/go/internal/module"
)

var tests = `
# Scenario from blog.
name: blog
A: B1 C2
B1: D3
C1: D2
C2: D4
C3: D5
C4: G1
D2: E1
D3: E2
D4: E2 F1
D5: E2
G1: C4
A2: B1 C4 D4
build A: A B1 C2 D4 E2 F1
upgrade* A: A B1 C4 D5 E2 G1
upgrade A C4: A B1 C4 D4 E2 F1 G1
downgrade A2 D2: A2 C4 D2

# Cross-dependency between D and E.
# No matter how it arises, should get result of merging all build lists via max,
# which leads to including both D2 and E2.

name: cross1
A: B C
B: D1
C: D2
D1: E2
D2: E1
build A: A B C D2 E2

name: cross1V
A: B2 C D2 E1
B1:
B2: D1
C: D2
D1: E2
D2: E1
build A: A B2 C D2 E2

name: cross1U
A: B1 C
B1:
B2: D1
C: D2
D1: E2
D2: E1
build A: A B1 C D2 E1
upgrade A B2: A B2 C D2 E2

name: cross1R
A: B C
B: D2
C: D1
D1: E2
D2: E1
build A: A B C D2 E2

name: cross1X
A: B C
B: D1 E2
C: D2
D1: E2
D2: E1
build A: A B C D2 E2

name: cross2
A: B D2
B: D1
D1: E2
D2: E1
build A: A B D2 E2

name: cross2X
A: B D2
B: D1 E2
C: D2
D1: E2
D2: E1
build A: A B D2 E2

name: cross3
A: B D2 E1
B: D1
D1: E2
D2: E1
build A: A B D2 E2

name: cross3X
A: B D2 E1
B: D1 E2
D1: E2
D2: E1
build A: A B D2 E2

# Should not get E2 here, because B has been updated
# not to depend on D1 anymore.
name: cross4
A1: B1 D2
A2: B2 D2
B1: D1
B2: D2
D1: E2
D2: E1
build A1: A1 B1 D2 E2
build A2: A2 B2 D2 E1

# But the upgrade from A1 preserves the E2 dep explicitly.
upgrade A1 B2: A1 B2 D2 E2
upgradereq A1 B2: B2 E2

name: cross5
A: D1
D1: E2
D2: E1
build A: A D1 E2
upgrade* A: A D2 E2
upgrade A D2: A D2 E2
upgradereq A D2: D2 E2

name: cross6
A: D2
D1: E2
D2: E1
build A: A D2 E1
upgrade* A: A D2 E2
upgrade A E2: A D2 E2

name: cross7
A: B C
B: D1
C: E1
D1: E2
E1: D2
build A: A B C D2 E2

name: down1
A: B2
B1: C1
B2: C2
build A: A B2 C2
downgrade A C1: A B1

name: down2
A: B2 E2
B1:
B2: C2 F2
C1:
D1:
C2: D2 E2
D2: B2
E2: D2
E1:
F1:
downgrade A F1: A B1 E1

name: down3
A:
`

func Test(t *testing.T) {
	var (
		name string
		reqs reqsMap
		fns  []func(*testing.T)
	)
	flush := func() {
		if name != "" {
			t.Run(name, func(t *testing.T) {
				for _, fn := range fns {
					fn(t)
				}
			})
		}
	}
	m := func(s string) module.Version {
		return module.Version{s[:1], s[1:]}
	}
	ms := func(list []string) []module.Version {
		var mlist []module.Version
		for _, s := range list {
			mlist = append(mlist, m(s))
		}
		return mlist
	}
	checkList := func(t *testing.T, desc string, list []module.Version, err error, val string) {
		if err != nil {
			t.Fatalf("%s: %v", desc, err)
		}
		vs := ms(strings.Fields(val))
		if !reflect.DeepEqual(list, vs) {
			t.Errorf("%s = %v, want %v", desc, list, vs)
		}
	}

	for _, line := range strings.Split(tests, "\n") {
		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "#") || line == "" {
			continue
		}
		i := strings.Index(line, ":")
		if i < 0 {
			t.Fatalf("missing colon: %q", line)
		}
		key := strings.TrimSpace(line[:i])
		val := strings.TrimSpace(line[i+1:])
		if key == "" {
			t.Fatalf("missing key: %q", line)
		}
		kf := strings.Fields(key)
		switch kf[0] {
		case "name":
			if len(kf) != 1 {
				t.Fatalf("name takes no arguments: %q", line)
			}
			flush()
			reqs = make(reqsMap)
			fns = nil
			name = val
			continue
		case "build":
			if len(kf) != 2 {
				t.Fatalf("build takes one argument: %q", line)
			}
			fns = append(fns, func(t *testing.T) {
				list, err := BuildList(m(kf[1]), reqs)
				checkList(t, key, list, err, val)
			})
			continue
		case "upgrade*":
			if len(kf) != 2 {
				t.Fatalf("upgrade* takes one argument: %q", line)
			}
			fns = append(fns, func(t *testing.T) {
				list, err := UpgradeAll(m(kf[1]), reqs)
				checkList(t, key, list, err, val)
			})
			continue
		case "upgradereq":
			if len(kf) < 2 {
				t.Fatalf("upgrade takes at least one arguments: %q", line)
			}
			fns = append(fns, func(t *testing.T) {
				list, err := Upgrade(m(kf[1]), reqs, ms(kf[2:])...)
				if err == nil {
					list, err = Req(m(kf[1]), list, reqs)
				}
				checkList(t, key, list, err, val)
			})
			continue
		case "upgrade":
			if len(kf) < 2 {
				t.Fatalf("upgrade takes at least one arguments: %q", line)
			}
			fns = append(fns, func(t *testing.T) {
				list, err := Upgrade(m(kf[1]), reqs, ms(kf[2:])...)
				checkList(t, key, list, err, val)
			})
			continue
		case "downgrade":
			if len(kf) < 2 {
				t.Fatalf("downgrade takes at least one arguments: %q", line)
			}
			fns = append(fns, func(t *testing.T) {
				list, err := Downgrade(m(kf[1]), reqs, ms(kf[1:])...)
				checkList(t, key, list, err, val)
			})
			continue
		}
		if len(kf) == 1 && 'A' <= key[0] && key[0] <= 'Z' {
			var rs []module.Version
			for _, f := range strings.Fields(val) {
				r := m(f)
				if reqs[r] == nil {
					reqs[r] = []module.Version{}
				}
				rs = append(rs, r)
			}
			reqs[m(key)] = rs
			continue
		}
		t.Fatalf("bad line: %q", line)
	}
	flush()
}

type reqsMap map[module.Version][]module.Version

func (r reqsMap) Max(v1, v2 string) string {
	if v1 < v2 {
		return v2
	}
	return v1
}

func (r reqsMap) Latest(path string) (module.Version, error) {
	var m module.Version
	for k := range r {
		if k.Path == path && m.Version < k.Version {
			m = k
		}
	}
	if m.Path == "" {
		return module.Version{}, &MissingModuleError{module.Version{path, ""}}
	}
	return m, nil
}

func (r reqsMap) Previous(m module.Version) (module.Version, error) {
	var p module.Version
	for k := range r {
		if k.Path == m.Path && p.Version < k.Version && k.Version < m.Version {
			p = k
		}
	}
	if p.Path == "" {
		return module.Version{m.Path, "none"}, nil
	}
	return p, nil
}

func (r reqsMap) Required(m module.Version) ([]module.Version, error) {
	rr, ok := r[m]
	if !ok {
		return nil, &MissingModuleError{m}
	}
	return rr, nil
}
<commit_msg>cmd/go/internal/mvs: add test case demonstrating trimming<commit_after>// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package mvs

import (
	"reflect"
	"strings"
	"testing"

	"cmd/go/internal/module"
)

var tests = `
# Scenario from blog.
name: blog
A: B1 C2
B1: D3
C1: D2
C2: D4
C3: D5
C4: G1
D2: E1
D3: E2
D4: E2 F1
D5: E2
G1: C4
A2: B1 C4 D4
build A: A B1 C2 D4 E2 F1
upgrade* A: A B1 C4 D5 E2 G1
upgrade A C4: A B1 C4 D4 E2 F1 G1
downgrade A2 D2: A2 C4 D2

name: trim
A: B1 C2
B1: D3
C2: B2
B2:
build A: A B2 C2

# Cross-dependency between D and E.
# No matter how it arises, should get result of merging all build lists via max,
# which leads to including both D2 and E2.

name: cross1
A: B C
B: D1
C: D2
D1: E2
D2: E1
build A: A B C D2 E2

name: cross1V
A: B2 C D2 E1
B1:
B2: D1
C: D2
D1: E2
D2: E1
build A: A B2 C D2 E2

name: cross1U
A: B1 C
B1:
B2: D1
C: D2
D1: E2
D2: E1
build A: A B1 C D2 E1
upgrade A B2: A B2 C D2 E2

name: cross1R
A: B C
B: D2
C: D1
D1: E2
D2: E1
build A: A B C D2 E2

name: cross1X
A: B C
B: D1 E2
C: D2
D1: E2
D2: E1
build A: A B C D2 E2

name: cross2
A: B D2
B: D1
D1: E2
D2: E1
build A: A B D2 E2

name: cross2X
A: B D2
B: D1 E2
C: D2
D1: E2
D2: E1
build A: A B D2 E2

name: cross3
A: B D2 E1
B: D1
D1: E2
D2: E1
build A: A B D2 E2

name: cross3X
A: B D2 E1
B: D1 E2
D1: E2
D2: E1
build A: A B D2 E2

# Should not get E2 here, because B has been updated
# not to depend on D1 anymore.
name: cross4
A1: B1 D2
A2: B2 D2
B1: D1
B2: D2
D1: E2
D2: E1
build A1: A1 B1 D2 E2
build A2: A2 B2 D2 E1

# But the upgrade from A1 preserves the E2 dep explicitly.
upgrade A1 B2: A1 B2 D2 E2
upgradereq A1 B2: B2 E2

name: cross5
A: D1
D1: E2
D2: E1
build A: A D1 E2
upgrade* A: A D2 E2
upgrade A D2: A D2 E2
upgradereq A D2: D2 E2

name: cross6
A: D2
D1: E2
D2: E1
build A: A D2 E1
upgrade* A: A D2 E2
upgrade A E2: A D2 E2

name: cross7
A: B C
B: D1
C: E1
D1: E2
E1: D2
build A: A B C D2 E2

name: down1
A: B2
B1: C1
B2: C2
build A: A B2 C2
downgrade A C1: A B1

name: down2
A: B2 E2
B1:
B2: C2 F2
C1:
D1:
C2: D2 E2
D2: B2
E2: D2
E1:
F1:
downgrade A F1: A B1 E1

name: down3
A:
`

func Test(t *testing.T) {
	var (
		name string
		reqs reqsMap
		fns  []func(*testing.T)
	)
	flush := func() {
		if name != "" {
			t.Run(name, func(t *testing.T) {
				for _, fn := range fns {
					fn(t)
				}
			})
		}
	}
	m := func(s string) module.Version {
		return module.Version{s[:1], s[1:]}
	}
	ms := func(list []string) []module.Version {
		var mlist []module.Version
		for _, s := range list {
			mlist = append(mlist, m(s))
		}
		return mlist
	}
	checkList := func(t *testing.T, desc string, list []module.Version, err error, val string) {
		if err != nil {
			t.Fatalf("%s: %v", desc, err)
		}
		vs := ms(strings.Fields(val))
		if !reflect.DeepEqual(list, vs) {
			t.Errorf("%s = %v, want %v", desc, list, vs)
		}
	}

	for _, line := range strings.Split(tests, "\n") {
		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "#") || line == "" {
			continue
		}
		i := strings.Index(line, ":")
		if i < 0 {
			t.Fatalf("missing colon: %q", line)
		}
		key := strings.TrimSpace(line[:i])
		val := strings.TrimSpace(line[i+1:])
		if key == "" {
			t.Fatalf("missing key: %q", line)
		}
		kf := strings.Fields(key)
		switch kf[0] {
		case "name":
			if len(kf) != 1 {
				t.Fatalf("name takes no arguments: %q", line)
			}
			flush()
			reqs = make(reqsMap)
			fns = nil
			name = val
			continue
		case "build":
			if len(kf) != 2 {
				t.Fatalf("build takes one argument: %q", line)
			}
			fns = append(fns, func(t *testing.T) {
				list, err := BuildList(m(kf[1]), reqs)
				checkList(t, key, list, err, val)
			})
			continue
		case "upgrade*":
			if len(kf) != 2 {
				t.Fatalf("upgrade* takes one argument: %q", line)
			}
			fns = append(fns, func(t *testing.T) {
				list, err := UpgradeAll(m(kf[1]), reqs)
				checkList(t, key, list, err, val)
			})
			continue
		case "upgradereq":
			if len(kf) < 2 {
				t.Fatalf("upgrade takes at least one arguments: %q", line)
			}
			fns = append(fns, func(t *testing.T) {
				list, err := Upgrade(m(kf[1]), reqs, ms(kf[2:])...)
				if err == nil {
					list, err = Req(m(kf[1]), list, reqs)
				}
				checkList(t, key, list, err, val)
			})
			continue
		case "upgrade":
			if len(kf) < 2 {
				t.Fatalf("upgrade takes at least one arguments: %q", line)
			}
			fns = append(fns, func(t *testing.T) {
				list, err := Upgrade(m(kf[1]), reqs, ms(kf[2:])...)
				checkList(t, key, list, err, val)
			})
			continue
		case "downgrade":
			if len(kf) < 2 {
				t.Fatalf("downgrade takes at least one arguments: %q", line)
			}
			fns = append(fns, func(t *testing.T) {
				list, err := Downgrade(m(kf[1]), reqs, ms(kf[1:])...)
				checkList(t, key, list, err, val)
			})
			continue
		}
		if len(kf) == 1 && 'A' <= key[0] && key[0] <= 'Z' {
			var rs []module.Version
			for _, f := range strings.Fields(val) {
				r := m(f)
				if reqs[r] == nil {
					reqs[r] = []module.Version{}
				}
				rs = append(rs, r)
			}
			reqs[m(key)] = rs
			continue
		}
		t.Fatalf("bad line: %q", line)
	}
	flush()
}

type reqsMap map[module.Version][]module.Version

func (r reqsMap) Max(v1, v2 string) string {
	if v1 < v2 {
		return v2
	}
	return v1
}

func (r reqsMap) Latest(path string) (module.Version, error) {
	var m module.Version
	for k := range r {
		if k.Path == path && m.Version < k.Version {
			m = k
		}
	}
	if m.Path == "" {
		return module.Version{}, &MissingModuleError{module.Version{path, ""}}
	}
	return m, nil
}

func (r reqsMap) Previous(m module.Version) (module.Version, error) {
	var p module.Version
	for k := range r {
		if k.Path == m.Path && p.Version < k.Version && k.Version < m.Version {
			p = k
		}
	}
	if p.Path == "" {
		return module.Version{m.Path, "none"}, nil
	}
	return p, nil
}

func (r reqsMap) Required(m module.Version) ([]module.Version, error) {
	rr, ok := r[m]
	if !ok {
		return nil, &MissingModuleError{m}
	}
	return rr, nil
}
<|endoftext|>


<commit_before>package main

/*
  REVISION HISTORY
  ================
  19 Aug 16 -- First Go version completed to test all parts of tokenize.go package
  21 Sep 16 -- Now need to test my new GetTknStrPreserveCase routine.  And test the change I made to GETCHR.
  13 Oct 17 -- Testing the inclusion of horizontal tab as a delim, needed for comparehashes.
  18 Oct 17 -- Now called testfilepicker, derived from testtoken.go
*/

import (
	"bufio"
	"filepicker"
	"fmt"
	"getcommandline"
	"log"
	"os"
	"strconv"
	"strings"
	"tknptr"
)

// var FSAnameType = [...]string{"DELIM","OP","DGT","ALLELSE"};
const maxchoices = 10

func main() {
	var ans string
	// testingstate = 0: gettkn, 1: gettknreal, 2: gettknstr, 3: gettkneol, 4: string lower case,
	// 5: token lower case
	testingstate := 0
	commandline := getcommandline.GetCommandLineString()
	filenames := filepicker.GetFilenames(commandline)
	fmt.Println(" Number of filenames in the string slice are", len(filenames))
	for i := 0; i < maxchoices; i++ {
		fmt.Println("filename[", i, "] is", filenames[i])
	}
	fmt.Print(" Enter filename choice : ")
	fmt.Scanln(&ans)
	fmt.Printf(" ans is string %s, hex %x \n", ans, ans)
	i, err := strconv.Atoi(ans)
	if err == nil {
		fmt.Println(" ans as int is", i)
	} else {
		s := strings.ToUpper(ans)
		s = strings.TrimSpace(s)
		s0 := s[0]
		i = int(s0 - 'A') // may need byte(s0) - 'A' or byte(s0-'A') or some other permutation
		fmt.Println(" string ans as int is", i, " referenced filename is", filenames[i])
	}
	fmt.Println(" Picked filename is", filenames[i])
	fmt.Println()

	a := 'a'
	b := a ^ 32
	c := a &^ 32
	fmt.Println(" playing with bit clearing.  a,b,c =", a, b, c)

	fmt.Println()

	//	commandline = strings.ToUpper(commandline)
	floatflag := false
	if (commandline == "REAL") || (commandline == "FLOAT") {
		floatflag = true
		testingstate = 1
	} else if commandline == "STRING" {
		testingstate = 2
	} else if commandline == "EOL" {
		testingstate = 3
	} else if commandline == "STR" {
		testingstate = 4
	} else if commandline == "LOWER" {
		testingstate = 5
	}

	fmt.Print(" Will be testing GetTknReal? ", floatflag, ", testingstate is ", testingstate)
	fmt.Println()

	scanner := bufio.NewScanner(os.Stdin)
	for {
		fmt.Print(" Input test text: ")
		scanner.Scan()
		inputline := scanner.Text()
		if err := scanner.Err(); err != nil {
			fmt.Fprintln(os.Stderr, "reading standard input:", err)
			os.Exit(1)
		}
		if len(inputline) == 0 {
			os.Exit(0)
		}
		fmt.Println(" After the call to Scan(), before TrimSpace: ", inputline, ".")
		inputline = strings.TrimSpace(inputline)
		fmt.Println(" After call to TrimSpace: ", inputline)
		if strings.ToUpper(inputline) == "QUIT" {
			log.Println(" Test Token finished.")
			os.Exit(0)
		}
		tkn := tknptr.NewToken(inputline)
		EOL := false
		token := tknptr.TokenType{}
		for !EOL {
			if floatflag || testingstate == 1 {
				token, EOL = tkn.GETTKNREAL()
			} else if testingstate == 2 {
				token, EOL = tkn.GETTKNSTR()
			} else if testingstate == 3 {
				token, EOL = tkn.GETTKNEOL()
			} else if testingstate == 4 {
				token, EOL = tkn.GetTokenString(false)
			} else if testingstate == 5 {
				token, EOL = tkn.GetToken(false)
			} else {
				token, EOL = tkn.GETTKN()
			}

			fmt.Printf(" Token : %#v \n", token)
			//      if floatflag {     I think this is just an error.  I missed something in testing
			//        fmt.Println(" R = ",token.Rsum);
			//      }
			fmt.Println(" EOL : ", EOL)
			if EOL {
				break
			} // I don't want it to ask about ungettkn if there is an EOL condition.
			fmt.Print(" call UnGetTkn? (Y/N) ")
			//			scanner.Scan()
			//			ans := scanner.Text()
			//			if err := scanner.Err(); err != nil {
			//				fmt.Fprintln(os.Stderr, "reading standard input:", err)
			//				os.Exit(1)
			//			}
			fmt.Scan(&ans)
			ans = strings.TrimSpace(ans)
			ans = strings.ToUpper(ans)
			if strings.HasPrefix(ans, "Y") {
				tkn.UNGETTKN()
			}
		}
		fmt.Println()
		log.Println(" Finished processing the inputline.")
	}
}

/*  from the web documentation at golang.org
        scanner := bufio.NewScanner(os.Stdin)
        for scanner.Scan() {
          fmt.Println(scanner.Text()) // Println will add back the final '\n'
	}
        if err := scanner.Err(); err != nil {
          fmt.Fprintln(os.Stderr, "reading standard input:", err)
	}
*/
<commit_msg>modified:   testfilepicker/testfilepicker.go -- output tweaking<commit_after>package main

/*
  REVISION HISTORY
  ================
  19 Aug 16 -- First Go version completed to test all parts of tokenize.go package
  21 Sep 16 -- Now need to test my new GetTknStrPreserveCase routine.  And test the change I made to GETCHR.
  13 Oct 17 -- Testing the inclusion of horizontal tab as a delim, needed for comparehashes.
  18 Oct 17 -- Now called testfilepicker, derived from testtoken.go
*/

import (
	"bufio"
	"filepicker"
	"fmt"
	"getcommandline"
	"log"
	"os"
	"strconv"
	"strings"
	"tknptr"
)

// var FSAnameType = [...]string{"DELIM","OP","DGT","ALLELSE"};
const maxchoices = 10

func main() {
	var ans string
	// testingstate = 0: gettkn, 1: gettknreal, 2: gettknstr, 3: gettkneol, 4: string lower case,
	// 5: token lower case
	testingstate := 0
	commandline := getcommandline.GetCommandLineString()
	filenames := filepicker.GetFilenames(commandline)
	fmt.Println(" Number of filenames in the string slice are", len(filenames))
	for i := 0; i < maxchoices; i++ {
		fmt.Println("filename[", i, "] is", filenames[i])
	}
	fmt.Print(" Enter filename choice : ")
	fmt.Scanln(&ans)
	fmt.Printf(" ans is string %s, hex %x \n", ans, ans)
	i, err := strconv.Atoi(ans)
	if err == nil {
		fmt.Println(" ans as int is", i)
	} else {
		s := strings.ToUpper(ans)
		s = strings.TrimSpace(s)
		s0 := s[0]
		i = int(s0 - 'A') // may need byte(s0) - 'A' or byte(s0-'A') or some other permutation
		fmt.Println(" string ans as int is", i, " referenced filename is", filenames[i])
	}
	fmt.Println(" Picked filename is", filenames[i])
	fmt.Println()

	a := 'a'
	b := a ^ 32
	c := a &^ 32
	d := a | 32
	fmt.Printf(" Bit fiddling:  a,b,c,d = %d %c, %d %c, %d %c, %d  %c \n", a, a, b, b, c, c, d, d)

	fmt.Println()

	//	commandline = strings.ToUpper(commandline)
	floatflag := false
	if (commandline == "REAL") || (commandline == "FLOAT") {
		floatflag = true
		testingstate = 1
	} else if commandline == "STRING" {
		testingstate = 2
	} else if commandline == "EOL" {
		testingstate = 3
	} else if commandline == "STR" {
		testingstate = 4
	} else if commandline == "LOWER" {
		testingstate = 5
	}

	fmt.Print(" Will be testing GetTknReal? ", floatflag, ", testingstate is ", testingstate)
	fmt.Println()

	scanner := bufio.NewScanner(os.Stdin)
	for {
		fmt.Print(" Input test text: ")
		scanner.Scan()
		inputline := scanner.Text()
		if err := scanner.Err(); err != nil {
			fmt.Fprintln(os.Stderr, "reading standard input:", err)
			os.Exit(1)
		}
		if len(inputline) == 0 {
			os.Exit(0)
		}
		fmt.Println(" After the call to Scan(), before TrimSpace: ", inputline, ".")
		inputline = strings.TrimSpace(inputline)
		fmt.Println(" After call to TrimSpace: ", inputline)
		if strings.ToUpper(inputline) == "QUIT" {
			log.Println(" Test Token finished.")
			os.Exit(0)
		}
		tkn := tknptr.NewToken(inputline)
		EOL := false
		token := tknptr.TokenType{}
		for !EOL {
			if floatflag || testingstate == 1 {
				token, EOL = tkn.GETTKNREAL()
			} else if testingstate == 2 {
				token, EOL = tkn.GETTKNSTR()
			} else if testingstate == 3 {
				token, EOL = tkn.GETTKNEOL()
			} else if testingstate == 4 {
				token, EOL = tkn.GetTokenString(false)
			} else if testingstate == 5 {
				token, EOL = tkn.GetToken(false)
			} else {
				token, EOL = tkn.GETTKN()
			}

			fmt.Printf(" Token : %#v \n", token)
			//      if floatflag {     I think this is just an error.  I missed something in testing
			//        fmt.Println(" R = ",token.Rsum);
			//      }
			fmt.Println(" EOL : ", EOL)
			if EOL {
				break
			} // I don't want it to ask about ungettkn if there is an EOL condition.
			fmt.Print(" call UnGetTkn? (Y/N) ")
			//			scanner.Scan()
			//			ans := scanner.Text()
			//			if err := scanner.Err(); err != nil {
			//				fmt.Fprintln(os.Stderr, "reading standard input:", err)
			//				os.Exit(1)
			//			}
			fmt.Scan(&ans)
			ans = strings.TrimSpace(ans)
			ans = strings.ToUpper(ans)
			if strings.HasPrefix(ans, "Y") {
				tkn.UNGETTKN()
			}
		}
		fmt.Println()
		log.Println(" Finished processing the inputline.")
	}
}

/*  from the web documentation at golang.org
        scanner := bufio.NewScanner(os.Stdin)
        for scanner.Scan() {
          fmt.Println(scanner.Text()) // Println will add back the final '\n'
	}
        if err := scanner.Err(); err != nil {
          fmt.Fprintln(os.Stderr, "reading standard input:", err)
	}
*/
<|endoftext|>


<commit_before>package main

/*
#include <stdlib.h>
#include <jni.h>
#include <android/input.h>
#include <GLES2/gl2.h>
#cgo android LDFLAGS: -lGLESv2
*/
import "C"

import (
	"fmt"
	"log"
	"math"
	"runtime"
	"sync"
	"unsafe"
)

const (
	AMOTION_EVENT_ACTION_DOWN = C.AMOTION_EVENT_ACTION_DOWN
	AMOTION_EVENT_ACTION_UP   = C.AMOTION_EVENT_ACTION_UP
	AMOTION_EVENT_ACTION_MOVE = C.AMOTION_EVENT_ACTION_MOVE
)

var time float64

type game struct {
	prog                C.GLuint
	width, height       int
	offsetUni, colorUni int

	mu               sync.Mutex // Protects offsetX, offsetY
	offsetX, offsetY float32

	touching         bool
	touchX, touchY   float32
}

var g game

const vertShaderSrcDef = `
	uniform vec2 offset;
	attribute vec4 vPosition;

	void main() {
		gl_Position = vec4(vPosition.xy+offset, vPosition.zw);
	}
`

const fragShaderSrcDef = `
	precision mediump float;

	uniform vec3 color;

	void main() {
		gl_FragColor = vec4(color.xyz, 1.0);
	}
`

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
}

func GetShaderInfoLog(shader C.GLuint) string {
	var logLen C.GLint
	C.glGetShaderiv(shader, C.GL_INFO_LOG_LENGTH, &logLen)
	var c C.GLchar
	logLenBytes := int(logLen) * int(unsafe.Sizeof(c))
	log := C.malloc(C.size_t(logLenBytes))
	if log == nil {
		panic("Failed to allocate shader log buffer")
	}
	defer C.free(log)
	C.glGetShaderInfoLog(C.GLuint(shader), C.GLsizei(logLen), (*C.GLsizei)(unsafe.Pointer(nil)), (*C.GLchar)(log))
	return string(C.GoBytes(log, C.int(logLenBytes)))
}

func GetProgramInfoLog(program C.GLuint) string {
	var logLen C.GLint
	C.glGetProgramiv(program, C.GL_INFO_LOG_LENGTH, &logLen)
	var c C.GLchar
	logLenBytes := int(logLen) * int(unsafe.Sizeof(c))
	log := C.malloc(C.size_t(logLenBytes))
	if log == nil {
		panic("Failed to allocate shader log buffer")
	}
	defer C.free(log)
	C.glGetProgramInfoLog(C.GLuint(program), C.GLsizei(logLen), (*C.GLsizei)(unsafe.Pointer(nil)), (*C.GLchar)(log))
	return string(C.GoBytes(log, C.int(logLenBytes)))
}

func loadShader(shaderType C.GLenum, source string) C.GLuint {
	handle := C.glCreateShader(shaderType)
	if handle == 0 {
		panic(fmt.Errorf("Failed to create shader of type %v", shaderType))
	}
	sourceC := C.CString(source)
	defer C.free(unsafe.Pointer(sourceC))
	C.glShaderSource(handle, 1, (**C.GLchar)(unsafe.Pointer(&sourceC)), (*C.GLint)(unsafe.Pointer(nil)))
	C.glCompileShader(handle)
	var compiled C.GLint
	C.glGetShaderiv(handle, C.GL_COMPILE_STATUS, &compiled)
	if compiled != C.GL_TRUE {
		log := GetShaderInfoLog(handle)
		panic(fmt.Errorf("Failed to compile shader: %v, shader: %v", log, source))
	}
	return handle
}

func GenBuffer() C.GLuint {
	var buf C.GLuint
	C.glGenBuffers(1, &buf)
	return C.GLuint(buf)
}

func checkGLError() {
	if glErr := C.glGetError(); glErr != C.GL_NO_ERROR {
		panic(fmt.Errorf("C.gl error: %v", glErr))
	}
}

func createProgram(vertShaderSrc string, fragShaderSrc string) C.GLuint {
	vertShader := loadShader(C.GL_VERTEX_SHADER, vertShaderSrc)
	fragShader := loadShader(C.GL_FRAGMENT_SHADER, fragShaderSrc)
	prog := C.glCreateProgram()
	if prog == 0 {
		panic("Failed to create shader program")
	}
	C.glAttachShader(prog, vertShader)
	checkGLError()
	C.glAttachShader(prog, fragShader)
	checkGLError()
	C.glLinkProgram(prog)
	var linkStatus C.GLint
	C.glGetProgramiv(prog, C.GL_LINK_STATUS, &linkStatus)
	if linkStatus != C.GL_TRUE {
		log := GetProgramInfoLog(prog)
		panic(fmt.Errorf("Failed to link program: %v", log))
	}
	return prog
}

func attribLocation(prog C.GLuint, name string) int {
	nameC := C.CString(name)
	defer C.free(unsafe.Pointer(nameC))
	attrib := int(C.glGetAttribLocation(C.GLuint(prog), (*C.GLchar)(unsafe.Pointer(nameC))))
	checkGLError()
	if attrib == -1 {
		panic(fmt.Errorf("Failed to find attrib position for %v", name))
	}
	return attrib
}

func uniformLocation(prog C.GLuint, name string) int {
	nameC := C.CString(name)
	defer C.free(unsafe.Pointer(nameC))
	attrib := int(C.glGetUniformLocation(C.GLuint(prog), (*C.GLchar)(unsafe.Pointer(nameC))))
	checkGLError()
	if attrib == -1 {
		panic(fmt.Errorf("Failed to find attrib position for %v", name))
	}
	return attrib
}

func GetString(name C.GLenum) string {
	val := C.glGetString(C.GLenum(name))
	return C.GoString((*C.char)(unsafe.Pointer(val)))
}

func (game *game) resize(width, height int) {
	game.width = width
	game.height = height
	C.glViewport(0, 0, C.GLsizei(width), C.GLsizei(height))
}

func (game *game) initGL() {
	log.Printf("GL_VERSION: %v GL_RENDERER: %v GL_VENDOR %v\n",
		GetString(C.GL_VERSION), GetString(C.GL_RENDERER), GetString(C.GL_VENDOR))
	log.Printf("GL_EXTENSIONS: %v\n", GetString(C.GL_EXTENSIONS))
	C.glClearColor(0.0, 0.0, 0.0, 1.0)
	C.glEnable(C.GL_CULL_FACE)
	C.glEnable(C.GL_DEPTH_TEST)

	game.prog = createProgram(vertShaderSrcDef, fragShaderSrcDef)
	posAttrib := attribLocation(game.prog, "vPosition")
	game.offsetUni = uniformLocation(game.prog, "offset")
	game.colorUni = uniformLocation(game.prog, "color")
	C.glUseProgram(game.prog)
	C.glEnableVertexAttribArray(C.GLuint(posAttrib))

	vertVBO := GenBuffer()
	checkGLError()
	C.glBindBuffer(C.GL_ARRAY_BUFFER, vertVBO)
	verts := []float32{.0, 0.5, -0.5, -0.5, 0.5, -0.5}
	C.glBufferData(C.GL_ARRAY_BUFFER, C.GLsizeiptr(len(verts)*int(unsafe.Sizeof(verts[0]))), unsafe.Pointer(&verts[0]), C.GL_STATIC_DRAW)
	C.glVertexAttribPointer(C.GLuint(posAttrib), 2, C.GL_FLOAT, C.GL_FALSE, 0, unsafe.Pointer(uintptr(0)))
}

func (game *game) drawFrame() {
	time += .05
	color := (C.GLclampf(math.Sin(time)) + 1) * .5

	game.mu.Lock()
	offX := game.offsetX
	offY := game.offsetY
	game.mu.Unlock()
	C.glUniform2f(C.GLint(game.offsetUni), C.GLfloat(offX), C.GLfloat(offY))
	C.glUniform3f(C.GLint(game.colorUni), 1.0, C.GLfloat(color), 0)
	C.glClear(C.GL_COLOR_BUFFER_BIT | C.GL_DEPTH_BUFFER_BIT)

	C.glUseProgram(game.prog)
	C.glDrawArrays(C.GL_TRIANGLES, 0, 3)
}

func (game *game) onTouch(action int, x, y float32) {
	switch action {
	case AMOTION_EVENT_ACTION_UP:
		game.touching = false
	case AMOTION_EVENT_ACTION_DOWN:
		game.touching = true
		game.touchX, game.touchY = x, y
	case AMOTION_EVENT_ACTION_MOVE:
		if !game.touching {
			break
		}
		game.mu.Lock()
		game.offsetX += 2 * (x - game.touchX) / float32(game.width)
		game.offsetY += 2 * -(y - game.touchY) / float32(game.height)
		game.mu.Unlock()
		game.touchX, game.touchY = x, y
	}
}

// Use JNI_OnLoad to ensure that the go runtime is initialized at a predictable time,
// namely at System.loadLibrary()
//export JNI_OnLoad
func JNI_OnLoad(vm *C.JavaVM, reserved unsafe.Pointer) C.jint {
	return C.JNI_VERSION_1_6
}

//export Java_net_goandroid_hellogl2_Engine_drawFrame
func Java_net_goandroid_hellogl2_Engine_drawFrame(env *C.JNIEnv, clazz C.jclass) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: drawFrame: %v\n", err)
		}
	}()
	g.drawFrame()
}

//export Java_net_goandroid_hellogl2_Engine_init
func Java_net_goandroid_hellogl2_Engine_init(env *C.JNIEnv, clazz C.jclass) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: init: %v\n", err)
		}
	}()
	g.initGL()
}

//export Java_net_goandroid_hellogl2_Engine_resize
func Java_net_goandroid_hellogl2_Engine_resize(env *C.JNIEnv, clazz C.jclass, width, height C.jint) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: resize: %v\n", err)
		}
	}()
	g.resize(int(width), int(height))
}

//export Java_net_goandroid_hellogl2_Engine_onTouch
func Java_net_goandroid_hellogl2_Engine_onTouch(env *C.JNIEnv, clazz C.jclass, action C.jint, x, y C.jfloat) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: resize: %v\n", err)
		}
	}()
	g.onTouch(int(action), float32(x), float32(y))
}
<commit_msg>removed redundant consts<commit_after>package main

/*
#include <stdlib.h>
#include <jni.h>
#include <android/input.h>
#include <GLES2/gl2.h>
#cgo android LDFLAGS: -lGLESv2
*/
import "C"

import (
	"fmt"
	"log"
	"math"
	"runtime"
	"sync"
	"unsafe"
)

var time float64

type game struct {
	prog                C.GLuint
	width, height       int
	offsetUni, colorUni int

	mu               sync.Mutex // Protects offsetX, offsetY
	offsetX, offsetY float32

	touching         bool
	touchX, touchY   float32
}

var g game

const vertShaderSrcDef = `
	uniform vec2 offset;
	attribute vec4 vPosition;

	void main() {
		gl_Position = vec4(vPosition.xy+offset, vPosition.zw);
	}
`

const fragShaderSrcDef = `
	precision mediump float;

	uniform vec3 color;

	void main() {
		gl_FragColor = vec4(color.xyz, 1.0);
	}
`

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
}

func GetShaderInfoLog(shader C.GLuint) string {
	var logLen C.GLint
	C.glGetShaderiv(shader, C.GL_INFO_LOG_LENGTH, &logLen)
	var c C.GLchar
	logLenBytes := int(logLen) * int(unsafe.Sizeof(c))
	log := C.malloc(C.size_t(logLenBytes))
	if log == nil {
		panic("Failed to allocate shader log buffer")
	}
	defer C.free(log)
	C.glGetShaderInfoLog(C.GLuint(shader), C.GLsizei(logLen), (*C.GLsizei)(unsafe.Pointer(nil)), (*C.GLchar)(log))
	return string(C.GoBytes(log, C.int(logLenBytes)))
}

func GetProgramInfoLog(program C.GLuint) string {
	var logLen C.GLint
	C.glGetProgramiv(program, C.GL_INFO_LOG_LENGTH, &logLen)
	var c C.GLchar
	logLenBytes := int(logLen) * int(unsafe.Sizeof(c))
	log := C.malloc(C.size_t(logLenBytes))
	if log == nil {
		panic("Failed to allocate shader log buffer")
	}
	defer C.free(log)
	C.glGetProgramInfoLog(C.GLuint(program), C.GLsizei(logLen), (*C.GLsizei)(unsafe.Pointer(nil)), (*C.GLchar)(log))
	return string(C.GoBytes(log, C.int(logLenBytes)))
}

func loadShader(shaderType C.GLenum, source string) C.GLuint {
	handle := C.glCreateShader(shaderType)
	if handle == 0 {
		panic(fmt.Errorf("Failed to create shader of type %v", shaderType))
	}
	sourceC := C.CString(source)
	defer C.free(unsafe.Pointer(sourceC))
	C.glShaderSource(handle, 1, (**C.GLchar)(unsafe.Pointer(&sourceC)), (*C.GLint)(unsafe.Pointer(nil)))
	C.glCompileShader(handle)
	var compiled C.GLint
	C.glGetShaderiv(handle, C.GL_COMPILE_STATUS, &compiled)
	if compiled != C.GL_TRUE {
		log := GetShaderInfoLog(handle)
		panic(fmt.Errorf("Failed to compile shader: %v, shader: %v", log, source))
	}
	return handle
}

func GenBuffer() C.GLuint {
	var buf C.GLuint
	C.glGenBuffers(1, &buf)
	return C.GLuint(buf)
}

func checkGLError() {
	if glErr := C.glGetError(); glErr != C.GL_NO_ERROR {
		panic(fmt.Errorf("C.gl error: %v", glErr))
	}
}

func createProgram(vertShaderSrc string, fragShaderSrc string) C.GLuint {
	vertShader := loadShader(C.GL_VERTEX_SHADER, vertShaderSrc)
	fragShader := loadShader(C.GL_FRAGMENT_SHADER, fragShaderSrc)
	prog := C.glCreateProgram()
	if prog == 0 {
		panic("Failed to create shader program")
	}
	C.glAttachShader(prog, vertShader)
	checkGLError()
	C.glAttachShader(prog, fragShader)
	checkGLError()
	C.glLinkProgram(prog)
	var linkStatus C.GLint
	C.glGetProgramiv(prog, C.GL_LINK_STATUS, &linkStatus)
	if linkStatus != C.GL_TRUE {
		log := GetProgramInfoLog(prog)
		panic(fmt.Errorf("Failed to link program: %v", log))
	}
	return prog
}

func attribLocation(prog C.GLuint, name string) int {
	nameC := C.CString(name)
	defer C.free(unsafe.Pointer(nameC))
	attrib := int(C.glGetAttribLocation(C.GLuint(prog), (*C.GLchar)(unsafe.Pointer(nameC))))
	checkGLError()
	if attrib == -1 {
		panic(fmt.Errorf("Failed to find attrib position for %v", name))
	}
	return attrib
}

func uniformLocation(prog C.GLuint, name string) int {
	nameC := C.CString(name)
	defer C.free(unsafe.Pointer(nameC))
	attrib := int(C.glGetUniformLocation(C.GLuint(prog), (*C.GLchar)(unsafe.Pointer(nameC))))
	checkGLError()
	if attrib == -1 {
		panic(fmt.Errorf("Failed to find attrib position for %v", name))
	}
	return attrib
}

func GetString(name C.GLenum) string {
	val := C.glGetString(C.GLenum(name))
	return C.GoString((*C.char)(unsafe.Pointer(val)))
}

func (game *game) resize(width, height int) {
	game.width = width
	game.height = height
	C.glViewport(0, 0, C.GLsizei(width), C.GLsizei(height))
}

func (game *game) initGL() {
	log.Printf("GL_VERSION: %v GL_RENDERER: %v GL_VENDOR %v\n",
		GetString(C.GL_VERSION), GetString(C.GL_RENDERER), GetString(C.GL_VENDOR))
	log.Printf("GL_EXTENSIONS: %v\n", GetString(C.GL_EXTENSIONS))
	C.glClearColor(0.0, 0.0, 0.0, 1.0)
	C.glEnable(C.GL_CULL_FACE)
	C.glEnable(C.GL_DEPTH_TEST)

	game.prog = createProgram(vertShaderSrcDef, fragShaderSrcDef)
	posAttrib := attribLocation(game.prog, "vPosition")
	game.offsetUni = uniformLocation(game.prog, "offset")
	game.colorUni = uniformLocation(game.prog, "color")
	C.glUseProgram(game.prog)
	C.glEnableVertexAttribArray(C.GLuint(posAttrib))

	vertVBO := GenBuffer()
	checkGLError()
	C.glBindBuffer(C.GL_ARRAY_BUFFER, vertVBO)
	verts := []float32{.0, 0.5, -0.5, -0.5, 0.5, -0.5}
	C.glBufferData(C.GL_ARRAY_BUFFER, C.GLsizeiptr(len(verts)*int(unsafe.Sizeof(verts[0]))), unsafe.Pointer(&verts[0]), C.GL_STATIC_DRAW)
	C.glVertexAttribPointer(C.GLuint(posAttrib), 2, C.GL_FLOAT, C.GL_FALSE, 0, unsafe.Pointer(uintptr(0)))
}

func (game *game) drawFrame() {
	time += .05
	color := (C.GLclampf(math.Sin(time)) + 1) * .5

	game.mu.Lock()
	offX := game.offsetX
	offY := game.offsetY
	game.mu.Unlock()
	C.glUniform2f(C.GLint(game.offsetUni), C.GLfloat(offX), C.GLfloat(offY))
	C.glUniform3f(C.GLint(game.colorUni), 1.0, C.GLfloat(color), 0)
	C.glClear(C.GL_COLOR_BUFFER_BIT | C.GL_DEPTH_BUFFER_BIT)

	C.glUseProgram(game.prog)
	C.glDrawArrays(C.GL_TRIANGLES, 0, 3)
}

func (game *game) onTouch(action int, x, y float32) {
	switch action {
	case C.AMOTION_EVENT_ACTION_UP:
		game.touching = false
	case C.AMOTION_EVENT_ACTION_DOWN:
		game.touching = true
		game.touchX, game.touchY = x, y
	case C.AMOTION_EVENT_ACTION_MOVE:
		if !game.touching {
			break
		}
		game.mu.Lock()
		game.offsetX += 2 * (x - game.touchX) / float32(game.width)
		game.offsetY += 2 * -(y - game.touchY) / float32(game.height)
		game.mu.Unlock()
		game.touchX, game.touchY = x, y
	}
}

// Use JNI_OnLoad to ensure that the go runtime is initialized at a predictable time,
// namely at System.loadLibrary()
//export JNI_OnLoad
func JNI_OnLoad(vm *C.JavaVM, reserved unsafe.Pointer) C.jint {
	return C.JNI_VERSION_1_6
}

//export Java_net_goandroid_hellogl2_Engine_drawFrame
func Java_net_goandroid_hellogl2_Engine_drawFrame(env *C.JNIEnv, clazz C.jclass) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: drawFrame: %v\n", err)
		}
	}()
	g.drawFrame()
}

//export Java_net_goandroid_hellogl2_Engine_init
func Java_net_goandroid_hellogl2_Engine_init(env *C.JNIEnv, clazz C.jclass) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: init: %v\n", err)
		}
	}()
	g.initGL()
}

//export Java_net_goandroid_hellogl2_Engine_resize
func Java_net_goandroid_hellogl2_Engine_resize(env *C.JNIEnv, clazz C.jclass, width, height C.jint) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: resize: %v\n", err)
		}
	}()
	g.resize(int(width), int(height))
}

//export Java_net_goandroid_hellogl2_Engine_onTouch
func Java_net_goandroid_hellogl2_Engine_onTouch(env *C.JNIEnv, clazz C.jclass, action C.jint, x, y C.jfloat) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: resize: %v\n", err)
		}
	}()
	g.onTouch(int(action), float32(x), float32(y))
}
<|endoftext|>


<commit_before>package hashigo

import (
	"testing"

	"github.com/hashicorp/go-version"
)

func TestProjectLatestVersion(t *testing.T) {
	p := &Project{Name: "vagrant"}

	vsn, err := p.LatestVersion()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	min := version.Must(version.NewVersion("1.0.0"))
	max := version.Must(version.NewVersion("2.0.0"))
	if vsn.LessThan(min) || vsn.GreaterThan(max) {
		t.Fatalf("bad: %s", vsn)
	}
}

func TestVersionRe(t *testing.T) {
	cases := []struct {
		Input  string
		Output string
	}{
		{
			"0.8.7",
			"0.8.7",
		},

		{
			"Terraform v0.6.4-dev (4d37704d532ae3effdfe2c5b6254bee0b94e8d8e+CHANGES)\n",
			"0.6.4-dev",
		},

		{
			"Consul v0.5.0\nConsul Protocol: 2 (Understands back to: 1)",
			"0.5.0",
		},
	}

	for _, tc := range cases {
		matches := versionRe.FindStringSubmatch(tc.Input)
		if len(matches) == 0 {
			t.Fatalf("bad: %s", tc.Input)
		}
		if matches[1] != tc.Output {
			t.Fatalf("bad: %s != %s\n\n%s", matches[1], tc.Output, tc.Input)
		}
	}
}
<commit_msg>helper/hashigo: add Vagrant test<commit_after>package hashigo

import (
	"testing"

	"github.com/hashicorp/go-version"
)

func TestProjectLatestVersion(t *testing.T) {
	p := &Project{Name: "vagrant"}

	vsn, err := p.LatestVersion()
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	min := version.Must(version.NewVersion("1.0.0"))
	max := version.Must(version.NewVersion("2.0.0"))
	if vsn.LessThan(min) || vsn.GreaterThan(max) {
		t.Fatalf("bad: %s", vsn)
	}
}

func TestVersionRe(t *testing.T) {
	cases := []struct {
		Input  string
		Output string
	}{
		{
			"0.8.7",
			"0.8.7",
		},

		{
			"Terraform v0.6.4-dev (4d37704d532ae3effdfe2c5b6254bee0b94e8d8e+CHANGES)\n",
			"0.6.4-dev",
		},

		{
			"Consul v0.5.0\nConsul Protocol: 2 (Understands back to: 1)",
			"0.5.0",
		},

		{
			"Vagrant 1.7.4",
			"1.7.4",
		},
	}

	for _, tc := range cases {
		matches := versionRe.FindStringSubmatch(tc.Input)
		if len(matches) == 0 {
			t.Fatalf("bad: %s", tc.Input)
		}
		if matches[1] != tc.Output {
			t.Fatalf("bad: %s != %s\n\n%s", matches[1], tc.Output, tc.Input)
		}
	}
}
<|endoftext|>


<commit_before>package helpers

import (
	"fmt"
	"os"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/cloudfoundry-incubator/garden"
	"github.com/mitchellh/go-ps"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
	"github.com/pivotal-golang/localip"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
	"github.com/tedsuo/ifrit/grouper"

	garden_runner "github.com/cloudfoundry-incubator/garden-windows/integration/runner"
	"github.com/cloudfoundry/loggregator/src/bitbucket.org/kardianos/osext"
)

func BuildContainerizer() string {
	currentDir, err := osext.ExecutableFolder()
	Expect(err).ShouldNot(HaveOccurred())
	containerizerDir := path.Join(currentDir, "..", "..", "..", "Containerizer", "Containerizer")
	windir := os.Getenv("WINDIR")
	msbuild := path.Join(windir, "Microsoft.NET", "Framework64", "v4.0.30319", "MSBuild")
	cmd := exec.Command(msbuild, `Containerizer.csproj`)
	cmd.Dir = containerizerDir
	Expect(cmd.Run()).To(Succeed(), "Cannot build containerizer. Make sure there are no compilation errors")
	containerizerBin := path.Join(containerizerDir, "bin", "Containerizer.exe")
	Expect(containerizerBin).To(BeAnExistingFile())

	cmd = exec.Command(msbuild, `Containerizer.csproj`)
	cmd.Dir = containerizerDir
	Expect(cmd.Run()).To(Succeed(), "Cannot build containerizer. Make sure there are no compilation errors")
	containerizerBin = path.Join(containerizerDir, "bin", "Containerizer.exe")
	Expect(containerizerBin).To(BeAnExistingFile())
	return containerizerBin
}

func BuildGarden() string {
	gardenPath, err := gexec.Build("github.com/cloudfoundry-incubator/garden-windows", "-a", "-race", "-tags", "daemon")
	Expect(err).ShouldNot(HaveOccurred())
	return gardenPath
}

func StartGarden(gardenBin, containerizerBin string, argv ...string) (ifrit.Process, garden.Client) {
	gardenPort, err := localip.LocalPort()
	Expect(err).NotTo(HaveOccurred())
	gardenAddr := fmt.Sprintf("127.0.0.1:%d", gardenPort)

	tmpDir := os.TempDir()

	// If below fails, try
	// netsh advfirewall firewall add rule name="Open Port 48080"  dir=in action=allow protocol=TCP localport=48080

	containerizerPort, err := localip.LocalPort()
	Expect(err).NotTo(HaveOccurred())
	gardenRunner := garden_runner.New("tcp4", gardenAddr, tmpDir, gardenBin, fmt.Sprintf("http://127.0.0.1:%d", containerizerPort))
	containerizerRunner := ginkgomon.New(ginkgomon.Config{
		Name:              "containerizer",
		Command:           exec.Command(containerizerBin, "127.0.0.1", strconv.Itoa(int(containerizerPort))),
		AnsiColorCode:     "",
		StartCheck:        "Control-C to quit.",
		StartCheckTimeout: 10 * time.Second,
		Cleanup:           func() {},
	})

	group := grouper.NewOrdered(syscall.SIGTERM, []grouper.Member{
		{Name: "containerizer", Runner: containerizerRunner},
		{Name: "garden", Runner: gardenRunner},
	})

	gardenProcess := ifrit.Invoke(group)

	return gardenProcess, gardenRunner.NewClient()
}

func StopGarden(process ifrit.Process, client garden.Client) {
	process.Signal(syscall.SIGKILL)
	Eventually(process.Wait(), 10).Should(Receive())
}

func KillAllGarden() {
	killAllProcs("garden-windows", "test")
}

func KillAllContainerizer() {
	killAllProcs("containerizer", "test")
}

func killAllProcs(matchingProcName string, matchingParentProcName string) {
	matchingProcName = strings.ToUpper(matchingProcName)
	matchingParentProcName = strings.ToUpper(matchingParentProcName)
	killCount := 0

	for {
		processes, err := ps.Processes()
		Expect(err).ShouldNot(HaveOccurred())

		matchingProcExists := false

		for _, proc := range processes {
			killCount++
			procName := strings.ToUpper(proc.Executable())

			if strings.Contains(procName, matchingProcName) {
				parent, err := ps.FindProcess(proc.PPid())
				Expect(err).ShouldNot(HaveOccurred())
				parentProcName := strings.ToUpper(parent.Executable())
				if strings.Contains(parentProcName, matchingParentProcName) {
					matchingProcExists = true
					goProc, err := os.FindProcess(proc.Pid())
					if err == nil {
						goProc.Kill()
					}
				}
			}
		}

		if !matchingProcExists {
			break
		}

		if killCount > 100 {
			panic("killAllProcs stuck in a loop, exiting")
		}
	}
}

func AssertProcessExitsWith(expectedExitCode int, f func() (garden.Process, error)) {
	process, err := f()
	Expect(err).ShouldNot(HaveOccurred())
	actualExitCode, err := process.Wait()
	Expect(err).ShouldNot(HaveOccurred())
	Expect(actualExitCode).To(Equal(expectedExitCode))
}

func AssertEventuallyProcessExitsWith(expectedExitCode int, f func() (garden.Process, error)) {
	Eventually(func() int {
		process, err := f()
		Expect(err).ShouldNot(HaveOccurred())
		actualExitCode, err := process.Wait()
		Expect(err).ShouldNot(HaveOccurred())
		return actualExitCode
	}).Should(Equal(expectedExitCode))
}
<commit_msg>modifies ifrit's start check for containerizer<commit_after>package helpers

import (
	"fmt"
	"os"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/cloudfoundry-incubator/garden"
	"github.com/mitchellh/go-ps"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
	"github.com/pivotal-golang/localip"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
	"github.com/tedsuo/ifrit/grouper"

	garden_runner "github.com/cloudfoundry-incubator/garden-windows/integration/runner"
	"github.com/cloudfoundry/loggregator/src/bitbucket.org/kardianos/osext"
)

func BuildContainerizer() string {
	currentDir, err := osext.ExecutableFolder()
	Expect(err).ShouldNot(HaveOccurred())
	containerizerDir := path.Join(currentDir, "..", "..", "..", "Containerizer", "Containerizer")
	windir := os.Getenv("WINDIR")
	msbuild := path.Join(windir, "Microsoft.NET", "Framework64", "v4.0.30319", "MSBuild")
	cmd := exec.Command(msbuild, `Containerizer.csproj`)
	cmd.Dir = containerizerDir
	Expect(cmd.Run()).To(Succeed(), "Cannot build containerizer. Make sure there are no compilation errors")
	containerizerBin := path.Join(containerizerDir, "bin", "Containerizer.exe")
	Expect(containerizerBin).To(BeAnExistingFile())

	cmd = exec.Command(msbuild, `Containerizer.csproj`)
	cmd.Dir = containerizerDir
	Expect(cmd.Run()).To(Succeed(), "Cannot build containerizer. Make sure there are no compilation errors")
	containerizerBin = path.Join(containerizerDir, "bin", "Containerizer.exe")
	Expect(containerizerBin).To(BeAnExistingFile())
	return containerizerBin
}

func BuildGarden() string {
	gardenPath, err := gexec.Build("github.com/cloudfoundry-incubator/garden-windows", "-a", "-race", "-tags", "daemon")
	Expect(err).ShouldNot(HaveOccurred())
	return gardenPath
}

func StartGarden(gardenBin, containerizerBin string, argv ...string) (ifrit.Process, garden.Client) {
	gardenPort, err := localip.LocalPort()
	Expect(err).NotTo(HaveOccurred())
	gardenAddr := fmt.Sprintf("127.0.0.1:%d", gardenPort)

	tmpDir := os.TempDir()

	// If below fails, try
	// netsh advfirewall firewall add rule name="Open Port 48080"  dir=in action=allow protocol=TCP localport=48080

	containerizerPort, err := localip.LocalPort()
	Expect(err).NotTo(HaveOccurred())
	gardenRunner := garden_runner.New("tcp4", gardenAddr, tmpDir, gardenBin, fmt.Sprintf("http://127.0.0.1:%d", containerizerPort))
	containerizerRunner := ginkgomon.New(ginkgomon.Config{
		Name:              "containerizer",
		Command:           exec.Command(containerizerBin, "127.0.0.1", strconv.Itoa(int(containerizerPort))),
		AnsiColorCode:     "",
		StartCheck:        "containerizer.started",
		StartCheckTimeout: 10 * time.Second,
		Cleanup:           func() {},
	})

	group := grouper.NewOrdered(syscall.SIGTERM, []grouper.Member{
		{Name: "containerizer", Runner: containerizerRunner},
		{Name: "garden", Runner: gardenRunner},
	})

	gardenProcess := ifrit.Invoke(group)

	// wait for the processes to start before returning
	<-gardenProcess.Ready()

	return gardenProcess, gardenRunner.NewClient()
}

func StopGarden(process ifrit.Process, client garden.Client) {
	process.Signal(syscall.SIGKILL)
	Eventually(process.Wait(), 10).Should(Receive())
}

func KillAllGarden() {
	killAllProcs("garden-windows", "test")
}

func KillAllContainerizer() {
	killAllProcs("containerizer", "test")
}

func killAllProcs(matchingProcName string, matchingParentProcName string) {
	matchingProcName = strings.ToUpper(matchingProcName)
	matchingParentProcName = strings.ToUpper(matchingParentProcName)
	killCount := 0

	for {
		processes, err := ps.Processes()
		Expect(err).ShouldNot(HaveOccurred())

		matchingProcExists := false

		for _, proc := range processes {
			killCount++
			procName := strings.ToUpper(proc.Executable())

			if strings.Contains(procName, matchingProcName) {
				parent, err := ps.FindProcess(proc.PPid())
				Expect(err).ShouldNot(HaveOccurred())
				parentProcName := strings.ToUpper(parent.Executable())
				if strings.Contains(parentProcName, matchingParentProcName) {
					matchingProcExists = true
					goProc, err := os.FindProcess(proc.Pid())
					if err == nil {
						goProc.Kill()
					}
				}
			}
		}

		if !matchingProcExists {
			break
		}

		if killCount > 100 {
			panic("killAllProcs stuck in a loop, exiting")
		}
	}
}

func AssertProcessExitsWith(expectedExitCode int, f func() (garden.Process, error)) {
	process, err := f()
	Expect(err).ShouldNot(HaveOccurred())
	actualExitCode, err := process.Wait()
	Expect(err).ShouldNot(HaveOccurred())
	Expect(actualExitCode).To(Equal(expectedExitCode))
}

func AssertEventuallyProcessExitsWith(expectedExitCode int, f func() (garden.Process, error)) {
	Eventually(func() int {
		process, err := f()
		Expect(err).ShouldNot(HaveOccurred())
		actualExitCode, err := process.Wait()
		Expect(err).ShouldNot(HaveOccurred())
		return actualExitCode
	}).Should(Equal(expectedExitCode))
}
<|endoftext|>