<commit_before>package gatt

import (
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"strings"

	"github.com/paypal/gatt/linux"
)

type peripheral struct {
	// NameChanged is called whenever the peripheral GAP device name has changed.
	NameChanged func(*peripheral)

	// ServicedModified is called when one or more service of a peripheral have changed.
	// A list of invalid service is provided in the parameter.
	ServicesModified func(*peripheral, []*Service)

	d    *device
	svcs []*Service

	sub *subscriber

	mtu uint16
	l2c io.ReadWriteCloser

	reqc  chan message
	quitc chan struct{}

	pd *linux.PlatData // platform specific data
}

func (p *peripheral) Device() Device       { return p.d }
func (p *peripheral) ID() string           { return strings.ToUpper(net.HardwareAddr(p.pd.Address[:]).String()) }
func (p *peripheral) Name() string         { return p.pd.Name }
func (p *peripheral) Services() []*Service { return p.svcs }

func finish(op byte, h uint16, b []byte) bool {
	done := b[0] == attOpError && b[1] == op && b[2] == byte(h) && b[3] == byte(h>>8)
	e := attEcode(b[4])
	if e != attEcodeAttrNotFound {
		// log.Printf("unexpected protocol error: %s", e)
		// FIXME: terminate the connection
	}
	return done
}

func (p *peripheral) DiscoverServices(s []UUID) ([]*Service, error) {
	// TODO: implement the UUID filters
	// p.pd.Conn.Write([]byte{0x02, 0x87, 0x00}) // MTU
	done := false
	start := uint16(0x0001)
	for !done {
		op := byte(attOpReadByGroupReq)
		b := make([]byte, 7)
		b[0] = op
		binary.LittleEndian.PutUint16(b[1:3], start)
		binary.LittleEndian.PutUint16(b[3:5], 0xFFFF)
		binary.LittleEndian.PutUint16(b[5:7], 0x2800)

		b = p.sendReq(op, b)
		if finish(op, start, b) {
			break
		}
		b = b[1:]
		l, b := int(b[0]), b[1:]
		switch {
		case l == 6 && (len(b)%6 == 0):
		case l == 20 && (len(b)%20 == 0):
		default:
			return nil, ErrInvalidLength
		}

		for len(b) != 0 {
			h := binary.LittleEndian.Uint16(b[:2])
			endh := binary.LittleEndian.Uint16(b[2:4])
			s := &Service{
				uuid: UUID{b[4:l]},
				h:    h,
				endh: endh,
			}
			p.svcs = append(p.svcs, s)
			b = b[l:]
			done = endh == 0xFFFF
			start = endh + 1
		}
	}
	return p.svcs, nil
}

func (p *peripheral) DiscoverIncludedServices(ss []UUID, s *Service) ([]*Service, error) {
	// TODO
	return nil, nil
}

func (p *peripheral) DiscoverCharacteristics(cs []UUID, s *Service) ([]*Characteristic, error) {
	// TODO: implement the UUID filters
	done := false
	start := s.h
	var prev *Characteristic
	for !done {
		op := byte(attOpReadByTypeReq)
		b := make([]byte, 7)
		b[0] = op
		binary.LittleEndian.PutUint16(b[1:3], start)
		binary.LittleEndian.PutUint16(b[3:5], s.endh)
		binary.LittleEndian.PutUint16(b[5:7], 0x2803)

		b = p.sendReq(op, b)
		if finish(op, start, b) {
			break
		}
		b = b[1:]

		l, b := int(b[0]), b[1:]
		switch {
		case l == 7 && (len(b)%7 == 0):
		case l == 21 && (len(b)%21 == 0):
		default:
			return nil, ErrInvalidLength
		}

		for len(b) != 0 {
			h := binary.LittleEndian.Uint16(b[:2])
			props := Property(b[2])
			vh := binary.LittleEndian.Uint16(b[3:5])
			u := UUID{b[5:l]}
			s := searchService(p.svcs, h, vh)
			if s == nil {
				log.Printf("Can't find service range that contains 0x%04X - 0x%04X", h, vh)
				return nil, fmt.Errorf("Can't find service range that contains 0x%04X - 0x%04X", h, vh)
			}
			c := &Characteristic{
				uuid:  u,
				svc:   s,
				props: props,
				h:     h,
				vh:    vh,
			}
			s.chars = append(s.chars, c)
			b = b[l:]
			done = vh == s.endh
			start = vh + 1
			if prev != nil {
				prev.endh = c.h - 1
			}
			prev = c
		}
	}
	if len(s.chars) > 1 {
		s.chars[len(s.chars)-1].endh = s.endh
	}
	return s.chars, nil
}

func (p *peripheral) DiscoverDescriptors(ds []UUID, c *Characteristic) ([]*Descriptor, error) {
	// TODO: implement the UUID filters
	done := false
	start := c.vh + 1
	for !done {
		if c.endh == 0 {
			c.endh = c.svc.endh
		}
		op := byte(attOpFindInfoReq)
		b := make([]byte, 5)
		b[0] = op
		binary.LittleEndian.PutUint16(b[1:3], start)
		binary.LittleEndian.PutUint16(b[3:5], c.endh)

		b = p.sendReq(op, b)
		if finish(attOpFindInfoReq, start, b) {
			break
		}
		b = b[1:]

		var l int
		f, b := int(b[0]), b[1:]
		switch {
		case f == 1 && (len(b)%4 == 0):
			l = 4
		case f == 2 && (len(b)%18 == 0):
			l = 18
		default:
			return nil, ErrInvalidLength
		}

		for len(b) != 0 {
			h := binary.LittleEndian.Uint16(b[:2])
			u := UUID{b[2:l]}
			d := &Descriptor{uuid: u, h: h, char: c}
			c.descs = append(c.descs, d)
			if u.Equal(attrClientCharacteristicConfigUUID) {
				c.cccd = d
			}
			b = b[l:]
			done = h == c.endh
			start = h + 1
		}
	}
	return c.descs, nil
}

func (p *peripheral) ReadCharacteristic(c *Characteristic) ([]byte, error) {
	b := make([]byte, 3)
	op := byte(attOpReadReq)
	b[0] = op
	binary.LittleEndian.PutUint16(b[1:3], c.vh)

	b = p.sendReq(op, b)
	b = b[1:]
	return b, nil
}

func (p *peripheral) WriteCharacteristic(c *Characteristic, value []byte, noRsp bool) error {
	b := make([]byte, 3+len(value))
	op := byte(attOpWriteReq)
	b[0] = op
	if noRsp {
		b[0] = attOpWriteCmd
	}
	binary.LittleEndian.PutUint16(b[1:3], c.vh)
	copy(b[3:], value)

	if !noRsp {
		p.sendCmd(op, b)
		return nil
	}
	b = p.sendReq(op, b)
	// TODO: error handling
	b = b[1:]
	return nil
}

func (p *peripheral) ReadDescriptor(d *Descriptor) ([]byte, error) {
	b := make([]byte, 3)
	op := byte(attOpReadReq)
	b[0] = op
	binary.LittleEndian.PutUint16(b[1:3], d.h)

	b = p.sendReq(op, b)
	b = b[1:]
	// TODO: error handling
	return b, nil
}

func (p *peripheral) WriteDescriptor(d *Descriptor, value []byte) error {
	b := make([]byte, 3+len(value))
	op := byte(attOpWriteReq)
	b[0] = op
	binary.LittleEndian.PutUint16(b[1:3], d.h)
	copy(b[3:], value)

	b = p.sendReq(op, b)
	b = b[1:]
	// TODO: error handling
	return nil
}

func (p *peripheral) setNotifyValue(c *Characteristic, flag uint16,
	f func(*Characteristic, []byte, error)) error {
	if c.cccd == nil {
		return errors.New("no cccd") // FIXME
	}
	ccc := uint16(0)
	if f != nil {
		ccc = flag
		p.sub.subscribe(c.vh, func(b []byte, err error) { f(c, b, err) })
	}
	b := make([]byte, 5)
	op := byte(attOpWriteReq)
	b[0] = op
	binary.LittleEndian.PutUint16(b[1:3], c.cccd.h)
	binary.LittleEndian.PutUint16(b[3:5], ccc)

	b = p.sendReq(op, b)
	b = b[1:]
	// TODO: error handling
	if f == nil {
		p.sub.unsubscribe(c.vh)
	}
	return nil
}

func (p *peripheral) SetNotifyValue(c *Characteristic,
	f func(*Characteristic, []byte, error)) error {
	return p.setNotifyValue(c, gattCCCNotifyFlag, f)
}

func (p *peripheral) SetIndicateValue(c *Characteristic,
	f func(*Characteristic, []byte, error)) error {
	return p.setNotifyValue(c, gattCCCIndicateFlag, f)
}

func (p *peripheral) ReadRSSI() int {
	// TODO: implement
	return -1
}

func searchService(ss []*Service, start, end uint16) *Service {
	for _, s := range ss {
		if s.h < start && s.endh >= end {
			return s
		}
	}
	return nil
}

// TODO: unifiy the message with OS X pots and refactor
type message struct {
	op   byte
	b    []byte
	rspc chan []byte
}

func (p *peripheral) sendCmd(op byte, b []byte) {
	p.reqc <- message{op: op, b: b}
}

func (p *peripheral) sendReq(op byte, b []byte) []byte {
	m := message{op: op, b: b, rspc: make(chan []byte)}
	p.reqc <- m
	return <-m.rspc
}

func (p *peripheral) loop() {
	// Serialize the request.
	rspc := make(chan []byte)

	// Dequeue request loop
	go func() {
		for {
			select {
			case req := <-p.reqc:
				p.l2c.Write(req.b)
				if req.rspc == nil {
					break
				}
				r := <-rspc
				switch reqOp, rspOp := req.b[0], r[0]; {
				case rspOp == attRspFor[reqOp]:
				case rspOp == attOpError && r[1] == reqOp:
				default:
					log.Printf("Request 0x%02x got a mismatched response: 0x%02x", reqOp, rspOp)
					// FIXME: terminate the connection?
				}
				req.rspc <- r
			case <-p.quitc:
				return
			}
		}
	}()

	// L2CAP implementations shall support a minimum MTU size of 48 bytes.
	// The default value is 672 bytes
	buf := make([]byte, 672)

	// Handling response or notification/indication
	for {
		n, err := p.l2c.Read(buf)
		if n == 0 || err != nil {
			close(p.quitc)
			return
		}

		b := make([]byte, n)
		copy(b, buf)

		if (b[0] != attOpHandleNotify) && (b[0] != attOpHandleInd) {
			rspc <- b
			continue
		}

		h := binary.LittleEndian.Uint16(b[1:3])
		f := p.sub.fn(h)
		if f == nil {
			log.Printf("notified by unsubscribed handle")
			// FIXME: terminate the connection?
			continue
		}
		go f(b[3:], nil)

		if b[0] == attOpHandleInd {
			// write aknowledgement for indication
			p.l2c.Write([]byte{attOpHandleCnf})
		}

	}
}
<commit_msg>Fixing indications ACK on early exit<commit_after>package gatt

import (
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"strings"

	"github.com/paypal/gatt/linux"
)

type peripheral struct {
	// NameChanged is called whenever the peripheral GAP device name has changed.
	NameChanged func(*peripheral)

	// ServicedModified is called when one or more service of a peripheral have changed.
	// A list of invalid service is provided in the parameter.
	ServicesModified func(*peripheral, []*Service)

	d    *device
	svcs []*Service

	sub *subscriber

	mtu uint16
	l2c io.ReadWriteCloser

	reqc  chan message
	quitc chan struct{}

	pd *linux.PlatData // platform specific data
}

func (p *peripheral) Device() Device       { return p.d }
func (p *peripheral) ID() string           { return strings.ToUpper(net.HardwareAddr(p.pd.Address[:]).String()) }
func (p *peripheral) Name() string         { return p.pd.Name }
func (p *peripheral) Services() []*Service { return p.svcs }

func finish(op byte, h uint16, b []byte) bool {
	done := b[0] == attOpError && b[1] == op && b[2] == byte(h) && b[3] == byte(h>>8)
	e := attEcode(b[4])
	if e != attEcodeAttrNotFound {
		// log.Printf("unexpected protocol error: %s", e)
		// FIXME: terminate the connection
	}
	return done
}

func (p *peripheral) DiscoverServices(s []UUID) ([]*Service, error) {
	// TODO: implement the UUID filters
	// p.pd.Conn.Write([]byte{0x02, 0x87, 0x00}) // MTU
	done := false
	start := uint16(0x0001)
	for !done {
		op := byte(attOpReadByGroupReq)
		b := make([]byte, 7)
		b[0] = op
		binary.LittleEndian.PutUint16(b[1:3], start)
		binary.LittleEndian.PutUint16(b[3:5], 0xFFFF)
		binary.LittleEndian.PutUint16(b[5:7], 0x2800)

		b = p.sendReq(op, b)
		if finish(op, start, b) {
			break
		}
		b = b[1:]
		l, b := int(b[0]), b[1:]
		switch {
		case l == 6 && (len(b)%6 == 0):
		case l == 20 && (len(b)%20 == 0):
		default:
			return nil, ErrInvalidLength
		}

		for len(b) != 0 {
			h := binary.LittleEndian.Uint16(b[:2])
			endh := binary.LittleEndian.Uint16(b[2:4])
			s := &Service{
				uuid: UUID{b[4:l]},
				h:    h,
				endh: endh,
			}
			p.svcs = append(p.svcs, s)
			b = b[l:]
			done = endh == 0xFFFF
			start = endh + 1
		}
	}
	return p.svcs, nil
}

func (p *peripheral) DiscoverIncludedServices(ss []UUID, s *Service) ([]*Service, error) {
	// TODO
	return nil, nil
}

func (p *peripheral) DiscoverCharacteristics(cs []UUID, s *Service) ([]*Characteristic, error) {
	// TODO: implement the UUID filters
	done := false
	start := s.h
	var prev *Characteristic
	for !done {
		op := byte(attOpReadByTypeReq)
		b := make([]byte, 7)
		b[0] = op
		binary.LittleEndian.PutUint16(b[1:3], start)
		binary.LittleEndian.PutUint16(b[3:5], s.endh)
		binary.LittleEndian.PutUint16(b[5:7], 0x2803)

		b = p.sendReq(op, b)
		if finish(op, start, b) {
			break
		}
		b = b[1:]

		l, b := int(b[0]), b[1:]
		switch {
		case l == 7 && (len(b)%7 == 0):
		case l == 21 && (len(b)%21 == 0):
		default:
			return nil, ErrInvalidLength
		}

		for len(b) != 0 {
			h := binary.LittleEndian.Uint16(b[:2])
			props := Property(b[2])
			vh := binary.LittleEndian.Uint16(b[3:5])
			u := UUID{b[5:l]}
			s := searchService(p.svcs, h, vh)
			if s == nil {
				log.Printf("Can't find service range that contains 0x%04X - 0x%04X", h, vh)
				return nil, fmt.Errorf("Can't find service range that contains 0x%04X - 0x%04X", h, vh)
			}
			c := &Characteristic{
				uuid:  u,
				svc:   s,
				props: props,
				h:     h,
				vh:    vh,
			}
			s.chars = append(s.chars, c)
			b = b[l:]
			done = vh == s.endh
			start = vh + 1
			if prev != nil {
				prev.endh = c.h - 1
			}
			prev = c
		}
	}
	if len(s.chars) > 1 {
		s.chars[len(s.chars)-1].endh = s.endh
	}
	return s.chars, nil
}

