<commit_before>package webcam

import (
	"bytes"
	"encoding/binary"
	"unsafe"

	"github.com/blackjack/webcam/ioctl"
	"golang.org/x/sys/unix"
)

const (
	V4L2_CAP_VIDEO_CAPTURE      uint32 = 0x00000001
	V4L2_CAP_STREAMING          uint32 = 0x04000000
	V4L2_BUF_TYPE_VIDEO_CAPTURE uint32 = 1
	V4L2_MEMORY_MMAP            uint32 = 1
	V4L2_FIELD_ANY              uint32 = 0
)

const (
	V4L2_FRMSIZE_TYPE_DISCRETE   uint32 = 1
	V4L2_FRMSIZE_TYPE_CONTINUOUS uint32 = 2
	V4L2_FRMSIZE_TYPE_STEPWISE   uint32 = 3
)

var (
	VIDIOC_QUERYCAP = ioctl.IoR(uintptr('V'), 0, unsafe.Sizeof(v4l2_capability{}))
	VIDIOC_ENUM_FMT = ioctl.IoRW(uintptr('V'), 2, unsafe.Sizeof(v4l2_fmtdesc{}))
	VIDIOC_S_FMT    = ioctl.IoRW(uintptr('V'), 5, unsafe.Sizeof(v4l2_format{}))
	VIDIOC_REQBUFS  = ioctl.IoRW(uintptr('V'), 8, unsafe.Sizeof(v4l2_requestbuffers{}))
	VIDIOC_QUERYBUF = ioctl.IoRW(uintptr('V'), 9, unsafe.Sizeof(v4l2_buffer{}))
	VIDIOC_QBUF     = ioctl.IoRW(uintptr('V'), 15, unsafe.Sizeof(v4l2_buffer{}))
	VIDIOC_DQBUF    = ioctl.IoRW(uintptr('V'), 17, unsafe.Sizeof(v4l2_buffer{}))
	//sizeof int32
	VIDIOC_STREAMON        = ioctl.IoW(uintptr('V'), 18, 4)
	VIDIOC_STREAMOFF       = ioctl.IoW(uintptr('V'), 19, 4)
	VIDIOC_ENUM_FRAMESIZES = ioctl.IoRW(uintptr('V'), 74, unsafe.Sizeof(v4l2_frmsizeenum{}))
	__p                    = unsafe.Pointer(uintptr(0))
	NativeByteOrder        = getNativeByteOrder()
)

type v4l2_capability struct {
	driver       [16]uint8
	card         [32]uint8
	bus_info     [32]uint8
	version      uint32
	capabilities uint32
	device_caps  uint32
	reserved     [3]uint32
}

type v4l2_fmtdesc struct {
	index       uint32
	_type       uint32
	flags       uint32
	description [32]uint8
	pixelformat uint32
	reserved    [4]uint32
}

type v4l2_frmsizeenum struct {
	index        uint32
	pixel_format uint32
	_type        uint32
	union        [24]uint8
	reserved     [2]uint32
}

type v4l2_frmsize_discrete struct {
	Width  uint32
	Height uint32
}

type v4l2_frmsize_stepwise struct {
	Min_width   uint32
	Max_width   uint32
	Step_width  uint32
	Min_height  uint32
	Max_height  uint32
	Step_height uint32
}

//Hack to make go compiler properly align union
type v4l2_format_aligned_union struct {
	data [200 - unsafe.Sizeof(__p)]byte
	_    unsafe.Pointer
}

type v4l2_format struct {
	_type uint32
	union v4l2_format_aligned_union
}

type v4l2_pix_format struct {
	Width        uint32
	Height       uint32
	Pixelformat  uint32
	Field        uint32
	Bytesperline uint32
	Sizeimage    uint32
	Colorspace   uint32
	Priv         uint32
	Flags        uint32
	Ycbcr_enc    uint32
	Quantization uint32
	Xfer_func    uint32
}

type v4l2_requestbuffers struct {
	count    uint32
	_type    uint32
	memory   uint32
	reserved [2]uint32
}

type v4l2_buffer struct {
	index     uint32
	_type     uint32
	bytesused uint32
	flags     uint32
	field     uint32
	timestamp unix.Timeval
	timecode  v4l2_timecode
	sequence  uint32
	memory    uint32
	union     [unsafe.Sizeof(__p)]uint8
	length    uint32
	reserved2 uint32
	reserved  uint32
}

type v4l2_timecode struct {
	_type    uint32
	flags    uint32
	frames   uint8
	seconds  uint8
	minutes  uint8
	hours    uint8
	userbits [4]uint8
}

func checkCapabilities(fd uintptr) (supportsVideoCapture bool, supportsVideoStreaming bool, err error) {

	caps := &v4l2_capability{}

	err = ioctl.Ioctl(fd, VIDIOC_QUERYCAP, uintptr(unsafe.Pointer(caps)))

	if err != nil {
		return
	}

	supportsVideoCapture = (caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) != 0
	supportsVideoStreaming = (caps.capabilities & V4L2_CAP_STREAMING) != 0
	return

}

func getPixelFormat(fd uintptr, index uint32) (code uint32, description string, err error) {

	fmtdesc := &v4l2_fmtdesc{}

	fmtdesc.index = index
	fmtdesc._type = V4L2_BUF_TYPE_VIDEO_CAPTURE

	err = ioctl.Ioctl(fd, VIDIOC_ENUM_FMT, uintptr(unsafe.Pointer(fmtdesc)))

	if err != nil {
		return
	}

	code = fmtdesc.pixelformat
	description = CToGoString(fmtdesc.description[:])

	return
}

func getFrameSize(fd uintptr, index uint32, code uint32) (frameSize FrameSize, err error) {

	frmsizeenum := &v4l2_frmsizeenum{}
	frmsizeenum.index = index
	frmsizeenum.pixel_format = code

	err = ioctl.Ioctl(fd, VIDIOC_ENUM_FRAMESIZES, uintptr(unsafe.Pointer(frmsizeenum)))

	if err != nil {
		return
	}

	switch frmsizeenum._type {

	case V4L2_FRMSIZE_TYPE_DISCRETE:
		discrete := &v4l2_frmsize_discrete{}
		err = binary.Read(bytes.NewBuffer(frmsizeenum.union[:]), NativeByteOrder, discrete)

		if err != nil {
			return
		}

		frameSize.MinWidth = discrete.Width
		frameSize.MaxWidth = discrete.Width
		frameSize.StepWidth = 0
		frameSize.MinHeight = discrete.Height
		frameSize.MaxHeight = discrete.Height
		frameSize.StepHeight = 0

	case V4L2_FRMSIZE_TYPE_CONTINUOUS:

	case V4L2_FRMSIZE_TYPE_STEPWISE:
		stepwise := &v4l2_frmsize_stepwise{}
		err = binary.Read(bytes.NewBuffer(frmsizeenum.union[:]), NativeByteOrder, stepwise)

		if err != nil {
			return
		}

		frameSize.MinWidth = stepwise.Min_width
		frameSize.MaxWidth = stepwise.Max_width
		frameSize.StepWidth = stepwise.Step_width
		frameSize.MinHeight = stepwise.Min_height
		frameSize.MaxHeight = stepwise.Max_height
		frameSize.StepHeight = stepwise.Step_height
	}

	return
}

func setImageFormat(fd uintptr, formatcode *uint32, width *uint32, height *uint32) (err error) {

	format := &v4l2_format{
		_type: V4L2_BUF_TYPE_VIDEO_CAPTURE,
	}

	pix := v4l2_pix_format{
		Width:       *width,
		Height:      *height,
		Pixelformat: *formatcode,
		Field:       V4L2_FIELD_ANY,
	}

	pixbytes := &bytes.Buffer{}
	err = binary.Write(pixbytes, NativeByteOrder, pix)

	if err != nil {
		return
	}

	copy(format.union.data[:], pixbytes.Bytes())

	err = ioctl.Ioctl(fd, VIDIOC_S_FMT, uintptr(unsafe.Pointer(format)))

	if err != nil {
		return
	}

	pixReverse := &v4l2_pix_format{}
	err = binary.Read(bytes.NewBuffer(format.union.data[:]), NativeByteOrder, pixReverse)

	if err != nil {
		return
	}

	*width = pixReverse.Width
	*height = pixReverse.Height
	*formatcode = pixReverse.Pixelformat

	return

}

func mmapRequestBuffers(fd uintptr, buf_count *uint32) (err error) {

	req := &v4l2_requestbuffers{}
	req.count = *buf_count
	req._type = V4L2_BUF_TYPE_VIDEO_CAPTURE
	req.memory = V4L2_MEMORY_MMAP

	err = ioctl.Ioctl(fd, VIDIOC_REQBUFS, uintptr(unsafe.Pointer(req)))

	if err != nil {
		return
	}

	*buf_count = req.count

	return

}