func (p *peripheral) DiscoverDescriptors(ds []UUID, c *Characteristic) ([]*Descriptor, error) {
	// TODO: implement the UUID filters
	done := false
	start := c.vh + 1
	for !done {
		if c.endh == 0 {
			c.endh = c.svc.endh
		}
		op := byte(attOpFindInfoReq)
		b := make([]byte, 5)
		b[0] = op
		binary.LittleEndian.PutUint16(b[1:3], start)
		binary.LittleEndian.PutUint16(b[3:5], c.endh)

		b = p.sendReq(op, b)
		if finish(attOpFindInfoReq, start, b) {
			break
		}
		b = b[1:]

		var l int
		f, b := int(b[0]), b[1:]
		switch {
		case f == 1 && (len(b)%4 == 0):
			l = 4
		case f == 2 && (len(b)%18 == 0):
			l = 18
		default:
			return nil, ErrInvalidLength
		}

		for len(b) != 0 {
			h := binary.LittleEndian.Uint16(b[:2])
			u := UUID{b[2:l]}
			d := &Descriptor{uuid: u, h: h, char: c}
			c.descs = append(c.descs, d)
			if u.Equal(attrClientCharacteristicConfigUUID) {
				c.cccd = d
			}
			b = b[l:]
			done = h == c.endh
			start = h + 1
		}
	}
	return c.descs, nil
}

func (p *peripheral) ReadCharacteristic(c *Characteristic) ([]byte, error) {
	b := make([]byte, 3)
	op := byte(attOpReadReq)
	b[0] = op
	binary.LittleEndian.PutUint16(b[1:3], c.vh)

	b = p.sendReq(op, b)
	b = b[1:]
	return b, nil
}

func (p *peripheral) WriteCharacteristic(c *Characteristic, value []byte, noRsp bool) error {
	b := make([]byte, 3+len(value))
	op := byte(attOpWriteReq)
	b[0] = op
	if noRsp {
		b[0] = attOpWriteCmd
	}
	binary.LittleEndian.PutUint16(b[1:3], c.vh)
	copy(b[3:], value)

	if !noRsp {
		p.sendCmd(op, b)
		return nil
	}
	b = p.sendReq(op, b)
	// TODO: error handling
	b = b[1:]
	return nil
}

func (p *peripheral) ReadDescriptor(d *Descriptor) ([]byte, error) {
	b := make([]byte, 3)
	op := byte(attOpReadReq)
	b[0] = op
	binary.LittleEndian.PutUint16(b[1:3], d.h)

	b = p.sendReq(op, b)
	b = b[1:]
	// TODO: error handling
	return b, nil
}

func (p *peripheral) WriteDescriptor(d *Descriptor, value []byte) error {
	b := make([]byte, 3+len(value))
	op := byte(attOpWriteReq)
	b[0] = op
	binary.LittleEndian.PutUint16(b[1:3], d.h)
	copy(b[3:], value)

	b = p.sendReq(op, b)
	b = b[1:]
	// TODO: error handling
	return nil
}

func (p *peripheral) setNotifyValue(c *Characteristic, flag uint16,
	f func(*Characteristic, []byte, error)) error {
	if c.cccd == nil {
		return errors.New("no cccd") // FIXME
	}
	ccc := uint16(0)
	if f != nil {
		ccc = flag
		p.sub.subscribe(c.vh, func(b []byte, err error) { f(c, b, err) })
	}
	b := make([]byte, 5)
	op := byte(attOpWriteReq)
	b[0] = op
	binary.LittleEndian.PutUint16(b[1:3], c.cccd.h)
	binary.LittleEndian.PutUint16(b[3:5], ccc)

	b = p.sendReq(op, b)
	b = b[1:]
	// TODO: error handling
	if f == nil {
		p.sub.unsubscribe(c.vh)
	}
	return nil
}

func (p *peripheral) SetNotifyValue(c *Characteristic,
	f func(*Characteristic, []byte, error)) error {
	return p.setNotifyValue(c, gattCCCNotifyFlag, f)
}

func (p *peripheral) SetIndicateValue(c *Characteristic,
	f func(*Characteristic, []byte, error)) error {
	return p.setNotifyValue(c, gattCCCIndicateFlag, f)
}

func (p *peripheral) ReadRSSI() int {
	// TODO: implement
	return -1
}

func searchService(ss []*Service, start, end uint16) *Service {
	for _, s := range ss {
		if s.h < start && s.endh >= end {
			return s
		}
	}
	return nil
}

// TODO: unifiy the message with OS X pots and refactor
type message struct {
	op   byte
	b    []byte
	rspc chan []byte
}

func (p *peripheral) sendCmd(op byte, b []byte) {
	p.reqc <- message{op: op, b: b}
}

func (p *peripheral) sendReq(op byte, b []byte) []byte {
	m := message{op: op, b: b, rspc: make(chan []byte)}
	p.reqc <- m
	return <-m.rspc
}

func (p *peripheral) loop() {
	// Serialize the request.
	rspc := make(chan []byte)

	// Dequeue request loop
	go func() {
		for {
			select {
			case req := <-p.reqc:
				p.l2c.Write(req.b)
				if req.rspc == nil {
					break
				}
				r := <-rspc
				switch reqOp, rspOp := req.b[0], r[0]; {
				case rspOp == attRspFor[reqOp]:
				case rspOp == attOpError && r[1] == reqOp:
				default:
					log.Printf("Request 0x%02x got a mismatched response: 0x%02x", reqOp, rspOp)
					// FIXME: terminate the connection?
				}
				req.rspc <- r
			case <-p.quitc:
				return
			}
		}
	}()

	// L2CAP implementations shall support a minimum MTU size of 48 bytes.
	// The default value is 672 bytes
	buf := make([]byte, 672)

	// Handling response or notification/indication
	for {
		n, err := p.l2c.Read(buf)
		if n == 0 || err != nil {
			close(p.quitc)
			return
		}

		b := make([]byte, n)
		copy(b, buf)

		if (b[0] != attOpHandleNotify) && (b[0] != attOpHandleInd) {
			rspc <- b
			continue
		}

		h := binary.LittleEndian.Uint16(b[1:3])
		f := p.sub.fn(h)
		if f == nil {
			log.Printf("notified by unsubscribed handle")
			// FIXME: terminate the connection?
		} else {
			go f(b[3:], nil)
		}

		if b[0] == attOpHandleInd {
			// write aknowledgement for indication
			p.l2c.Write([]byte{attOpHandleCnf})
		}

	}
}
<|endoftext|>


<commit_before>package pig

import (
	"fmt"
	"os"
	"sort"
	"strings"
)

//
// Language names exist in 3 forms : nice, standard, lowercase
// Ex : "C++", "Cpp", "cpp"
//

var mainStreamLangs = [...]string{"C", "Cpp", "Csharp", "Go", "Java", "JS", "Obj-C", "PHP", "Python", "Ruby"}

// Return alpha codes for each language (no encoding problems).
// See PrintNiceLang to display them more fancy.
func MainStreamLanguages() []string {
	return mainStreamLangs[:]
}

var moreLangs = [...]string{"Ada", "Caml", "Clojure", "Cobol", "D", "Dart", "Elixir", "Erlang", "Fortran", "Haskell", "Lua", "Lisp", "Pascal", "Perl", "Prolog", "Rust", "Scala", "Scheme", "VB"}

func MoreLanguages() []string {
	// These do *not* include the MainStreamLanguages()
	return moreLangs[:]
}

var synonymLangs = map[string]string{
	"Javascript":   "JS",
	"Objective C":  "Obj-C",
	"Visual Basic": "VB",
}

var AllLangs []string
var AllNiceLangs []string

func AllLanguages() []string {
	if AllLangs == nil {
		mainstream := MainStreamLanguages()
		more := MoreLanguages()
		AllLangs = make([]string, len(mainstream)+len(more))
		copy(AllLangs, mainstream)
		copy(AllLangs[len(mainstream):], more)
		sort.Strings(AllLangs)
		AllNiceLangs = make([]string, len(AllLangs))
		for i, lg := range AllLangs {
			AllNiceLangs[i] = PrintNiceLang(lg)
		}
	}
	return AllLangs
}

// autocompletions is a map[string][]string
var autocompletions = precomputeAutocompletions()

func LanguageAutoComplete(fragment string) []string {
	fragment = strings.ToLower(fragment)

	// Dynamic search (slow)
	// options := []string{}
	// for _, lg := range AllLanguages() {
	// 	if strings.Contains(strings.ToLower(lg), fragment) || strings.Contains(strings.ToLower(PrintNiceLang(lg)), fragment) {
	// 		options = append(options, PrintNiceLang(lg))
	// 	}
	// }
	// return options

	// Precomputed search (fast)
	return autocompletions[fragment]
}

func PrintNiceLang(lang string) string {
	switch strings.TrimSpace(strings.ToLower(lang)) {
	case "cpp":
		return "C++"
	case "csharp":
		return "C#"
	default:
		return lang
	}
}

func PrintNiceLangs(langs []string) []string {
	nice := make([]string, len(langs))
	for i, lang := range langs {
		nice[i] = PrintNiceLang(lang)
	}
	return nice
}

func PrintShortLang(lang string) string {
	switch strings.TrimSpace(strings.ToLower(lang)) {
	case "clojure":
		return "Clj"
	case "cobol":
		return "Co bol"
	case "cpp":
		return "C++"
	case "csharp":
		return "C#"
	case "erlang":
		return "Er lang"
	case "elixir":
		return "Eli xir"
	case "fortran":
		return "For tran"
	case "haskell":
		return "Has kell"
	case "obj-c":
		return "Obj C"
	case "pascal":
		return "Pas"
	case "python":
		return "Py"
	case "scheme":
		return "scm"
	case "prolog":
		return "Pro log"
	default:
		return lang
	}
}

func indexByLowerCase(langs []string) map[string]string {
	m := map[string]string{}
	for _, lg := range langs {
		m[strings.ToLower(lg)] = lg
	}
	return m
}

var langLowerCaseIndex = indexByLowerCase(AllLanguages())

func NormLang(lang string) string {
	lg := strings.TrimSpace(strings.ToLower(lang))
	switch lg {
	case "c++":
		return "Cpp"
	case "cc":
		return "Cpp"
	case "c#":
		return "Csharp"
	case "javascript":
		return "JS"
	case "golang":
		return "Go"
	case "py":
		return "Python"
	case "objective c":
		return "Obj-C"
	default:
		return langLowerCaseIndex[lg]
	}
}

func precomputeAutocompletions() map[string][]string {
	m := make(map[string][]string, 100)

	// Crazy shadowing of variable "lg" is allowed in go...
	for _, trueLg := range AllLanguages() {
		niceLg := PrintNiceLang(trueLg)
		for _, lg := range []string{trueLg, niceLg} {
			lg := strings.ToLower(lg)
			fragments := substrings(lg)
			for _, frag := range fragments {
				if !StringSliceContains(m[frag], niceLg) {
					m[frag] = append(m[frag], niceLg)
				}
			}
		}
	}

	for lg, trueLg := range synonymLangs {
		niceLg := PrintNiceLang(trueLg)
		lg := strings.ToLower(lg)
		fragments := substrings(lg)
		for _, frag := range fragments {
			if !StringSliceContains(m[frag], niceLg) {
				m[frag] = append(m[frag], niceLg)
			}
		}
	}

	fmt.Fprintf(os.Stderr, "---\n")
	return m
}

func substrings(s string) []string {
	L := len(s)
	seen := make(map[string]bool, L*L)
	fragments := make([]string, L*L)
	// This works well for language names with only 1-byte chars, not for any string
	for i := 0; i < L; i++ {
		for j := i + 1; j <= L; j++ {
			frag := s[i:j]
			if seen[frag] {
				continue
			}
			seen[frag] = true
			fragments = append(fragments, frag)
		}
	}
	return fragments
}

var langsExtraKeywords = map[string][]string{
	"Clojure": []string{"clj", "cljs", "cljc", "edn"},
	"Csharp":  []string{"cs"},
	"D":       []string{"dlang"},
	"Elixir":  []string{"ex", "exs"},
	"Go":      []string{"golang"},
	"JS":      []string{"javascript"},
	"Obj-C":   []string{"Objective", "Objective-C", "mm"},
	"Python":  []string{"py"},
	"Ruby":    []string{"rb"},
	"VB":      []string{"visual", "basic"},
	"Erlang":  []string{"erl", "hrl"},
	"Fortran": []string{"for", "f90", "f95"},
	"Haskell": []string{"hs", "lhs"},
	"Pascal":  []string{"pp", "pas", "inc"},
	"Perl":    []string{"pl"},
	"Rust":    []string{"rs"},
	"Scala":   []string{"sc"},
	"Scheme":  []string{"scs", "ss"},
}
<commit_msg>Languages += Kotlin.<commit_after>package pig

import (
	"fmt"
	"os"
	"sort"
	"strings"
)

//
// Language names exist in 3 forms : nice, standard, lowercase
// Ex : "C++", "Cpp", "cpp"
//

var mainStreamLangs = [...]string{"C", "Cpp", "Csharp", "Go", "Java", "JS", "Obj-C", "PHP", "Python", "Ruby"}

// Return alpha codes for each language (no encoding problems).
// See PrintNiceLang to display them more fancy.
func MainStreamLanguages() []string {
	return mainStreamLangs[:]
}

var moreLangs = [...]string{"Ada", "Caml", "Clojure", "Cobol", "D", "Dart", "Elixir", "Erlang", "Fortran", "Haskell", "Kotlin", "Lua", "Lisp", "Pascal", "Perl", "Prolog", "Rust", "Scala", "Scheme", "VB"}

func MoreLanguages() []string {
	// These do *not* include the MainStreamLanguages()
	return moreLangs[:]
}

var synonymLangs = map[string]string{
	"Javascript":   "JS",
	"Objective C":  "Obj-C",
	"Visual Basic": "VB",
}

var AllLangs []string
var AllNiceLangs []string

func AllLanguages() []string {
	if AllLangs == nil {
		mainstream := MainStreamLanguages()
		more := MoreLanguages()
		AllLangs = make([]string, len(mainstream)+len(more))
		copy(AllLangs, mainstream)
		copy(AllLangs[len(mainstream):], more)
		sort.Strings(AllLangs)
		AllNiceLangs = make([]string, len(AllLangs))
		for i, lg := range AllLangs {
			AllNiceLangs[i] = PrintNiceLang(lg)
		}
	}
	return AllLangs
}

// autocompletions is a map[string][]string
var autocompletions = precomputeAutocompletions()

func LanguageAutoComplete(fragment string) []string {
	fragment = strings.ToLower(fragment)

	// Dynamic search (slow)
	// options := []string{}
	// for _, lg := range AllLanguages() {
	// 	if strings.Contains(strings.ToLower(lg), fragment) || strings.Contains(strings.ToLower(PrintNiceLang(lg)), fragment) {
	// 		options = append(options, PrintNiceLang(lg))
	// 	}
	// }
	// return options

	// Precomputed search (fast)
	return autocompletions[fragment]
}

func PrintNiceLang(lang string) string {
	switch strings.TrimSpace(strings.ToLower(lang)) {
	case "cpp":
		return "C++"
	case "csharp":
		return "C#"
	default:
		return lang
	}
}

func PrintNiceLangs(langs []string) []string {
	nice := make([]string, len(langs))
	for i, lang := range langs {
		nice[i] = PrintNiceLang(lang)
	}
	return nice
}

func PrintShortLang(lang string) string {
	switch strings.TrimSpace(strings.ToLower(lang)) {
	case "clojure":
		return "Clj"
	case "cobol":
		return "Co bol"
	case "cpp":
		return "C++"
	case "csharp":
		return "C#"
	case "erlang":
		return "Er lang"
	case "elixir":
		return "Eli xir"
	case "fortran":
		return "For tran"
	case "haskell":
		return "Has kell"
	case "obj-c":
		return "Obj C"
	case "pascal":
		return "Pas"
	case "python":
		return "Py"
	case "scheme":
		return "scm"
	case "prolog":
		return "Pro log"
	default:
		return lang
	}
}

func indexByLowerCase(langs []string) map[string]string {
	m := map[string]string{}
	for _, lg := range langs {
		m[strings.ToLower(lg)] = lg
	}
	return m
}

var langLowerCaseIndex = indexByLowerCase(AllLanguages())

func NormLang(lang string) string {
	lg := strings.TrimSpace(strings.ToLower(lang))
	switch lg {
	case "c++":
		return "Cpp"
	case "cc":
		return "Cpp"
	case "c#":
		return "Csharp"
	case "javascript":
		return "JS"
	case "golang":
		return "Go"
	case "py":
		return "Python"
	case "objective c":
		return "Obj-C"
	default:
		return langLowerCaseIndex[lg]
	}
}

func precomputeAutocompletions() map[string][]string {
	m := make(map[string][]string, 100)

	// Crazy shadowing of variable "lg" is allowed in go...
	for _, trueLg := range AllLanguages() {
		niceLg := PrintNiceLang(trueLg)
		for _, lg := range []string{trueLg, niceLg} {
			lg := strings.ToLower(lg)
			fragments := substrings(lg)
			for _, frag := range fragments {
				if !StringSliceContains(m[frag], niceLg) {
					m[frag] = append(m[frag], niceLg)
				}
			}
		}
	}

	for lg, trueLg := range synonymLangs {
		niceLg := PrintNiceLang(trueLg)
		lg := strings.ToLower(lg)
		fragments := substrings(lg)
		for _, frag := range fragments {
			if !StringSliceContains(m[frag], niceLg) {
				m[frag] = append(m[frag], niceLg)
			}
		}
	}

	fmt.Fprintf(os.Stderr, "---\n")
	return m
}

func substrings(s string) []string {
	L := len(s)
	seen := make(map[string]bool, L*L)
	fragments := make([]string, L*L)
	// This works well for language names with only 1-byte chars, not for any string
	for i := 0; i < L; i++ {
		for j := i + 1; j <= L; j++ {
			frag := s[i:j]
			if seen[frag] {
				continue
			}
			seen[frag] = true
			fragments = append(fragments, frag)
		}
	}
	return fragments
}

var langsExtraKeywords = map[string][]string{
	"Clojure": []string{"clj", "cljs", "cljc", "edn"},
	"Csharp":  []string{"cs"},
	"D":       []string{"dlang"},
	"Elixir":  []string{"ex", "exs"},
	"Go":      []string{"golang"},
	"JS":      []string{"javascript"},
	"Obj-C":   []string{"Objective", "Objective-C", "mm"},
	"Python":  []string{"py"},
	"Ruby":    []string{"rb"},
	"VB":      []string{"visual", "basic"},
	"Erlang":  []string{"erl", "hrl"},
	"Fortran": []string{"for", "f90", "f95"},
	"Haskell": []string{"hs", "lhs"},
	"Pascal":  []string{"pp", "pas", "inc"},
	"Perl":    []string{"pl"},
	"Rust":    []string{"rs"},
	"Scala":   []string{"sc"},
	"Scheme":  []string{"scs", "ss"},
}
<|endoftext|>


<commit_before>// +build darwin,kqueue dragonfly freebsd netbsd openbsd

package notify

import (
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"syscall"
)

// TODO: Take into account currently monitored files with those read from dir.

// newWatcher returns `kqueue` Watcher implementation.
func newWatcher(c chan<- EventInfo) watcher {
	k := &kqueue{
		idLkp:  make(map[int]*watched, 0),
		pthLkp: make(map[string]*watched, 0),
		c:      c,
		s:      make(chan struct{}, 1),
	}
	if err := k.init(); err != nil {
		// TODO: Does it really has to be this way?
		panic(err)
	}
	go k.monitor()
	return k
}

// KqEvent represents a single event.
type KqEvent struct {
	Kq *syscall.Kevent_t
	FI os.FileInfo
}

// Close closes all still open file descriptors and kqueue.
func (k *kqueue) Close() error {
	k.s <- struct{}{}
	k.Lock()
	if k.fd != nil {
		syscall.Close(*k.fd)
	}
	for _, w := range k.idLkp {
		syscall.Close(w.fd)
	}
	k.idLkp, k.pthLkp = nil, nil
	k.Unlock()
	return nil
}

// sendEvents sends reported events one by one through chan.
func (k *kqueue) sendEvents(evn []event) {
	for i := range evn {
		k.c <- &evn[i]
	}
}

// mask converts requested events to `kqueue` representation.
func mask(e Event) (o uint32) {
	o = uint32(e &^ Create)
	for k, n := range ekind {
		if e&n != 0 {
			o = (o &^ uint32(n)) | uint32(k)
		}
	}
	return
}

// unmask converts event received from `kqueue` to `notify.Event`
// representation taking into account requested events (`w`).
func unmask(o uint32, w Event) (e Event) {
	for k, n := range ekind {
		if o&uint32(k) != 0 {
			if w&k != 0 {
				e |= k
			}
			if w&n != 0 {
				e |= n
			}
		}
	}
	e |= Event(o) & w
	return
}

// del closes fd for `watched` and removes it from internal cache of monitored
// files/directories.
func (k *kqueue) del(w *watched) {
	syscall.Close(w.fd)
	delete(k.idLkp, w.fd)
	delete(k.pthLkp, w.p)
}

// monitor reads reported kqueue events and forwards them further after
// performing additional processing. If read event concerns directory,
// it generates Create/Delete event and sent them further instead of directory
// event. This event is detected based on reading contents of analyzed
// directory. If no changes in file list are detected, no event is send further.
// Reading directory structure is less accurate than kqueue and can lead
// to lack of detection of all events.
func (k *kqueue) monitor() {
	var evn []event
	// TODO: This is poor, and incorrect way to debug issue with events for
	// not registered fds, but temporarily for now might do. To be dropped.
	// TODO: Maybe len(kevn)>1 would help. What len in this case?
	hist := make(map[int]struct{})
	for {
		k.sendEvents(evn)
		evn = make([]event, 0)
		var kevn [1]syscall.Kevent_t
		n, err := syscall.Kevent(*k.fd, nil, kevn[:], nil)
		select {
		case <-k.s:
			return
		default:
		}
		if err != nil {
			fmt.Fprintf(os.Stderr, "kqueue: failed to read events: %q\n", err)
			continue
		}
		if n > 0 {
			k.Lock()
			w := k.idLkp[int(kevn[0].Ident)]
			if w == nil {
				if _, ok := hist[int(kevn[0].Ident)]; ok {
					fmt.Fprintf(os.Stderr, "kqueue: %v was previously registered\n", kevn[0])
				} else {
					fmt.Fprintf(os.Stderr, "kqueue: %v kevent is not registerd\n", kevn[0])
				}
				continue
			}
			hist[int(kevn[0].Ident)] = struct{}{}
			o := unmask(kevn[0].Fflags, w.eDir|w.eNonDir)
			if w.fi.IsDir() {
				// If it's dir and delete we have to send it and continue, because
				// other processing relies on opening (in this case not existing) dir.
				if (Event(kevn[0].Fflags) & NoteDelete) != 0 {
					// Write is reported also for Delete on directory. Because of that
					// we have to filter it out explicitly.
					evn = append(evn, event{w.p,
						o & ^Write & ^NoteWrite, KqEvent{&kevn[0], w.fi}})
					k.del(w)
					k.Unlock()
					continue
				}
				if (Event(kevn[0].Fflags) & NoteWrite) != 0 {
					switch err := k.walk(w.p, func(fi os.FileInfo) error {
						p := filepath.Join(w.p, fi.Name())
						if err := k.singlewatch(p, w.eDir, false, fi); err != nil {
							if err != errAlreadyWatched && !os.IsNotExist(err) {
								// TODO: pass error via chan because state of monitoring is
								// invalid.
								panic(err)
							}
						} else if (w.eDir & Create) != 0 {
							evn = append(evn, event{p, Create, KqEvent{nil, fi}})
						}
						return nil
					}); {
					// If file is already watched, kqueue will return remove event.
					// If it's not yet monitored.. TODO: Reconsider.
					case os.IsNotExist(err):
						continue
					case err != nil:
						// TODO: pass error via chan because state of monitoring is invalid.
						panic(err)
					default:
					}
				}
			} else {
				evn = append(evn, event{w.p, o, KqEvent{&kevn[0], w.fi}})
			}
			if (Event(kevn[0].Fflags) & NoteDelete) != 0 {
				k.del(w)
			}
			k.Unlock()
		}
	}
}

// kqueue is a type holding data for kqueue watcher.
type kqueue struct {
	sync.Mutex
	// fd is a kqueue file descriptor
	fd *int
	// idLkp is a data structure mapping file descriptors with data about watching
	// represented by them files/directories.
	idLkp map[int]*watched
	// pthLkp is a data structure mapping file names with data about watching
	// represented by them files/directories.
	pthLkp map[string]*watched
	// c is a channel used to pass events further.
	c chan<- EventInfo
	// s is a channel used to stop monitoring.
	s chan struct{}
}

// watched is a data structure representing watched file/directory.
type watched struct {
	// p is a path to watched file/directory.
	p string
	// fd is a file descriptor for watched file/directory.
	fd int
	// fi provides information about watched file/dir.
	fi os.FileInfo
	// eDir represents events watched directly.
	eDir Event
	// eNonDir represents events watched indirectly.
	eNonDir Event
}

// init initializes kqueue.
func (k *kqueue) init() error {
	fd, err := syscall.Kqueue()
	if err != nil {
		return err
	}
	k.fd = &fd
	return nil
}

func (k *kqueue) watch(p string, e Event, fi os.FileInfo) error {
	if err := k.singlewatch(p, e, true, fi); err != nil {
		if err != errAlreadyWatched {
			return nil
		}
	}
	if fi.IsDir() {
		err := k.walk(p, func(fi os.FileInfo) (err error) {
			if err = k.singlewatch(filepath.Join(p, fi.Name()), e, false,
				fi); err != nil {
				if err != errAlreadyWatched {
					return
				}
			}
			return nil
		})
		if err != nil {
			return err
		}
	}
	return nil
}

// watch starts to watch given `p` file/directory.
func (k *kqueue) singlewatch(p string, e Event, direct bool,
	fi os.FileInfo) error {
	w, ok := k.pthLkp[p]
	if !ok {
		fd, err := syscall.Open(p, syscall.O_NONBLOCK|syscall.O_RDONLY, 0)
		if err != nil {
			return err
		}
		w = &watched{fd: fd, p: p, fi: fi}
	}
	if direct {
		w.eDir |= e
	} else {
		w.eNonDir |= e
	}
	var kevn [1]syscall.Kevent_t
	syscall.SetKevent(&kevn[0], w.fd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR)
	kevn[0].Fflags = mask(w.eDir | w.eNonDir)
	if _, err := syscall.Kevent(*k.fd, kevn[:], nil, nil); err != nil {
		return err
	}
	if !ok {
		k.idLkp[w.fd], k.pthLkp[w.p] = w, w
		return nil
	}
	return errAlreadyWatched
}

// unwatch stops watching `p` file/directory.
func (k *kqueue) singleunwatch(p string, direct bool) error {
	w := k.pthLkp[p]
	if w == nil {
		return errNotWatched
	}
	if direct {
		w.eDir = 0
	} else {
		w.eNonDir = 0
	}
	var kevn [1]syscall.Kevent_t
	syscall.SetKevent(&kevn[0], w.fd, syscall.EVFILT_VNODE, syscall.EV_DELETE)
	if _, err := syscall.Kevent(*k.fd, kevn[:], nil, nil); err != nil {
		return err
	}
	if w.eNonDir&w.eDir != 0 {
		if err := k.singlewatch(p, w.eNonDir|w.eDir, w.eNonDir == 0,
			w.fi); err != nil {
			return err
		}
	} else {
		k.del(w)
	}
	return nil
}

// walk runs `f` func on each file from `p` directory.
func (k *kqueue) walk(p string, f func(os.FileInfo) error) error {
	fp, err := os.Open(p)
	if err != nil {
		return err
	}
	ls, err := fp.Readdir(0)
	fp.Close()
	if err != nil {
		return err
	}
	for i := range ls {
		if err := f(ls[i]); err != nil {
			return err
		}
	}
	return nil
}

func (k *kqueue) unwatch(p string, fi os.FileInfo) error {
	if fi.IsDir() {
		err := k.walk(p, func(fi os.FileInfo) error {
			if !fi.IsDir() {
				return k.singleunwatch(filepath.Join(p, fi.Name()), false)
			}
			return nil
		})
		if err != nil {
			return err
		}
	}
	return k.singleunwatch(p, true)
}

// Watch implements Watcher interface.
func (k *kqueue) Watch(p string, e Event) error {
	fi, err := os.Stat(p)
	if err != nil {
		return err
	}
	k.Lock()
	err = k.watch(p, e, fi)
	k.Unlock()
	return nil
}

// Unwatch implements Watcher interface.
func (k *kqueue) Unwatch(p string) error {
	fi, err := os.Stat(p)
	if err != nil {
		return err
	}
	k.Lock()
	err = k.unwatch(p, fi)
	k.Unlock()
	return nil
}

// Rewatch implements Watcher interface.
//
// TODO(rjeczalik): This is a naive hack. Rewrite might help.
func (k *kqueue) Rewatch(p string, _, e Event) error {
	fi, err := os.Stat(p)
	if err != nil {
		return err
	}
	k.Lock()
	if err = k.unwatch(p, fi); err == nil {
		// TODO(rjeczalik): If watch fails then we leave kqueue in inconsistent
		// state. Handle? Panic? Native version of rewatch?
		err = k.watch(p, e, fi)
	}
	k.Unlock()
	return nil
}
<commit_msg>kqueue: Refactoring of monitoring loop.<commit_after>// +build darwin,kqueue dragonfly freebsd netbsd openbsd