func mmapQueryBuffer(fd uintptr, index uint32, length *uint32) (buffer []byte, err error) {

	req := &v4l2_buffer{}

	req._type = V4L2_BUF_TYPE_VIDEO_CAPTURE
	req.memory = V4L2_MEMORY_MMAP
	req.index = index

	err = ioctl.Ioctl(fd, VIDIOC_QUERYBUF, uintptr(unsafe.Pointer(req)))

	if err != nil {
		return
	}

	var offset uint32
	err = binary.Read(bytes.NewBuffer(req.union[:]), NativeByteOrder, &offset)

	if err != nil {
		return
	}

	*length = req.length

	buffer, err = unix.Mmap(int(fd), int64(offset), int(req.length), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
	return
}

func mmapDequeueBuffer(fd uintptr, index *uint32, length *uint32) (err error) {

	buffer := &v4l2_buffer{}

	buffer._type = V4L2_BUF_TYPE_VIDEO_CAPTURE
	buffer.memory = V4L2_MEMORY_MMAP

	err = ioctl.Ioctl(fd, VIDIOC_DQBUF, uintptr(unsafe.Pointer(buffer)))

	if err != nil {
		return
	}

	*index = buffer.index
	*length = buffer.bytesused

	return

}

func mmapEnqueueBuffer(fd uintptr, index uint32) (err error) {

	buffer := &v4l2_buffer{}

	buffer._type = V4L2_BUF_TYPE_VIDEO_CAPTURE
	buffer.memory = V4L2_MEMORY_MMAP
	buffer.index = index

	err = ioctl.Ioctl(fd, VIDIOC_QBUF, uintptr(unsafe.Pointer(buffer)))
	return

}

func mmapReleaseBuffer(buffer []byte) (err error) {
	err = unix.Munmap(buffer)
	return
}

func startStreaming(fd uintptr) (err error) {

	var uintPointer uint32 = V4L2_BUF_TYPE_VIDEO_CAPTURE
	err = ioctl.Ioctl(fd, VIDIOC_STREAMON, uintptr(unsafe.Pointer(&uintPointer)))
	return

}

func stopStreaming(fd uintptr) (err error) {

	var uintPointer uint32 = V4L2_BUF_TYPE_VIDEO_CAPTURE
	err = ioctl.Ioctl(fd, VIDIOC_STREAMOFF, uintptr(unsafe.Pointer(&uintPointer)))
	return

}

func FD_SET(p *unix.FdSet, i int) {
	var l int = int(len(p.Bits))
	p.Bits[i/l] |= 1 << uintptr(i%l)
}

func waitForFrame(fd uintptr, timeout uint32) (count int, err error) {

	for {
		fds := &unix.FdSet{}
		FD_SET(fds, int(fd))

		var oneSecInNsec int64 = 1e9
		timeoutNsec := int64(timeout) * oneSecInNsec
		nativeTimeVal := unix.NsecToTimeval(timeoutNsec)
		tv := &nativeTimeVal

		count, err = unix.Select(int(fd+1), fds, nil, nil, tv)

		if count < 0 && err == unix.EINTR {
			continue
		}
		return
	}

}

func getNativeByteOrder() binary.ByteOrder {
	var i int32 = 0x01020304
	u := unsafe.Pointer(&i)
	pb := (*byte)(u)
	b := *pb
	if b == 0x04 {
		return binary.LittleEndian
	} else {
		return binary.BigEndian
	}
}

func CToGoString(c []byte) string {
	n := -1
	for i, b := range c {
		if b == 0 {
			break
		}
		n = i
	}
	return string(c[:n+1])
}
<commit_msg>Add support for getting/setting device controls (#11)<commit_after>package webcam

import (
	"bytes"
	"encoding/binary"
	"unsafe"

	"github.com/blackjack/webcam/ioctl"
	"golang.org/x/sys/unix"
)

const (
	V4L2_CAP_VIDEO_CAPTURE      uint32 = 0x00000001
	V4L2_CAP_STREAMING          uint32 = 0x04000000
	V4L2_BUF_TYPE_VIDEO_CAPTURE uint32 = 1
	V4L2_MEMORY_MMAP            uint32 = 1
	V4L2_FIELD_ANY              uint32 = 0
)

const (
	V4L2_FRMSIZE_TYPE_DISCRETE   uint32 = 1
	V4L2_FRMSIZE_TYPE_CONTINUOUS uint32 = 2
	V4L2_FRMSIZE_TYPE_STEPWISE   uint32 = 3
)

const (
	V4L2_CID_BASE               uint32 = 0x00980900
	V4L2_CID_PRIVATE_BASE       uint32 = 0x08000000
)

var (
	VIDIOC_QUERYCAP  = ioctl.IoR(uintptr('V'), 0, unsafe.Sizeof(v4l2_capability{}))
	VIDIOC_ENUM_FMT  = ioctl.IoRW(uintptr('V'), 2, unsafe.Sizeof(v4l2_fmtdesc{}))
	VIDIOC_S_FMT     = ioctl.IoRW(uintptr('V'), 5, unsafe.Sizeof(v4l2_format{}))
	VIDIOC_REQBUFS   = ioctl.IoRW(uintptr('V'), 8, unsafe.Sizeof(v4l2_requestbuffers{}))
	VIDIOC_QUERYBUF  = ioctl.IoRW(uintptr('V'), 9, unsafe.Sizeof(v4l2_buffer{}))
	VIDIOC_QBUF      = ioctl.IoRW(uintptr('V'), 15, unsafe.Sizeof(v4l2_buffer{}))
	VIDIOC_DQBUF     = ioctl.IoRW(uintptr('V'), 17, unsafe.Sizeof(v4l2_buffer{}))
	VIDIOC_S_CTRL    = ioctl.IoRW(uintptr('V'), 28, unsafe.Sizeof(v4l2_control{}))
	VIDIOC_QUERYCTRL = ioctl.IoRW(uintptr('V'), 36, unsafe.Sizeof(v4l2_queryctrl{}))
	//sizeof int32
	VIDIOC_STREAMON        = ioctl.IoW(uintptr('V'), 18, 4)
	VIDIOC_STREAMOFF       = ioctl.IoW(uintptr('V'), 19, 4)
	VIDIOC_ENUM_FRAMESIZES = ioctl.IoRW(uintptr('V'), 74, unsafe.Sizeof(v4l2_frmsizeenum{}))
	__p                    = unsafe.Pointer(uintptr(0))
	NativeByteOrder        = getNativeByteOrder()
)

type v4l2_capability struct {
	driver       [16]uint8
	card         [32]uint8
	bus_info     [32]uint8
	version      uint32
	capabilities uint32
	device_caps  uint32
	reserved     [3]uint32
}

type v4l2_fmtdesc struct {
	index       uint32
	_type       uint32
	flags       uint32
	description [32]uint8
	pixelformat uint32
	reserved    [4]uint32
}

type v4l2_frmsizeenum struct {
	index        uint32
	pixel_format uint32
	_type        uint32
	union        [24]uint8
	reserved     [2]uint32
}

type v4l2_frmsize_discrete struct {
	Width  uint32
	Height uint32
}

type v4l2_frmsize_stepwise struct {
	Min_width   uint32
	Max_width   uint32
	Step_width  uint32
	Min_height  uint32
	Max_height  uint32
	Step_height uint32
}

//Hack to make go compiler properly align union
type v4l2_format_aligned_union struct {
	data [200 - unsafe.Sizeof(__p)]byte
	_    unsafe.Pointer
}

type v4l2_format struct {
	_type uint32
	union v4l2_format_aligned_union
}

type v4l2_pix_format struct {
	Width        uint32
	Height       uint32
	Pixelformat  uint32
	Field        uint32
	Bytesperline uint32
	Sizeimage    uint32
	Colorspace   uint32
	Priv         uint32
	Flags        uint32
	Ycbcr_enc    uint32
	Quantization uint32
	Xfer_func    uint32
}

type v4l2_requestbuffers struct {
	count    uint32
	_type    uint32
	memory   uint32
	reserved [2]uint32
}

type v4l2_buffer struct {
	index     uint32
	_type     uint32
	bytesused uint32
	flags     uint32
	field     uint32
	timestamp unix.Timeval
	timecode  v4l2_timecode
	sequence  uint32
	memory    uint32
	union     [unsafe.Sizeof(__p)]uint8
	length    uint32
	reserved2 uint32
	reserved  uint32
}

type v4l2_timecode struct {
	_type    uint32
	flags    uint32
	frames   uint8
	seconds  uint8
	minutes  uint8
	hours    uint8
	userbits [4]uint8
}

type v4l2_queryctrl struct {
	id            uint32
	_type         uint32
	name          [32]uint8
	minimum       int32
	maximum       int32
	step          int32
	default_value int32
	flags         uint32
	reserved      [2]uint32
}

type v4l2_control struct {
	id    uint32
	value int32
}

func checkCapabilities(fd uintptr) (supportsVideoCapture bool, supportsVideoStreaming bool, err error) {

	caps := &v4l2_capability{}

	err = ioctl.Ioctl(fd, VIDIOC_QUERYCAP, uintptr(unsafe.Pointer(caps)))

	if err != nil {
		return
	}

	supportsVideoCapture = (caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) != 0
	supportsVideoStreaming = (caps.capabilities & V4L2_CAP_STREAMING) != 0
	return

}

func getPixelFormat(fd uintptr, index uint32) (code uint32, description string, err error) {

	fmtdesc := &v4l2_fmtdesc{}

	fmtdesc.index = index
	fmtdesc._type = V4L2_BUF_TYPE_VIDEO_CAPTURE

	err = ioctl.Ioctl(fd, VIDIOC_ENUM_FMT, uintptr(unsafe.Pointer(fmtdesc)))

	if err != nil {
		return
	}

	code = fmtdesc.pixelformat
	description = CToGoString(fmtdesc.description[:])

	return
}

func getFrameSize(fd uintptr, index uint32, code uint32) (frameSize FrameSize, err error) {

	frmsizeenum := &v4l2_frmsizeenum{}
	frmsizeenum.index = index
	frmsizeenum.pixel_format = code

	err = ioctl.Ioctl(fd, VIDIOC_ENUM_FRAMESIZES, uintptr(unsafe.Pointer(frmsizeenum)))

	if err != nil {
		return
	}

	switch frmsizeenum._type {

	case V4L2_FRMSIZE_TYPE_DISCRETE:
		discrete := &v4l2_frmsize_discrete{}
		err = binary.Read(bytes.NewBuffer(frmsizeenum.union[:]), NativeByteOrder, discrete)

		if err != nil {
			return
		}

		frameSize.MinWidth = discrete.Width
		frameSize.MaxWidth = discrete.Width
		frameSize.StepWidth = 0
		frameSize.MinHeight = discrete.Height
		frameSize.MaxHeight = discrete.Height
		frameSize.StepHeight = 0

	case V4L2_FRMSIZE_TYPE_CONTINUOUS:

	case V4L2_FRMSIZE_TYPE_STEPWISE:
		stepwise := &v4l2_frmsize_stepwise{}
		err = binary.Read(bytes.NewBuffer(frmsizeenum.union[:]), NativeByteOrder, stepwise)

		if err != nil {
			return
		}

		frameSize.MinWidth = stepwise.Min_width
		frameSize.MaxWidth = stepwise.Max_width
		frameSize.StepWidth = stepwise.Step_width
		frameSize.MinHeight = stepwise.Min_height
		frameSize.MaxHeight = stepwise.Max_height
		frameSize.StepHeight = stepwise.Step_height
	}

	return
}

func setImageFormat(fd uintptr, formatcode *uint32, width *uint32, height *uint32) (err error) {

	format := &v4l2_format{
		_type: V4L2_BUF_TYPE_VIDEO_CAPTURE,
	}

	pix := v4l2_pix_format{
		Width:       *width,
		Height:      *height,
		Pixelformat: *formatcode,
		Field:       V4L2_FIELD_ANY,
	}

	pixbytes := &bytes.Buffer{}
	err = binary.Write(pixbytes, NativeByteOrder, pix)

	if err != nil {
		return
	}

	copy(format.union.data[:], pixbytes.Bytes())

	err = ioctl.Ioctl(fd, VIDIOC_S_FMT, uintptr(unsafe.Pointer(format)))

	if err != nil {
		return
	}

	pixReverse := &v4l2_pix_format{}
	err = binary.Read(bytes.NewBuffer(format.union.data[:]), NativeByteOrder, pixReverse)

	if err != nil {
		return
	}

	*width = pixReverse.Width
	*height = pixReverse.Height
	*formatcode = pixReverse.Pixelformat

	return

}

func mmapRequestBuffers(fd uintptr, buf_count *uint32) (err error) {

	req := &v4l2_requestbuffers{}
	req.count = *buf_count
	req._type = V4L2_BUF_TYPE_VIDEO_CAPTURE
	req.memory = V4L2_MEMORY_MMAP

	err = ioctl.Ioctl(fd, VIDIOC_REQBUFS, uintptr(unsafe.Pointer(req)))

	if err != nil {
		return
	}

	*buf_count = req.count

	return

}

func mmapQueryBuffer(fd uintptr, index uint32, length *uint32) (buffer []byte, err error) {

	req := &v4l2_buffer{}

	req._type = V4L2_BUF_TYPE_VIDEO_CAPTURE
	req.memory = V4L2_MEMORY_MMAP
	req.index = index

	err = ioctl.Ioctl(fd, VIDIOC_QUERYBUF, uintptr(unsafe.Pointer(req)))

	if err != nil {
		return
	}

	var offset uint32
	err = binary.Read(bytes.NewBuffer(req.union[:]), NativeByteOrder, &offset)

	if err != nil {
		return
	}

	*length = req.length

	buffer, err = unix.Mmap(int(fd), int64(offset), int(req.length), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
	return
}

func mmapDequeueBuffer(fd uintptr, index *uint32, length *uint32) (err error) {

	buffer := &v4l2_buffer{}

	buffer._type = V4L2_BUF_TYPE_VIDEO_CAPTURE
	buffer.memory = V4L2_MEMORY_MMAP

	err = ioctl.Ioctl(fd, VIDIOC_DQBUF, uintptr(unsafe.Pointer(buffer)))

	if err != nil {
		return
	}

	*index = buffer.index
	*length = buffer.bytesused

	return

}

func mmapEnqueueBuffer(fd uintptr, index uint32) (err error) {

	buffer := &v4l2_buffer{}

	buffer._type = V4L2_BUF_TYPE_VIDEO_CAPTURE
	buffer.memory = V4L2_MEMORY_MMAP
	buffer.index = index

	err = ioctl.Ioctl(fd, VIDIOC_QBUF, uintptr(unsafe.Pointer(buffer)))
	return

}

func mmapReleaseBuffer(buffer []byte) (err error) {
	err = unix.Munmap(buffer)
	return
}

func startStreaming(fd uintptr) (err error) {

	var uintPointer uint32 = V4L2_BUF_TYPE_VIDEO_CAPTURE
	err = ioctl.Ioctl(fd, VIDIOC_STREAMON, uintptr(unsafe.Pointer(&uintPointer)))
	return

}

func stopStreaming(fd uintptr) (err error) {

	var uintPointer uint32 = V4L2_BUF_TYPE_VIDEO_CAPTURE
	err = ioctl.Ioctl(fd, VIDIOC_STREAMOFF, uintptr(unsafe.Pointer(&uintPointer)))
	return

}

func FD_SET(p *unix.FdSet, i int) {
	var l int = int(len(p.Bits))
	p.Bits[i/l] |= 1 << uintptr(i%l)
}

func waitForFrame(fd uintptr, timeout uint32) (count int, err error) {

	for {
		fds := &unix.FdSet{}
		FD_SET(fds, int(fd))

		var oneSecInNsec int64 = 1e9
		timeoutNsec := int64(timeout) * oneSecInNsec
		nativeTimeVal := unix.NsecToTimeval(timeoutNsec)
		tv := &nativeTimeVal

		count, err = unix.Select(int(fd+1), fds, nil, nil, tv)

		if count < 0 && err == unix.EINTR {
			continue
		}
		return
	}

}

func setControl(fd uintptr, id uint32, val int32) error {
	ctrl := &v4l2_control{}
	ctrl.id = id
	ctrl.value = val
	return ioctl.Ioctl(fd, VIDIOC_S_CTRL, uintptr(unsafe.Pointer(ctrl)))
}

func getControls(fd uintptr) map[uint32]string {
	query := &v4l2_queryctrl{}
	var controls map[uint32]string
	var err error
	for query.id = V4L2_CID_BASE; err == nil; query.id++ {
		err = ioctl.Ioctl(fd, VIDIOC_QUERYCTRL, uintptr(unsafe.Pointer(query)))
		if err == nil {
			controls[query.id] = CToGoString(query.name[:])
		}
	}
	err = nil
	for query.id = V4L2_CID_PRIVATE_BASE; err == nil; query.id++ {
		err = ioctl.Ioctl(fd, VIDIOC_QUERYCTRL, uintptr(unsafe.Pointer(query)))
		if err == nil {
			controls[query.id] = CToGoString(query.name[:])
		}
	}
	return controls
}

func getNativeByteOrder() binary.ByteOrder {
	var i int32 = 0x01020304
	u := unsafe.Pointer(&i)
	pb := (*byte)(u)
	b := *pb
	if b == 0x04 {
		return binary.LittleEndian
	} else {
		return binary.BigEndian
	}
}

func CToGoString(c []byte) string {
	n := -1
	for i, b := range c {
		if b == 0 {
			break
		}
		n = i
	}
	return string(c[:n+1])
}
<|endoftext|>


<commit_before>package d3dmath

import (
	"fmt"
	"math"
)

type Vec3 [3]float32

func (v Vec3) Add(w Vec3) Vec3 {
	return Vec3{v[0] + w[0], v[1] + w[1], v[2] + w[2]}
}

func (v Vec3) Sub(w Vec3) Vec3 {
	return Vec3{v[0] - w[0], v[1] - w[1], v[2] - w[2]}
}

func (v Vec3) Dot(w Vec3) float32 {
	return v[0]*w[0] + v[1]*w[1] + v[2]*w[2]
}

func (v Vec3) Cross(w Vec3) Vec3 {
	return Vec3{
		v[1]*w[2] - v[2]*w[1],
		v[2]*w[0] - v[0]*w[2],
		v[0]*w[1] - v[1]*w[0],
	}
}

func (v Vec3) MulScalar(s float32) Vec3 {
	return Vec3{v[0] * s, v[1] * s, v[2] * s}
}

//          | m0 m1 m2
//          | m3 m4 m5
//    *     | m6 m7 m8
// ---------+---------
// v0 v1 v2 | ...
func (v Vec3) MulMat(m Mat3) Vec3 {
	return Vec3{
		v[0]*m[0] + v[1]*m[3] + v[2]*m[6],
		v[0]*m[1] + v[1]*m[4] + v[2]*m[7],
		v[0]*m[2] + v[1]*m[5] + v[2]*m[8],
	}
}

func (v Vec3) SquareNorm() float32 {
	return v[0]*v[0] + v[1]*v[1] + v[2]*v[2]
}

func (v Vec3) Norm() float32 {
	return float32(math.Sqrt(float64(v.SquareNorm())))
}

func (v Vec3) Normalized() Vec3 {
	f := 1.0 / v.Norm()
	return Vec3{f * v[0], f * v[1], f * v[2]}
}

func (v Vec3) Homgeneous() Vec4 {
	return Vec4{v[0], v[1], v[2], 1}
}

func (v Vec3) String() string {
	return fmt.Sprintf("(%.2f %.2f %.2f)", v[0], v[1], v[2])
}

func AddVec3(v0 Vec3, v ...Vec3) Vec3 {
	if len(v) == 0 {
		return v0
	}
	return v0.Add(AddVec3(v[0], v[1:]...))
}
<commit_msg>Fix typo<commit_after>package d3dmath

import (
	"fmt"
	"math"
)

type Vec3 [3]float32

func (v Vec3) Add(w Vec3) Vec3 {
	return Vec3{v[0] + w[0], v[1] + w[1], v[2] + w[2]}
}

func (v Vec3) Sub(w Vec3) Vec3 {
	return Vec3{v[0] - w[0], v[1] - w[1], v[2] - w[2]}
}

func (v Vec3) Dot(w Vec3) float32 {
	return v[0]*w[0] + v[1]*w[1] + v[2]*w[2]
}

func (v Vec3) Cross(w Vec3) Vec3 {
	return Vec3{
		v[1]*w[2] - v[2]*w[1],
		v[2]*w[0] - v[0]*w[2],
		v[0]*w[1] - v[1]*w[0],
	}
}

func (v Vec3) MulScalar(s float32) Vec3 {
	return Vec3{v[0] * s, v[1] * s, v[2] * s}
}

//          | m0 m1 m2
//          | m3 m4 m5
//    *     | m6 m7 m8
// ---------+---------
// v0 v1 v2 | ...
func (v Vec3) MulMat(m Mat3) Vec3 {
	return Vec3{
		v[0]*m[0] + v[1]*m[3] + v[2]*m[6],
		v[0]*m[1] + v[1]*m[4] + v[2]*m[7],
		v[0]*m[2] + v[1]*m[5] + v[2]*m[8],
	}
}

func (v Vec3) SquareNorm() float32 {
	return v[0]*v[0] + v[1]*v[1] + v[2]*v[2]
}

func (v Vec3) Norm() float32 {
	return float32(math.Sqrt(float64(v.SquareNorm())))
}

func (v Vec3) Normalized() Vec3 {
	f := 1.0 / v.Norm()
	return Vec3{f * v[0], f * v[1], f * v[2]}
}

func (v Vec3) Homogeneous() Vec4 {
	return Vec4{v[0], v[1], v[2], 1}
}

func (v Vec3) String() string {
	return fmt.Sprintf("(%.2f %.2f %.2f)", v[0], v[1], v[2])
}

func AddVec3(v0 Vec3, v ...Vec3) Vec3 {
	if len(v) == 0 {
		return v0
	}
	return v0.Add(AddVec3(v[0], v[1:]...))
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"os/exec"
	"regexp"
	"strconv"
	"strings"

	"gopkg.in/telegram-bot-api.v4"
)

type VLAN struct {
	Selected   int
	Interfaces *Interfaces
	Device     string
	IFB        string
}

// Select a particular VLAN
func (v *VLAN) ReplyToVLAN(msg *tgbotapi.Message, fields []string) (string, []string) {
	if len(fields) < 2 {
		return "Error: must provide the VLAN number (vlan <vlan_number>)", nil
	}
	vlan, err := strconv.Atoi(fields[1])
	if err != nil {
		return err.Error(), nil
	}
	if vlan < 1 || vlan > 4094 {
		return "Error: VLAN number must be between 1 and 4094", nil
	}
	suffix := fmt.Sprintf(".%d", vlan)
	found := ""
	for name := range v.Interfaces.Current {
		if strings.HasSuffix(name, suffix) {
			found = name
			break
		}
	}
	if found == "" {
		return fmt.Sprintf("Error: VLAN %d is not found. Run \"ip\" for more info", vlan), nil
	}
	v.Selected = vlan
	v.Device = found
	ifb, err := v.getIFB()
	if err != nil {
		return fmt.Sprintf("Could not get IFB: %s", err.Error()), fields[2:]
	}
	v.IFB = ifb
	return fmt.Sprintf("VLAN %d selected", vlan), fields[2:]
}

type params struct {
	delay, jitter     int
	loss, correlation float64
	err               string
}

func (v *VLAN) getParams(msg *tgbotapi.Message, fields []string) (params, []string) {
	if v.Selected == 0 {
		return params{err: "No VLAN selected. Run \"vlan\" for more info"}, nil
	}
	if len(fields) < 2 {
		return params{err: "Error: must at least provide delay (ms). Format: [in|out] <delay_ms> <jitter_ms> <PL %> <correlation %>"}, nil
	}
	result := params{}
	spent := 2
	msDelay, err := strconv.Atoi(fields[1])
	if err != nil {
		return params{err: fmt.Sprintf("delay is not an int: %s", err.Error())}, nil
	}
	if msDelay < 1 || msDelay > 4094 {
		return params{err: "Error: Delay must be between 1 and 4094 milliseconds"}, nil
	}
	result.delay = msDelay
	if len(fields) > 2 {
		if msJitter, err := strconv.Atoi(fields[2]); err == nil {
			if msJitter < 1 || msJitter > 4094 {
				return params{err: "Error: Delay must be between 1 and 4094 milliseconds"}, nil
			}
			result.jitter = msJitter
			spent = 3
			if len(fields) > 3 {
				if pl, err := strconv.ParseFloat(fields[3], 32); err == nil {
					if pl < 0 || pl > 100 {
						return params{err: "Error: Packet loss must be between 0.0 and 100.0 percent"}, nil
					}
					result.loss = pl
					spent = 4
					if len(fields) > 4 {
						if corr, err := strconv.ParseFloat(fields[4], 32); err == nil {
							if corr < 0 || corr > 100 {
								return params{err: "Error: Correlation must be between 0.0 and 100.0 percent"}, nil
							}
							result.correlation = corr
							spent = 5
						}
					}
				}
			}
		}
	}
	return result, fields[spent:]
}

// Add delay to an interface
func (v *VLAN) delay(iface string, p params, remainder []string) (string, []string) {
	// Remove any qdisc
	cmd := exec.Command("tc", "qdisc", "del", "dev", iface, "root")
	var outDel bytes.Buffer
	cmd.Stdout = &outDel
	header := ""
	if err := cmd.Run(); err != nil {
		header = fmt.Sprintf("(Ignore) Error at qdisc del: %s", err.Error())
	}
	// Add a new qdisc
	delay := fmt.Sprintf("%dms", p.delay)
	jitter := fmt.Sprintf("%dms", p.jitter)
	cmd = exec.Command("tc", "qdisc", "add", "dev", v.Device, "root", "netem", "delay", delay, jitter, "distribution", "normal")
	var outAdd bytes.Buffer
	cmd.Stdout = &outAdd
	if err := cmd.Run(); err != nil {
		return fmt.Sprintf("Error at qdisc add: %s", err.Error()), nil
	}
	// Return the output of the qdisc commands
	return strings.Join([]string{
		fmt.Sprintf("Delay of %d ms (jitter %d) added outbound of %s", p.delay, p.jitter, iface),
		header,
		outDel.String(),
		outAdd.String(),
	}, "\n"), remainder
}

func (v *VLAN) getIFB() (string, error) {
	cmd := exec.Command("tc", "filter", "show", "dev", v.Device, "root")
	var outShow bytes.Buffer
	cmd.Stdout = &outShow
	if err := cmd.Run(); err != nil {
		return "", fmt.Errorf("Error at filter show: %s", err.Error())
	}
	data := outShow.String()
	re := regexp.MustCompile("Egress Redirect to device ifb[0-9]")
	match := re.FindString(data)
	if match == "" {
		return "", fmt.Errorf("Missing IFB device for %s in %s", v.Device, data)
	}
	ifbFields := strings.Fields(match)
	return ifbFields[len(ifbFields)-1], nil
}

// Add delay in the outbound direction
func (v *VLAN) ReplyToOut(msg *tgbotapi.Message, fields []string) (string, []string) {
	data, remainder := v.getParams(msg, fields)
	if remainder == nil {
		return data.err, remainder
	}
	return v.delay(v.Device, data, remainder)
}

// Add delay in the outbound direction
func (v *VLAN) ReplyToIn(msg *tgbotapi.Message, fields []string) (string, []string) {
	if v.IFB == "" {
		return "Current VLAN does not have IFB device assigned", nil
	}
	data, remainder := v.getParams(msg, fields)
	if remainder == nil {
		return data.err, remainder
	}
	return v.delay(v.IFB, data, remainder)
}
<commit_msg>Added packet loss support<commit_after>package main

import (
	"bytes"
	"fmt"
	"os/exec"
	"regexp"
	"strconv"
	"strings"

	"gopkg.in/telegram-bot-api.v4"
)

type VLAN struct {
	Selected   int
	Interfaces *Interfaces
	Device     string
	IFB        string
}

// Select a particular VLAN
func (v *VLAN) ReplyToVLAN(msg *tgbotapi.Message, fields []string) (string, []string) {
	if len(fields) < 2 {
		return "Error: must provide the VLAN number (vlan <vlan_number>)", nil
	}
	vlan, err := strconv.Atoi(fields[1])
	if err != nil {
		return err.Error(), nil
	}
	if vlan < 1 || vlan > 4094 {
		return "Error: VLAN number must be between 1 and 4094", nil
	}
	suffix := fmt.Sprintf(".%d", vlan)
	found := ""
	for name := range v.Interfaces.Current {
		if strings.HasSuffix(name, suffix) {
			found = name
			break
		}
	}
	if found == "" {
		return fmt.Sprintf("Error: VLAN %d is not found. Run \"ip\" for more info", vlan), nil
	}
	v.Selected = vlan
	v.Device = found
	ifb, err := v.getIFB()
	if err != nil {
		return fmt.Sprintf("Could not get IFB: %s", err.Error()), fields[2:]
	}
	v.IFB = ifb
	return fmt.Sprintf("VLAN %d selected", vlan), fields[2:]
}

type params struct {
	delay, jitter     int
	loss, correlation float64
	err               string
}

// Get Delay, Jitter, PL and PL correlation from command
func (v *VLAN) getParams(msg *tgbotapi.Message, fields []string) (params, []string) {
	if v.Selected == 0 {
		return params{err: "No VLAN selected. Run \"vlan\" for more info"}, nil
	}
	if len(fields) < 2 {
		return params{err: "Error: must at least provide delay (ms). Format: [in|out] <delay_ms> <jitter_ms> <PL %> <correlation %>"}, nil
	}
	result := params{}
	spent := 2
	msDelay, err := strconv.Atoi(fields[1])
	if err != nil {
		return params{err: fmt.Sprintf("delay is not an int: %s", err.Error())}, nil
	}
	if msDelay < 1 || msDelay > 4094 {
		return params{err: "Error: Delay must be between 1 and 4094 milliseconds"}, nil
	}
	result.delay = msDelay
	if len(fields) > 2 {
		if msJitter, err := strconv.Atoi(fields[2]); err == nil {
			if msJitter < 1 || msJitter > 4094 {
				return params{err: "Error: Delay must be between 1 and 4094 milliseconds"}, nil
			}
			result.jitter = msJitter
			spent = 3
			if len(fields) > 3 {
				if pl, err := strconv.ParseFloat(fields[3], 32); err == nil {
					if pl < 0 || pl > 100 {
						return params{err: "Error: Packet loss must be between 0.0 and 100.0 percent"}, nil
					}
					result.loss = pl
					spent = 4
					if len(fields) > 4 {
						if corr, err := strconv.ParseFloat(fields[4], 32); err == nil {
							if corr < 0 || corr > 100 {
								return params{err: "Error: Correlation must be between 0.0 and 100.0 percent"}, nil
							}
							result.correlation = corr
							spent = 5
						}
					}
				}
			}
		}
	}
	return result, fields[spent:]
}

// Add impairments (delay, jitter, loss...) to an interface
func (v *VLAN) impair(iface string, p params, remainder []string) (string, []string) {
	// Remove any qdisc
	cmd := exec.Command("tc", "qdisc", "del", "dev", iface, "root")
	var outDel bytes.Buffer
	cmd.Stdout = &outDel
	header := ""
	if err := cmd.Run(); err != nil {
		header = fmt.Sprintf("(Ignore) Error at qdisc del: %s", err.Error())
	}
	// Prepare for adding jitter and oacket loss
	cmdLine := fmt.Sprintf("tc qdisc add dev %s root netem", iface)
	if p.delay != 0 {
		cmdLine = fmt.Sprintf("%s delay %dms", cmdLine, p.delay)
		if p.jitter != 0 {
			cmdLine = fmt.Sprintf("%s %dms distribution normal", cmdLine, p.jitter)
		}
	}
	if p.loss != 0 {
		cmdLine = fmt.Sprintf("%s loss %f%%", cmdLine, p.loss)
		if p.correlation != 0 {
			cmdLine = fmt.Sprintf("%s %f%%", cmdLine, p.correlation)
		}
	}
	fields := strings.Fields(cmdLine)
	cmd = exec.Command(fields[0], fields[1:]...)
	var outAdd bytes.Buffer
	cmd.Stdout = &outAdd
	if err := cmd.Run(); err != nil {
		return fmt.Sprintf("Error at qdisc add: %s", err.Error()), nil
	}
	// Return the output of the qdisc commands
	return strings.Join([]string{
		fmt.Sprintf("Policy for interface %s: %dms delay (%dms jitter), %f%% PL (%f%% correlation)", p.delay, p.jitter, p.loss, p.correlation),
		header,
		outDel.String(),
		outAdd.String(),
	}, "\n"), remainder
}

func (v *VLAN) getIFB() (string, error) {
	cmd := exec.Command("tc", "filter", "show", "dev", v.Device, "root")
	var outShow bytes.Buffer
	cmd.Stdout = &outShow
	if err := cmd.Run(); err != nil {
		return "", fmt.Errorf("Error at filter show: %s", err.Error())
	}
	data := outShow.String()
	re := regexp.MustCompile("Egress Redirect to device ifb[0-9]")
	match := re.FindString(data)
	if match == "" {
		return "", fmt.Errorf("Missing IFB device for %s in %s", v.Device, data)
	}
	ifbFields := strings.Fields(match)
	return ifbFields[len(ifbFields)-1], nil
}

// Add delay in the outbound direction
func (v *VLAN) ReplyToOut(msg *tgbotapi.Message, fields []string) (string, []string) {
	data, remainder := v.getParams(msg, fields)
	if remainder == nil {
		return data.err, remainder
	}
	return v.impair(v.Device, data, remainder)
}

// Add delay in the outbound direction
func (v *VLAN) ReplyToIn(msg *tgbotapi.Message, fields []string) (string, []string) {
	if v.IFB == "" {
		return "Current VLAN does not have IFB device assigned", nil
	}
	data, remainder := v.getParams(msg, fields)
	if remainder == nil {
		return data.err, remainder
	}
	return v.impair(v.IFB, data, remainder)
}
<|endoftext|>


<commit_before>package vkutil

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"strings"

	"github.com/fatih/color"
	vk "github.com/zhuharev/vk"
)

func (api *Api) WallGet(ownerId int, filter ...url.Values) ([]*Post, error) {
	vals := url.Values{}
	if len(filter) == 1 {
		vals = filter[0]
	}
	vals.Set("owner_id", fmt.Sprint(ownerId))
	resp, err := api.VkApi.Request(vk.METHOD_WALL_GET, vals)
	if err != nil {
		return nil, err
	}
	var r ResponsePosts
	err = json.Unmarshal(resp, &r)
	if err != nil {
		return nil, err
	}
	if r.Error.Msg != "" {
		return nil, errors.New(r.Error.Msg)
	}
	return r.Response.Items, nil
}

func (api *Api) UtilsWallPostCount(ownerId int) (count int, e error) {
	fcode := `return API.wall.get({owner_id:%d,count:1}).count;`
	bts, e := api.Execute(fcode)
	count, e = ParseIntResponse(bts)
	return
}

func (api *Api) GoUtilsWallGet(ownerId int) (resp chan []*Post, done chan struct{}, errs chan error) {

	var (
		//	postCount int
		posts []*Post
		e     error
	)

	resp = make(chan []*Post)
	done = make(chan struct{})
	errs = make(chan error)

	posts, e = api.WallGet(ownerId, url.Values{"count": {"100"}})
	if e != nil {
		errs <- e
		done <- struct{}{}
		return
	}

	resp <- posts

	// return if post count < requested
	if len(posts) != 200 {
		done <- struct{}{}
		return
	}

	return
}

func (api *Api) UtilsWallGetAll(ownerId int) (posts []*Post, e error) {

	var (
		offset = 0
	)

	for {
		tposts, e := api.utilsWallGetTwoThousandPost(ownerId, offset)
		if e != nil {
			return nil, e
		}

		posts = append(posts, tposts...)

		color.Green("LEN %d", len(tposts))

		if len(tposts) < 2000 {
			break
		}

		offset += 2000
	}

	return

}

func (a *Api) utilsWallGetTwoThousandPost(ownerId int, offsets ...int) ([]*Post, error) {

	var (
		offset = 0
	)

	if len(offsets) > 0 {
		offset = offsets[0]
	}

	fcode := `var cnt = 1;
	var offs = %d;
	var own = %d;
var a = API.wall.get({owner_id:own,count:100,offset:offs,filter:"all"}).items;
while(cnt<20) {
a = a+ API.wall.get({owner_id:own,count:100,offset:offs+(100*cnt),filter:"all"}).items;
cnt = cnt+1;
}
return a;`

	type Resp struct {
		Items []*Post `json:"response"`
		ResponseError
	}

	var r Resp

	bts, e := a.Execute(fmt.Sprintf(fcode, offset, ownerId))
	if e != nil {
		return nil, e
	}
	e = json.Unmarshal(bts, &r)
	if e != nil {
		color.Yellow("%s", bts)
		return nil, e
	}
	if r.Error.Code != 0 {
		return nil, fmt.Errorf(r.Error.Msg)
	}
	return r.Items, nil
}

func (a *Api) UtilsGetTwoThousandComments(ownerId int, postId int, offsets ...int) ([]*Comment, error) {

	var (
		offset = 0
	)

	if len(offsets) > 0 {
		offset = offsets[0]
	}

	fcode := `var cnt = 1;
	var offs = %d;
	var own = %d;
	var post_id = %d;
var a = API.wall.getComments({post_id:post_id,owner_id:own,count:100,offset:offs}).items;
while(cnt<25) {
a = a+ API.wall.getComments({post_id:post_id,owner_id:own,count:100,offset:offs+(100*cnt)}).items;
cnt = cnt+1;
}
return a;`

	var r ResponseCommentsList

	bts, e := a.Execute(fmt.Sprintf(fcode, offset, ownerId, postId))
	if e != nil {
		return nil, e
	}
	e = json.Unmarshal(bts, &r)
	if e != nil {
		color.Yellow("%s", bts)
		return nil, e
	}
	if r.Error.Code != 0 {
		return nil, fmt.Errorf(r.Error.Msg)
	}
	return r.Response, nil
}

func (api *Api) uploadAttachments(groupID int, imageURLs []string) ([]string, error) {
	var (
		resAttachments []string

		attachments []io.Reader
	)

	for _, imgURL := range imageURLs {
		resp, err := http.Get(imgURL)
		if err != nil {
			return nil, err
		}
		attachments = append(attachments, resp.Body)
	}

	for _, img := range attachments {
		photo, err := api.PhotosUploadWall(groupID, img)
		if err != nil {
			log.Println(err)
			continue
		}

		// close if http connection or opened file
		if closer, ok := img.(io.ReadCloser); ok {
			closer.Close()
		}

		resAttachments = append(resAttachments, fmt.Sprintf("photo%d_%d", photo.OwnerId, photo.Id))
	}

	return resAttachments, nil
}

type OptsWallPost struct {
	OwnerId   int
	Message   string
	FromGroup bool
	ImageURLs []string
}

func (api *Api) WallPost(opts OptsWallPost, filter ...url.Values) (int, error) {
	vals := url.Values{}
	if len(filter) == 1 {
		vals = filter[0]
	}
	vals.Set("owner_id", fmt.Sprint(opts.OwnerId))
	vals.Set("message", opts.Message)
	if opts.FromGroup {
		vals.Set("from_group", "1")
	}

	if len(opts.ImageURLs) != 0 {
		attachments, err := api.uploadAttachments(opts.OwnerId, opts.ImageURLs)
		if err != nil {
			return 0, err
		}
		vals.Set("attachments", strings.Join(attachments, ","))
	}

	resp, err := api.VkApi.Request(vk.METHOD_WALL_POST, vals)
	if err != nil {
		return 0, err
	}

	type rsp struct {
		Response struct {
			PostId int `json:"post_id"`
		} `json:"response"`
		ResponseError
	}

	var r = rsp{}

	err = json.Unmarshal(resp, &r)
	if err != nil {
		return 0, err
	}
	if r.Error.Msg != "" {
		return 0, errors.New(r.Error.Msg)
	}
	return r.Response.PostId, nil
}

func (api *Api) WallRepost(ot ObjectType, ownerId int, objectId int, params ...url.Values) (int, error) {
	var (
		param = url.Values{}
		sId   = fmt.Sprintf("%s%d_%d", ot, ownerId, objectId)
	)
	if params != nil {
		param = params[0]
	}
	param.Set("object", sId)
	bts, e := api.VkApi.Request(vk.METHOD_WALL_REPOST, param)
	if e != nil {
		return 0, e
	}
	type wallPostResponse struct {
		Success int `json:"success"`
		PostId  int `json:"post_id"`
	}
	var wpr wallPostResponse
	e = json.Unmarshal(bts, &wpr)
	if e != nil {
		return 0, e
	}
	return wpr.PostId, nil
}

func (api *Api) WallDelete(ownerId int, postId int, params ...url.Values) error {
	var (
		param = url.Values{}
	)
	if params != nil {
		param = params[0]
	}
	param.Set("owner_id", fmt.Sprint(ownerId))
	param.Set("post_id", fmt.Sprint(postId))
	bts, e := api.VkApi.Request(vk.METHOD_WALL_DELETE, param)
	if e != nil {
		return e
	}
	_, e = ParseIntResponse(bts)
	if e != nil {
		return e
	}
	return nil
}

// ResponseRepost represent repost response
type ResponseRepost struct {
	Items    []Post  `json:"items"`
	Profiles []User  `json:"profiles"`
	Groups   []Group `json:"groups"`
}

// WallGetRepostsIds return ids about wall reposts
func (api *Api) WallGetRepostsIds(ownerID int, postID int, params ...url.Values) (rr ResponseRepost, err error) {
	type RRWrap struct {
		Resp ResponseRepost `json:"response"`
	}
	var rrw RRWrap
	param := setToUrlValues("owner_id", ownerID, params...)
	param = setToUrlValues("post_id", postID, param)
	bts, err := api.VkApi.Request(vk.METHOD_WALL_GET_REPOSTS, param)
	if err != nil {
		return
	}
	err = json.Unmarshal(bts, &rrw)
	if err != nil {
		return
	}
	return rrw.Resp, nil
}

// TODO
// WallGetReposts return info about wall reposts
func (api *Api) WallGetReposts(ownerID int, postID int, params ...url.Values) (rr ResponseRepost, err error) {
	type RRWrap struct {
		Resp ResponseRepost `json:"response"`
	}
	var rrw RRWrap
	param := setToUrlValues("owner_id", ownerID, params...)
	param = setToUrlValues("post_id", postID, param)
	bts, err := api.VkApi.Request(vk.METHOD_WALL_GET_REPOSTS, param)
	if err != nil {
		return
	}
	err = json.Unmarshal(bts, &rrw)
	if err != nil {
		return
	}
	return rrw.Resp, nil
}

func (api *Api) WallGetByID(postID string) (p Post, err error) {
	type Resp struct {
		Response []Post `json:"response"`
	}
	var r Resp

	param := setToUrlValues("posts", postID)
	bts, err := api.VkApi.Request(vk.METHOD_WALL_GET_BY_ID, param)
	if err != nil {
		return
	}
	err = json.Unmarshal(bts, &r)
	if err != nil {
		return
	}
	if len(r.Response) != 1 {
		return Post{}, fmt.Errorf("not found")
	}
	return r.Response[0], nil
}
<commit_msg>fix bug<commit_after>package vkutil

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"strings"

	"github.com/fatih/color"
	vk "github.com/zhuharev/vk"
)

func (api *Api) WallGet(ownerId int, filter ...url.Values) ([]*Post, error) {
	vals := url.Values{}
	if len(filter) == 1 {
		vals = filter[0]
	}
	vals.Set("owner_id", fmt.Sprint(ownerId))
	resp, err := api.VkApi.Request(vk.METHOD_WALL_GET, vals)
	if err != nil {
		return nil, err
	}
	var r ResponsePosts
	err = json.Unmarshal(resp, &r)
	if err != nil {
		return nil, err
	}
	if r.Error.Msg != "" {
		return nil, errors.New(r.Error.Msg)
	}
	return r.Response.Items, nil
}

func (api *Api) UtilsWallPostCount(ownerId int) (count int, e error) {
	fcode := `return API.wall.get({owner_id:%d,count:1}).count;`
	bts, e := api.Execute(fcode)
	count, e = ParseIntResponse(bts)
	return
}

func (api *Api) GoUtilsWallGet(ownerId int) (resp chan []*Post, done chan struct{}, errs chan error) {

	var (
		//	postCount int
		posts []*Post
		e     error
	)

	resp = make(chan []*Post)
	done = make(chan struct{})
	errs = make(chan error)

	posts, e = api.WallGet(ownerId, url.Values{"count": {"100"}})
	if e != nil {
		errs <- e
		done <- struct{}{}
		return
	}

	resp <- posts

	// return if post count < requested
	if len(posts) != 200 {
		done <- struct{}{}
		return
	}

	return
}

func (api *Api) UtilsWallGetAll(ownerId int) (posts []*Post, e error) {

	var (
		offset = 0
	)

	for {
		tposts, e := api.utilsWallGetTwoThousandPost(ownerId, offset)
		if e != nil {
			return nil, e
		}

		posts = append(posts, tposts...)

		color.Green("LEN %d", len(tposts))

		if len(tposts) < 2000 {
			break
		}

		offset += 2000
	}

	return

}

func (a *Api) utilsWallGetTwoThousandPost(ownerId int, offsets ...int) ([]*Post, error) {

	var (
		offset = 0
	)

	if len(offsets) > 0 {
		offset = offsets[0]
	}

	fcode := `var cnt = 1;
	var offs = %d;
	var own = %d;
var a = API.wall.get({owner_id:own,count:100,offset:offs,filter:"all"}).items;
while(cnt<20) {
a = a+ API.wall.get({owner_id:own,count:100,offset:offs+(100*cnt),filter:"all"}).items;
cnt = cnt+1;
}
return a;`

	type Resp struct {
		Items []*Post `json:"response"`
		ResponseError
	}

	var r Resp

	bts, e := a.Execute(fmt.Sprintf(fcode, offset, ownerId))
	if e != nil {
		return nil, e
	}
	e = json.Unmarshal(bts, &r)
	if e != nil {
		color.Yellow("%s", bts)
		return nil, e
	}
	if r.Error.Code != 0 {
		return nil, fmt.Errorf(r.Error.Msg)
	}
	return r.Items, nil
}

func (a *Api) UtilsGetTwoThousandComments(ownerId int, postId int, offsets ...int) ([]*Comment, error) {

	var (
		offset = 0
	)

	if len(offsets) > 0 {
		offset = offsets[0]
	}

	fcode := `var cnt = 1;
	var offs = %d;
	var own = %d;
	var post_id = %d;
var a = API.wall.getComments({post_id:post_id,owner_id:own,count:100,offset:offs}).items;
while(cnt<25) {
a = a+ API.wall.getComments({post_id:post_id,owner_id:own,count:100,offset:offs+(100*cnt)}).items;
cnt = cnt+1;
}
return a;`

	var r ResponseCommentsList

	bts, e := a.Execute(fmt.Sprintf(fcode, offset, ownerId, postId))
	if e != nil {
		return nil, e
	}
	e = json.Unmarshal(bts, &r)
	if e != nil {
		color.Yellow("%s", bts)
		return nil, e
	}
	if r.Error.Code != 0 {
		return nil, fmt.Errorf(r.Error.Msg)
	}
	return r.Response, nil
}

func (api *Api) uploadAttachments(groupID int, imageURLs []string) ([]string, error) {

	if groupID < 0 {
		groupID = -groupID
	}

	var (
		resAttachments []string

		attachments []io.Reader
	)

	for _, imgURL := range imageURLs {
		resp, err := http.Get(imgURL)
		if err != nil {
			return nil, err
		}
		attachments = append(attachments, resp.Body)
	}

	for _, img := range attachments {
		photo, err := api.PhotosUploadWall(groupID, img)
		if err != nil {
			log.Println(err)
			continue
		}

		// close if http connection or opened file
		if closer, ok := img.(io.ReadCloser); ok {
			closer.Close()
		}

		resAttachments = append(resAttachments, fmt.Sprintf("photo%d_%d", photo.OwnerId, photo.Id))
	}

	return resAttachments, nil
}

type OptsWallPost struct {
	OwnerId   int
	Message   string
	FromGroup bool
	ImageURLs []string
}

func (api *Api) WallPost(opts OptsWallPost, filter ...url.Values) (int, error) {
	vals := url.Values{}
	if len(filter) == 1 {
		vals = filter[0]
	}
	vals.Set("owner_id", fmt.Sprint(opts.OwnerId))
	vals.Set("message", opts.Message)
	if opts.FromGroup {
		vals.Set("from_group", "1")
	}

	if len(opts.ImageURLs) != 0 {
		attachments, err := api.uploadAttachments(opts.OwnerId, opts.ImageURLs)
		if err != nil {
			return 0, err
		}
		vals.Set("attachments", strings.Join(attachments, ","))
	}

	resp, err := api.VkApi.Request(vk.METHOD_WALL_POST, vals)
	if err != nil {
		return 0, err
	}

	type rsp struct {
		Response struct {
			PostId int `json:"post_id"`
		} `json:"response"`
		ResponseError
	}

	var r = rsp{}

	err = json.Unmarshal(resp, &r)
	if err != nil {
		return 0, err
	}
	if r.Error.Msg != "" {
		return 0, errors.New(r.Error.Msg)
	}
	return r.Response.PostId, nil
}

func (api *Api) WallRepost(ot ObjectType, ownerId int, objectId int, params ...url.Values) (int, error) {
	var (
		param = url.Values{}
		sId   = fmt.Sprintf("%s%d_%d", ot, ownerId, objectId)
	)
	if params != nil {
		param = params[0]
	}
	param.Set("object", sId)
	bts, e := api.VkApi.Request(vk.METHOD_WALL_REPOST, param)
	if e != nil {
		return 0, e
	}
	type wallPostResponse struct {
		Success int `json:"success"`
		PostId  int `json:"post_id"`
	}
	var wpr wallPostResponse
	e = json.Unmarshal(bts, &wpr)
	if e != nil {
		return 0, e
	}
	return wpr.PostId, nil
}

func (api *Api) WallDelete(ownerId int, postId int, params ...url.Values) error {
	var (
		param = url.Values{}
	)
	if params != nil {
		param = params[0]
	}
	param.Set("owner_id", fmt.Sprint(ownerId))
	param.Set("post_id", fmt.Sprint(postId))
	bts, e := api.VkApi.Request(vk.METHOD_WALL_DELETE, param)
	if e != nil {
		return e
	}
	_, e = ParseIntResponse(bts)
	if e != nil {
		return e
	}
	return nil
}

// ResponseRepost represent repost response
type ResponseRepost struct {
	Items    []Post  `json:"items"`
	Profiles []User  `json:"profiles"`
	Groups   []Group `json:"groups"`
}

// WallGetRepostsIds return ids about wall reposts
func (api *Api) WallGetRepostsIds(ownerID int, postID int, params ...url.Values) (rr ResponseRepost, err error) {
	type RRWrap struct {
		Resp ResponseRepost `json:"response"`
	}
	var rrw RRWrap
	param := setToUrlValues("owner_id", ownerID, params...)
	param = setToUrlValues("post_id", postID, param)
	bts, err := api.VkApi.Request(vk.METHOD_WALL_GET_REPOSTS, param)
	if err != nil {
		return
	}
	err = json.Unmarshal(bts, &rrw)
	if err != nil {
		return
	}
	return rrw.Resp, nil
}

// TODO
// WallGetReposts return info about wall reposts
func (api *Api) WallGetReposts(ownerID int, postID int, params ...url.Values) (rr ResponseRepost, err error) {
	type RRWrap struct {
		Resp ResponseRepost `json:"response"`
	}
	var rrw RRWrap
	param := setToUrlValues("owner_id", ownerID, params...)
	param = setToUrlValues("post_id", postID, param)
	bts, err := api.VkApi.Request(vk.METHOD_WALL_GET_REPOSTS, param)
	if err != nil {
		return
	}
	err = json.Unmarshal(bts, &rrw)
	if err != nil {
		return
	}
	return rrw.Resp, nil
}

func (api *Api) WallGetByID(postID string) (p Post, err error) {
	type Resp struct {
		Response []Post `json:"response"`
	}
	var r Resp

	param := setToUrlValues("posts", postID)
	bts, err := api.VkApi.Request(vk.METHOD_WALL_GET_BY_ID, param)
	if err != nil {
		return
	}
	err = json.Unmarshal(bts, &r)
	if err != nil {
		return
	}
	if len(r.Response) != 1 {
		return Post{}, fmt.Errorf("not found")
	}
	return r.Response[0], nil
}
<|endoftext|>


<commit_before>// Package implements creation of XLSX simple spreadsheet files

package xlsx

import (
	"archive/zip"
	"bufio"
	"bytes"
	"fmt"
	"html"
	"io"
	"os"
	"strconv"
	"time"
)

type CellType uint

// Basic spreadsheet cell types
const (
	CellTypeNumber CellType = iota
	CellTypeString
	CellTypeDatetime
	CellTypeInlineString
)

// XLSX Spreadsheet Cell
type Cell struct {
	Type  CellType
	Value string
}

// XLSX Spreadsheet Row
type Row struct {
	Cells []Cell
}

// XLSX Spreadsheet Column
type Column struct {
	Name  string
	Width uint64
}

// XLSX Workbook Document Properties
type DocumentInfo struct {
	CreatedBy  string
	ModifiedBy string
	CreatedAt  time.Time
	ModifiedAt time.Time
}

// XLSX Spreadsheet
type Sheet struct {
	Title           string
	columns         []Column
	rows            []Row
	sharedStringMap map[string]int
	sharedStrings   []string
	DocumentInfo    DocumentInfo
}

// Create a sheet with no dimensions
func NewSheet() Sheet {
	c := make([]Column, 0)
	r := make([]Row, 0)
	ssm := make(map[string]int)
	sst := make([]string, 0)

	s := Sheet{
		Title:           "Data",
		columns:         c,
		rows:            r,
		sharedStringMap: ssm,
		sharedStrings:   sst,
	}

	return s
}

// Create a sheet with dimensions derived from the given columns
func NewSheetWithColumns(c []Column) Sheet {
	r := make([]Row, 0)
	ssm := make(map[string]int)
	sst := make([]string, 0)

	s := Sheet{
		Title:           "Data",
		columns:         c,
		rows:            r,
		sharedStringMap: ssm,
		sharedStrings:   sst,
	}

	s.DocumentInfo.CreatedBy = "xlsx.go"
	s.DocumentInfo.CreatedAt = time.Now()

	s.DocumentInfo.ModifiedBy = s.DocumentInfo.CreatedBy
	s.DocumentInfo.ModifiedAt = s.DocumentInfo.CreatedAt

	return s
}

// Create a new row with a length caculated by the sheets known column count
func (s *Sheet) NewRow() Row {
	c := make([]Cell, len(s.columns))
	r := Row{
		Cells: c,
	}
	return r
}

// Append a row to the sheet
func (s *Sheet) AppendRow(r Row) error {
	if len(r.Cells) != len(s.columns) {
		return fmt.Errorf("the given row has %d cells and %d were expected", len(r.Cells), len(s.columns))
	}

	cells := make([]Cell, len(s.columns))

	for n, c := range r.Cells {
		cells[n].Type = c.Type
		cells[n].Value = c.Value

		if cells[n].Type == CellTypeString {
			// calculate string reference
			cells[n].Value = html.EscapeString(cells[n].Value)
			i, exists := s.sharedStringMap[cells[n].Value]
			if !exists {
				i = len(s.sharedStrings)
				s.sharedStringMap[cells[n].Value] = i
				s.sharedStrings = append(s.sharedStrings, cells[n].Value)
			}
			cells[n].Value = strconv.Itoa(i)
		}
	}

	row := s.NewRow()
	row.Cells = cells

	s.rows = append(s.rows, row)

	return nil
}

// Get the Shared Strings in the order they were added to the map
func (s *Sheet) SharedStrings() []string {
	return s.sharedStrings
}

// Given zero-based array indices output the Excel cell reference. For
// example (0,0) => "A1"; (2,2) => "C3"; (26,45) => "AA46"
func CellIndex(x, y uint64) (string, uint64) {
	return colName(x), (y + 1)
}

// From a zero-based column number return the Excel column name.
// For example: 0 => "A"; 2 => "C"; 26 => "AA"
func colName(n uint64) string {
	var s string
	n += 1

	for n > 0 {
		n -= 1
		s = string(65+(n%26)) + s
		n /= 26
	}

	return s
}

// Convert time to the OLE Automation format.
func OADate(d time.Time) string {
	epoch := time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
	nsPerDay := 24 * time.Hour

	v := -1 * float64(epoch.Sub(d)) / float64(nsPerDay)

	// TODO: deal with dates before epoch
	// e.g. http://stackoverflow.com/questions/15549823/oadate-to-milliseconds-timestamp-in-javascript/15550284#15550284

	if d.Hour() == 0 && d.Minute() == 0 && d.Second() == 0 {
		return fmt.Sprintf("%d", int64(v))
	} else {
		return fmt.Sprintf("%f", v)
	}
}

// Create filename and save the XLSX file
func (s *Sheet) SaveToFile(filename string) error {
	outputfile, err := os.Create(filename)
	if err != nil {
		return err
	}
	w := bufio.NewWriter(outputfile)
	err = s.SaveToWriter(w)
	defer w.Flush()
	return err
}

// Save the XLSX file to the given writer
func (s *Sheet) SaveToWriter(w io.Writer) error {

	ww := NewWorkbookWriter(w)

	sw, err := ww.NewSheetWriter(s)
	if err != nil {
		return err
	}

	err = sw.WriteRows(s.rows)
	if err != nil {
		return err
	}

	err = ww.Close()

	return err
}

// Handles the writing of an XLSX workbook
type WorkbookWriter struct {
	zipWriter     *zip.Writer
	sheetWriter   *SheetWriter
	headerWritten bool
	closed        bool
}

// NewWorkbookWriter creates a new WorkbookWriter, which SheetWriters will
// operate on. It must be closed when all Sheets have been written.
func NewWorkbookWriter(w io.Writer) *WorkbookWriter {
	return &WorkbookWriter{zip.NewWriter(w), nil, false, false}
}

// Write the header files of the workbook
func (ww *WorkbookWriter) WriteHeader() error {
	if ww.headerWritten {
		panic("Workbook header already written")
	}

	z := ww.zipWriter

	f, err := z.Create("[Content_Types].xml")
	err = TemplateContentTypes.Execute(f, nil)
	if err != nil {
		return err
	}

	f, err = z.Create("docProps/app.xml")
	err = TemplateApp.Execute(f, ww.sheetNames)
	err = TemplateApp.Execute(os.Stdout, ww.sheetNames)
	if err != nil {
		return err
	}

	f, err = z.Create("docProps/core.xml")
	err = TemplateCore.Execute(f, ww.DocumentInfo)
	if err != nil {
		return err
	}

	f, err = z.Create("_rels/.rels")
	err = TemplateRelationships.Execute(f, nil)
	if err != nil {
		return err
	}

	f, err = z.Create("xl/workbook.xml")
	err = TemplateWorkbook.Execute(f, ww.sheetNames)
	if err != nil {
		return err
	}

	f, err = z.Create("xl/_rels/workbook.xml.rels")
	err = TemplateWorkbookRelationships.Execute(f, ww.sheetNames)
	if err != nil {
		return err
	}

	f, err = z.Create("xl/styles.xml")
	err = TemplateStyles.Execute(f, nil)
	if err != nil {
		return err
	}

	//f, err = z.Create("xl/sharedStrings.xml")
	//err = TemplateStringLookups.Execute(f, s[0].SharedStrings())
	//if err != nil {
	//	return err
	//}

	return nil
}

// Handles the writing of an XLSX workbook
type WorkbookWriter struct {
	zipWriter     *zip.Writer
	sheetWriter   *SheetWriter
	headerWritten bool
	sheetNames    []string
	SharedStrings []string
	DocumentInfo  *DocumentInfo
}

// Creates a new WorkbookWriter
func NewWorkbookWriter(w io.Writer) *WorkbookWriter {
	return &WorkbookWriter{zip.NewWriter(w), nil, false, []string{}, nil, nil}
}

// Closes the WorkbookWriter
func (ww *WorkbookWriter) Close() error {
	if ww.closed {
		panic("WorkbookWriter already closed")
	}

	if ww.sheetWriter != nil {
		err := ww.sheetWriter.Close()
		if err != nil {
			return err
		}
	}

	ww.closed = true

	return ww.zipWriter.Close()
}

// NewSheetWriter creates a new SheetWriter in this workbook using the given sheet.
// It returns a SheetWriter to which rows can be written.
// All rows must be written to the SheetWriter before the next call to NewSheetWriter,
// as this will automatically close the previous SheetWriter.
func (ww *WorkbookWriter) NewSheetWriter(s *Sheet) (*SheetWriter, error) {
	if ww.closed {
		panic("Can not write to closed WorkbookWriter")
	}

	if !ww.headerWritten {
		err := ww.WriteHeader(s)
		if err != nil {
			return nil, err
		}
	}

	f, err := ww.zipWriter.Create("xl/worksheets/" + "sheet1" + ".xml")
	sw := &SheetWriter{f, err, 0, 0, false}

	ww.DocumentInfo = &s.DocumentInfo

	if ww.sheetWriter != nil {
		err = ww.sheetWriter.Close()
		if err != nil {
			return nil, err
		}
	}

	ww.sheetWriter = sw
	err = sw.WriteHeader(s)

	ww.sheetNames = append(ww.sheetNames, s.Title)

	ww.sheetNames = append(ww.sheetNames, s.Title)

	return sw, err
}

// Handles the writing of a sheet
type SheetWriter struct {
	f            io.Writer
	err          error
	currentIndex uint64
	maxNCols     uint64
	closed       bool
}

// Write the given rows to this SheetWriter
func (sw *SheetWriter) WriteRows(rows []Row) error {
	if sw.closed {
		panic("Can not write to closed SheetWriter")
	}

	var err error

	for i, r := range rows {
		rb := &bytes.Buffer{}

		if sw.maxNCols < uint64(len(r.Cells)) {
			sw.maxNCols = uint64(len(r.Cells))
		}

		for j, c := range r.Cells {

			cellX, cellY := CellIndex(uint64(j), uint64(i)+sw.currentIndex)

			if c.Type == CellTypeDatetime {
				d, err := time.Parse(time.RFC3339, c.Value)
				if err == nil {
					c.Value = OADate(d)
				}
			} else if c.Type == CellTypeInlineString {
				c.Value = html.EscapeString(c.Value)
			}

			var cellString string

			switch c.Type {
			case CellTypeString:
				cellString = `<c r="%s%d" t="s" s="1"><v>%s</v></c>`
			case CellTypeInlineString:
				cellString = `<c r="%s%d" t="inlineStr"><is><t>%s</t></is></c>`
			case CellTypeNumber:
				cellString = `<c r="%s%d" t="n" s="1"><v>%s</v></c>`
			case CellTypeDatetime:
				cellString = `<c r="%s%d" s="2"><v>%s</v></c>`
			}

			io.WriteString(rb, fmt.Sprintf(cellString, cellX, cellY, c.Value))

			if err != nil {
				return err
			}
		}

		rowString := fmt.Sprintf(`<row r="%d">%s</row>`, uint64(i)+sw.currentIndex+1, rb.String())

		_, err = io.WriteString(sw.f, rowString)
		if err != nil {
			return err
		}

	}

	sw.currentIndex += uint64(len(rows))

	return nil
}

// Closes the SheetWriter
func (sw *SheetWriter) Close() error {
	if sw.closed {
		panic("SheetWriter already closed")
	}

	cellEndX, cellEndY := CellIndex(sw.maxNCols-1, sw.currentIndex-1)
	sheetEnd := fmt.Sprintf(`<dimension ref="A1:%s%d"/>`, cellEndX, cellEndY)
	sheetEnd += `</sheetData></worksheet>`
	_, err := io.WriteString(sw.f, sheetEnd)

	sw.closed = true

	return err
}

// Writes the header of a sheet
func (sw *SheetWriter) WriteHeader(s *Sheet) error {
	if sw.closed {
		panic("Can not write to closed SheetWriter")
	}

	sheet := struct {
		Cols []Column
	}{
		Cols: s.columns,
	}

	return TemplateSheetStart.Execute(sw.f, sheet)
}
<commit_msg>Fix merge conflict<commit_after>// Package implements creation of XLSX simple spreadsheet files

package xlsx

import (
	"archive/zip"
	"bufio"
	"bytes"
	"fmt"
	"html"
	"io"
	"os"
	"strconv"
	"time"
)

type CellType uint

// Basic spreadsheet cell types
const (
	CellTypeNumber CellType = iota
	CellTypeString
	CellTypeDatetime
	CellTypeInlineString
)

// XLSX Spreadsheet Cell
type Cell struct {
	Type  CellType
	Value string
}

// XLSX Spreadsheet Row
type Row struct {
	Cells []Cell
}

// XLSX Spreadsheet Column
type Column struct {
	Name  string
	Width uint64
}

// XLSX Workbook Document Properties
type DocumentInfo struct {
	CreatedBy  string
	ModifiedBy string
	CreatedAt  time.Time
	ModifiedAt time.Time
}

// XLSX Spreadsheet
type Sheet struct {
	Title           string
	columns         []Column
	rows            []Row
	sharedStringMap map[string]int
	sharedStrings   []string
	DocumentInfo    DocumentInfo
}

// Create a sheet with no dimensions
func NewSheet() Sheet {
	c := make([]Column, 0)
	r := make([]Row, 0)
	ssm := make(map[string]int)
	sst := make([]string, 0)

	s := Sheet{
		Title:           "Data",
		columns:         c,
		rows:            r,
		sharedStringMap: ssm,
		sharedStrings:   sst,
	}

	return s
}

// Create a sheet with dimensions derived from the given columns
func NewSheetWithColumns(c []Column) Sheet {
	r := make([]Row, 0)
	ssm := make(map[string]int)
	sst := make([]string, 0)

	s := Sheet{
		Title:           "Data",
		columns:         c,
		rows:            r,
		sharedStringMap: ssm,
		sharedStrings:   sst,
	}

	s.DocumentInfo.CreatedBy = "xlsx.go"
	s.DocumentInfo.CreatedAt = time.Now()

	s.DocumentInfo.ModifiedBy = s.DocumentInfo.CreatedBy
	s.DocumentInfo.ModifiedAt = s.DocumentInfo.CreatedAt

	return s
}

// Create a new row with a length caculated by the sheets known column count
func (s *Sheet) NewRow() Row {
	c := make([]Cell, len(s.columns))
	r := Row{
		Cells: c,
	}
	return r
}

// Append a row to the sheet
func (s *Sheet) AppendRow(r Row) error {
	if len(r.Cells) != len(s.columns) {
		return fmt.Errorf("the given row has %d cells and %d were expected", len(r.Cells), len(s.columns))
	}

	cells := make([]Cell, len(s.columns))

	for n, c := range r.Cells {
		cells[n].Type = c.Type
		cells[n].Value = c.Value

		if cells[n].Type == CellTypeString {
			// calculate string reference
			cells[n].Value = html.EscapeString(cells[n].Value)
			i, exists := s.sharedStringMap[cells[n].Value]
			if !exists {
				i = len(s.sharedStrings)
				s.sharedStringMap[cells[n].Value] = i
				s.sharedStrings = append(s.sharedStrings, cells[n].Value)
			}
			cells[n].Value = strconv.Itoa(i)
		}
	}

	row := s.NewRow()
	row.Cells = cells

	s.rows = append(s.rows, row)

	return nil
}

// Get the Shared Strings in the order they were added to the map
func (s *Sheet) SharedStrings() []string {
	return s.sharedStrings
}

// Given zero-based array indices output the Excel cell reference. For
// example (0,0) => "A1"; (2,2) => "C3"; (26,45) => "AA46"
func CellIndex(x, y uint64) (string, uint64) {
	return colName(x), (y + 1)
}

// From a zero-based column number return the Excel column name.
// For example: 0 => "A"; 2 => "C"; 26 => "AA"
func colName(n uint64) string {
	var s string
	n += 1

	for n > 0 {
		n -= 1
		s = string(65+(n%26)) + s
		n /= 26
	}

	return s
}

// Convert time to the OLE Automation format.
func OADate(d time.Time) string {
	epoch := time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
	nsPerDay := 24 * time.Hour

	v := -1 * float64(epoch.Sub(d)) / float64(nsPerDay)

	// TODO: deal with dates before epoch
	// e.g. http://stackoverflow.com/questions/15549823/oadate-to-milliseconds-timestamp-in-javascript/15550284#15550284

	if d.Hour() == 0 && d.Minute() == 0 && d.Second() == 0 {
		return fmt.Sprintf("%d", int64(v))
	} else {
		return fmt.Sprintf("%f", v)
	}
}

// Create filename and save the XLSX file
func (s *Sheet) SaveToFile(filename string) error {
	outputfile, err := os.Create(filename)
	if err != nil {
		return err
	}
	w := bufio.NewWriter(outputfile)
	err = s.SaveToWriter(w)
	defer w.Flush()
	return err
}

// Save the XLSX file to the given writer
func (s *Sheet) SaveToWriter(w io.Writer) error {

	ww := NewWorkbookWriter(w)

	sw, err := ww.NewSheetWriter(s)
	if err != nil {
		return err
	}

	err = sw.WriteRows(s.rows)
	if err != nil {
		return err
	}

	err = ww.Close()

	return err
}

// Handles the writing of an XLSX workbook
type WorkbookWriter struct {
	zipWriter     *zip.Writer
	sheetWriter   *SheetWriter
	headerWritten bool
	closed        bool
}

// NewWorkbookWriter creates a new WorkbookWriter, which SheetWriters will
// operate on. It must be closed when all Sheets have been written.
func NewWorkbookWriter(w io.Writer) *WorkbookWriter {
	return &WorkbookWriter{zip.NewWriter(w), nil, false, false}
}

// Write the header files of the workbook
func (ww *WorkbookWriter) WriteHeader() error {
	if ww.headerWritten {
		panic("Workbook header already written")
	}

	z := ww.zipWriter

	f, err := z.Create("[Content_Types].xml")
	err = TemplateContentTypes.Execute(f, nil)
	if err != nil {
		return err
	}

	f, err = z.Create("docProps/app.xml")
	err = TemplateApp.Execute(f, ww.sheetNames)
	if err != nil {
		return err
	}

	f, err = z.Create("docProps/core.xml")
	err = TemplateCore.Execute(f, ww.DocumentInfo)
	if err != nil {
		return err
	}

	f, err = z.Create("_rels/.rels")
	err = TemplateRelationships.Execute(f, nil)
	if err != nil {
		return err
	}

	f, err = z.Create("xl/workbook.xml")
	err = TemplateWorkbook.Execute(f, ww.sheetNames)
	if err != nil {
		return err
	}

	f, err = z.Create("xl/_rels/workbook.xml.rels")
	err = TemplateWorkbookRelationships.Execute(f, ww.sheetNames)
	if err != nil {
		return err
	}

	f, err = z.Create("xl/styles.xml")
	err = TemplateStyles.Execute(f, nil)
	if err != nil {
		return err
	}

	//f, err = z.Create("xl/sharedStrings.xml")
	//err = TemplateStringLookups.Execute(f, s[0].SharedStrings())
	//if err != nil {
	//	return err
	//}

	return nil
}

// Handles the writing of an XLSX workbook
type WorkbookWriter struct {
	zipWriter     *zip.Writer
	sheetWriter   *SheetWriter
	headerWritten bool
	sheetNames    []string
	SharedStrings []string
	DocumentInfo  *DocumentInfo
}

// Creates a new WorkbookWriter
func NewWorkbookWriter(w io.Writer) *WorkbookWriter {
	return &WorkbookWriter{zip.NewWriter(w), nil, false, []string{}, nil, nil}
}

// Closes the WorkbookWriter
func (ww *WorkbookWriter) Close() error {
	if ww.closed {
		panic("WorkbookWriter already closed")
	}

	if ww.sheetWriter != nil {
		err := ww.sheetWriter.Close()
		if err != nil {
			return err
		}
	}

	ww.closed = true

	return ww.zipWriter.Close()
}

// NewSheetWriter creates a new SheetWriter in this workbook using the given sheet.
// It returns a SheetWriter to which rows can be written.
// All rows must be written to the SheetWriter before the next call to NewSheetWriter,
// as this will automatically close the previous SheetWriter.
func (ww *WorkbookWriter) NewSheetWriter(s *Sheet) (*SheetWriter, error) {
	if ww.closed {
		panic("Can not write to closed WorkbookWriter")
	}

	if !ww.headerWritten {
		err := ww.WriteHeader(s)
		if err != nil {
			return nil, err
		}
	}

	f, err := ww.zipWriter.Create("xl/worksheets/" + "sheet1" + ".xml")
	sw := &SheetWriter{f, err, 0, 0, false}

	ww.DocumentInfo = &s.DocumentInfo

	if ww.sheetWriter != nil {
		err = ww.sheetWriter.Close()
		if err != nil {
			return nil, err
		}
	}

	ww.sheetWriter = sw
	err = sw.WriteHeader(s)

	ww.sheetNames = append(ww.sheetNames, s.Title)

	ww.sheetNames = append(ww.sheetNames, s.Title)

	return sw, err
}

// Handles the writing of a sheet
type SheetWriter struct {
	f            io.Writer
	err          error
	currentIndex uint64
	maxNCols     uint64
	closed       bool
}

// Write the given rows to this SheetWriter
func (sw *SheetWriter) WriteRows(rows []Row) error {
	if sw.closed {
		panic("Can not write to closed SheetWriter")
	}

	var err error

	for i, r := range rows {
		rb := &bytes.Buffer{}

		if sw.maxNCols < uint64(len(r.Cells)) {
			sw.maxNCols = uint64(len(r.Cells))
		}

		for j, c := range r.Cells {

			cellX, cellY := CellIndex(uint64(j), uint64(i)+sw.currentIndex)

			if c.Type == CellTypeDatetime {
				d, err := time.Parse(time.RFC3339, c.Value)
				if err == nil {
					c.Value = OADate(d)
				}
			} else if c.Type == CellTypeInlineString {
				c.Value = html.EscapeString(c.Value)
			}

			var cellString string

			switch c.Type {
			case CellTypeString:
				cellString = `<c r="%s%d" t="s" s="1"><v>%s</v></c>`
			case CellTypeInlineString:
				cellString = `<c r="%s%d" t="inlineStr"><is><t>%s</t></is></c>`
			case CellTypeNumber:
				cellString = `<c r="%s%d" t="n" s="1"><v>%s</v></c>`
			case CellTypeDatetime:
				cellString = `<c r="%s%d" s="2"><v>%s</v></c>`
			}

			io.WriteString(rb, fmt.Sprintf(cellString, cellX, cellY, c.Value))

			if err != nil {
				return err
			}
		}

		rowString := fmt.Sprintf(`<row r="%d">%s</row>`, uint64(i)+sw.currentIndex+1, rb.String())

		_, err = io.WriteString(sw.f, rowString)
		if err != nil {
			return err
		}

	}

	sw.currentIndex += uint64(len(rows))

	return nil
}

// Closes the SheetWriter
func (sw *SheetWriter) Close() error {
	if sw.closed {
		panic("SheetWriter already closed")
	}

	cellEndX, cellEndY := CellIndex(sw.maxNCols-1, sw.currentIndex-1)
	sheetEnd := fmt.Sprintf(`<dimension ref="A1:%s%d"/>`, cellEndX, cellEndY)
	sheetEnd += `</sheetData></worksheet>`
	_, err := io.WriteString(sw.f, sheetEnd)

	sw.closed = true

	return err
}

// Writes the header of a sheet
func (sw *SheetWriter) WriteHeader(s *Sheet) error {
	if sw.closed {
		panic("Can not write to closed SheetWriter")
	}

	sheet := struct {
		Cols []Column
	}{
		Cols: s.columns,
	}

	return TemplateSheetStart.Execute(sw.f, sheet)
}
<|endoftext|>


<commit_before>/*
Package zgok provides a simple library to create a single binary with asset files in Go (Golang).

Example:

	package main

	import (
		"net/http"
		"github.com/srtkkou/zgok"
		"os"
	)

	func main() {
		zfs, err := zgok.RestoreFileSystem(os.Args[0])
		if err != nil {
			panic(err)
		}
		assetServer := zfs.FileServer("web/public")
		http.Handle("/assets/", http.StripPrefix("/assets/", assetServer))
		http.ListenAndServe(":8080", nil)
	}
*/
package zgok

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

const (
	APP   = "zgok" // Application name.
	MAJOR = 0      // Major version.
	MINOR = 0      // Minor version.
	REV   = 1      // Revision.
)

// Get version string.
func Version() string {
	return fmt.Sprintf("%s-%d.%d.%d", APP, MAJOR, MINOR, REV)
}

// File system interface.
// Implements [net/http.FileSystem]
type FileSystem interface {
	AddFile(file File)
	GetFile(path string) (File, error)
	ReadFile(path string) ([]byte, error)
	ReadFileString(path string) (string, error)
	Paths() []string
	SubFileSystem(rootPath string) (FileSystem, error)
	Signature() Signature
	SetSignature(signature Signature)
	String() string
	Open(name string) (http.File, error)     // Implements [net/http.FileSystem.Open]
	FileServer(basePath string) http.Handler // Get a static file server.
}

// Zgok file system.
type zgokFileSystem struct {
	signature Signature       // Zgok signature.
	rootPath  string          // Root path of the file system.
	fileMap   map[string]File // Map of files.
}

// Create a new file system.
func NewFileSystem() FileSystem {
	return &zgokFileSystem{
		signature: nil,
		rootPath:  APP,
		fileMap:   make(map[string]File),
	}
}

// Restore file system.
func RestoreFileSystem(path string) (FileSystem, error) {
	// Get bytes of exe file.
	exeBytes, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	// Restore signature.
	sigOffset := len(exeBytes) - SIGNATURE_BYTE_SIZE
	sigBytes := exeBytes[sigOffset:]
	signature, err := RestoreSignature(sigBytes)
	if err != nil {
		return nil, err
	}
	// Unzip zip section.
	zipOffset := signature.ExeSize()
	zipBytes := exeBytes[zipOffset:sigOffset]
	unzipper := NewUnzipper(&zipBytes)
	zfs, err := unzipper.Unzip()
	if err != nil {
		return nil, err
	}
	// Set signature.
	zfs.SetSignature(signature)
	return zfs, nil
}

// Add file to file system.
func (zfs *zgokFileSystem) AddFile(file File) {
	key := filepath.ToSlash(file.Path())
	zfs.fileMap[key] = file
}

// Get file from file system.
func (zfs *zgokFileSystem) GetFile(path string) (File, error) {
	key := filepath.ToSlash(filepath.Join(zfs.rootPath, path))
	file, exists := zfs.fileMap[key]
	if !exists {
		return nil, errors.New("File doesn't exist.")
	}
	return file, nil
}

// Get the content of file in bytes from file system.
func (zfs *zgokFileSystem) ReadFile(path string) ([]byte, error) {
	file, err := zfs.GetFile(path)
	if err != nil {
		return []byte{}, nil
	}
	return file.Bytes(), nil
}

// Get the content of file in string from file system.
func (zfs *zgokFileSystem) ReadFileString(path string) (string, error) {
	bytes, err := zfs.ReadFile(path)
	if err != nil {
		return "", nil
	}
	str := string(bytes)
	return str, nil
}

// Get all the paths stored in the file system.
func (zfs *zgokFileSystem) Paths() []string {
	paths := []string{}
	prefix := zfs.rootPath + "/"
	for key := range zfs.fileMap {
		if strings.HasPrefix(key, prefix) {
			relPath := strings.TrimPrefix(key, prefix)
			paths = append(paths, relPath)
		}
	}
	sort.Strings(paths)
	return paths
}

// Get a sub file system.
func (zfs *zgokFileSystem) SubFileSystem(rootPath string) (FileSystem, error) {
	// Check root path.
	if strings.Contains(rootPath, "..") {
		return nil, errors.New("No double dots [..] are allowed in root path.")
	}
	// Initialize sub file system.
	newRootPath := filepath.ToSlash(filepath.Join(zfs.rootPath, rootPath))
	subFs := &zgokFileSystem{
		signature: zfs.signature,
		rootPath:  newRootPath,
		fileMap:   make(map[string]File),
	}
	// Add all the sets matching the new root path.
	for key, value := range zfs.fileMap {
		if key[0:len(newRootPath)] == newRootPath {
			subFs.fileMap[key] = value
		}
	}
	return subFs, nil
}

// Get signature.
func (zfs *zgokFileSystem) Signature() Signature {
	return zfs.signature
}

// Set signature.
func (zfs *zgokFileSystem) SetSignature(signature Signature) {
	zfs.signature = signature
}

// Get string.
func (zfs *zgokFileSystem) String() string {
	return zfs.Signature().String()
}

// Open the file.
// Implements [net/http.FileSystem.Open]
func (zfs *zgokFileSystem) Open(name string) (http.File, error) {
	path := strings.TrimLeft(name, "/")
	file, err := zfs.GetFile(path)
	if err != nil {
		// Return an abstract directory.
		dir := &zgokFile{
			fileInfo: zgokFileInfo{
				name: filepath.Base(path),
				mode: os.ModeDir | os.ModePerm,
			},
		}
		return dir, nil
	}
	// Set a new file reader
	file.SetNewReader()
	return file, nil
}

// File interface.
type File interface {
	SetPath(path string)                          // Set file path.
	Path() string                                 // Get file path.
	SetFileInfo(fileInfo os.FileInfo)             // Set file info.
	FileInfo() os.FileInfo                        // Get file info.
	SetBytes(content []byte)                      // Set content bytes.
	Bytes() []byte                                // Get content bytes.
	SetNewReader()                                // Set a new reader.
	Close() error                                 // Implements [net/http.File.Close]
	Read(p []byte) (int, error)                   // Implements [net/http.File.Read]
	Readdir(count int) ([]os.FileInfo, error)     // Implements [net/http.File.Readdir]
	Seek(offset int64, whence int) (int64, error) // Implements [net/http.File.Seek]
	Stat() (os.FileInfo, error)                   // Implements [net/http.File.Stat]
}

// Zgok file.
type zgokFile struct {
	path     string        // Path of the file.
	fileInfo os.FileInfo   // File info.
	content  []byte        // Content of the file.
	reader   *bytes.Reader // File reader.
}

// Create a new zgok file.
func NewZgokFile() File {
	return &zgokFile{}
}

// Set file path.
func (zf *zgokFile) SetPath(path string) {
	zf.path = strings.Replace(path, `\`, "/", -1)
}

// Get file path.
func (zf *zgokFile) Path() string {
	return zf.path
}

// Set file info.
func (zf *zgokFile) SetFileInfo(fileInfo os.FileInfo) {
	zf.fileInfo = fileInfo
}

// Get file info.
func (zf *zgokFile) FileInfo() os.FileInfo {
	return zf.fileInfo
}

// Set content bytes.
func (zf *zgokFile) SetBytes(content []byte) {
	zf.content = content
}

// Get content bytes.
func (zf *zgokFile) Bytes() []byte {
	return zf.content
}

// Set a new reader.
func (zf *zgokFile) SetNewReader() {
	reader := bytes.NewReader(zf.content)
	zf.reader = reader
}

// Close file.
// Implements [net/http.File.Close]
func (zf *zgokFile) Close() error {
	return nil
}

// Read file.
// Implements [net/http.File.Read]
func (zf *zgokFile) Read(p []byte) (int, error) {
	return zf.reader.Read(p)
}

// Read directories.
// Implements [net/http.File.Readdir]
func (zf *zgokFile) Readdir(count int) ([]os.FileInfo, error) {
	return nil, errors.New("Readdir is not allowed.")
}

// Seek file.
// Implements [net/http.File.Seek]
func (zf *zgokFile) Seek(offset int64, whence int) (int64, error) {
	return zf.reader.Seek(offset, whence)
}

// Get file info.
// Implements [net/http.File.Stat]
func (zf *zgokFile) Stat() (os.FileInfo, error) {
	return zf.fileInfo, nil
}

// Zgok file info.
// Implements [os.FileInfo]
type zgokFileInfo struct {
	name    string      // File name.
	size    int64       // Size of the file.
	mode    os.FileMode // File mode.
	modTime time.Time   // Modified time of the file.
}

// Get name.
// Implements [os.FileInfo.Name]
func (i zgokFileInfo) Name() string {
	return i.name
}

// Get size.
// Implements [os.FileInfo.Size]
func (i zgokFileInfo) Size() int64 {
	return i.size
}

// Get mode.
// Implements [os.FileInfo.Mode]
func (i zgokFileInfo) Mode() os.FileMode {
	return i.mode
}

// Get modified time.
// Implements [os.FileInfo.ModTime]
func (i zgokFileInfo) ModTime() time.Time {
	return i.modTime
}

// Check if it is a directory.
// Implements [os.FileInfo.IsDir]
func (i zgokFileInfo) IsDir() bool {
	return i.mode.IsDir()
}

// Get sys information. (Only returns nil.)
// Implements [os.FileInfo.Sys]
func (i zgokFileInfo) Sys() interface{} {
	return nil
}
<commit_msg>[BugFix] Fixed bug in zgok that zfs.ReadFile/ReadFileString was returning nil even on error.<commit_after>/*
Package zgok provides a simple library to create a single binary with asset files in Go (Golang).

Example:

	package main

	import (
		"net/http"
		"github.com/srtkkou/zgok"
		"os"
	)

	func main() {
		zfs, err := zgok.RestoreFileSystem(os.Args[0])
		if err != nil {
			panic(err)
		}
		assetServer := zfs.FileServer("web/public")
		http.Handle("/assets/", http.StripPrefix("/assets/", assetServer))
		http.ListenAndServe(":8080", nil)
	}
*/
package zgok

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

const (
	APP   = "zgok" // Application name.
	MAJOR = 0      // Major version.
	MINOR = 0      // Minor version.
	REV   = 1      // Revision.
)

// Get version string.
func Version() string {
	return fmt.Sprintf("%s-%d.%d.%d", APP, MAJOR, MINOR, REV)
}

// File system interface.
// Implements [net/http.FileSystem]
type FileSystem interface {
	AddFile(file File)
	GetFile(path string) (File, error)
	ReadFile(path string) ([]byte, error)
	ReadFileString(path string) (string, error)
	Paths() []string
	SubFileSystem(rootPath string) (FileSystem, error)
	Signature() Signature
	SetSignature(signature Signature)
	String() string
	Open(name string) (http.File, error)     // Implements [net/http.FileSystem.Open]
	FileServer(basePath string) http.Handler // Get a static file server.
}

// Zgok file system.
type zgokFileSystem struct {
	signature Signature       // Zgok signature.
	rootPath  string          // Root path of the file system.
	fileMap   map[string]File // Map of files.
}

// Create a new file system.
func NewFileSystem() FileSystem {
	return &zgokFileSystem{
		signature: nil,
		rootPath:  APP,
		fileMap:   make(map[string]File),
	}
}

// Restore file system.
func RestoreFileSystem(path string) (FileSystem, error) {
	// Get bytes of exe file.
	exeBytes, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	// Restore signature.
	sigOffset := len(exeBytes) - SIGNATURE_BYTE_SIZE
	sigBytes := exeBytes[sigOffset:]
	signature, err := RestoreSignature(sigBytes)
	if err != nil {
		return nil, err
	}
	// Unzip zip section.
	zipOffset := signature.ExeSize()
	zipBytes := exeBytes[zipOffset:sigOffset]
	unzipper := NewUnzipper(&zipBytes)
	zfs, err := unzipper.Unzip()
	if err != nil {
		return nil, err
	}
	// Set signature.
	zfs.SetSignature(signature)
	return zfs, nil
}

// Add file to file system.
func (zfs *zgokFileSystem) AddFile(file File) {
	key := filepath.ToSlash(file.Path())
	zfs.fileMap[key] = file
}

// Get file from file system.
func (zfs *zgokFileSystem) GetFile(path string) (File, error) {
	key := filepath.ToSlash(filepath.Join(zfs.rootPath, path))
	file, exists := zfs.fileMap[key]
	if !exists {
		return nil, errors.New("File doesn't exist.")
	}
	return file, nil
}

// Get the content of file in bytes from file system.
func (zfs *zgokFileSystem) ReadFile(path string) ([]byte, error) {
	file, err := zfs.GetFile(path)
	if err != nil {
		return []byte{}, err
	}
	return file.Bytes(), nil
}

// Get the content of file in string from file system.
func (zfs *zgokFileSystem) ReadFileString(path string) (string, error) {
	bytes, err := zfs.ReadFile(path)
	if err != nil {
		return "", err
	}
	str := string(bytes)
	return str, nil
}

// Get all the paths stored in the file system.
func (zfs *zgokFileSystem) Paths() []string {
	paths := []string{}
	prefix := zfs.rootPath + "/"
	for key := range zfs.fileMap {
		if strings.HasPrefix(key, prefix) {
			relPath := strings.TrimPrefix(key, prefix)
			paths = append(paths, relPath)
		}
	}
	sort.Strings(paths)
	return paths
}

// Get a sub file system.
func (zfs *zgokFileSystem) SubFileSystem(rootPath string) (FileSystem, error) {
	// Check root path.
	if strings.Contains(rootPath, "..") {
		return nil, errors.New("No double dots [..] are allowed in root path.")
	}
	// Initialize sub file system.
	newRootPath := filepath.ToSlash(filepath.Join(zfs.rootPath, rootPath))
	subFs := &zgokFileSystem{
		signature: zfs.signature,
		rootPath:  newRootPath,
		fileMap:   make(map[string]File),
	}
	// Add all the sets matching the new root path.
	for key, value := range zfs.fileMap {
		if key[0:len(newRootPath)] == newRootPath {
			subFs.fileMap[key] = value
		}
	}
	return subFs, nil
}

// Get signature.
func (zfs *zgokFileSystem) Signature() Signature {
	return zfs.signature
}

// Set signature.
func (zfs *zgokFileSystem) SetSignature(signature Signature) {
	zfs.signature = signature
}

// Get string.
func (zfs *zgokFileSystem) String() string {
	return zfs.Signature().String()
}

// Open the file.
// Implements [net/http.FileSystem.Open]
func (zfs *zgokFileSystem) Open(name string) (http.File, error) {
	path := strings.TrimLeft(name, "/")
	file, err := zfs.GetFile(path)
	if err != nil {
		// Return an abstract directory.
		dir := &zgokFile{
			fileInfo: zgokFileInfo{
				name: filepath.Base(path),
				mode: os.ModeDir | os.ModePerm,
			},
		}
		return dir, nil
	}
	// Set a new file reader
	file.SetNewReader()
	return file, nil
}

// File interface.
type File interface {
	SetPath(path string)                          // Set file path.
	Path() string                                 // Get file path.
	SetFileInfo(fileInfo os.FileInfo)             // Set file info.
	FileInfo() os.FileInfo                        // Get file info.
	SetBytes(content []byte)                      // Set content bytes.
	Bytes() []byte                                // Get content bytes.
	SetNewReader()                                // Set a new reader.
	Close() error                                 // Implements [net/http.File.Close]
	Read(p []byte) (int, error)                   // Implements [net/http.File.Read]
	Readdir(count int) ([]os.FileInfo, error)     // Implements [net/http.File.Readdir]
	Seek(offset int64, whence int) (int64, error) // Implements [net/http.File.Seek]
	Stat() (os.FileInfo, error)                   // Implements [net/http.File.Stat]
}

// Zgok file.
type zgokFile struct {
	path     string        // Path of the file.
	fileInfo os.FileInfo   // File info.
	content  []byte        // Content of the file.
	reader   *bytes.Reader // File reader.
}

// Create a new zgok file.
func NewZgokFile() File {
	return &zgokFile{}
}

// Set file path.
func (zf *zgokFile) SetPath(path string) {
	zf.path = strings.Replace(path, `\`, "/", -1)
}

// Get file path.
func (zf *zgokFile) Path() string {
	return zf.path
}

// Set file info.
func (zf *zgokFile) SetFileInfo(fileInfo os.FileInfo) {
	zf.fileInfo = fileInfo
}

// Get file info.
func (zf *zgokFile) FileInfo() os.FileInfo {
	return zf.fileInfo
}

// Set content bytes.
func (zf *zgokFile) SetBytes(content []byte) {
	zf.content = content
}

// Get content bytes.
func (zf *zgokFile) Bytes() []byte {
	return zf.content
}

// Set a new reader.
func (zf *zgokFile) SetNewReader() {
	reader := bytes.NewReader(zf.content)
	zf.reader = reader
}

// Close file.
// Implements [net/http.File.Close]
func (zf *zgokFile) Close() error {
	return nil
}

// Read file.
// Implements [net/http.File.Read]
func (zf *zgokFile) Read(p []byte) (int, error) {
	return zf.reader.Read(p)
}

// Read directories.
// Implements [net/http.File.Readdir]
func (zf *zgokFile) Readdir(count int) ([]os.FileInfo, error) {
	return nil, errors.New("Readdir is not allowed.")
}

// Seek file.
// Implements [net/http.File.Seek]
func (zf *zgokFile) Seek(offset int64, whence int) (int64, error) {
	return zf.reader.Seek(offset, whence)
}

// Get file info.
// Implements [net/http.File.Stat]
func (zf *zgokFile) Stat() (os.FileInfo, error) {
	return zf.fileInfo, nil
}

// Zgok file info.
// Implements [os.FileInfo]
type zgokFileInfo struct {
	name    string      // File name.
	size    int64       // Size of the file.
	mode    os.FileMode // File mode.
	modTime time.Time   // Modified time of the file.
}

// Get name.
// Implements [os.FileInfo.Name]
func (i zgokFileInfo) Name() string {
	return i.name
}

// Get size.
// Implements [os.FileInfo.Size]
func (i zgokFileInfo) Size() int64 {
	return i.size
}

// Get mode.
// Implements [os.FileInfo.Mode]
func (i zgokFileInfo) Mode() os.FileMode {
	return i.mode
}

// Get modified time.
// Implements [os.FileInfo.ModTime]
func (i zgokFileInfo) ModTime() time.Time {
	return i.modTime
}

// Check if it is a directory.
// Implements [os.FileInfo.IsDir]
func (i zgokFileInfo) IsDir() bool {
	return i.mode.IsDir()
}

// Get sys information. (Only returns nil.)
// Implements [os.FileInfo.Sys]
func (i zgokFileInfo) Sys() interface{} {
	return nil
}
<|endoftext|>


<commit_before>// Package ztex manages ZTEX USB FPGA modules.
package ztex

import (
	"time"

	"github.com/google/gousb"
)

var (
	// VendorID is the ZTEX USB vendor ID (VID).
	VendorID = gousb.ID(0x221A)

	// ProductID is the standard ZTEX USB product ID (PID)
	ProductID = gousb.ID(0x0100)

	// ControlTimeout is the timeout for control transfers.
	ControlTimeout = 1000 * time.Millisecond
)

// Device represents a ZTEX USB FPGA module.
type Device struct {
	ProductName  [128]byte
	SerialNumber [64]byte
	FXVersion    byte    // 2 = FX2, 3 = FX3.
	BoardSeries  byte    // 1 = Series 1, 2 = Series 2.  (255 = Unknown)
	BoardNumber  byte    // E.g., 2.14b -> 2.  (255 = Unknown)
	BoardVariant [2]byte // E.g., 2.14b -> [2]byte{'b', 0}.

	// Endpoint for fast FPGA configuration.  (0 = Unsupported)
	FastConfigurationEndpoint byte
	// Interface for fast FPGA configuration.  (0 = Unsupported)
	FastConfigurationInterface byte
	/// Default interface major version number.  (0 = Not Available)
	DefaultInterfaceMajorVersion byte
	/// Default interface minor version number.  (0 = Not Available)
	DefaultInterfaceMinorVersion byte
	/// Output endpoint of default interface.  (255 = Not Available)
	DefaultInterfaceOutputEndpoint byte
	/// Input endpoint of default interface.  (255 = Not Available)
	DefaultInterfaceInputEndpoint byte

	// Device is the USB device handle for the module.
	Device *gousb.Device
}

func OpenDevice(ctx *gousb.Context) (*Device, error) {
	d := &Device{}

	if dev, err := ctx.OpenDeviceWithVIDPID(VendorID, ProductID); err != nil {
		return nil, err
	} else {
		dev.ControlTimeout = ControlTimeout
		d.Device = dev
	}

	buf := make([]byte, 128)

	// VR 0x33: High speed FPGA configuration support: Read Endpoint settings
	if nbt, err := d.Device.Control(0xc0, 0x33, 0, 0, buf); err != nil {
		return nil, err
	} else if nbt == 2 {
		d.FastConfigurationEndpoint = buf[0]
		d.FastConfigurationInterface = buf[1]
	}

	// VR 0x3b: MAC EEPROM support: Read from MAC EEPROM
	if nbt, err := d.Device.Control(0xc0, 0x3b, 0, 0, buf); err != nil {
		return nil, err
	} else if nbt == 128 {
		d.FXVersion = buf[3]
		d.BoardSeries = buf[4]
		d.BoardNumber = buf[5]
		d.BoardVariant[0] = buf[6]
		d.BoardVariant[1] = buf[7]
	} else {
		d.BoardSeries = 255
		d.BoardNumber = 255
	}

	// VR 0x64: Default firmware interface: Return Default Interface information
	if nbt, err := d.Device.Control(0xc0, 0x64, 0, 0, buf); err != nil {
		return nil, err
	} else if nbt == 4 {
		d.DefaultInterfaceMajorVersion = buf[0]
		d.DefaultInterfaceMinorVersion = buf[3]
		d.DefaultInterfaceOutputEndpoint = buf[1] & 127
		d.DefaultInterfaceInputEndpoint = buf[2] | 128
	} else if nbt == 3 {
		d.DefaultInterfaceMajorVersion = buf[0]
		d.DefaultInterfaceOutputEndpoint = buf[1] & 127
		d.DefaultInterfaceInputEndpoint = buf[2] | 128
	} else {
		d.DefaultInterfaceOutputEndpoint = 255
		d.DefaultInterfaceInputEndpoint = 255
	}

	return d, nil
}

// Close releases all external resources associated with the device.
func (d *Device) Close() error { return d.Device.Close() }
<commit_msg>Extract more information from USB device handles.<commit_after>// Package ztex manages ZTEX USB FPGA modules.
package ztex

import (
	"time"

	"github.com/google/gousb"
)

var (
	// VendorID is the ZTEX USB vendor ID (VID).
	VendorID = gousb.ID(0x221A)

	// ProductID is the standard ZTEX USB product ID (PID)
	ProductID = gousb.ID(0x0100)

	// ControlTimeout is the timeout for control transfers.
	ControlTimeout = 1000 * time.Millisecond
)

// Device represents a ZTEX USB FPGA module.
type Device struct {
	ProductName  [128]byte
	SerialNumber [64]byte
	FXVersion    byte    // 2 = FX2, 3 = FX3.
	BoardSeries  byte    // 1 = Series 1, 2 = Series 2.  (255 = Unknown)
	BoardNumber  byte    // E.g., 2.14b -> 2.  (255 = Unknown)
	BoardVariant [2]byte // E.g., 2.14b -> [2]byte{'b', 0}.

	// Endpoint for fast FPGA configuration.  (0 = Unsupported)
	FastConfigurationEndpoint byte
	// Interface for fast FPGA configuration.  (0 = Unsupported)
	FastConfigurationInterface byte
	/// Default interface major version number.  (0 = Not Available)
	DefaultInterfaceMajorVersion byte
	/// Default interface minor version number.  (0 = Not Available)
	DefaultInterfaceMinorVersion byte
	/// Output endpoint of default interface.  (255 = Not Available)
	DefaultInterfaceOutputEndpoint byte
	/// Input endpoint of default interface.  (255 = Not Available)
	DefaultInterfaceInputEndpoint byte

	// Device is the USB device handle for the module.
	Device *gousb.Device
}

func OpenDevice(ctx *gousb.Context) (*Device, error) {
	d := &Device{}

	if dev, err := ctx.OpenDeviceWithVIDPID(VendorID, ProductID); err != nil {
		return nil, err
	} else {
		dev.ControlTimeout = ControlTimeout
		d.Device = dev
	}

	buf := make([]byte, 128)

	// VR 0x33: High speed FPGA configuration support: Read Endpoint settings
	if nbt, err := d.Device.Control(0xc0, 0x33, 0, 0, buf); err != nil {
		return nil, err
	} else if nbt == 2 {
		d.FastConfigurationEndpoint = buf[0]
		d.FastConfigurationInterface = buf[1]
	}

	// VR 0x3b: MAC EEPROM support: Read from MAC EEPROM
	if nbt, err := d.Device.Control(0xc0, 0x3b, 0, 0, buf); err != nil {
		return nil, err
	} else if nbt == 128 {
		d.FXVersion = buf[3]
		d.BoardSeries = buf[4]
		d.BoardNumber = buf[5]
		d.BoardVariant[0] = buf[6]
		d.BoardVariant[1] = buf[7]
	} else {
		d.BoardSeries = 255
		d.BoardNumber = 255
	}

	// VR 0x64: Default firmware interface: Return Default Interface information
	if nbt, err := d.Device.Control(0xc0, 0x64, 0, 0, buf); err != nil {
		return nil, err
	} else if nbt == 4 {
		d.DefaultInterfaceMajorVersion = buf[0]
		d.DefaultInterfaceMinorVersion = buf[3]
		d.DefaultInterfaceOutputEndpoint = buf[1] & 127
		d.DefaultInterfaceInputEndpoint = buf[2] | 128
	} else if nbt == 3 {
		d.DefaultInterfaceMajorVersion = buf[0]
		d.DefaultInterfaceOutputEndpoint = buf[1] & 127
		d.DefaultInterfaceInputEndpoint = buf[2] | 128
	} else {
		d.DefaultInterfaceOutputEndpoint = 255
		d.DefaultInterfaceInputEndpoint = 255
	}

	if prd, err := d.Device.Product(); err != nil {
		return nil, err
	} else {
		for i, b := range []byte(prd) {
			if i >= 128 {
				break
			}
			d.ProductName[i] = b
		}
	}

	if srl, prd, err := d.Device.SerialNumber(); err != nil {
		return nil, err
	} else {
		for i, b := range []byte(srl) {
			if i >= 128 {
				break
			}
			d.ProductName[i] = b
		}
	}

	return d, nil
}

// Close releases all external resources associated with the device.
func (d *Device) Close() error { return d.Device.Close() }
<|endoftext|>