package notify

import (
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"syscall"
)

// TODO: Take into account currently monitored files with those read from dir.

// newWatcher returns `kqueue` Watcher implementation.
func newWatcher(c chan<- EventInfo) watcher {
	k := &kqueue{
		idLkp:  make(map[int]*watched, 0),
		pthLkp: make(map[string]*watched, 0),
		c:      c,
		s:      make(chan struct{}, 1),
	}
	if err := k.init(); err != nil {
		// TODO: Does it really has to be this way?
		panic(err)
	}
	go k.monitor()
	return k
}

// KqEvent represents a single event.
type KqEvent struct {
	Kq *syscall.Kevent_t
	FI os.FileInfo
}

// Close closes all still open file descriptors and kqueue.
func (k *kqueue) Close() (err error) {
	if _, err = syscall.Write(k.pipefds[1], []byte(` `)); err != nil {
		return
	}
	<-k.s
	k.Lock()
	var e error
	for _, w := range k.idLkp {
		if e = syscall.Close(w.fd); e != nil && err == nil {
			err = e
		}
	}
	if err = syscall.Close(k.fd); err != nil {
		return
	}
	k.idLkp, k.pthLkp = nil, nil
	k.Unlock()
	return
}

// sendEvents sends reported events one by one through chan.
func (k *kqueue) sendEvents(evn []event) {
	for i := range evn {
		k.c <- &evn[i]
	}
}

// mask converts requested events to `kqueue` representation.
func mask(e Event) (o uint32) {
	o = uint32(e &^ Create)
	for k, n := range ekind {
		if e&n != 0 {
			o = (o &^ uint32(n)) | uint32(k)
		}
	}
	return
}

// unmask converts event received from `kqueue` to `notify.Event`
// representation taking into account requested events (`w`).
func unmask(o uint32, w Event) (e Event) {
	for k, n := range ekind {
		if o&uint32(k) != 0 {
			if w&k != 0 {
				e |= k
			}
			if w&n != 0 {
				e |= n
			}
		}
	}
	e |= Event(o) & w
	return
}

// del closes fd for `watched` and removes it from internal cache of monitored
// files/directories.
func (k *kqueue) del(w watched) {
	syscall.Close(w.fd)
	delete(k.idLkp, w.fd)
	delete(k.pthLkp, w.p)
}

// monitor reads reported kqueue events and forwards them further after
// performing additional processing. If read event concerns directory,
// it generates Create/Delete event and sent them further instead of directory
// event. This event is detected based on reading contents of analyzed
// directory. If no changes in file list are detected, no event is send further.
// Reading directory structure is less accurate than kqueue and can lead
// to lack of detection of all events.
func (k *kqueue) monitor() {
	var (
		kevn [1]syscall.Kevent_t
		n    int
		err  error
	)
	for {
		kevn[0] = syscall.Kevent_t{}
		switch n, err = syscall.Kevent(k.fd, nil, kevn[:], nil); {
		case err != nil:
			fmt.Fprintf(os.Stderr, "kqueue: failed to read events: %q\n", err)
			continue
		case int(kevn[0].Ident) == k.pipefds[0]:
			k.s <- struct{}{}
			return
		case n > 0:
			k.sendEvents(k.process(kevn[0]))
		}
	}
}

func (k *kqueue) dir(w watched, kevn syscall.Kevent_t, e Event) (evn []event) {
	// If it's dir and delete we have to send it and continue, because
	// other processing relies on opening (in this case not existing) dir.
	if (Event(kevn.Fflags) & NoteDelete) != 0 {
		// Write is reported also for Delete on directory. Because of that
		// we have to filter it out explicitly.
		evn = append(evn, event{w.p,
			e & ^Write & ^NoteWrite, KqEvent{&kevn, w.fi}})
		k.del(w)
		return
	}
	if (Event(kevn.Fflags) & NoteWrite) != 0 {
		switch err := k.walk(w.p, func(fi os.FileInfo) error {
			p := filepath.Join(w.p, fi.Name())
			if err := k.singlewatch(p, w.eDir, false, fi); err != nil {
				if err != errAlreadyWatched && !os.IsNotExist(err) {
					evn = append(evn, event{p, Error, KqEvent{nil, fi}})
				}
			} else if (w.eDir & Create) != 0 {
				evn = append(evn, event{p, Create, KqEvent{nil, fi}})
			}
			return nil
		}); {
		// If file is already watched, kqueue will return remove event.
		// If it's not yet monitored.. TODO: Reconsider.
		case os.IsNotExist(err):
			return
		case err != nil:
			// TODO: pass error via chan because state of monitoring is invalid.
			panic(err)
		default:
		}
	}
	return
}

func (*kqueue) file(w watched, kevn syscall.Kevent_t, e Event) (evn []event) {
	evn = append(evn, event{w.p, e, KqEvent{&kevn, w.fi}})
	return
}

// process event returned by `Kevent` call.
func (k *kqueue) process(kevn syscall.Kevent_t) (evn []event) {
	k.Lock()
	w := k.idLkp[int(kevn.Ident)]
	if w == nil {
		fmt.Fprintf(os.Stderr, "kqueue: %v event for not registered fd", kevn)
		return
	}
	e := unmask(kevn.Fflags, w.eDir|w.eNonDir)
	if w.fi.IsDir() {
		evn = k.dir(*w, kevn, e)
	} else {
		evn = k.file(*w, kevn, e)
	}
	if (Event(kevn.Fflags) & NoteDelete) != 0 {
		k.del(*w)
	}
	k.Unlock()
	return
}

// kqueue is a type holding data for kqueue watcher.
type kqueue struct {
	sync.Mutex
	// fd is a kqueue file descriptor
	fd int
	// pipefds are file descriptors used to stop `Kevent` call.
	pipefds [2]int
	// idLkp is a data structure mapping file descriptors with data about watching
	// represented by them files/directories.
	idLkp map[int]*watched
	// pthLkp is a data structure mapping file names with data about watching
	// represented by them files/directories.
	pthLkp map[string]*watched
	// c is a channel used to pass events further.
	c chan<- EventInfo
	// s is a channel used to stop monitoring.
	s chan struct{}
}

// watched is a data structure representing watched file/directory.
type watched struct {
	// p is a path to watched file/directory.
	p string
	// fd is a file descriptor for watched file/directory.
	fd int
	// fi provides information about watched file/dir.
	fi os.FileInfo
	// eDir represents events watched directly.
	eDir Event
	// eNonDir represents events watched indirectly.
	eNonDir Event
}

// init initializes kqueue.
func (k *kqueue) init() (err error) {
	if k.fd, err = syscall.Kqueue(); err != nil {
		return
	}
	// Creates pipe used to stop `Kevent` call by registering it,
	// watching read end and writing to other end of it.
	if err = syscall.Pipe(k.pipefds[:]); err != nil {
		return
	}
	var kevn [1]syscall.Kevent_t
	syscall.SetKevent(&kevn[0], k.pipefds[0], syscall.EVFILT_READ, syscall.EV_ADD)
	_, err = syscall.Kevent(k.fd, kevn[:], nil, nil)
	return
}

func (k *kqueue) watch(p string, e Event, fi os.FileInfo) error {
	if err := k.singlewatch(p, e, true, fi); err != nil {
		if err != errAlreadyWatched {
			return nil
		}
	}
	if fi.IsDir() {
		err := k.walk(p, func(fi os.FileInfo) (err error) {
			if err = k.singlewatch(filepath.Join(p, fi.Name()), e, false,
				fi); err != nil {
				if err != errAlreadyWatched {
					return
				}
			}
			return nil
		})
		if err != nil {
			return err
		}
	}
	return nil
}

// watch starts to watch given `p` file/directory.
func (k *kqueue) singlewatch(p string, e Event, direct bool,
	fi os.FileInfo) error {
	w, ok := k.pthLkp[p]
	if !ok {
		fd, err := syscall.Open(p, syscall.O_NONBLOCK|syscall.O_RDONLY, 0)
		if err != nil {
			return err
		}
		w = &watched{fd: fd, p: p, fi: fi}
	}
	if direct {
		w.eDir |= e
	} else {
		w.eNonDir |= e
	}
	var kevn [1]syscall.Kevent_t
	syscall.SetKevent(&kevn[0], w.fd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR)
	kevn[0].Fflags = mask(w.eDir | w.eNonDir)
	if _, err := syscall.Kevent(k.fd, kevn[:], nil, nil); err != nil {
		return err
	}
	if !ok {
		k.idLkp[w.fd], k.pthLkp[w.p] = w, w
		return nil
	}
	return errAlreadyWatched
}

// unwatch stops watching `p` file/directory.
func (k *kqueue) singleunwatch(p string, direct bool) error {
	w := k.pthLkp[p]
	if w == nil {
		return errNotWatched
	}
	if direct {
		w.eDir = 0
	} else {
		w.eNonDir = 0
	}
	var kevn [1]syscall.Kevent_t
	syscall.SetKevent(&kevn[0], w.fd, syscall.EVFILT_VNODE, syscall.EV_DELETE)
	if _, err := syscall.Kevent(k.fd, kevn[:], nil, nil); err != nil {
		return err
	}
	if w.eNonDir&w.eDir != 0 {
		if err := k.singlewatch(p, w.eNonDir|w.eDir, w.eNonDir == 0,
			w.fi); err != nil {
			return err
		}
	} else {
		k.del(*w)
	}
	return nil
}

// walk runs `f` func on each file from `p` directory.
func (k *kqueue) walk(p string, f func(os.FileInfo) error) error {
	fp, err := os.Open(p)
	if err != nil {
		return err
	}
	ls, err := fp.Readdir(0)
	fp.Close()
	if err != nil {
		return err
	}
	for i := range ls {
		if err := f(ls[i]); err != nil {
			return err
		}
	}
	return nil
}

func (k *kqueue) unwatch(p string, fi os.FileInfo) error {
	if fi.IsDir() {
		err := k.walk(p, func(fi os.FileInfo) error {
			if !fi.IsDir() {
				return k.singleunwatch(filepath.Join(p, fi.Name()), false)
			}
			return nil
		})
		if err != nil {
			return err
		}
	}
	return k.singleunwatch(p, true)
}

// Watch implements Watcher interface.
func (k *kqueue) Watch(p string, e Event) error {
	fi, err := os.Stat(p)
	if err != nil {
		return err
	}
	k.Lock()
	err = k.watch(p, e, fi)
	k.Unlock()
	return nil
}

// Unwatch implements Watcher interface.
func (k *kqueue) Unwatch(p string) error {
	fi, err := os.Stat(p)
	if err != nil {
		return err
	}
	k.Lock()
	err = k.unwatch(p, fi)
	k.Unlock()
	return nil
}

// Rewatch implements Watcher interface.
//
// TODO(rjeczalik): This is a naive hack. Rewrite might help.
func (k *kqueue) Rewatch(p string, _, e Event) error {
	fi, err := os.Stat(p)
	if err != nil {
		return err
	}
	k.Lock()
	if err = k.unwatch(p, fi); err == nil {
		// TODO(rjeczalik): If watch fails then we leave kqueue in inconsistent
		// state. Handle? Panic? Native version of rewatch?
		err = k.watch(p, e, fi)
	}
	k.Unlock()
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gogs 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 user

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/Unknwon/com"

	"github.com/gogits/gogs/models"
	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/middleware"
	"github.com/gogits/gogs/modules/setting"
)

const (
	DASHBOARD base.TplName = "user/dashboard/dashboard"
	PULLS     base.TplName = "user/dashboard/pulls"
	ISSUES    base.TplName = "user/issues"
	STARS     base.TplName = "user/stars"
	PROFILE   base.TplName = "user/profile"
)

func Dashboard(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("dashboard")
	ctx.Data["PageIsDashboard"] = true
	ctx.Data["PageIsNews"] = true

	var ctxUser *models.User
	// Check context type.
	orgName := ctx.Params(":org")
	if len(orgName) > 0 {
		// Organization.
		org, err := models.GetUserByName(orgName)
		if err != nil {
			if err == models.ErrUserNotExist {
				ctx.Handle(404, "GetUserByName", err)
			} else {
				ctx.Handle(500, "GetUserByName", err)
			}
			return
		}
		ctxUser = org
	} else {
		// Normal user.
		ctxUser = ctx.User
		collaborates, err := ctx.User.GetAccessibleRepositories()
		if err != nil {
			ctx.Handle(500, "GetAccessibleRepositories", err)
			return
		}

		repositories := make([]*models.Repository, 0, len(collaborates))
		for repo := range collaborates {
			repositories = append(repositories, repo)
		}

		ctx.Data["CollaborateCount"] = len(repositories)
		ctx.Data["CollaborativeRepos"] = repositories
	}
	ctx.Data["ContextUser"] = ctxUser

	if err := ctx.User.GetOrganizations(); err != nil {
		ctx.Handle(500, "GetOrganizations", err)
		return
	}
	ctx.Data["Orgs"] = ctx.User.Orgs

	repos, err := models.GetRepositories(ctxUser.Id, true)
	if err != nil {
		ctx.Handle(500, "GetRepositories", err)
		return
	}
	ctx.Data["Repos"] = repos

	// Get mirror repositories.
	mirrors := make([]*models.Repository, 0, len(repos)/2)
	for _, repo := range repos {
		if repo.IsMirror {
			if err = repo.GetMirror(); err != nil {
				ctx.Handle(500, "GetMirror: "+repo.Name, err)
				return
			}
			mirrors = append(mirrors, repo)
		}
	}
	ctx.Data["MirrorCount"] = len(mirrors)
	ctx.Data["Mirrors"] = mirrors

	// Get feeds.
	actions, err := models.GetFeeds(ctxUser.Id, 0, false)
	if err != nil {
		ctx.Handle(500, "GetFeeds", err)
		return
	}

	// Check access of private repositories.
	feeds := make([]*models.Action, 0, len(actions))
	for _, act := range actions {
		if act.IsPrivate {
			repo := &models.Repository{Id: act.RepoId, IsPrivate: true}
			// This prevents having to retrieve the repository for each action
			if act.RepoUserName == ctx.User.LowerName {
				repo.OwnerId = ctx.User.Id
			}
			if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has {
				continue
			}
		}
		// FIXME: cache results?
		u, err := models.GetUserByName(act.ActUserName)
		if err != nil {
			if err == models.ErrUserNotExist {
				continue
			}
			ctx.Handle(500, "GetUserByName", err)
			return
		}
		act.ActAvatar = u.AvatarLink()
		feeds = append(feeds, act)
	}
	ctx.Data["Feeds"] = feeds
	ctx.HTML(200, DASHBOARD)
}

func Pulls(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("pull_requests")
	ctx.Data["PageIsDashboard"] = true
	ctx.Data["PageIsPulls"] = true

	if err := ctx.User.GetOrganizations(); err != nil {
		ctx.Handle(500, "GetOrganizations", err)
		return
	}
	ctx.Data["ContextUser"] = ctx.User

	ctx.HTML(200, PULLS)
}

func ShowSSHKeys(ctx *middleware.Context, uid int64) {
	keys, err := models.ListPublicKeys(uid)
	if err != nil {
		ctx.Handle(500, "ListPublicKeys", err)
		return
	}

	var buf bytes.Buffer
	for i := range keys {
		buf.WriteString(keys[i].OmitEmail())
	}
	ctx.RenderData(200, buf.Bytes())
}

func Profile(ctx *middleware.Context) {
	ctx.Data["Title"] = "Profile"
	ctx.Data["PageIsUserProfile"] = true

	uname := ctx.Params(":username")
	// Special handle for FireFox requests favicon.ico.
	if uname == "favicon.ico" {
		ctx.Redirect(setting.AppSubUrl + "/img/favicon.png")
		return
	}

	isShowKeys := false
	if strings.HasSuffix(uname, ".keys") {
		isShowKeys = true
		uname = strings.TrimSuffix(uname, ".keys")
	}

	u, err := models.GetUserByName(uname)
	if err != nil {
		if err == models.ErrUserNotExist {
			ctx.Handle(404, "GetUserByName", err)
		} else {
			ctx.Handle(500, "GetUserByName", err)
		}
		return
	}

	// Show SSH keys.
	if isShowKeys {
		ShowSSHKeys(ctx, u.Id)
		return
	}

	if u.IsOrganization() {
		ctx.Redirect(setting.AppSubUrl + "/org/" + u.Name)
		return
	}

	// For security reason, hide e-mail address for anonymous visitors.
	if !ctx.IsSigned {
		u.Email = ""
	}
	ctx.Data["Owner"] = u

	tab := ctx.Query("tab")
	ctx.Data["TabName"] = tab
	switch tab {
	case "activity":
		actions, err := models.GetFeeds(u.Id, 0, false)
		if err != nil {
			ctx.Handle(500, "GetFeeds", err)
			return
		}
		feeds := make([]*models.Action, 0, len(actions))
		for _, act := range actions {
			if act.IsPrivate {
				if !ctx.IsSigned {
					continue
				}
				repo := &models.Repository{Id: act.RepoId, IsPrivate: true}
				// This prevents having to retrieve the repository for each action
				if act.RepoUserName == ctx.User.LowerName {
					repo.OwnerId = ctx.User.Id
				}
				if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has {
					continue
				}
			}
			// FIXME: cache results?
			u, err := models.GetUserByName(act.ActUserName)
			if err != nil {
				if err == models.ErrUserNotExist {
					continue
				}
				ctx.Handle(500, "GetUserByName", err)
				return
			}
			act.ActAvatar = u.AvatarLink()
			feeds = append(feeds, act)
		}
		ctx.Data["Feeds"] = feeds
	default:
		ctx.Data["Repos"], err = models.GetRepositories(u.Id, ctx.IsSigned && ctx.User.Id == u.Id)
		if err != nil {
			ctx.Handle(500, "GetRepositories", err)
			return
		}
	}

	ctx.HTML(200, PROFILE)
}

func Email2User(ctx *middleware.Context) {
	u, err := models.GetUserByEmail(ctx.Query("email"))
	if err != nil {
		if err == models.ErrUserNotExist {
			ctx.Handle(404, "user.Email2User(GetUserByEmail)", err)
		} else {
			ctx.Handle(500, "user.Email2User(GetUserByEmail)", err)
		}
		return
	}
	ctx.Redirect(setting.AppSubUrl + "/user/" + u.Name)
}

func Issues(ctx *middleware.Context) {
	ctx.Data["Title"] = "Your Issues"

	viewType := ctx.Query("type")
	types := []string{"assigned", "created_by"}
	if !com.IsSliceContainsStr(types, viewType) {
		viewType = "all"
	}

	isShowClosed := ctx.Query("state") == "closed"

	var filterMode int
	switch viewType {
	case "assigned":
		filterMode = models.FM_ASSIGN
	case "created_by":
		filterMode = models.FM_CREATE
	}

	repoId, _ := com.StrTo(ctx.Query("repoid")).Int64()
	issueStats := models.GetUserIssueStats(ctx.User.Id, filterMode)

	// Get all repositories.
	repos, err := models.GetRepositories(ctx.User.Id, true)
	if err != nil {
		ctx.Handle(500, "user.Issues(GetRepositories)", err)
		return
	}

	repoIds := make([]int64, 0, len(repos))
	showRepos := make([]*models.Repository, 0, len(repos))
	for _, repo := range repos {
		if repo.NumIssues == 0 {
			continue
		}

		repoIds = append(repoIds, repo.Id)
		repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues
		issueStats.AllCount += int64(repo.NumOpenIssues)

		if isShowClosed {
			if repo.NumClosedIssues > 0 {
				if filterMode == models.FM_CREATE {
					repo.NumClosedIssues = int(models.GetIssueCountByPoster(ctx.User.Id, repo.Id, isShowClosed))
				}
				showRepos = append(showRepos, repo)
			}
		} else {
			if repo.NumOpenIssues > 0 {
				if filterMode == models.FM_CREATE {
					repo.NumOpenIssues = int(models.GetIssueCountByPoster(ctx.User.Id, repo.Id, isShowClosed))
				}
				showRepos = append(showRepos, repo)
			}
		}
	}

	if repoId > 0 {
		repoIds = []int64{repoId}
	}

	page, _ := com.StrTo(ctx.Query("page")).Int()

	// Get all issues.
	var ius []*models.IssueUser
	switch viewType {
	case "assigned":
		fallthrough
	case "created_by":
		ius, err = models.GetIssueUserPairsByMode(ctx.User.Id, repoId, isShowClosed, page, filterMode)
	default:
		ius, err = models.GetIssueUserPairsByRepoIds(repoIds, isShowClosed, page)
	}
	if err != nil {
		ctx.Handle(500, "user.Issues(GetAllIssueUserPairs)", err)
		return
	}

	issues := make([]*models.Issue, len(ius))
	for i := range ius {
		issues[i], err = models.GetIssueById(ius[i].IssueId)
		if err != nil {
			if err == models.ErrIssueNotExist {
				log.Warn("user.Issues(GetIssueById #%d): issue not exist", ius[i].IssueId)
				continue
			} else {
				ctx.Handle(500, fmt.Sprintf("user.Issues(GetIssueById #%d)", ius[i].IssueId), err)
				return
			}
		}

		issues[i].Repo, err = models.GetRepositoryById(issues[i].RepoId)
		if err != nil {
			if err == models.ErrRepoNotExist {
				log.Warn("user.Issues(GetRepositoryById #%d): repository not exist", issues[i].RepoId)
				continue
			} else {
				ctx.Handle(500, fmt.Sprintf("user.Issues(GetRepositoryById #%d)", issues[i].RepoId), err)
				return
			}
		}

		if err = issues[i].Repo.GetOwner(); err != nil {
			ctx.Handle(500, "user.Issues(GetOwner)", err)
			return
		}

		if err = issues[i].GetPoster(); err != nil {
			ctx.Handle(500, "user.Issues(GetUserById)", err)
			return
		}
	}

	ctx.Data["RepoId"] = repoId
	ctx.Data["Repos"] = showRepos
	ctx.Data["Issues"] = issues
	ctx.Data["ViewType"] = viewType
	ctx.Data["IssueStats"] = issueStats
	ctx.Data["IsShowClosed"] = isShowClosed
	if isShowClosed {
		ctx.Data["State"] = "closed"
		ctx.Data["ShowCount"] = issueStats.ClosedCount
	} else {
		ctx.Data["ShowCount"] = issueStats.OpenCount
	}
	ctx.HTML(200, ISSUES)
}
<commit_msg>Update/simplify fix that owners also see actions on their repositories<commit_after>// Copyright 2014 The Gogs 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 user

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/Unknwon/com"

	"github.com/gogits/gogs/models"
	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/middleware"
	"github.com/gogits/gogs/modules/setting"
)

const (
	DASHBOARD base.TplName = "user/dashboard/dashboard"
	PULLS     base.TplName = "user/dashboard/pulls"
	ISSUES    base.TplName = "user/issues"
	STARS     base.TplName = "user/stars"
	PROFILE   base.TplName = "user/profile"
)

func Dashboard(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("dashboard")
	ctx.Data["PageIsDashboard"] = true
	ctx.Data["PageIsNews"] = true

	var ctxUser *models.User
	// Check context type.
	orgName := ctx.Params(":org")
	if len(orgName) > 0 {
		// Organization.
		org, err := models.GetUserByName(orgName)
		if err != nil {
			if err == models.ErrUserNotExist {
				ctx.Handle(404, "GetUserByName", err)
			} else {
				ctx.Handle(500, "GetUserByName", err)
			}
			return
		}
		ctxUser = org
	} else {
		// Normal user.
		ctxUser = ctx.User
		collaborates, err := ctx.User.GetAccessibleRepositories()
		if err != nil {
			ctx.Handle(500, "GetAccessibleRepositories", err)
			return
		}

		repositories := make([]*models.Repository, 0, len(collaborates))
		for repo := range collaborates {
			repositories = append(repositories, repo)
		}

		ctx.Data["CollaborateCount"] = len(repositories)
		ctx.Data["CollaborativeRepos"] = repositories
	}
	ctx.Data["ContextUser"] = ctxUser

	if err := ctx.User.GetOrganizations(); err != nil {
		ctx.Handle(500, "GetOrganizations", err)
		return
	}
	ctx.Data["Orgs"] = ctx.User.Orgs

	repos, err := models.GetRepositories(ctxUser.Id, true)
	if err != nil {
		ctx.Handle(500, "GetRepositories", err)
		return
	}
	ctx.Data["Repos"] = repos

	// Get mirror repositories.
	mirrors := make([]*models.Repository, 0, len(repos)/2)
	for _, repo := range repos {
		if repo.IsMirror {
			if err = repo.GetMirror(); err != nil {
				ctx.Handle(500, "GetMirror: "+repo.Name, err)
				return
			}
			mirrors = append(mirrors, repo)
		}
	}
	ctx.Data["MirrorCount"] = len(mirrors)
	ctx.Data["Mirrors"] = mirrors

	// Get feeds.
	actions, err := models.GetFeeds(ctxUser.Id, 0, false)
	if err != nil {
		ctx.Handle(500, "GetFeeds", err)
		return
	}

	// Check access of private repositories.
	feeds := make([]*models.Action, 0, len(actions))
	for _, act := range actions {
		if act.IsPrivate {
			// This prevents having to retrieve the repository for each action
			repo := &models.Repository{Id: act.RepoId, IsPrivate: true}
			if act.RepoUserName != ctx.User.LowerName {
				if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has {
					continue
				}
			}

		}
		// FIXME: cache results?
		u, err := models.GetUserByName(act.ActUserName)
		if err != nil {
			if err == models.ErrUserNotExist {
				continue
			}
			ctx.Handle(500, "GetUserByName", err)
			return
		}
		act.ActAvatar = u.AvatarLink()
		feeds = append(feeds, act)
	}
	ctx.Data["Feeds"] = feeds
	ctx.HTML(200, DASHBOARD)
}

func Pulls(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("pull_requests")
	ctx.Data["PageIsDashboard"] = true
	ctx.Data["PageIsPulls"] = true

	if err := ctx.User.GetOrganizations(); err != nil {
		ctx.Handle(500, "GetOrganizations", err)
		return
	}
	ctx.Data["ContextUser"] = ctx.User

	ctx.HTML(200, PULLS)
}

func ShowSSHKeys(ctx *middleware.Context, uid int64) {
	keys, err := models.ListPublicKeys(uid)
	if err != nil {
		ctx.Handle(500, "ListPublicKeys", err)
		return
	}

	var buf bytes.Buffer
	for i := range keys {
		buf.WriteString(keys[i].OmitEmail())
	}
	ctx.RenderData(200, buf.Bytes())
}

func Profile(ctx *middleware.Context) {
	ctx.Data["Title"] = "Profile"
	ctx.Data["PageIsUserProfile"] = true

	uname := ctx.Params(":username")
	// Special handle for FireFox requests favicon.ico.
	if uname == "favicon.ico" {
		ctx.Redirect(setting.AppSubUrl + "/img/favicon.png")
		return
	}

	isShowKeys := false
	if strings.HasSuffix(uname, ".keys") {
		isShowKeys = true
		uname = strings.TrimSuffix(uname, ".keys")
	}

	u, err := models.GetUserByName(uname)
	if err != nil {
		if err == models.ErrUserNotExist {
			ctx.Handle(404, "GetUserByName", err)
		} else {
			ctx.Handle(500, "GetUserByName", err)
		}
		return
	}

	// Show SSH keys.
	if isShowKeys {
		ShowSSHKeys(ctx, u.Id)
		return
	}

	if u.IsOrganization() {
		ctx.Redirect(setting.AppSubUrl + "/org/" + u.Name)
		return
	}

	// For security reason, hide e-mail address for anonymous visitors.
	if !ctx.IsSigned {
		u.Email = ""
	}
	ctx.Data["Owner"] = u

	tab := ctx.Query("tab")
	ctx.Data["TabName"] = tab
	switch tab {
	case "activity":
		actions, err := models.GetFeeds(u.Id, 0, false)
		if err != nil {
			ctx.Handle(500, "GetFeeds", err)
			return
		}
		feeds := make([]*models.Action, 0, len(actions))
		for _, act := range actions {
			if act.IsPrivate {
				if !ctx.IsSigned {
					continue
				}
				// This prevents having to retrieve the repository for each action
				repo := &models.Repository{Id: act.RepoId, IsPrivate: true}
				if act.RepoUserName != ctx.User.LowerName {
					if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has {
						continue
					}
				}

			}
			// FIXME: cache results?
			u, err := models.GetUserByName(act.ActUserName)
			if err != nil {
				if err == models.ErrUserNotExist {
					continue
				}
				ctx.Handle(500, "GetUserByName", err)
				return
			}
			act.ActAvatar = u.AvatarLink()
			feeds = append(feeds, act)
		}
		ctx.Data["Feeds"] = feeds
	default:
		ctx.Data["Repos"], err = models.GetRepositories(u.Id, ctx.IsSigned && ctx.User.Id == u.Id)
		if err != nil {
			ctx.Handle(500, "GetRepositories", err)
			return
		}
	}

	ctx.HTML(200, PROFILE)
}

func Email2User(ctx *middleware.Context) {
	u, err := models.GetUserByEmail(ctx.Query("email"))
	if err != nil {
		if err == models.ErrUserNotExist {
			ctx.Handle(404, "user.Email2User(GetUserByEmail)", err)
		} else {
			ctx.Handle(500, "user.Email2User(GetUserByEmail)", err)
		}
		return
	}
	ctx.Redirect(setting.AppSubUrl + "/user/" + u.Name)
}

func Issues(ctx *middleware.Context) {
	ctx.Data["Title"] = "Your Issues"

	viewType := ctx.Query("type")
	types := []string{"assigned", "created_by"}
	if !com.IsSliceContainsStr(types, viewType) {
		viewType = "all"
	}

	isShowClosed := ctx.Query("state") == "closed"

	var filterMode int
	switch viewType {
	case "assigned":
		filterMode = models.FM_ASSIGN
	case "created_by":
		filterMode = models.FM_CREATE
	}

	repoId, _ := com.StrTo(ctx.Query("repoid")).Int64()
	issueStats := models.GetUserIssueStats(ctx.User.Id, filterMode)

	// Get all repositories.
	repos, err := models.GetRepositories(ctx.User.Id, true)
	if err != nil {
		ctx.Handle(500, "user.Issues(GetRepositories)", err)
		return
	}

	repoIds := make([]int64, 0, len(repos))
	showRepos := make([]*models.Repository, 0, len(repos))
	for _, repo := range repos {
		if repo.NumIssues == 0 {
			continue
		}

		repoIds = append(repoIds, repo.Id)
		repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues
		issueStats.AllCount += int64(repo.NumOpenIssues)

		if isShowClosed {
			if repo.NumClosedIssues > 0 {
				if filterMode == models.FM_CREATE {
					repo.NumClosedIssues = int(models.GetIssueCountByPoster(ctx.User.Id, repo.Id, isShowClosed))
				}
				showRepos = append(showRepos, repo)
			}
		} else {
			if repo.NumOpenIssues > 0 {
				if filterMode == models.FM_CREATE {
					repo.NumOpenIssues = int(models.GetIssueCountByPoster(ctx.User.Id, repo.Id, isShowClosed))
				}
				showRepos = append(showRepos, repo)
			}
		}
	}

	if repoId > 0 {
		repoIds = []int64{repoId}
	}

	page, _ := com.StrTo(ctx.Query("page")).Int()

	// Get all issues.
	var ius []*models.IssueUser
	switch viewType {
	case "assigned":
		fallthrough
	case "created_by":
		ius, err = models.GetIssueUserPairsByMode(ctx.User.Id, repoId, isShowClosed, page, filterMode)
	default:
		ius, err = models.GetIssueUserPairsByRepoIds(repoIds, isShowClosed, page)
	}
	if err != nil {
		ctx.Handle(500, "user.Issues(GetAllIssueUserPairs)", err)
		return
	}

	issues := make([]*models.Issue, len(ius))
	for i := range ius {
		issues[i], err = models.GetIssueById(ius[i].IssueId)
		if err != nil {
			if err == models.ErrIssueNotExist {
				log.Warn("user.Issues(GetIssueById #%d): issue not exist", ius[i].IssueId)
				continue
			} else {
				ctx.Handle(500, fmt.Sprintf("user.Issues(GetIssueById #%d)", ius[i].IssueId), err)
				return
			}
		}

		issues[i].Repo, err = models.GetRepositoryById(issues[i].RepoId)
		if err != nil {
			if err == models.ErrRepoNotExist {
				log.Warn("user.Issues(GetRepositoryById #%d): repository not exist", issues[i].RepoId)
				continue
			} else {
				ctx.Handle(500, fmt.Sprintf("user.Issues(GetRepositoryById #%d)", issues[i].RepoId), err)
				return
			}
		}

		if err = issues[i].Repo.GetOwner(); err != nil {
			ctx.Handle(500, "user.Issues(GetOwner)", err)
			return
		}

		if err = issues[i].GetPoster(); err != nil {
			ctx.Handle(500, "user.Issues(GetUserById)", err)
			return
		}
	}

	ctx.Data["RepoId"] = repoId
	ctx.Data["Repos"] = showRepos
	ctx.Data["Issues"] = issues
	ctx.Data["ViewType"] = viewType
	ctx.Data["IssueStats"] = issueStats
	ctx.Data["IsShowClosed"] = isShowClosed
	if isShowClosed {
		ctx.Data["State"] = "closed"
		ctx.Data["ShowCount"] = issueStats.ClosedCount
	} else {
		ctx.Data["ShowCount"] = issueStats.OpenCount
	}
	ctx.HTML(200, ISSUES)
}
<|endoftext|>


<commit_before>// Package sigmon simplifies os.Signal handling.
package sigmon

import (
	"os"
	"os/signal"
	"sync"
	"syscall"
)

// Signal wraps the string type to reduce confusion when checking Sig.
type Signal string

// Signal constants are string representations of the handled os.Signals.
const (
	SIGHUP  Signal = "HUP"
	SIGINT  Signal = "INT"
	SIGTERM Signal = "TERM"
	SIGUSR1 Signal = "USR1"
	SIGUSR2 Signal = "USR2"
)

type signalJunction struct {
	sync.Mutex
	sighup  chan os.Signal
	sigint  chan os.Signal
	sigterm chan os.Signal
	sigusr1 chan os.Signal
	sigusr2 chan os.Signal
}

func newSignalJunction() *signalJunction {
	return &signalJunction{
		sighup:  make(chan os.Signal, 1),
		sigint:  make(chan os.Signal, 1),
		sigterm: make(chan os.Signal, 1),
		sigusr1: make(chan os.Signal, 1),
		sigusr2: make(chan os.Signal, 1),
	}
}

func (j *signalJunction) connect() {
	j.Lock()
	defer j.Unlock()

	signal.Notify(j.sighup, syscall.SIGHUP)
	signal.Notify(j.sigint, syscall.SIGINT)
	signal.Notify(j.sigterm, syscall.SIGTERM)
	notifyUSR(j.sigusr1, j.sigusr2)
}

func (j *signalJunction) disconnect() {
	defer signal.Stop(j.sighup)
	defer signal.Stop(j.sigint)
	defer signal.Stop(j.sigterm)
	defer signal.Stop(j.sigusr1)
	defer signal.Stop(j.sigusr2)
}

type signalHandler struct {
	sync.Mutex
	handler  func(*SignalMonitor)
	registry chan func(*SignalMonitor)
}

func newSignalHandler(handler func(*SignalMonitor)) *signalHandler {
	return &signalHandler{
		handler:  handler,
		registry: make(chan func(*SignalMonitor), 1),
	}
}

func (h *signalHandler) register(handler func(*SignalMonitor)) {
	select {
	case <-h.registry:
	default:
	}

	h.registry <- handler
}

func (h *signalHandler) set(handler func(*SignalMonitor)) {
	h.Lock()
	defer h.Unlock()

	h.handler = handler
}

func (h *signalHandler) handle(sm *SignalMonitor) {
	h.Lock()
	defer h.Unlock()

	if h.handler != nil {
		h.handler(sm)
	}
}

// SignalMonitor helps manage signal handling.
type SignalMonitor struct {
	sync.Mutex
	sig Signal
	on  bool

	off chan struct{}

	junction *signalJunction
	handler  *signalHandler
}

// New takes a function and returns a SignalMonitor.  When a nil arg is
// provided, no action will be taken during signal handling.  Run must be
// called in order to begin monitoring.
func New(handler func(*SignalMonitor)) (s *SignalMonitor) {
	return &SignalMonitor{
		off:      make(chan struct{}, 1),
		junction: newSignalJunction(),
		handler:  newSignalHandler(handler),
	}
}

// Set allows the handler function to be added or removed.  Only the most
// recently passed function will have any relevance.
func (m *SignalMonitor) Set(handler func(*SignalMonitor)) {
	m.handler.register(handler)
}

// Run starts signal monitoring.  If no function has been provided, no action
// will be taken during signal handling.  The os.Signal which was called will
// be stored as a typed string (Signal) within the SignalMonitor for retrieval
// using Sig. Stop should be called within the provided handler functions and
// is not a default behavior.
func (m *SignalMonitor) Run() {
	m.Lock()
	defer m.Unlock()

	if m.on {
		return
	}
	m.on = true

	wg := &sync.WaitGroup{}
	wg.Add(1)

	go m.monitor(wg)

	wg.Wait()
}

func (m *SignalMonitor) monitor(wg *sync.WaitGroup) {
	m.junction.connect()
	defer m.junction.disconnect()

	wg.Done()

	for {
		if !m.biasedScan() {
			return
		}
	}
}

func (m *SignalMonitor) biasedScan() (alive bool) {
	select {
	case <-m.off:
		return false
	case fn := <-m.handler.registry:
		m.handler.set(fn)
	default:
		return m.scan()
	}

	return true
}

func (m *SignalMonitor) scan() (alive bool) {
	select {
	case <-m.off:
		return false
	case fn := <-m.handler.registry:
		m.handler.set(fn)
	case <-m.junction.sighup:
		m.handle(SIGHUP)
	case <-m.junction.sigint:
		m.handle(SIGINT)
	case <-m.junction.sigterm:
		m.handle(SIGTERM)
	case <-m.junction.sigusr1:
		m.handle(SIGUSR1)
	case <-m.junction.sigusr2:
		m.handle(SIGUSR2)
	}

	return true
}

// Stop ends the goroutine which monitors signals.
func (m *SignalMonitor) Stop() {
	m.Lock()
	defer m.Unlock()

	if m.on {
		m.on = false
		m.off <- struct{}{}
	}
}

func (m *SignalMonitor) setSig(sig Signal) {
	m.Lock()
	defer m.Unlock()

	m.sig = sig
}

// Sig returns a typed string (Signal) representing the most recently called
// os.Signal.
func (m *SignalMonitor) Sig() Signal {
	m.Lock()
	defer m.Unlock()

	return m.sig
}

func (m *SignalMonitor) handle(sig Signal) {
	m.setSig(sig)
	m.handler.handle(m)
}
<commit_msg>Reorder signalMonitor methods.<commit_after>// Package sigmon simplifies os.Signal handling.
package sigmon

import (
	"os"
	"os/signal"
	"sync"
	"syscall"
)

// Signal wraps the string type to reduce confusion when checking Sig.
type Signal string

// Signal constants are string representations of the handled os.Signals.
const (
	SIGHUP  Signal = "HUP"
	SIGINT  Signal = "INT"
	SIGTERM Signal = "TERM"
	SIGUSR1 Signal = "USR1"
	SIGUSR2 Signal = "USR2"
)

type signalJunction struct {
	sync.Mutex
	sighup  chan os.Signal
	sigint  chan os.Signal
	sigterm chan os.Signal
	sigusr1 chan os.Signal
	sigusr2 chan os.Signal
}

func newSignalJunction() *signalJunction {
	return &signalJunction{
		sighup:  make(chan os.Signal, 1),
		sigint:  make(chan os.Signal, 1),
		sigterm: make(chan os.Signal, 1),
		sigusr1: make(chan os.Signal, 1),
		sigusr2: make(chan os.Signal, 1),
	}
}

func (j *signalJunction) connect() {
	j.Lock()
	defer j.Unlock()

	signal.Notify(j.sighup, syscall.SIGHUP)
	signal.Notify(j.sigint, syscall.SIGINT)
	signal.Notify(j.sigterm, syscall.SIGTERM)
	notifyUSR(j.sigusr1, j.sigusr2)
}

func (j *signalJunction) disconnect() {
	defer signal.Stop(j.sighup)
	defer signal.Stop(j.sigint)
	defer signal.Stop(j.sigterm)
	defer signal.Stop(j.sigusr1)
	defer signal.Stop(j.sigusr2)
}

type signalHandler struct {
	sync.Mutex
	handler  func(*SignalMonitor)
	registry chan func(*SignalMonitor)
}

func newSignalHandler(handler func(*SignalMonitor)) *signalHandler {
	return &signalHandler{
		handler:  handler,
		registry: make(chan func(*SignalMonitor), 1),
	}
}

func (h *signalHandler) register(handler func(*SignalMonitor)) {
	select {
	case <-h.registry:
	default:
	}

	h.registry <- handler
}

func (h *signalHandler) set(handler func(*SignalMonitor)) {
	h.Lock()
	defer h.Unlock()

	h.handler = handler
}

func (h *signalHandler) handle(sm *SignalMonitor) {
	h.Lock()
	defer h.Unlock()

	if h.handler != nil {
		h.handler(sm)
	}
}

// SignalMonitor helps manage signal handling.
type SignalMonitor struct {
	sync.Mutex
	sig Signal
	on  bool

	off chan struct{}

	junction *signalJunction
	handler  *signalHandler
}

// New takes a function and returns a SignalMonitor.  When a nil arg is
// provided, no action will be taken during signal handling.  Run must be
// called in order to begin monitoring.
func New(handler func(*SignalMonitor)) (s *SignalMonitor) {
	return &SignalMonitor{
		off:      make(chan struct{}, 1),
		junction: newSignalJunction(),
		handler:  newSignalHandler(handler),
	}
}

// Set allows the handler function to be added or removed.  Only the most
// recently passed function will have any relevance.
func (m *SignalMonitor) Set(handler func(*SignalMonitor)) {
	m.handler.register(handler)
}

func (m *SignalMonitor) monitor(wg *sync.WaitGroup) {
	m.junction.connect()
	defer m.junction.disconnect()

	wg.Done()

	for {
		if !m.biasedScan() {
			return
		}
	}
}

func (m *SignalMonitor) biasedScan() (alive bool) {
	select {
	case <-m.off:
		return false
	case fn := <-m.handler.registry:
		m.handler.set(fn)
	default:
		return m.scan()
	}

	return true
}

func (m *SignalMonitor) scan() (alive bool) {
	select {
	case <-m.off:
		return false
	case fn := <-m.handler.registry:
		m.handler.set(fn)
	case <-m.junction.sighup:
		m.handle(SIGHUP)
	case <-m.junction.sigint:
		m.handle(SIGINT)
	case <-m.junction.sigterm:
		m.handle(SIGTERM)
	case <-m.junction.sigusr1:
		m.handle(SIGUSR1)
	case <-m.junction.sigusr2:
		m.handle(SIGUSR2)
	}

	return true
}

// Run starts signal monitoring.  If no function has been provided, no action
// will be taken during signal handling.  The os.Signal which was called will
// be stored as a typed string (Signal) within the SignalMonitor for retrieval
// using Sig. Stop should be called within the provided handler functions and
// is not a default behavior.
func (m *SignalMonitor) Run() {
	m.Lock()
	defer m.Unlock()

	if m.on {
		return
	}
	m.on = true

	wg := &sync.WaitGroup{}
	wg.Add(1)

	go m.monitor(wg)

	wg.Wait()
}

// Stop ends the goroutine which monitors signals.
func (m *SignalMonitor) Stop() {
	m.Lock()
	defer m.Unlock()

	if m.on {
		m.on = false
		m.off <- struct{}{}
	}
}

// Sig returns a typed string (Signal) representing the most recently called
// os.Signal.
func (m *SignalMonitor) Sig() Signal {
	m.Lock()
	defer m.Unlock()

	return m.sig
}

func (m *SignalMonitor) setSig(sig Signal) {
	m.Lock()
	defer m.Unlock()

	m.sig = sig
}

func (m *SignalMonitor) handle(sig Signal) {
	m.setSig(sig)
	m.handler.handle(m)
}
<|endoftext|>


<commit_before>package wxwork

import (
	"fhyx.online/tencent-api-go/client"
	"fhyx.online/tencent-api-go/gender"
)

type Status uint8

const (
	SNone     Status = 0
	SActived  Status = 1
	SInactive Status = 2
	SUnlit    Status = 4
)

func (s Status) String() string {
	switch s {
	case SActived:
		return "actived"
	case SInactive:
		return "inactive"
	case SUnlit:
		return "unlit"
	case SNone:
		return "none"
	default:
		return "unknown"
	}
}

// UserAttribute 为用户扩展信息
type UserAttribute struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

// UserAttributes 为用户扩展信息列表
type UserAttributes struct {
	Attrs []*UserAttribute `json:"attrs,omitempty"`
}

// User 为企业用户信息
// 参数	说明
// errcode	返回码
// errmsg	对返回码的文本描述内容
// userlist	成员列表
// userid	成员UserID。对应管理端的帐号
// name	成员名称，此字段从2019年12月30日起，对新创建第三方应用不再返回，2020年6月30日起，对所有历史第三方应用不再返回，后续第三方仅通讯录应用可获取，第三方页面需要通过通讯录展示组件来展示名字
// mobile	手机号码，第三方仅通讯录应用可获取
// department	成员所属部门id列表，仅返回该应用有查看权限的部门id
// order	部门内的排序值，32位整数，默认为0。数量必须和department一致，数值越大排序越前面。
// position	职务信息；第三方仅通讯录应用可获取
// gender	性别。0表示未定义，1表示男性，2表示女性
// email	邮箱，第三方仅通讯录应用可获取
// is_leader_in_dept	表示在所在的部门内是否为上级；第三方仅通讯录应用可获取
// avatar	头像url。第三方仅通讯录应用可获取
// thumb_avatar	头像缩略图url。第三方仅通讯录应用可获取
// telephone	座机。第三方仅通讯录应用可获取
// alias	别名；第三方仅通讯录应用可获取
// status	激活状态: 1=已激活，2=已禁用，4=未激活，5=退出企业。
// 已激活代表已激活企业微信或已关注微工作台（原企业号）。未激活代表既未激活企业微信又未关注微工作台（原企业号）。
// extattr	扩展属性，第三方仅通讯录应用可获取
// qr_code	员工个人二维码，扫描可添加为外部联系人；第三方仅通讯录应用可获取
// external_profile	成员对外属性，字段详情见对外属性；第三方仅通讯录应用可获取
// external_position	对外职务。 第三方仅通讯录应用可获取
// address	地址，第三方仅通讯录应用可获取
// hide_mobile	是否隐藏手机号
// english_name	英文名
// open_userid	全局唯一。对于同一个服务商，不同应用获取到企业内同一个成员的open_userid是相同的，最多64个字节。仅第三方应用可获取
// main_department	主部门
type User struct {
	UID           string         `json:"userid"`
	Name          string         `json:"name,omitempty"`
	Alias         string         `json:"alias,omitempty"`
	EnglishName   string         `json:"english_name,omitempty"`
	OpenID        string         `json:"open_userid,omitempty"`
	DepartmentIds []int          `json:"department,omitempty"`
	Title         string         `json:"position,omitempty"`
	Mobile        string         `json:"mobile,omitempty"`
	Email         string         `json:"email,omitempty"`
	Tel           string         `json:"telephone,omitempty"`
	Gender        gender.Gender  `json:"gender,omitempty"`
	Status        Status         `json:"status,omitempty"`
	Enabled       int8           `json:"enable,omitempty"`
	Avatar        string         `json:"avatar,omitempty"`
	IsLeader      uint8          `json:"isleader,omitempty"`
	LeaderDepts   []int          `json:"is_leader_in_dept,omitempty"`
	ExtAttr       UserAttributes `json:"extattr,omitempty"`

	ExternalPosition string `json:"external_position,omitempty"`

	client.Error
}

func (u User) IsActived() bool {
	return u.Status == SActived
}

func (u User) IsEnabled() bool {
	return u.Enabled == 1
}

// Users ...
type Users []User

type UserUp = User

type usersResponse struct {
	client.Error

	UserList Users `json:"userlist"`
}

func (ur *usersResponse) Users() Users {
	return ur.UserList
}

// OAuth2UserInfo 为用户 OAuth2 验证登录后的简单信息
type OAuth2UserInfo struct {
	UserID     string `json:"UserId,omitempty"`
	DeviceID   string `json:"DeviceId,omitempty"`
	UserTicket string `json:"user_ticket,omitempty"`
	OpenId     string `json:"OpenId,omitempty"` // 非企业成员
	client.Error
}
<commit_msg>以最新版文档更新用户结构<commit_after>package wxwork

import (
	"fhyx.online/tencent-api-go/client"
	"fhyx.online/tencent-api-go/gender"
)

type Status uint8

const (
	SNone     Status = 0
	SActived  Status = 1
	SInactive Status = 2
	SUnlit    Status = 4
)

func (s Status) String() string {
	switch s {
	case SActived:
		return "actived"
	case SInactive:
		return "inactive"
	case SUnlit:
		return "unlit"
	case SNone:
		return "none"
	default:
		return "unknown"
	}
}

// Attribute 为用户扩展信息
type Attribute struct {
	Type    int32        `json:"type,omitempty"`
	Name    string       `json:"name"`
	Text    *attrText    `json:"text,omitempty"`
	Web     *attrWeb     `json:"web,omitempty"`
	MiniApp *attrMiniApp `json:"miniprogram,omitempty"`
}

type attrText struct {
	Value string `json:"value,omitempty"`
}

type attrWeb struct {
	Title string `json:"title,omitempty"`
	URL   string `json:"url,omitempty"`
}

type attrMiniApp struct {
	AppID    string `json:"appid,omitempty"`
	PagePath string `json:"pagepath,omitempty"`
	Title    string `json:"title,omitempty"`
}

// Attributes 扩展信息列表
type Attributes struct {
	Attrs []Attribute `json:"attrs,omitempty"`
}

type externalProfile struct {
	ExternalCorpName string      `json:"external_corp_name,omitempty"`
	ExternalAttrs    []Attribute `json:"external_attr,omitempty"`
}

// User 为企业用户信息
// 参数	说明
// userid	成员UserID。对应管理端的帐号，企业内必须唯一。不区分大小写，长度为1~64个字节
// name	成员名称；第三方不可获取，调用时返回userid以代替name；代开发自建应用需要管理员授权才返回；对于非第三方创建的成员，第三方通讯录应用也不可获取；未返回name的情况需要通过通讯录展示组件来展示名字
// mobile	手机号码，代开发自建应用需要管理员授权才返回；第三方仅通讯录应用可获取；对于非第三方创建的成员，第三方通讯录应用也不可获取；上游企业不可获取下游企业成员该字段
// department	成员所属部门id列表，仅返回该应用有查看权限的部门id；成员授权模式下，固定返回根部门id，即固定为1。对授权了“组织架构信息”权限的第三方应用，返回成员所属的全部部门id
// order	部门内的排序值，默认为0。数量必须和department一致，数值越大排序越前面。值范围是0, 2^32)。[成员授权模式下不返回该字段
// position	职务信息；代开发自建应用需要管理员授权才返回；第三方仅通讯录应用可获取；对于非第三方创建的成员，第三方通讯录应用也不可获取；上游企业不可获取下游企业成员该字段
// gender	性别。0表示未定义，1表示男性，2表示女性。第三方仅通讯录应用可获取；对于非第三方创建的成员，第三方通讯录应用也不可获取；上游企业不可获取下游企业成员该字段。注：不可获取指返回值0
// email	邮箱，代开发自建应用需要管理员授权才返回；第三方仅通讯录应用可获取；对于非第三方创建的成员，第三方通讯录应用也不可获取；上游企业不可获取下游企业成员该字段
// biz_mail	企业邮箱，代开发自建应用不返回；第三方仅通讯录应用可获取；对于非第三方创建的成员，第三方通讯录应用也不可获取；上游企业不可获取下游企业成员该字段
// is_leader_in_dept	表示在所在的部门内是否为部门负责人，数量与department一致；第三方通讯录应用或者授权了“组织架构信息-应用可获取企业的部门组织架构信息-部门负责人”权限的第三方应用可获取；对于非第三方创建的成员，第三方通讯录应用不可获取；上游企业不可获取下游企业成员该字段
// direct_leader	直属上级UserID，返回在应用可见范围内的直属上级列表，最多有五个直属上级；第三方通讯录应用或者授权了“组织架构信息-应用可获取可见范围内成员组织架构信息-直属上级”权限的第三方应用可获取；对于非第三方创建的成员，第三方通讯录应用不可获取；上游企业不可获取下游企业成员该字段
// avatar	头像url。 第三方仅通讯录应用可获取；对于非第三方创建的成员，第三方通讯录应用也不可获取；上游企业不可获取下游企业成员该字段
// thumb_avatar	头像缩略图url。第三方仅通讯录应用可获取；对于非第三方创建的成员，第三方通讯录应用也不可获取；上游企业不可获取下游企业成员该字段
// telephone	座机。代开发自建应用需要管理员授权才返回；第三方仅通讯录应用可获取；对于非第三方创建的成员，第三方通讯录应用也不可获取；上游企业不可获取下游企业成员该字段
// alias	别名；第三方仅通讯录应用可获取；对于非第三方创建的成员，第三方通讯录应用也不可获取；上游企业不可获取下游企业成员该字段
// extattr	扩展属性，代开发自建应用需要管理员授权才返回；第三方仅通讯录应用可获取；对于非第三方创建的成员，第三方通讯录应用也不可获取；上游企业不可获取下游企业成员该字段
// status	激活状态: 1=已激活，2=已禁用，4=未激活，5=退出企业。
// qr_code	员工个人二维码，扫描可添加为外部联系人(注意返回的是一个url，可在浏览器上打开该url以展示二维码)；第三方仅通讯录应用可获取；对于非第三方创建的成员，第三方通讯录应用也不可获取；上游企业不可获取下游企业成员该字段
// external_profile	成员对外属性，字段详情见对外属性；代开发自建应用需要管理员授权才返回；第三方仅通讯录应用可获取；对于非第三方创建的成员，第三方通讯录应用也不可获取；上游企业不可获取下游企业成员该字段
// external_position	对外职务，如果设置了该值，则以此作为对外展示的职务，否则以position来展示。代开发自建应用需要管理员授权才返回；第三方仅通讯录应用可获取；对于非第三方创建的成员，第三方通讯录应用也不可获取；上游企业不可获取下游企业成员该字段
// address	地址。代开发自建应用需要管理员授权才返回；第三方仅通讯录应用可获取；对于非第三方创建的成员，第三方通讯录应用也不可获取；上游企业不可获取下游企业成员该字段
// open_userid	全局唯一。对于同一个服务商，不同应用获取到企业内同一个成员的open_userid是相同的，最多64个字节。仅第三方应用可获取
// main_department	主部门，仅当应用对主部门有查看权限时返回。
type User struct {
	UID           string        `json:"userid"`
	Name          string        `json:"name,omitempty"`
	Alias         string        `json:"alias,omitempty"`
	OpenID        string        `json:"open_userid,omitempty"`
	DepartmentIds []int         `json:"department,omitempty"`
	Title         string        `json:"position,omitempty"`
	Mobile        string        `json:"mobile,omitempty"`
	Email         string        `json:"email,omitempty"`
	BizMail       string        `json:"biz_mail,omitempty"`
	Tel           string        `json:"telephone,omitempty"`
	Gender        gender.Gender `json:"gender,omitempty"`
	Status        Status        `json:"status,omitempty"`
	Enabled       int8          `json:"enable,omitempty"`
	Avatar        string        `json:"avatar,omitempty"`
	QRCode        string        `json:"qr_code,omitempty"`
	Address       string        `json:"address,omitempty"`
	LeaderInDepts []int         `json:"is_leader_in_dept,omitempty"`
	OrderInDepts  []int         `json:"order,omitempty"`
	ExtAttr       *Attributes   `json:"extattr,omitempty"`

	MainDepartment int32    `json:"main_department,omitempty"`
	DirectLeader   []string `json:"direct_leader,omitempty"`

	ExternalPosition string           `json:"external_position,omitempty"`
	ExternalProfile  *externalProfile `json:"external_profile,omitempty"`

	client.Error
}

func (u User) IsActived() bool {
	return u.Status == SActived
}

func (u User) IsEnabled() bool {
	return u.Enabled == 1
}

// Users ...
type Users []User

type UserUp = User

type usersResponse struct {
	client.Error

	UserList Users `json:"userlist"`
}

func (ur *usersResponse) Users() Users {
	return ur.UserList
}

// OAuth2UserInfo 为用户 OAuth2 验证登录后的简单信息
type OAuth2UserInfo struct {
	UserID     string `json:"UserId,omitempty"`
	DeviceID   string `json:"DeviceId,omitempty"`
	UserTicket string `json:"user_ticket,omitempty"`
	OpenId     string `json:"OpenId,omitempty"` // 非企业成员
	client.Error
}
<|endoftext|>


<commit_before>package stackerr_test

import (
	"errors"
	"fmt"
	"regexp"
	"strings"
	"testing"

	"github.com/facebookgo/stackerr"
)

func TestNew(t *testing.T) {
	const errStr = "foo bar baz"
	e := stackerr.New(errStr)
	matches := []string{
		errStr,
		"^github.com/facebookgo/stackerr/stackerr_test.go:15 +TestNew$",
	}
	match(t, e.Error(), matches)
}

func TestNewf(t *testing.T) {
	const fmtStr = "%s 42"
	const errStr = "foo bar baz"
	e := stackerr.Newf(fmtStr, errStr)
	matches := []string{
		fmt.Sprintf(fmtStr, errStr),
		"^github.com/facebookgo/stackerr/stackerr_test.go:26 +TestNewf$",
	}
	match(t, e.Error(), matches)
}

func TestWrap(t *testing.T) {
	const errStr = "foo bar baz"
	e := stackerr.Wrap(errors.New(errStr))
	matches := []string{
		errStr,
		"^github.com/facebookgo/stackerr/stackerr_test.go:36 +TestWrap$",
	}
	match(t, e.Error(), matches)
}

func TestNilWrap(t *testing.T) {
	if stackerr.WrapSkip(nil, 1) != nil {
		t.Fatal("did not get nil error")
	}
}

func TestDoubleWrap(t *testing.T) {
	e := stackerr.New("")
	if stackerr.WrapSkip(e, 1) != e {
		t.Fatal("double wrap failure")
	}
}

func TestLog(t *testing.T) {
	t.Log(stackerr.New("hello"))
}

func TestUnderlying(t *testing.T) {
	e1 := errors.New("")
	e2 := stackerr.Wrap(e1)
	errs := stackerr.Underlying(e2)
	if len(errs) != 2 || errs[0] != e2 || errs[1] != e1 {
		t.Fatal("failed Underlying")
	}
}

func match(t testing.TB, s string, matches []string) {
	lines := strings.Split(s, "\n")
	for i, m := range matches {
		if !regexp.MustCompile(m).MatchString(lines[i]) {
			t.Fatalf(
				"did not find expected match \"%s\" on line %d in:\n%s",
				m,
				i,
				s,
			)
		}
	}
}
<commit_msg>make tests not rely on package location to support vendoring<commit_after>package stackerr_test

import (
	"errors"
	"fmt"
	"regexp"
	"strings"
	"testing"

	"github.com/facebookgo/stackerr"
)

func TestNew(t *testing.T) {
	const errStr = "foo bar baz"
	e := stackerr.New(errStr)
	matches := []string{
		errStr,
		"stackerr_test.go:15 +TestNew$",
	}
	match(t, e.Error(), matches)
}

func TestNewf(t *testing.T) {
	const fmtStr = "%s 42"
	const errStr = "foo bar baz"
	e := stackerr.Newf(fmtStr, errStr)
	matches := []string{
		fmt.Sprintf(fmtStr, errStr),
		"stackerr_test.go:26 +TestNewf$",
	}
	match(t, e.Error(), matches)
}

func TestWrap(t *testing.T) {
	const errStr = "foo bar baz"
	e := stackerr.Wrap(errors.New(errStr))
	matches := []string{
		errStr,
		"stackerr_test.go:36 +TestWrap$",
	}
	match(t, e.Error(), matches)
}

func TestNilWrap(t *testing.T) {
	if stackerr.WrapSkip(nil, 1) != nil {
		t.Fatal("did not get nil error")
	}
}

func TestDoubleWrap(t *testing.T) {
	e := stackerr.New("")
	if stackerr.WrapSkip(e, 1) != e {
		t.Fatal("double wrap failure")
	}
}

func TestLog(t *testing.T) {
	t.Log(stackerr.New("hello"))
}

func TestUnderlying(t *testing.T) {
	e1 := errors.New("")
	e2 := stackerr.Wrap(e1)
	errs := stackerr.Underlying(e2)
	if len(errs) != 2 || errs[0] != e2 || errs[1] != e1 {
		t.Fatal("failed Underlying")
	}
}

func match(t testing.TB, s string, matches []string) {
	lines := strings.Split(s, "\n")
	for i, m := range matches {
		if !regexp.MustCompile(m).MatchString(lines[i]) {
			t.Fatalf(
				"did not find expected match \"%s\" on line %d in:\n%s",
				m,
				i,
				s,
			)
		}
	}
}
<|endoftext|>


<commit_before>package utp

/*
#include "utp.h"
*/
import "C"
import (
	"errors"
	"net"
	"time"
)

type Socket struct {
	pc            net.PacketConn
	ctx           *C.utp_context
	backlog       chan *Conn
	closed        bool
	conns         map[*C.utp_socket]*Conn
	nonUtpReads   chan packet
	writeDeadline time.Time
	readDeadline  time.Time
}

var (
	_ net.PacketConn = (*Socket)(nil)
)

type packet struct {
	b    []byte
	from net.Addr
}

func NewSocket(network, addr string) (*Socket, error) {
	pc, err := net.ListenPacket(network, addr)
	if err != nil {
		return nil, err
	}
	mu.Lock()
	defer mu.Unlock()
	ctx := C.utp_init(2)
	if ctx == nil {
		panic(ctx)
	}
	ctx.setCallbacks()
	// ctx.setOption(C.UTP_LOG_NORMAL, 1)
	// ctx.setOption(C.UTP_LOG_MTU, 1)
	// ctx.setOption(C.UTP_LOG_DEBUG, 1)
	s := &Socket{
		pc:          pc,
		ctx:         ctx,
		backlog:     make(chan *Conn, 5),
		conns:       make(map[*C.utp_socket]*Conn),
		nonUtpReads: make(chan packet, 100),
	}
	libContextToSocket[ctx] = s
	go s.packetReader()
	return s, nil
}

func (s *Socket) onLibSocketDestroyed(ls *C.utp_socket) {
	delete(s.conns, ls)
}

func (s *Socket) newConn(us *C.utp_socket) *Conn {
	c := &Conn{
		s: us,
	}
	c.cond.L = &mu
	s.conns[us] = c
	c.writeDeadlineTimer = time.AfterFunc(-1, c.cond.Broadcast)
	c.readDeadlineTimer = time.AfterFunc(-1, c.cond.Broadcast)
	return c
}

var reads int

func (s *Socket) packetReader() {
	for {
		b := make([]byte, 0x1000)
		n, addr, err := s.pc.ReadFrom(b)
		if err != nil {
			mu.Lock()
			closed := s.closed
			mu.Unlock()
			if closed {
				return
			}
			panic(err)
		}
		reads++
		// log.Printf("received %d bytes, %d packets", n, reads)
		sa, sal := netAddrToLibSockaddr(addr)
		func() {
			mu.Lock()
			defer mu.Unlock()
			if s.closed {
				return
			}
			if C.utp_process_udp(s.ctx, (*C.byte)(&b[0]), C.size_t(n), sa, sal) != 0 {
				socketUtpPacketsReceived.Add(1)
			} else {
				s.onReadNonUtp(b[:n], addr)
			}
			C.utp_issue_deferred_acks(s.ctx)
			C.utp_check_timeouts(s.ctx)
		}()
	}
}

func (s *Socket) timeoutChecker() {
	for {
		mu.Lock()
		if s.closed {
			mu.Unlock()
			return
		}
		// C.utp_issue_deferred_acks(s.ctx)
		C.utp_check_timeouts(s.ctx)
		mu.Unlock()
		time.Sleep(500 * time.Millisecond)
	}
}

func (me *Socket) Close() error {
	mu.Lock()
	defer mu.Unlock()
	if me.closed {
		return nil
	}
	C.utp_destroy(me.ctx)
	me.ctx = nil
	me.pc.Close()
	close(me.backlog)
	close(me.nonUtpReads)
	me.closed = true
	return nil
}

func (me *Socket) Addr() net.Addr {
	return me.pc.LocalAddr()
}

func (me *Socket) LocalAddr() net.Addr {
	return me.pc.LocalAddr()
}

func (s *Socket) Accept() (net.Conn, error) {
	nc, ok := <-s.backlog
	if !ok {
		return nil, errors.New("closed")
	}
	return nc, nil
}

func (s *Socket) Dial(addr string) (net.Conn, error) {
	return s.DialTimeout(addr, 0)
}

func (s *Socket) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
	ua, err := net.ResolveUDPAddr(s.Addr().Network(), addr)
	if err != nil {
		panic(err)
	}
	sa, sl := netAddrToLibSockaddr(ua)
	mu.Lock()
	c := s.newConn(C.utp_create_socket(s.ctx))
	C.utp_connect(c.s, sa, sl)
	defer mu.Unlock()
	err = c.waitForConnect()
	if err != nil {
		c.close()
		return nil, err
	}
	return c, nil
}

func (s *Socket) pushBacklog(c *Conn) {
	select {
	case s.backlog <- c:
	default:
		c.close()
	}
}

func (s *Socket) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
	p, ok := <-s.nonUtpReads
	if !ok {
		err = errors.New("closed")
		return
	}
	n = copy(b, p.b)
	addr = p.from
	return
}

func (s *Socket) onReadNonUtp(b []byte, from net.Addr) {
	socketNonUtpPacketsReceived.Add(1)
	select {
	case s.nonUtpReads <- packet{b, from}:
	default:
		// log.Printf("dropped non utp packet: no room in buffer")
		nonUtpPacketsDropped.Add(1)
	}
}

func (s *Socket) SetReadDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) SetWriteDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) SetDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) WriteTo(b []byte, addr net.Addr) (int, error) {
	return s.pc.WriteTo(b, addr)
}
<commit_msg>Fix data race on reads counter<commit_after>package utp

/*
#include "utp.h"
*/
import "C"
import (
	"errors"
	"net"
	"time"
)

type Socket struct {
	pc            net.PacketConn
	ctx           *C.utp_context
	backlog       chan *Conn
	closed        bool
	conns         map[*C.utp_socket]*Conn
	nonUtpReads   chan packet
	writeDeadline time.Time
	readDeadline  time.Time
}

var (
	_ net.PacketConn = (*Socket)(nil)
)

type packet struct {
	b    []byte
	from net.Addr
}

func NewSocket(network, addr string) (*Socket, error) {
	pc, err := net.ListenPacket(network, addr)
	if err != nil {
		return nil, err
	}
	mu.Lock()
	defer mu.Unlock()
	ctx := C.utp_init(2)
	if ctx == nil {
		panic(ctx)
	}
	ctx.setCallbacks()
	// ctx.setOption(C.UTP_LOG_NORMAL, 1)
	// ctx.setOption(C.UTP_LOG_MTU, 1)
	// ctx.setOption(C.UTP_LOG_DEBUG, 1)
	s := &Socket{
		pc:          pc,
		ctx:         ctx,
		backlog:     make(chan *Conn, 5),
		conns:       make(map[*C.utp_socket]*Conn),
		nonUtpReads: make(chan packet, 100),
	}
	libContextToSocket[ctx] = s
	go s.packetReader()
	return s, nil
}

func (s *Socket) onLibSocketDestroyed(ls *C.utp_socket) {
	delete(s.conns, ls)
}

func (s *Socket) newConn(us *C.utp_socket) *Conn {
	c := &Conn{
		s: us,
	}
	c.cond.L = &mu
	s.conns[us] = c
	c.writeDeadlineTimer = time.AfterFunc(-1, c.cond.Broadcast)
	c.readDeadlineTimer = time.AfterFunc(-1, c.cond.Broadcast)
	return c
}

var reads int

func (s *Socket) packetReader() {
	for {
		b := make([]byte, 0x1000)
		n, addr, err := s.pc.ReadFrom(b)
		if err != nil {
			mu.Lock()
			closed := s.closed
			mu.Unlock()
			if closed {
				return
			}
			panic(err)
		}
		sa, sal := netAddrToLibSockaddr(addr)
		func() {
			mu.Lock()
			defer mu.Unlock()
			reads++
			// log.Printf("received %d bytes, %d packets", n, reads)
			if s.closed {
				return
			}
			if C.utp_process_udp(s.ctx, (*C.byte)(&b[0]), C.size_t(n), sa, sal) != 0 {
				socketUtpPacketsReceived.Add(1)
			} else {
				s.onReadNonUtp(b[:n], addr)
			}
			C.utp_issue_deferred_acks(s.ctx)
			C.utp_check_timeouts(s.ctx)
		}()
	}
}

func (s *Socket) timeoutChecker() {
	for {
		mu.Lock()
		if s.closed {
			mu.Unlock()
			return
		}
		// C.utp_issue_deferred_acks(s.ctx)
		C.utp_check_timeouts(s.ctx)
		mu.Unlock()
		time.Sleep(500 * time.Millisecond)
	}
}

func (me *Socket) Close() error {
	mu.Lock()
	defer mu.Unlock()
	if me.closed {
		return nil
	}
	C.utp_destroy(me.ctx)
	me.ctx = nil
	me.pc.Close()
	close(me.backlog)
	close(me.nonUtpReads)
	me.closed = true
	return nil
}

func (me *Socket) Addr() net.Addr {
	return me.pc.LocalAddr()
}

func (me *Socket) LocalAddr() net.Addr {
	return me.pc.LocalAddr()
}

func (s *Socket) Accept() (net.Conn, error) {
	nc, ok := <-s.backlog
	if !ok {
		return nil, errors.New("closed")
	}
	return nc, nil
}

func (s *Socket) Dial(addr string) (net.Conn, error) {
	return s.DialTimeout(addr, 0)
}

func (s *Socket) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
	ua, err := net.ResolveUDPAddr(s.Addr().Network(), addr)
	if err != nil {
		panic(err)
	}
	sa, sl := netAddrToLibSockaddr(ua)
	mu.Lock()
	c := s.newConn(C.utp_create_socket(s.ctx))
	C.utp_connect(c.s, sa, sl)
	defer mu.Unlock()
	err = c.waitForConnect()
	if err != nil {
		c.close()
		return nil, err
	}
	return c, nil
}

func (s *Socket) pushBacklog(c *Conn) {
	select {
	case s.backlog <- c:
	default:
		c.close()
	}
}

func (s *Socket) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
	p, ok := <-s.nonUtpReads
	if !ok {
		err = errors.New("closed")
		return
	}
	n = copy(b, p.b)
	addr = p.from
	return
}

func (s *Socket) onReadNonUtp(b []byte, from net.Addr) {
	socketNonUtpPacketsReceived.Add(1)
	select {
	case s.nonUtpReads <- packet{b, from}:
	default:
		// log.Printf("dropped non utp packet: no room in buffer")
		nonUtpPacketsDropped.Add(1)
	}
}

func (s *Socket) SetReadDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) SetWriteDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) SetDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) WriteTo(b []byte, addr net.Addr) (int, error) {
	return s.pc.WriteTo(b, addr)
}
<|endoftext|>


<commit_before>package something

import (
	. "github.com/mdwhatcott/goconvey/convey"
	"testing"
)

func TestExample(t *testing.T) {
	Convey("Subject: Integer incrementation and decrementation", func() {
		x := 0

		Convey("Given a starting integer value", func() {
			x = 42

			Convey("When incremented", func() {
				x++

				Convey("The value should be greater by one", func() {
					So(x, ShouldEqual, 43)
				})
				Convey("The value should NOT be what it used to be", func() {
					// So(x, ShouldNotEqual, 42)
				})
			})
			Convey("When decremented", func() {
				x--

				Convey("The value should be lesser by one", func() {
					So(x, ShouldEqual, 41)
				})
				Convey("The value should NOT be what it used to be", func() {
					// So(x, ShouldNotEqual, 42)
				})
			})
			Reset(func() {
				x = 0
			})
		})
	})
}

/*
Output:

### file/to/test.go - Line 42 - TestExample ###

	- Subject: Integer incrementation and decrementation
		- Given a starting integer value
			- When incremented
				- The value should be greater by one
				- The value should NOT be what it used to be
			- When decremented
				- The value should be lesser by one

## Running Total: ##

1 Stor[y|ies] with 4 Assertion[s] (1 Failed)[, 0 Errors]

*/
<commit_msg>Corrected import path.<commit_after>package something

import (
	. "github.com/smartystreets/goconvey/convey"
	"testing"
)

func TestExample(t *testing.T) {
	Convey("Subject: Integer incrementation and decrementation", func() {
		x := 0

		Convey("Given a starting integer value", func() {
			x = 42

			Convey("When incremented", func() {
				x++

				Convey("The value should be greater by one", func() {
					So(x, ShouldEqual, 43)
				})
				Convey("The value should NOT be what it used to be", func() {
					// So(x, ShouldNotEqual, 42)
				})
			})
			Convey("When decremented", func() {
				x--

				Convey("The value should be lesser by one", func() {
					So(x, ShouldEqual, 41)
				})
				Convey("The value should NOT be what it used to be", func() {
					// So(x, ShouldNotEqual, 42)
				})
			})
			Reset(func() {
				x = 0
			})
		})
	})
}

/*
Output:

### file/to/test.go - Line 42 - TestExample ###

	- Subject: Integer incrementation and decrementation
		- Given a starting integer value
			- When incremented
				- The value should be greater by one
				- The value should NOT be what it used to be
			- When decremented
				- The value should be lesser by one

## Running Total: ##

1 Stor[y|ies] with 4 Assertion[s] (1 Failed)[, 0 Errors]

*/
<|endoftext|>