<commit_before>/*
Copyright 2014 Lee Boynton

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package midi

import (
	"github.com/boynton/ell"
	"github.com/rakyll/portmidi"
	"os"
	"sync"
	"time"
)

type Extension struct {
}

func (*Extension) Init() error {
	midiPath := os.Getenv("GOPATH") + "/src/github.com/boynton/midi-ell"
	ell.AddEllDirectory(midiPath)
	midiInit()
	return ell.Load("midi")
}

func (*Extension) Cleanup() {
	midiClose(nil)
}

func (*Extension) String() string {
	return "midi"
}

var inputKey = ell.Intern("input:")
var outputKey = ell.Intern("output:")
var bufsizeKey = ell.Intern("bufsize:")

func midiInit() error {
	ell.DefineFunctionKeyArgs("midi-open", midiOpen, ell.NullType,
		[]*ell.Object{ell.StringType, ell.StringType, ell.NumberType},
		[]*ell.Object{ell.EmptyString, ell.EmptyString, ell.Number(1024)},
		[]*ell.Object{inputKey, outputKey, bufsizeKey})
	ell.DefineFunction("midi-write", midiWrite, ell.NullType, ell.NumberType, ell.NumberType, ell.NumberType)
	ell.DefineFunction("midi-close", midiClose, ell.NullType)
	ell.DefineFunction("midi-listen", midiListen, ell.ChannelType)
	return nil
}

var midiOpened = false
var midiInDevice string
var midiOutDevice string
var midiBufsize int64
var midiBaseTime float64

var midiOut *portmidi.Stream
var midiIn *portmidi.Stream
var midiChannel chan portmidi.Event
var midiMutex = &sync.Mutex{}

func findMidiInputDevice(name string) portmidi.DeviceId {
	devcount := portmidi.CountDevices()
	for i := 0; i < devcount; i++ {
		id := portmidi.DeviceId(i)
		info := portmidi.GetDeviceInfo(id)
		if info.IsInputAvailable {
			if info.Name == name {
				return id
			}
		}
	}
	return portmidi.DeviceId(-1)
}

func findMidiOutputDevice(name string) (portmidi.DeviceId, string) {
	devcount := portmidi.CountDevices()
	for i := 0; i < devcount; i++ {
		id := portmidi.DeviceId(i)
		info := portmidi.GetDeviceInfo(id)
		if info.IsOutputAvailable {
			if info.Name == name {
				return id, info.Name
			}
		}
	}
	id := portmidi.GetDefaultOutputDeviceId()
	info := portmidi.GetDeviceInfo(id)
	return id, info.Name
}

func midiOpen(argv []*ell.Object) (*ell.Object, error) {
	//	defaultInput := "USB Oxygen 8 v2"
	//	defaultOutput := "IAC Driver Bus 1"
	latency := int64(10)
	if !midiOpened {
		err := portmidi.Initialize()
		if err != nil {
			return nil, err
		}
		midiOpened = true
		midiInDevice = ell.StringValue(argv[0])
		midiOutDevice = ell.StringValue(argv[1])
		midiBufsize = ell.Int64Value(argv[2])

		outdev, outname := findMidiOutputDevice(midiOutDevice)
		out, err := portmidi.NewOutputStream(outdev, midiBufsize, latency)
		if err != nil {
			return nil, err
		}
		midiOut = out
		midiOutDevice = outname
		if midiInDevice != "" {
			indev := findMidiInputDevice(midiInDevice)
			if indev >= 0 {
				in, err := portmidi.NewInputStream(indev, midiBufsize)
				if err != nil {
					return nil, err
				}
				midiIn = in
			}
		}
		midiBaseTime = ell.Now()

	}
	result := ell.MakeStruct(4)
	if midiInDevice != "" {
		ell.Put(result, inputKey, ell.String(midiInDevice))
	}
	if midiOutDevice != "" {
		ell.Put(result, outputKey, ell.String(midiOutDevice))
	}
	ell.Put(result, bufsizeKey, ell.Number(float64(midiBufsize)))
	return result, nil
}

func midiAllNotesOff() {
	midiOut.WriteShort(0xB0, 0x7B, 0x00)
}

func midiClose(argv []*ell.Object) (*ell.Object, error) {
	midiMutex.Lock()
	if midiOut != nil {
		midiAllNotesOff()
		midiOut.Close()
		midiOut = nil
	}
	midiMutex.Unlock()
	ell.Sleep(0.5)
	return ell.Null, nil
}

// (midi-write 144 60 80) -> middle C note on
// (midi-write 128 60 0) -> middle C note off
func midiWrite(argv []*ell.Object) (*ell.Object, error) {
	status := ell.Int64Value(argv[0])
	data1 := ell.Int64Value(argv[1])
	data2 := ell.Int64Value(argv[2])
	midiMutex.Lock()
	var err error
	if midiOut != nil {
		err = midiOut.WriteShort(status, data1, data2)
	}
	midiMutex.Unlock()
	return ell.Null, err
}

func midiListen(argv []*ell.Object) (*ell.Object, error) {
	ch := ell.Null
	midiMutex.Lock()
	if midiIn != nil {
		ch = ell.Channel(int(midiBufsize), "midi")
		go func(s *portmidi.Stream, ch *ell.Object) {
			for {
				time.Sleep(10 * time.Millisecond)
				events, err := s.Read(1024)
				if err != nil {
					continue
				}
				channel := ell.ChannelValue(ch)
				if channel != nil {
					for _, ev := range events {
						ts := (float64(ev.Timestamp) / 1000) + midiBaseTime
						st := ev.Status
						d1 := ev.Data1
						d2 := ev.Data2
						channel <- ell.List(ell.Number(ts), ell.Number(float64(st)), ell.Number(float64(d1)), ell.Number(float64(d2)))
					}
				}
			}
		}(midiIn, ch)
	}
	midiMutex.Unlock()
	return ch, nil
}
<commit_msg>updated to track portmidi changes<commit_after>/*
Copyright 2014 Lee Boynton

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package midi

import (
	"github.com/boynton/ell"
	"github.com/rakyll/portmidi"
	"os"
	"sync"
	"time"
)

type Extension struct {
}

func (*Extension) Init() error {
	midiPath := os.Getenv("GOPATH") + "/src/github.com/boynton/midi-ell"
	ell.AddEllDirectory(midiPath)
	midiInit()
	return ell.Load("midi")
}

func (*Extension) Cleanup() {
	midiClose(nil)
}

func (*Extension) String() string {
	return "midi"
}

var inputKey = ell.Intern("input:")
var outputKey = ell.Intern("output:")
var bufsizeKey = ell.Intern("bufsize:")

func midiInit() error {
	ell.DefineFunctionKeyArgs("midi-open", midiOpen, ell.NullType,
		[]*ell.Object{ell.StringType, ell.StringType, ell.NumberType},
		[]*ell.Object{ell.EmptyString, ell.EmptyString, ell.Number(1024)},
		[]*ell.Object{inputKey, outputKey, bufsizeKey})
	ell.DefineFunction("midi-write", midiWrite, ell.NullType, ell.NumberType, ell.NumberType, ell.NumberType)
	ell.DefineFunction("midi-close", midiClose, ell.NullType)
	ell.DefineFunction("midi-listen", midiListen, ell.ChannelType)
	return nil
}

var midiOpened = false
var midiInDevice string
var midiOutDevice string
var midiBufsize int64
var midiBaseTime float64

var midiOut *portmidi.Stream
var midiIn *portmidi.Stream
var midiChannel chan portmidi.Event
var midiMutex = &sync.Mutex{}

func findMidiInputDevice(name string) portmidi.DeviceID {
	devcount := portmidi.CountDevices()
	for i := 0; i < devcount; i++ {
		id := portmidi.DeviceID(i)
		info := portmidi.Info(id)
		if info.IsInputAvailable {
			if info.Name == name {
				return id
			}
		}
	}
	return portmidi.DeviceID(-1)
}

func findMidiOutputDevice(name string) (portmidi.DeviceID, string) {
	devcount := portmidi.CountDevices()
	for i := 0; i < devcount; i++ {
		id := portmidi.DeviceID(i)
		info := portmidi.Info(id)
		if info.IsOutputAvailable {
			if info.Name == name {
				return id, info.Name
			}
		}
	}
	id := portmidi.DefaultOutputDeviceID()
	info := portmidi.Info(id)
	return id, info.Name
}

func midiOpen(argv []*ell.Object) (*ell.Object, error) {
	//	defaultInput := "USB Oxygen 8 v2"
	//	defaultOutput := "IAC Driver Bus 1"
	latency := int64(10)
	if !midiOpened {
		err := portmidi.Initialize()
		if err != nil {
			return nil, err
		}
		midiOpened = true
		midiInDevice = ell.StringValue(argv[0])
		midiOutDevice = ell.StringValue(argv[1])
		midiBufsize = ell.Int64Value(argv[2])

		outdev, outname := findMidiOutputDevice(midiOutDevice)
		out, err := portmidi.NewOutputStream(outdev, midiBufsize, latency)
		if err != nil {
			return nil, err
		}
		midiOut = out
		midiOutDevice = outname
		if midiInDevice != "" {
			indev := findMidiInputDevice(midiInDevice)
			if indev >= 0 {
				in, err := portmidi.NewInputStream(indev, midiBufsize)
				if err != nil {
					return nil, err
				}
				midiIn = in
			}
		}
		midiBaseTime = ell.Now()

	}
	result := ell.MakeStruct(4)
	if midiInDevice != "" {
		ell.Put(result, inputKey, ell.String(midiInDevice))
	}
	if midiOutDevice != "" {
		ell.Put(result, outputKey, ell.String(midiOutDevice))
	}
	ell.Put(result, bufsizeKey, ell.Number(float64(midiBufsize)))
	return result, nil
}

func midiAllNotesOff() {
	midiOut.WriteShort(0xB0, 0x7B, 0x00)
}

func midiClose(argv []*ell.Object) (*ell.Object, error) {
	midiMutex.Lock()
	if midiOut != nil {
		midiAllNotesOff()
		midiOut.Close()
		midiOut = nil
	}
	midiMutex.Unlock()
	ell.Sleep(0.5)
	return ell.Null, nil
}

// (midi-write 144 60 80) -> middle C note on
// (midi-write 128 60 0) -> middle C note off
func midiWrite(argv []*ell.Object) (*ell.Object, error) {
	status := ell.Int64Value(argv[0])
	data1 := ell.Int64Value(argv[1])
	data2 := ell.Int64Value(argv[2])
	midiMutex.Lock()
	var err error
	if midiOut != nil {
		err = midiOut.WriteShort(status, data1, data2)
	}
	midiMutex.Unlock()
	return ell.Null, err
}

func midiListen(argv []*ell.Object) (*ell.Object, error) {
	ch := ell.Null
	midiMutex.Lock()
	if midiIn != nil {
		ch = ell.Channel(int(midiBufsize), "midi")
		go func(s *portmidi.Stream, ch *ell.Object) {
			for {
				time.Sleep(10 * time.Millisecond)
				events, err := s.Read(1024)
				if err != nil {
					continue
				}
				channel := ell.ChannelValue(ch)
				if channel != nil {
					for _, ev := range events {
						ts := (float64(ev.Timestamp) / 1000) + midiBaseTime
						st := ev.Status
						d1 := ev.Data1
						d2 := ev.Data2
						channel <- ell.List(ell.Number(ts), ell.Number(float64(st)), ell.Number(float64(d1)), ell.Number(float64(d2)))
					}
				}
			}
		}(midiIn, ch)
	}
	midiMutex.Unlock()
	return ch, nil
}
<|endoftext|>


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

package main

import (
	"fmt"

	"github.com/danryan/hal"
)

var fatherHandler = hear(`your father was (a|an) (.*)`, "", "", func(res *hal.Response) error {
	return res.Send(fmt.Sprintf("%s, well your father was a hampster, and your mother spelled of elderberries!", res.UserName()))
})

var whoBackHandler = hear(`who has your back(\?)?`, "", "", func(res *hal.Response) error {
	return res.Send(fmt.Sprintf("%s has my back!", res.UserName()))
})
<commit_msg>Fix typo s/spelled/smelled/g<commit_after>// Copyright 2014 Chadev. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"

	"github.com/danryan/hal"
)

var fatherHandler = hear(`your father was (a|an) (.*)`, "", "", func(res *hal.Response) error {
	return res.Send(fmt.Sprintf("%s, well your father was a hampster, and your mother smelled of elderberries!", res.UserName()))
})

var whoBackHandler = hear(`who has your back(\?)?`, "", "", func(res *hal.Response) error {
	return res.Send(fmt.Sprintf("%s has my back!", res.UserName()))
})
<|endoftext|>


<commit_before>// 5 october 2014
package main

import (
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"sort"
	"io"
	"archive/zip"
	"compress/flate"
	"bytes"

	"github.com/andlabs/ohv/mshi"
	"golang.org/x/net/html"
	"github.com/andlabs/ohv/internal/ui"
)

const msxhelpURLScheme = "ms-xhelp"

type MSHI struct {
	dir			string
	containers	[][]*mshi.ContainerPath
	assets		[][]*mshi.AssetData
	topics		map[string]*MSHITopic		// maps asset IDs; these are case-insensitive, so are stored here lowercase
	books		[]Topic
	orphans		[]Topic
}

func (m *MSHI) Name() string {
	return "All Microsoft Help Viewer-format documentation"
}

func (m *MSHI) Books() []Topic {
	return m.books
}

func (m *MSHI) Orphans() []Topic {
	return m.orphans
}

func (m *MSHI) Lookup(url *url.URL) Topic {
	id := url.Query().Get("Id")
	id = strings.ToLower(id)
	return m.topics[id]
}

func OpenMSHI(dir string) (*MSHI, error) {
	m := new(MSHI)
	m.dir = dir

	// first read all indices
	err := m.readAllIndices()
	if err != nil {
		return nil, err
	}

	// then produce a map that goes from ID -> topic
	m.collectTopics()

	// now figure out the hierarchy, books, and orphans
	m.buildHierarchy()

	// now sort all children
	m.sortAllChildren()

	return m, nil
}

func (m *MSHI) readAllIndices() error {
	return filepath.Walk(m.dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		// TODO subdirectories
		if strings.ToLower(filepath.Ext(path)) != ".mshi" {
			return nil
		}
		of, err := os.Open(path)
		if err != nil {
			return err
		}
		f := mshi.Open(of)
		if f.Err() != nil {
			return f.Err()
		}
		c := f.ReadContainerPaths()
		if f.Err() != nil {
			return f.Err()
		}
		a := f.ReadAssetDatas()
		if f.Err() != nil {
			return f.Err()
		}
		of.Close()
		m.containers = append(m.containers, c)
		m.assets = append(m.assets, a)
		return nil
	})
}

func (m *MSHI) collectTopics() {
	// TODO deal with versions sensibly
	m.topics = make(map[string]*MSHITopic)
	for container, aa := range m.assets {
		for _, a := range aa {
			// TODO strings.ToLower() this?
			if m.topics[a.ID] != nil && m.topics[a.ID].asset.Version > a.Version {
				continue
			}
			m.topics[strings.ToLower(a.ID)] = &MSHITopic{
				dir:			m.dir,
				containers:	m.containers[container],
				asset:		a,
				source:		m,
			}
		}
	}
}

func (m *MSHI) buildHierarchy() {
	// TODO are parents case-insensitive?
	for _, v := range m.topics {
		if v.asset.ParentID == "-1" {		// is top-level
			m.books = append(m.books, v)
			continue
		}
		parent, ok := m.topics[strings.ToLower(v.asset.ParentID)]
		if ok {			// has parent
			parent.children = append(parent.children, v)
			v.parent = parent
			continue
		}
		m.orphans = append(m.orphans, v)			// parent elsewhere or missing
	}
}

func (m *MSHI) sortAllChildren() {
	for _, t := range m.topics {
		sort.Sort(TopicSorter(t.children))
	}
}

type MSHITopic struct {
	dir			string
	containers	[]*mshi.ContainerPath
	asset			*mshi.AssetData
	parent		Topic
	children		[]Topic
	source		HelpSource
}

func (m *MSHITopic) Name() string {
	return m.asset.Title
}

// TODO anchors - does the help viewer guess the anchor? because I don't see anything in the HTML to suggest that (unless x/net/html is stripping it?)
func (m *MSHITopic) Prepare() (*Prepared, error) {
	var r io.Reader

	mshc := filepath.Join(m.dir, m.containers[m.asset.ContainerPath].Filename)

	_, err := StartTempDir()
	if err != nil {
		return nil, err
	}

	// first get the HTML
	src, err := os.Open(mshc)
	if err != nil {
		return nil, err
	}
	_, err = src.Seek(int64(m.asset.CompressedDataOffset), 0)
	if err != nil {
		return nil, err
	}
	r = io.LimitReader(src, int64(m.asset.CompressedSize))
	switch m.asset.CompressionMethod {
	case zip.Store:
		// do nothing
	case zip.Deflate:
		r = flate.NewReader(r)
	default:
		return nil, zip.ErrAlgorithm
	}
	// we need to use two buffers here because reading from a bytes.Buffer removes those bytes :(
	// TODO use the file instead
	filebuf := new(bytes.Buffer)
	htmlbuf := new(bytes.Buffer)
	multi := io.MultiWriter(filebuf, htmlbuf)
	_, err = io.Copy(multi, r)
	if err != nil {
		return nil, err
	}

	// now generate the temporary HTML file
	htmlfile, err := TempFile("topic.html", filebuf)
	if err != nil {
		return nil, err
	}

	// now we need to extract the images
	src.Close()
	zipfile, err := zip.OpenReader(mshc)
	if err != nil {
		return nil, err
	}
	defer zipfile.Close()
	files := make(map[string]*zip.File)
	for _, f := range zipfile.File {
		files[strings.ToLower(f.Name)] = f
	}
	t := html.NewTokenizer(htmlbuf)
	for {
		tt := t.Next()
		if tt == html.ErrorToken {
			err := t.Err()
			if err == io.EOF {
				break
			}
			return nil, err
		}
		tok := t.Token()
		switch tok.Type {
		case html.StartTagToken, html.SelfClosingTagToken:
			if tok.Data != "img" {
				break
			}
			for _, a := range tok.Attr {
				if a.Key != "src" {
					continue
				}
				filename := strings.ToLower(a.Val)
				r, err := files[filename].Open()
				if err != nil {
					return nil, err
				}
				// note our use of a.Val here
				_, err = TempFile(a.Val, r)
				if err != nil {
					return nil, err
				}
				r.Close()
			}
		}
	}

	return &Prepared{
		Path:		htmlfile,
	}, nil
}

func (m *MSHITopic) Parent() Topic {
	return m.parent
}

func (m *MSHITopic) Children() []Topic {
	return m.children
}

func (m *MSHITopic) Source() HelpSource {
	return m.source
}

func (m *MSHITopic) Less(t Topic) bool {
	tt := t.(*MSHITopic)
	return m.asset.Order < tt.asset.Order
}

func (m *MSHITopic) TreeNodeText() string {
	return m.Name()
}

func (m *MSHITopic) TreeNodeParent() ui.TreeNode {
	return m.Parent()
}

// TODO somehow integrate this and the one in apple.go with the rest of Topic
func (m *MSHITopic) TreeNodeChildren() []ui.TreeNode {
	if len(m.children) == 0 {
		return nil
	}
	c := make([]ui.TreeNode, len(m.children))
	for i, n := range m.children {
		c[i] = n
	}
	return c
}
<commit_msg>Halved the time it took to read all the MSHI files by making the process concurrent. Nice!<commit_after>// 5 october 2014
package main

import (
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"sort"
	"io"
	"archive/zip"
	"compress/flate"
	"bytes"

	"github.com/andlabs/ohv/mshi"
	"golang.org/x/net/html"
	"github.com/andlabs/ohv/internal/ui"
)

const msxhelpURLScheme = "ms-xhelp"

type MSHI struct {
	dir			string
	containers	[][]*mshi.ContainerPath
	assets		[][]*mshi.AssetData
	topics		map[string]*MSHITopic		// maps asset IDs; these are case-insensitive, so are stored here lowercase
	books		[]Topic
	orphans		[]Topic
}

func (m *MSHI) Name() string {
	return "All Microsoft Help Viewer-format documentation"
}

func (m *MSHI) Books() []Topic {
	return m.books
}

func (m *MSHI) Orphans() []Topic {
	return m.orphans
}

func (m *MSHI) Lookup(url *url.URL) Topic {
	id := url.Query().Get("Id")
	id = strings.ToLower(id)
	return m.topics[id]
}

func OpenMSHI(dir string) (*MSHI, error) {
	m := new(MSHI)
	m.dir = dir

	// first read all indices
	err := m.readAllIndices()
	if err != nil {
		return nil, err
	}

	// then produce a map that goes from ID -> topic
	m.collectTopics()

	// now figure out the hierarchy, books, and orphans
	m.buildHierarchy()

	// now sort all children
	m.sortAllChildren()

	return m, nil
}

type mshiFileResult struct {
	c	[]*mshi.ContainerPath
	a	[]*mshi.AssetData
	err	error
}

func (m *MSHI) readOneIndex(path string, res chan<- *mshiFileResult) {
	r := new(mshiFileResult)
	defer func() {
		res <- r
	}()
	of, err := os.Open(path)
	if err != nil {
		r.err = err
		return
	}
	defer of.Close()
	f := mshi.Open(of)
	if f.Err() != nil {
		r.err = f.Err()
		return
	}
	c := f.ReadContainerPaths()
	if f.Err() != nil {
		r.err = f.Err()
		return
	}
	a := f.ReadAssetDatas()
	if f.Err() != nil {
		r.err = f.Err()
		return
	}
	r.c = c
	r.a = a
}

func (m *MSHI) readAllIndices() error {
	n := 0
	res := make(chan *mshiFileResult)
	err := filepath.Walk(m.dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		// TODO subdirectories
		if strings.ToLower(filepath.Ext(path)) != ".mshi" {
			return nil
		}
		go m.readOneIndex(path, res)
		n++
		return nil
	})
	if err != nil {
		return err
	}
	for n != 0 {
		r := <-res
		if r.err != nil {
			return r.err
		}
		m.containers = append(m.containers, r.c)
		m.assets = append(m.assets, r.a)
		n--
	}
	close(res)
	return nil
}

func (m *MSHI) collectTopics() {
	// TODO deal with versions sensibly
	m.topics = make(map[string]*MSHITopic)
	for container, aa := range m.assets {
		for _, a := range aa {
			// TODO strings.ToLower() this?
			if m.topics[a.ID] != nil && m.topics[a.ID].asset.Version > a.Version {
				continue
			}
			m.topics[strings.ToLower(a.ID)] = &MSHITopic{
				dir:			m.dir,
				containers:	m.containers[container],
				asset:		a,
				source:		m,
			}
		}
	}
}

func (m *MSHI) buildHierarchy() {
	// TODO are parents case-insensitive?
	for _, v := range m.topics {
		if v.asset.ParentID == "-1" {		// is top-level
			m.books = append(m.books, v)
			continue
		}
		parent, ok := m.topics[strings.ToLower(v.asset.ParentID)]
		if ok {			// has parent
			parent.children = append(parent.children, v)
			v.parent = parent
			continue
		}
		m.orphans = append(m.orphans, v)			// parent elsewhere or missing
	}
}

func (m *MSHI) sortAllChildren() {
	for _, t := range m.topics {
		sort.Sort(TopicSorter(t.children))
	}
}

type MSHITopic struct {
	dir			string
	containers	[]*mshi.ContainerPath
	asset			*mshi.AssetData
	parent		Topic
	children		[]Topic
	source		HelpSource
}

func (m *MSHITopic) Name() string {
	return m.asset.Title
}

// TODO anchors - does the help viewer guess the anchor? because I don't see anything in the HTML to suggest that (unless x/net/html is stripping it?)
func (m *MSHITopic) Prepare() (*Prepared, error) {
	var r io.Reader

	mshc := filepath.Join(m.dir, m.containers[m.asset.ContainerPath].Filename)

	_, err := StartTempDir()
	if err != nil {
		return nil, err
	}

	// first get the HTML
	src, err := os.Open(mshc)
	if err != nil {
		return nil, err
	}
	_, err = src.Seek(int64(m.asset.CompressedDataOffset), 0)
	if err != nil {
		return nil, err
	}
	r = io.LimitReader(src, int64(m.asset.CompressedSize))
	switch m.asset.CompressionMethod {
	case zip.Store:
		// do nothing
	case zip.Deflate:
		r = flate.NewReader(r)
	default:
		return nil, zip.ErrAlgorithm
	}
	// we need to use two buffers here because reading from a bytes.Buffer removes those bytes :(
	// TODO use the file instead
	filebuf := new(bytes.Buffer)
	htmlbuf := new(bytes.Buffer)
	multi := io.MultiWriter(filebuf, htmlbuf)
	_, err = io.Copy(multi, r)
	if err != nil {
		return nil, err
	}

	// now generate the temporary HTML file
	htmlfile, err := TempFile("topic.html", filebuf)
	if err != nil {
		return nil, err
	}

	// now we need to extract the images
	src.Close()
	zipfile, err := zip.OpenReader(mshc)
	if err != nil {
		return nil, err
	}
	defer zipfile.Close()
	files := make(map[string]*zip.File)
	for _, f := range zipfile.File {
		files[strings.ToLower(f.Name)] = f
	}
	t := html.NewTokenizer(htmlbuf)
	for {
		tt := t.Next()
		if tt == html.ErrorToken {
			err := t.Err()
			if err == io.EOF {
				break
			}
			return nil, err
		}
		tok := t.Token()
		switch tok.Type {
		case html.StartTagToken, html.SelfClosingTagToken:
			if tok.Data != "img" {
				break
			}
			for _, a := range tok.Attr {
				if a.Key != "src" {
					continue
				}
				filename := strings.ToLower(a.Val)
				r, err := files[filename].Open()
				if err != nil {
					return nil, err
				}
				// note our use of a.Val here
				_, err = TempFile(a.Val, r)
				if err != nil {
					return nil, err
				}
				r.Close()
			}
		}
	}

	return &Prepared{
		Path:		htmlfile,
	}, nil
}

func (m *MSHITopic) Parent() Topic {
	return m.parent
}

func (m *MSHITopic) Children() []Topic {
	return m.children
}

func (m *MSHITopic) Source() HelpSource {
	return m.source
}

func (m *MSHITopic) Less(t Topic) bool {
	tt := t.(*MSHITopic)
	return m.asset.Order < tt.asset.Order
}

func (m *MSHITopic) TreeNodeText() string {
	return m.Name()
}

func (m *MSHITopic) TreeNodeParent() ui.TreeNode {
	return m.Parent()
}

// TODO somehow integrate this and the one in apple.go with the rest of Topic
func (m *MSHITopic) TreeNodeChildren() []ui.TreeNode {
	if len(m.children) == 0 {
		return nil
	}
	c := make([]ui.TreeNode, len(m.children))
	for i, n := range m.children {
		c[i] = n
	}
	return c
}
<|endoftext|>


<commit_before>package oci8

/*
#include <oci.h>
#include <stdlib.h>
#include <string.h>

#cgo pkg-config: oci8
*/
import "C"
import (
	"database/sql"
	"database/sql/driver"
	"errors"
	"fmt"
	"io"
	"strings"
	"unsafe"
)

func init() {
	sql.Register("oci8", &OCI8Driver{})
}

type OCI8Driver struct {
}

type OCI8Conn struct {
	svc unsafe.Pointer
	env unsafe.Pointer
	err unsafe.Pointer
}

type OCI8Tx struct {
	c *OCI8Conn
}

func (tx *OCI8Tx) Commit() error {
	if err := tx.c.exec("COMMIT"); err != nil {
		return err
	}
	return nil
}

func (tx *OCI8Tx) Rollback() error {
	if err := tx.c.exec("ROLLBACK"); err != nil {
		return err
	}
	return nil
}

func (c *OCI8Conn) exec(cmd string) error {
	stmt, err := c.Prepare(cmd)
	if err == nil {
		defer stmt.Close()
		_, err = stmt.Exec(nil)
	}
	return err
}

func (c *OCI8Conn) Begin() (driver.Tx, error) {
	if err := c.exec("BEGIN"); err != nil {
		return nil, err
	}
	return &OCI8Tx{c}, nil
}

func (d *OCI8Driver) Open(dsn string) (driver.Conn, error) {
	var conn OCI8Conn
	token := strings.SplitN(dsn, "@", 2)
	userpass := strings.SplitN(token[0], "/", 2)

	rv := C.OCIInitialize(
		C.OCI_DEFAULT,
		nil,
		nil,
		nil,
		nil)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(conn.err)
	}

	rv = C.OCIEnvInit(
		(**C.OCIEnv)(unsafe.Pointer(&conn.env)),
		C.OCI_DEFAULT,
		0,
		nil)

	rv = C.OCIHandleAlloc(
		conn.env,
		&conn.err,
		C.OCI_HTYPE_ERROR,
		0,
		nil)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(conn.err)
	}

	var phost *C.char
	phostlen := C.size_t(0)
	if len(token) > 1 {
		phost = C.CString(token[1])
		defer C.free(unsafe.Pointer(phost))
		phostlen = C.strlen(phost)
	}
	puser := C.CString(userpass[0])
	defer C.free(unsafe.Pointer(puser))
	ppass := C.CString(userpass[1])
	defer C.free(unsafe.Pointer(ppass))

	rv = C.OCILogon(
		(*C.OCIEnv)(conn.env),
		(*C.OCIError)(conn.err),
		(**C.OCIServer)(unsafe.Pointer(&conn.svc)),
		(*C.OraText)(unsafe.Pointer(puser)),
		C.ub4(C.strlen(puser)),
		(*C.OraText)(unsafe.Pointer(ppass)),
		C.ub4(C.strlen(ppass)),
		(*C.OraText)(unsafe.Pointer(phost)),
		C.ub4(phostlen))
	if rv == C.OCI_ERROR {
		return nil, ociGetError(conn.err)
	}

	return &conn, nil
}

func (c *OCI8Conn) Close() error {
	rv := C.OCILogoff(
		(*C.OCIServer)(c.svc),
		(*C.OCIError)(c.err))
	if rv == C.OCI_ERROR {
		return ociGetError(c.err)
	}

	C.OCIHandleFree(
		c.env,
		C.OCI_HTYPE_ENV)

	c.svc = nil
	c.env = nil
	c.err = nil
	return nil
}

type OCI8Stmt struct {
	c      *OCI8Conn
	s      unsafe.Pointer
	closed bool
}

func (c *OCI8Conn) Prepare(query string) (driver.Stmt, error) {
	pquery := C.CString(query)
	defer C.free(unsafe.Pointer(pquery))
	var s unsafe.Pointer

	rv := C.OCIHandleAlloc(
		c.env,
		&s,
		C.OCI_HTYPE_STMT,
		0,
		nil)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(c.err)
	}

	rv = C.OCIStmtPrepare(
		(*C.OCIStmt)(s),
		(*C.OCIError)(c.err),
		(*C.OraText)(unsafe.Pointer(pquery)),
		C.ub4(C.strlen(pquery)),
		C.ub4(C.OCI_NTV_SYNTAX),
		C.ub4(C.OCI_DEFAULT))
	if rv == C.OCI_ERROR {
		return nil, ociGetError(c.err)
	}

	return &OCI8Stmt{c: c, s: s}, nil
}

func (s *OCI8Stmt) Close() error {
	if s.closed {
		return nil
	}
	s.closed = true

	C.OCIHandleFree(
		s.s,
		C.OCI_HTYPE_STMT)
	s.s = nil

	return nil
}

func (s *OCI8Stmt) NumInput() int {
	var num C.int
	C.OCIAttrGet(
		s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&num),
		nil,
		C.OCI_ATTR_BIND_COUNT,
		(*C.OCIError)(s.c.err))
	return int(num)
}

func (s *OCI8Stmt) bind(args []driver.Value) error {
	if args == nil {
		return nil
	}

	var bp *C.OCIBind
	for i, v := range args {
		b := []byte(fmt.Sprintf("%v", v))
		b = append(b, 0)
		rv := C.OCIBindByPos(
			(*C.OCIStmt)(s.s),
			&bp,
			(*C.OCIError)(s.c.err),
			C.ub4(i+1),
			unsafe.Pointer(&b[0]),
			C.sb4(len(b)),
			C.SQLT_STR,
			nil,
			nil,
			nil,
			0,
			nil,
			C.OCI_DEFAULT)

		if rv == C.OCI_ERROR {
			return ociGetError(s.c.err)
		}
	}
	return nil
}

func (s *OCI8Stmt) Query(args []driver.Value) (driver.Rows, error) {
	if err := s.bind(args); err != nil {
		return nil, err
	}

	var t C.int
	C.OCIAttrGet(
		s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&t),
		nil,
		C.OCI_ATTR_STMT_TYPE,
		(*C.OCIError)(s.c.err))
	iter := C.ub4(1)
	if t == C.OCI_STMT_SELECT {
		iter = 0
	}

	rv := C.OCIStmtExecute(
		(*C.OCIServer)(s.c.svc),
		(*C.OCIStmt)(s.s),
		(*C.OCIError)(s.c.err),
		iter,
		0,
		nil,
		nil,
		C.OCI_DEFAULT)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(s.c.err)
	}

	var rc C.ub2
	C.OCIAttrGet(
		s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&rc),
		nil,
		C.OCI_ATTR_PARAM_COUNT,
		(*C.OCIError)(s.c.err))

	oci8cols := make([]oci8col, int(rc))
	for i := 0; i < int(rc); i++ {
		var p unsafe.Pointer
		var np *C.char
		var ns C.ub4
		var tp C.ub2
		var lp C.ub2
		C.OCIParamGet(
			s.s,
			C.OCI_HTYPE_STMT,
			(*C.OCIError)(s.c.err),
			(*unsafe.Pointer)(unsafe.Pointer(&p)),
			C.ub4(i+1))
		C.OCIAttrGet(
			p,
			C.OCI_DTYPE_PARAM,
			unsafe.Pointer(&tp),
			nil,
			C.OCI_ATTR_DATA_TYPE,
			(*C.OCIError)(s.c.err))
		C.OCIAttrGet(
			p,
			C.OCI_DTYPE_PARAM,
			unsafe.Pointer(&np),
			&ns,
			C.OCI_ATTR_NAME,
			(*C.OCIError)(s.c.err))
		C.OCIAttrGet(
			p,
			C.OCI_DTYPE_PARAM,
			unsafe.Pointer(&lp),
			nil,
			C.OCI_ATTR_DATA_SIZE,
			(*C.OCIError)(s.c.err))
		oci8cols[i].name = string((*[1 << 30]byte)(unsafe.Pointer(np))[0:int(ns)])
		oci8cols[i].kind = int(tp)
		oci8cols[i].size = int(lp)
		oci8cols[i].pbuf = make([]byte, int(lp)+1)

		var defp *C.OCIDefine
		rv = C.OCIDefineByPos(
			(*C.OCIStmt)(s.s),
			&defp,
			(*C.OCIError)(s.c.err),
			C.ub4(i+1),
			unsafe.Pointer(&oci8cols[i].pbuf[0]),
			C.sb4(lp+1),
			C.SQLT_CHR,
			nil,
			nil,
			nil,
			C.OCI_DEFAULT)
		if rv == C.OCI_ERROR {
			return nil, ociGetError(s.c.err)
		}
	}
	return &OCI8Rows{s, oci8cols, false}, nil
}

type OCI8Result struct {
	s *OCI8Stmt
}

func (r *OCI8Result) LastInsertId() (int64, error) {
	var t C.ub4
	rv := C.OCIAttrGet(
		r.s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&t),
		nil,
		C.OCI_ATTR_ROWID,
		(*C.OCIError)(r.s.c.err))
	if rv == C.OCI_ERROR {
		return 0, ociGetError(r.s.c.err)
	}
	return int64(t), nil
}

func (r *OCI8Result) RowsAffected() (int64, error) {
	var t C.ub4
	rv := C.OCIAttrGet(
		r.s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&t),
		nil,
		C.OCI_ATTR_ROW_COUNT,
		(*C.OCIError)(r.s.c.err))
	if rv == C.OCI_ERROR {
		return 0, ociGetError(r.s.c.err)
	}
	return int64(t), nil
}

func (s *OCI8Stmt) Exec(args []driver.Value) (driver.Result, error) {
	if err := s.bind(args); err != nil {
		return nil, err
	}

	rv := C.OCIStmtExecute(
		(*C.OCIServer)(s.c.svc),
		(*C.OCIStmt)(s.s),
		(*C.OCIError)(s.c.err),
		1,
		0,
		nil,
		nil,
		C.OCI_DEFAULT)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(s.c.err)
	}
	return &OCI8Result{s}, nil
}

type oci8col struct {
	name string
	kind int
	size int
	pbuf []byte
}

type OCI8Rows struct {
	s    *OCI8Stmt
	cols []oci8col
	e    bool
}

func (rc *OCI8Rows) Close() error {
	return rc.s.Close()
}

func (rc *OCI8Rows) Columns() []string {
	cols := make([]string, len(rc.cols))
	for i, col := range rc.cols {
		cols[i] = col.name
	}
	return cols
}

func (rc *OCI8Rows) Next(dest []driver.Value) error {
	rv := C.OCIStmtFetch(
		(*C.OCIStmt)(rc.s.s),
		(*C.OCIError)(rc.s.c.err),
		1,
		C.OCI_FETCH_NEXT,
		C.OCI_DEFAULT)
	if rv == C.OCI_ERROR {
		return ociGetError(rc.s.c.err)
	}

	if rv == C.OCI_NO_DATA {
		return io.EOF
	}

	for i := range dest {
		dest[i] = string(rc.cols[i].pbuf)
	}

	return nil
}

func ociGetError(err unsafe.Pointer) error {
	var errcode C.sb4
	var errbuff [512]C.char
	C.OCIErrorGet(
		err,
		1,
		nil,
		&errcode,
		(*C.OraText)(unsafe.Pointer(&errbuff[0])),
		512,
		C.OCI_HTYPE_ERROR)
	s := C.GoString(&errbuff[0])
	//println(s)
	return errors.New(s)
}
<commit_msg>ignore 'ORA-01405: fetched column value is NULL' according to http://ora-01405.ora-code.com/<commit_after>package oci8

/*
#include <oci.h>
#include <stdlib.h>
#include <string.h>

#cgo pkg-config: oci8
*/
import "C"
import (
	"database/sql"
	"database/sql/driver"
	"errors"
	"fmt"
	"io"
	"strings"
	"unsafe"
)

func init() {
	sql.Register("oci8", &OCI8Driver{})
}

type OCI8Driver struct {
}

type OCI8Conn struct {
	svc unsafe.Pointer
	env unsafe.Pointer
	err unsafe.Pointer
}

type OCI8Tx struct {
	c *OCI8Conn
}

func (tx *OCI8Tx) Commit() error {
	if err := tx.c.exec("COMMIT"); err != nil {
		return err
	}
	return nil
}

func (tx *OCI8Tx) Rollback() error {
	if err := tx.c.exec("ROLLBACK"); err != nil {
		return err
	}
	return nil
}

func (c *OCI8Conn) exec(cmd string) error {
	stmt, err := c.Prepare(cmd)
	if err == nil {
		defer stmt.Close()
		_, err = stmt.Exec(nil)
	}
	return err
}

func (c *OCI8Conn) Begin() (driver.Tx, error) {
	if err := c.exec("BEGIN"); err != nil {
		return nil, err
	}
	return &OCI8Tx{c}, nil
}

func (d *OCI8Driver) Open(dsn string) (driver.Conn, error) {
	var conn OCI8Conn
	token := strings.SplitN(dsn, "@", 2)
	userpass := strings.SplitN(token[0], "/", 2)

	rv := C.OCIInitialize(
		C.OCI_DEFAULT,
		nil,
		nil,
		nil,
		nil)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(conn.err)
	}

	rv = C.OCIEnvInit(
		(**C.OCIEnv)(unsafe.Pointer(&conn.env)),
		C.OCI_DEFAULT,
		0,
		nil)

	rv = C.OCIHandleAlloc(
		conn.env,
		&conn.err,
		C.OCI_HTYPE_ERROR,
		0,
		nil)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(conn.err)
	}

	var phost *C.char
	phostlen := C.size_t(0)
	if len(token) > 1 {
		phost = C.CString(token[1])
		defer C.free(unsafe.Pointer(phost))
		phostlen = C.strlen(phost)
	}
	puser := C.CString(userpass[0])
	defer C.free(unsafe.Pointer(puser))
	ppass := C.CString(userpass[1])
	defer C.free(unsafe.Pointer(ppass))

	rv = C.OCILogon(
		(*C.OCIEnv)(conn.env),
		(*C.OCIError)(conn.err),
		(**C.OCIServer)(unsafe.Pointer(&conn.svc)),
		(*C.OraText)(unsafe.Pointer(puser)),
		C.ub4(C.strlen(puser)),
		(*C.OraText)(unsafe.Pointer(ppass)),
		C.ub4(C.strlen(ppass)),
		(*C.OraText)(unsafe.Pointer(phost)),
		C.ub4(phostlen))
	if rv == C.OCI_ERROR {
		return nil, ociGetError(conn.err)
	}

	return &conn, nil
}

func (c *OCI8Conn) Close() error {
	rv := C.OCILogoff(
		(*C.OCIServer)(c.svc),
		(*C.OCIError)(c.err))
	if rv == C.OCI_ERROR {
		return ociGetError(c.err)
	}

	C.OCIHandleFree(
		c.env,
		C.OCI_HTYPE_ENV)

	c.svc = nil
	c.env = nil
	c.err = nil
	return nil
}

type OCI8Stmt struct {
	c      *OCI8Conn
	s      unsafe.Pointer
	closed bool
}

func (c *OCI8Conn) Prepare(query string) (driver.Stmt, error) {
	pquery := C.CString(query)
	defer C.free(unsafe.Pointer(pquery))
	var s unsafe.Pointer

	rv := C.OCIHandleAlloc(
		c.env,
		&s,
		C.OCI_HTYPE_STMT,
		0,
		nil)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(c.err)
	}

	rv = C.OCIStmtPrepare(
		(*C.OCIStmt)(s),
		(*C.OCIError)(c.err),
		(*C.OraText)(unsafe.Pointer(pquery)),
		C.ub4(C.strlen(pquery)),
		C.ub4(C.OCI_NTV_SYNTAX),
		C.ub4(C.OCI_DEFAULT))
	if rv == C.OCI_ERROR {
		return nil, ociGetError(c.err)
	}

	return &OCI8Stmt{c: c, s: s}, nil
}

func (s *OCI8Stmt) Close() error {
	if s.closed {
		return nil
	}
	s.closed = true

	C.OCIHandleFree(
		s.s,
		C.OCI_HTYPE_STMT)
	s.s = nil

	return nil
}

func (s *OCI8Stmt) NumInput() int {
	var num C.int
	C.OCIAttrGet(
		s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&num),
		nil,
		C.OCI_ATTR_BIND_COUNT,
		(*C.OCIError)(s.c.err))
	return int(num)
}

func (s *OCI8Stmt) bind(args []driver.Value) error {
	if args == nil {
		return nil
	}

	var bp *C.OCIBind
	for i, v := range args {
		b := []byte(fmt.Sprintf("%v", v))
		b = append(b, 0)
		rv := C.OCIBindByPos(
			(*C.OCIStmt)(s.s),
			&bp,
			(*C.OCIError)(s.c.err),
			C.ub4(i+1),
			unsafe.Pointer(&b[0]),
			C.sb4(len(b)),
			C.SQLT_STR,
			nil,
			nil,
			nil,
			0,
			nil,
			C.OCI_DEFAULT)

		if rv == C.OCI_ERROR {
			return ociGetError(s.c.err)
		}
	}
	return nil
}

func (s *OCI8Stmt) Query(args []driver.Value) (driver.Rows, error) {
	if err := s.bind(args); err != nil {
		return nil, err
	}

	var t C.int
	C.OCIAttrGet(
		s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&t),
		nil,
		C.OCI_ATTR_STMT_TYPE,
		(*C.OCIError)(s.c.err))
	iter := C.ub4(1)
	if t == C.OCI_STMT_SELECT {
		iter = 0
	}

	rv := C.OCIStmtExecute(
		(*C.OCIServer)(s.c.svc),
		(*C.OCIStmt)(s.s),
		(*C.OCIError)(s.c.err),
		iter,
		0,
		nil,
		nil,
		C.OCI_DEFAULT)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(s.c.err)
	}

	var rc C.ub2
	C.OCIAttrGet(
		s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&rc),
		nil,
		C.OCI_ATTR_PARAM_COUNT,
		(*C.OCIError)(s.c.err))

	oci8cols := make([]oci8col, int(rc))
	for i := 0; i < int(rc); i++ {
		var p unsafe.Pointer
		var np *C.char
		var ns C.ub4
		var tp C.ub2
		var lp C.ub2
		C.OCIParamGet(
			s.s,
			C.OCI_HTYPE_STMT,
			(*C.OCIError)(s.c.err),
			(*unsafe.Pointer)(unsafe.Pointer(&p)),
			C.ub4(i+1))
		C.OCIAttrGet(
			p,
			C.OCI_DTYPE_PARAM,
			unsafe.Pointer(&tp),
			nil,
			C.OCI_ATTR_DATA_TYPE,
			(*C.OCIError)(s.c.err))
		C.OCIAttrGet(
			p,
			C.OCI_DTYPE_PARAM,
			unsafe.Pointer(&np),
			&ns,
			C.OCI_ATTR_NAME,
			(*C.OCIError)(s.c.err))
		C.OCIAttrGet(
			p,
			C.OCI_DTYPE_PARAM,
			unsafe.Pointer(&lp),
			nil,
			C.OCI_ATTR_DATA_SIZE,
			(*C.OCIError)(s.c.err))
		oci8cols[i].name = string((*[1 << 30]byte)(unsafe.Pointer(np))[0:int(ns)])
		oci8cols[i].kind = int(tp)
		oci8cols[i].size = int(lp)
		oci8cols[i].pbuf = make([]byte, int(lp)+1)

		var defp *C.OCIDefine
		rv = C.OCIDefineByPos(
			(*C.OCIStmt)(s.s),
			&defp,
			(*C.OCIError)(s.c.err),
			C.ub4(i+1),
			unsafe.Pointer(&oci8cols[i].pbuf[0]),
			C.sb4(lp+1),
			C.SQLT_CHR,
			nil,
			nil,
			nil,
			C.OCI_DEFAULT)
		if rv == C.OCI_ERROR {
			return nil, ociGetError(s.c.err)
		}
	}
	return &OCI8Rows{s, oci8cols, false}, nil
}

type OCI8Result struct {
	s *OCI8Stmt
}

func (r *OCI8Result) LastInsertId() (int64, error) {
	var t C.ub4
	rv := C.OCIAttrGet(
		r.s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&t),
		nil,
		C.OCI_ATTR_ROWID,
		(*C.OCIError)(r.s.c.err))
	if rv == C.OCI_ERROR {
		return 0, ociGetError(r.s.c.err)
	}
	return int64(t), nil
}

func (r *OCI8Result) RowsAffected() (int64, error) {
	var t C.ub4
	rv := C.OCIAttrGet(
		r.s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&t),
		nil,
		C.OCI_ATTR_ROW_COUNT,
		(*C.OCIError)(r.s.c.err))
	if rv == C.OCI_ERROR {
		return 0, ociGetError(r.s.c.err)
	}
	return int64(t), nil
}

func (s *OCI8Stmt) Exec(args []driver.Value) (driver.Result, error) {
	if err := s.bind(args); err != nil {
		return nil, err
	}

	rv := C.OCIStmtExecute(
		(*C.OCIServer)(s.c.svc),
		(*C.OCIStmt)(s.s),
		(*C.OCIError)(s.c.err),
		1,
		0,
		nil,
		nil,
		C.OCI_DEFAULT)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(s.c.err)
	}
	return &OCI8Result{s}, nil
}

type oci8col struct {
	name string
	kind int
	size int
	pbuf []byte
}

type OCI8Rows struct {
	s    *OCI8Stmt
	cols []oci8col
	e    bool
}

func (rc *OCI8Rows) Close() error {
	return rc.s.Close()
}

func (rc *OCI8Rows) Columns() []string {
	cols := make([]string, len(rc.cols))
	for i, col := range rc.cols {
		cols[i] = col.name
	}
	return cols
}

func (rc *OCI8Rows) Next(dest []driver.Value) error {
	rv := C.OCIStmtFetch(
		(*C.OCIStmt)(rc.s.s),
		(*C.OCIError)(rc.s.c.err),
		1,
		C.OCI_FETCH_NEXT,
		C.OCI_DEFAULT)
	if rv == C.OCI_ERROR {
		err := ociGetError(rc.s.c.err)
		if err.Error() != "ORA-01405: fetched column value is NULL\n" {
			return err
		}
	}

	if rv == C.OCI_NO_DATA {
		return io.EOF
	}

	for i := range dest {
		dest[i] = string(rc.cols[i].pbuf)
	}

	return nil
}

func ociGetError(err unsafe.Pointer) error {
	var errcode C.sb4
	var errbuff [512]C.char
	C.OCIErrorGet(
		err,
		1,
		nil,
		&errcode,
		(*C.OraText)(unsafe.Pointer(&errbuff[0])),
		512,
		C.OCI_HTYPE_ERROR)
	s := C.GoString(&errbuff[0])
	//println(s)
	return errors.New(s)
}
<|endoftext|>


<commit_before>// (c) 2012 Alexander Solovyov
// under terms of ISC license

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"time"
)

const (
	StateUnknown = iota
	StateChanged
	StateUnchanged
	StateIgnored
)

type Page struct {
	PageHeader

	Site    *Site `json:"-"`
	Rule    *Rule
	Pattern string
	Deps    PageSlice `json:"-"`

	Source  string
	Path    string
	ModTime time.Time

	processed bool
	state     int
	content   string
	wasread   bool // if content was read already
}

type PageSlice []*Page

func NewPage(site *Site, path string) *Page {
	stat, err := os.Stat(path)
	errhandle(err)

	relpath, err := filepath.Rel(site.Source, path)
	errhandle(err)

	pattern, rule := site.Rules.MatchedRule(relpath)

	page := &Page{
		Site:    site,
		Rule:    rule,
		Pattern: pattern,
		Source:  relpath,
		Path:    relpath,
		ModTime: stat.ModTime(),
	}
	page.peek()
	debug("Found page: %s; rule: %v\n",
		page.Source, page.Rule)
	return page
}

func (page *Page) Content() string {
	if !page.wasread {
		content, err := ioutil.ReadFile(page.FullPath())
		errhandle(err)
		page.SetContent(string(content))
		page.wasread = true
	}
	return page.content
}

func (page *Page) SetContent(content string) {
	page.content = content
}

func (page *Page) FullPath() string {
	return filepath.Join(page.Site.Source, page.Source)
}

func (page *Page) OutputPath() string {
	return filepath.Join(page.Site.Output, page.Path)
}

func (page *Page) Url() string {
	url := strings.Replace(page.Path, string(filepath.Separator), "/", -1)
	if url == "index.html" {
		return ""
	}
	if strings.HasSuffix(url, "/index.html") {
		return strings.TrimSuffix(url, "/index.html") + "/"
	}
	return url
}

func (page *Page) UrlTo(other *Page) string {
	return page.Rel(other.Url())
}

func (page *Page) Rel(path string) string {
	root := strings.Repeat("../", strings.Count(page.Url(), "/"))
	if path[0] == '/' {
		return root + path[1:]
	}
	return root + path
}

func (page *Page) Is(path string) bool {
	return page.Url() == path || page.Path == path
}

// Peek is used to run those processors which should be done before others can
// find out about us. Two actual examples include 'config' and 'rename'
// processors right now.
func (page *Page) peek() {
	if page.Rule == nil {
		return
	}

	for _, name := range PreProcessors {
		cmd := page.Rule.MatchedCommand(name)
		if cmd != nil {
			ProcessCommand(page, cmd)
		}
	}
}

func (page *Page) findDeps() {
	if page.Rule == nil {
		return
	}

	deps := make(PageSlice, 0)
	for _, other := range page.Site.Pages {
		if other != page && page.Rule.IsDep(other) {
			deps = append(deps, other)
		}
	}
	page.Deps = deps
}

func (page *Page) Changed() bool {
	if opts.Force {
		return true
	}

	if page.state == StateUnknown {
		page.state = StateUnchanged
		dest, err := os.Stat(page.OutputPath())

		if err != nil || dest.ModTime().Before(page.ModTime) {
			page.state = StateChanged
		} else {
			for _, dep := range page.Deps {
				if dep.Changed() {
					page.state = StateChanged
				}
			}
		}
	}

	return page.state == StateChanged
}

func (page *Page) Process() *Page {
	if page.processed || page.Rule == nil {
		return page
	}

	page.processed = true
	if page.Rule.Commands != nil {
		for _, cmd := range page.Rule.Commands {
			if !cmd.MatchesAny(PreProcessors) {
				ProcessCommand(page, &cmd)
			}
		}
	}

	return page
}

func (page *Page) WriteTo(writer io.Writer) (n int64, err error) {
	if page.Rule == nil {
		return 0, nil
	}

	if !page.processed {
		page.Process()
	}

	nint, err := writer.Write([]byte(page.Content()))
	return int64(nint), err
}

func (page *Page) Render() (n int64, err error) {
	if page.Rule == nil {
		return CopyFile(page.FullPath(), page.OutputPath())
	}

	file, err := os.Create(page.OutputPath())
	if err != nil {
		return 0, err
	}
	defer file.Close()

	return page.WriteTo(file)
}

func (page *Page) UrlMatches(regex string) bool {
	re, err := regexp.Compile(regex)
	if err != nil {
		errhandle(fmt.Errorf("Incorrect regex given to Page.UrlMatches: '%s' ", regex))
	}
	return re.Match([]byte(page.Url()))
}

// PageSlice manipulation

func (pages PageSlice) Get(i int) *Page { return pages[i] }
func (pages PageSlice) First() *Page    { return pages.Get(0) }
func (pages PageSlice) Last() *Page     { return pages.Get(len(pages) - 1) }

func (pages PageSlice) Prev(cur *Page) *Page {
	for i, page := range pages {
		if page == cur {
			if i == pages.Len() - 1 {
				return nil
			}
			return pages[i+1]
		}
	}
	return nil
}

func (pages PageSlice) Next(cur *Page) *Page {
	for i, page := range pages {
		if page == cur {
			if i == 0 {
				return nil
			}
			return pages[i-1]
		}
	}
	return nil
}

func (pages PageSlice) Slice(from int, to int) PageSlice {
	length := len(pages)

	if from > length {
		from = length
	}
	if to > length {
		to = length
	}

	return pages[from:to]
}

// Sorting interface
func (pages PageSlice) Len() int {
	return len(pages)
}
func (pages PageSlice) Less(i, j int) bool {
	left := pages.Get(i)
	right := pages.Get(j)
	if left.Date.Unix() == right.Date.Unix() {
		return left.ModTime.Unix() < right.ModTime.Unix()
	}
	return left.Date.Unix() > right.Date.Unix()
}
func (pages PageSlice) Swap(i, j int) {
	pages[i], pages[j] = pages[j], pages[i]
}

func (pages PageSlice) Sort() {
	sort.Sort(pages)
}

func (pages PageSlice) Children(root string) *PageSlice {
	children := make(PageSlice, 0)

	for _, page := range pages {
		if strings.HasPrefix(page.Url(), root) && page.Url() != root {
			children = append(children, page)
		}
	}

	return &children
}

func (pages PageSlice) WithTag(tag string) *PageSlice {
	tagged := make(PageSlice, 0)

	for _, page := range pages {
		if page.Tags != nil && SliceStringIndexOf(page.Tags, tag) != -1 {
			tagged = append(tagged, page)
		}
	}

	return &tagged
}

func (pages PageSlice) HasPage(check func(page *Page) bool) bool {
	for _, page := range pages {
		if check(page) {
			return true
		}
	}
	return false
}

func (pages PageSlice) BySource(s string) *Page {
	for _, page := range pages {
		if page.Source == s {
			return page
		}
	}
	return nil
}

func (pages PageSlice) ByPath(s string) *Page {
	for _, page := range pages {
		if page.Path == s {
			return page
		}
	}
	return nil
}
<commit_msg>Convert windows path separators to unix style, otherwise rules wouldn't match<commit_after>// (c) 2012 Alexander Solovyov
// under terms of ISC license

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"time"
)

const (
	StateUnknown = iota
	StateChanged
	StateUnchanged
	StateIgnored
)

type Page struct {
	PageHeader

	Site    *Site `json:"-"`
	Rule    *Rule
	Pattern string
	Deps    PageSlice `json:"-"`

	Source  string
	Path    string
	ModTime time.Time

	processed bool
	state     int
	content   string
	wasread   bool // if content was read already
}

type PageSlice []*Page

func NewPage(site *Site, path string) *Page {
	stat, err := os.Stat(path)
	errhandle(err)

	relpath, err := filepath.Rel(site.Source, path)
	errhandle(err)

	// convert windows path separators to unix style
	relpath = strings.Replace(relpath, "\\", "/", -1)

	pattern, rule := site.Rules.MatchedRule(relpath)

	page := &Page{
		Site:    site,
		Rule:    rule,
		Pattern: pattern,
		Source:  relpath,
		Path:    relpath,
		ModTime: stat.ModTime(),
	}
	page.peek()
	debug("Found page: %s; rule: %v\n",
		page.Source, page.Rule)
	return page
}

func (page *Page) Content() string {
	if !page.wasread {
		content, err := ioutil.ReadFile(page.FullPath())
		errhandle(err)
		page.SetContent(string(content))
		page.wasread = true
	}
	return page.content
}

func (page *Page) SetContent(content string) {
	page.content = content
}

func (page *Page) FullPath() string {
	return filepath.Join(page.Site.Source, page.Source)
}

func (page *Page) OutputPath() string {
	return filepath.Join(page.Site.Output, page.Path)
}

func (page *Page) Url() string {
	url := strings.Replace(page.Path, string(filepath.Separator), "/", -1)
	if url == "index.html" {
		return ""
	}
	if strings.HasSuffix(url, "/index.html") {
		return strings.TrimSuffix(url, "/index.html") + "/"
	}
	return url
}

func (page *Page) UrlTo(other *Page) string {
	return page.Rel(other.Url())
}

func (page *Page) Rel(path string) string {
	root := strings.Repeat("../", strings.Count(page.Url(), "/"))
	if path[0] == '/' {
		return root + path[1:]
	}
	return root + path
}

func (page *Page) Is(path string) bool {
	return page.Url() == path || page.Path == path
}

// Peek is used to run those processors which should be done before others can
// find out about us. Two actual examples include 'config' and 'rename'
// processors right now.
func (page *Page) peek() {
	if page.Rule == nil {
		return
	}

	for _, name := range PreProcessors {
		cmd := page.Rule.MatchedCommand(name)
		if cmd != nil {
			ProcessCommand(page, cmd)
		}
	}
}

func (page *Page) findDeps() {
	if page.Rule == nil {
		return
	}

	deps := make(PageSlice, 0)
	for _, other := range page.Site.Pages {
		if other != page && page.Rule.IsDep(other) {
			deps = append(deps, other)
		}
	}
	page.Deps = deps
}

func (page *Page) Changed() bool {
	if opts.Force {
		return true
	}

	if page.state == StateUnknown {
		page.state = StateUnchanged
		dest, err := os.Stat(page.OutputPath())

		if err != nil || dest.ModTime().Before(page.ModTime) {
			page.state = StateChanged
		} else {
			for _, dep := range page.Deps {
				if dep.Changed() {
					page.state = StateChanged
				}
			}
		}
	}

	return page.state == StateChanged
}

func (page *Page) Process() *Page {
	if page.processed || page.Rule == nil {
		return page
	}

	page.processed = true
	if page.Rule.Commands != nil {
		for _, cmd := range page.Rule.Commands {
			if !cmd.MatchesAny(PreProcessors) {
				ProcessCommand(page, &cmd)
			}
		}
	}

	return page
}

func (page *Page) WriteTo(writer io.Writer) (n int64, err error) {
	if page.Rule == nil {
		return 0, nil
	}

	if !page.processed {
		page.Process()
	}

	nint, err := writer.Write([]byte(page.Content()))
	return int64(nint), err
}

func (page *Page) Render() (n int64, err error) {
	if page.Rule == nil {
		return CopyFile(page.FullPath(), page.OutputPath())
	}

	file, err := os.Create(page.OutputPath())
	if err != nil {
		return 0, err
	}
	defer file.Close()

	return page.WriteTo(file)
}

func (page *Page) UrlMatches(regex string) bool {
	re, err := regexp.Compile(regex)
	if err != nil {
		errhandle(fmt.Errorf("Incorrect regex given to Page.UrlMatches: '%s' ", regex))
	}
	return re.Match([]byte(page.Url()))
}

// PageSlice manipulation

func (pages PageSlice) Get(i int) *Page { return pages[i] }
func (pages PageSlice) First() *Page    { return pages.Get(0) }
func (pages PageSlice) Last() *Page     { return pages.Get(len(pages) - 1) }

func (pages PageSlice) Prev(cur *Page) *Page {
	for i, page := range pages {
		if page == cur {
			if i == pages.Len()-1 {
				return nil
			}
			return pages[i+1]
		}
	}
	return nil
}

func (pages PageSlice) Next(cur *Page) *Page {
	for i, page := range pages {
		if page == cur {
			if i == 0 {
				return nil
			}
			return pages[i-1]
		}
	}
	return nil
}

func (pages PageSlice) Slice(from int, to int) PageSlice {
	length := len(pages)

	if from > length {
		from = length
	}
	if to > length {
		to = length
	}

	return pages[from:to]
}

// Sorting interface
func (pages PageSlice) Len() int {
	return len(pages)
}
func (pages PageSlice) Less(i, j int) bool {
	left := pages.Get(i)
	right := pages.Get(j)
	if left.Date.Unix() == right.Date.Unix() {
		return left.ModTime.Unix() < right.ModTime.Unix()
	}
	return left.Date.Unix() > right.Date.Unix()
}
func (pages PageSlice) Swap(i, j int) {
	pages[i], pages[j] = pages[j], pages[i]
}

func (pages PageSlice) Sort() {
	sort.Sort(pages)
}

func (pages PageSlice) Children(root string) *PageSlice {
	children := make(PageSlice, 0)

	for _, page := range pages {
		if strings.HasPrefix(page.Url(), root) && page.Url() != root {
			children = append(children, page)
		}
	}

	return &children
}

func (pages PageSlice) WithTag(tag string) *PageSlice {
	tagged := make(PageSlice, 0)

	for _, page := range pages {
		if page.Tags != nil && SliceStringIndexOf(page.Tags, tag) != -1 {
			tagged = append(tagged, page)
		}
	}

	return &tagged
}

func (pages PageSlice) HasPage(check func(page *Page) bool) bool {
	for _, page := range pages {
		if check(page) {
			return true
		}
	}
	return false
}

func (pages PageSlice) BySource(s string) *Page {
	for _, page := range pages {
		if page.Source == s {
			return page
		}
	}
	return nil
}

func (pages PageSlice) ByPath(s string) *Page {
	for _, page := range pages {
		if page.Path == s {
			return page
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package geom

// PathElementType is an enumeration representing the different kinds of path
// elements supported by 2D paths.
type PathElementType int

const (
	// MoveTo is used for path elements that move the path position to a new
	// location.
	MoveTo PathElementType = iota

	// LineTo is used for path elements that move the path position to a new
	// location while drawin a straight line.
	LineTo

	// QuandCurveTo is used for path elements that draw a quadriatic cruve.
	QuadCurveTo

	// CubicCurveTo is used for path elements that draw a cubic curve.
	CubicCurveTo

	// ClosePath is used for path elements that terminate drawing of a sub-path.
	ClosePath
)

// A PathElement represents a single step in a 2D path, it's composed of a type
// that must be one of the constant values of PathElementType, and a list of 2D
// points that are interpreted differently based on the element's type.
type PathElement struct {
	Type   PathElementType
	Points [3]Point
}

// A Path is a drawable representation of an arbitrary shape, it's copmosed of a
// list of elements describing each step of the whole drawing operation.
type Path struct {
	Elements []PathElement
}

// The Shape interface represents values that can be converted to 2D paths.
type Shape interface {

	// Returns a 2D path representing the abstract shape that satisifed the
	// interface.
	//
	// The returned path shouldn't be retained by the shape or any other part
	// of the program, the caller of that method is considered the owner of the
	// returned value and is free to modify it.
	Path() Path
}

// MakePath returns a Path value initialized with a slice of elements with the
// capacity given as argument.
func MakePath(cap int) Path {
	return Path{
		Elements: make([]PathElement, 0, cap),
	}
}

// AppendPath concatenates the Path given as second arugment to the one given as
// first argument. It behaves in a similar way to the builtin `append` function,
// the Path value given as first argument may or may not be modified by the append
// operation.
func AppendPath(path Path, other Path) Path {
	return Path{Elements: append(path.Elements, other.Elements...)}
}

// AppendRect efficiently append a rectangle to a Path and returns the modified
// value.
//
// Calling this function is equivalent to calling:
//
//	path = AppendPath(path, rect.Path())
//
// but the implementation is more optimized and avoid unnecessary memory
// allocations.
func AppendRect(path Path, rect Rect) Path {
	x0 := rect.X
	y0 := rect.Y
	x1 := rect.X + rect.W
	y1 := rect.Y + rect.H
	return AppendPolygon(path, Point{x0, y0}, Point{x1, y0}, Point{x1, y1}, Point{x0, y1})
}

// AppendPolygon appends an arbitrary polygon to a path and returns the modified
// value, the polygon is made of the points given as arguments to the function.
func AppendPolygon(path Path, points ...Point) Path {
	if len(points) != 0 {
		path.MoveTo(points[0])

		for _, p := range points[1:] {
			path.LineTo(p)
		}

		path.Close()
	}

	return path
}

// MoveTo appends a path element that moves the current path position to the
// point given as argument.
func (path *Path) MoveTo(p Point) {
	path.append(PathElement{
		Type:   MoveTo,
		Points: [...]Point{p, {}, {}},
	})
}

// LineTo appends a path element that draws a line from the current path
// position to the point given as argument.
func (path *Path) LineTo(p Point) {
	path.ensureStartWithMoveTo()
	path.append(PathElement{
		Type:   LineTo,
		Points: [...]Point{p, {}, {}},
	})
}

// QuadCurveTo appends a path element that draws a quadriatic curve from the
// current path position to `p` and centered in `c`.
func (path *Path) QuadCurveTo(c Point, p Point) {
	path.ensureStartWithMoveTo()
	path.append(PathElement{
		Type:   QuadCurveTo,
		Points: [...]Point{c, p, {}},
	})
}

// QuadCurveTo appends a path element that draws a cubic curve from the current
// path position to `p` with centers in `c1` and `c2`.
func (path *Path) CubicCurveTo(c1 Point, c2 Point, p Point) {
	path.ensureStartWithMoveTo()
	path.append(PathElement{
		Type:   CubicCurveTo,
		Points: [...]Point{c1, c2, p},
	})
}

// Close appends a path element that closes the current shape by drawing a line
// between the current path position and the last move-to element added to the
// path.
func (path *Path) Close() {
	path.append(PathElement{
		Type: ClosePath,
	})
}

// Clear erases every element in the path.
func (path *Path) Clear() {
	path.Elements = path.Elements[:0]
}

// Copy creates a copy of the path and returns it, the returned value and the
// receiver do not share the same slice of elements and can be safely modified
// independently.
func (path *Path) Copy() Path {
	if path.Empty() {
		return Path{}
	}

	p := Path{
		Elements: make([]PathElement, len(path.Elements)),
	}

	copy(p.Elements, path.Elements)
	return p
}

// LastPoint returns the 2D coordinates of the current path position.
func (path *Path) LastPoint() Point {
	return path.lastPointAt(len(path.Elements) - 1)
}

func (path *Path) lastPointAt(n int) Point {
	if n < 0 {
		return Point{}
	}

	switch e := path.Elements[n-1]; e.Type {
	case MoveTo, LineTo:
		return e.Points[0]

	case QuadCurveTo:
		return e.Points[1]

	case CubicCurveTo:
		return e.Points[2]

	default:
		return path.lastPointAt(n - 1)
	}
}

// Empty checks if the path is empty, which means it has no element.
func (path *Path) Empty() bool {
	return len(path.Elements) == 0
}

// Path satisfies the Shape interface by returning a copy of the path it is
// called on.
func (path Path) Path() Path {
	return path.Copy()
}

func (path *Path) append(e PathElement) {
	path.Elements = append(path.Elements, e)
}

func (path *Path) ensureStartWithMoveTo() {
	if n := len(path.Elements); n == 0 {
		// Empty paths must start with a MoveTo element.
		path.MoveTo(Point{})

	} else if path.Elements[n-1].Type == ClosePath {
		// When a subpath is closed the path that contains more elements must
		// be restarted with a MoveTo as well.
		path.MoveTo(path.LastPoint())
	}
}
<commit_msg>update path documentation<commit_after>package geom

// PathElementType is an enumeration representing the different kinds of path
// elements supported by 2D paths.
type PathElementType int

const (
	// MoveTo is used for path elements that move the path position to a new
	// location.
	MoveTo PathElementType = iota

	// LineTo is used for path elements that move the path position to a new
	// location while drawin a straight line.
	LineTo

	// QuadCurveTo is used for path elements that draw a quadriatic cruve.
	QuadCurveTo

	// CubicCurveTo is used for path elements that draw a cubic curve.
	CubicCurveTo

	// ClosePath is used for path elements that terminate drawing of a sub-path.
	ClosePath
)

// A PathElement represents a single step in a 2D path, it's composed of a type
// that must be one of the constant values of PathElementType, and a list of 2D
// points that are interpreted differently based on the element's type.
type PathElement struct {
	Type   PathElementType
	Points [3]Point
}

// A Path is a drawable representation of an arbitrary shape, it's copmosed of a
// list of elements describing each step of the whole drawing operation.
type Path struct {
	Elements []PathElement
}

// The Shape interface represents values that can be converted to 2D paths.
type Shape interface {

	// Returns a 2D path representing the abstract shape that satisifed the
	// interface.
	//
	// The returned path shouldn't be retained by the shape or any other part
	// of the program, the caller of that method is considered the owner of the
	// returned value and is free to modify it.
	Path() Path
}

// MakePath returns a Path value initialized with a slice of elements with the
// capacity given as argument.
func MakePath(cap int) Path {
	return Path{
		Elements: make([]PathElement, 0, cap),
	}
}

// AppendPath concatenates the Path given as second arugment to the one given as
// first argument. It behaves in a similar way to the builtin `append` function,
// the Path value given as first argument may or may not be modified by the append
// operation.
func AppendPath(path Path, other Path) Path {
	return Path{Elements: append(path.Elements, other.Elements...)}
}

// AppendRect efficiently append a rectangle to a Path and returns the modified
// value.
//
// Calling this function is equivalent to calling:
//
//	path = AppendPath(path, rect.Path())
//
// but the implementation is more optimized and avoid unnecessary memory
// allocations.
func AppendRect(path Path, rect Rect) Path {
	x0 := rect.X
	y0 := rect.Y
	x1 := rect.X + rect.W
	y1 := rect.Y + rect.H
	return AppendPolygon(path, Point{x0, y0}, Point{x1, y0}, Point{x1, y1}, Point{x0, y1})
}

// AppendPolygon appends an arbitrary polygon to a path and returns the modified
// value, the polygon is made of the points given as arguments to the function.
func AppendPolygon(path Path, points ...Point) Path {
	if len(points) != 0 {
		path.MoveTo(points[0])

		for _, p := range points[1:] {
			path.LineTo(p)
		}

		path.Close()
	}

	return path
}

// MoveTo appends a path element that moves the current path position to the
// point given as argument.
func (path *Path) MoveTo(p Point) {
	path.append(PathElement{
		Type:   MoveTo,
		Points: [...]Point{p, {}, {}},
	})
}

// LineTo appends a path element that draws a line from the current path
// position to the point given as argument.
func (path *Path) LineTo(p Point) {
	path.ensureStartWithMoveTo()
	path.append(PathElement{
		Type:   LineTo,
		Points: [...]Point{p, {}, {}},
	})
}

// QuadCurveTo appends a path element that draws a quadriatic curve from the
// current path position to `p` and centered in `c`.
func (path *Path) QuadCurveTo(c Point, p Point) {
	path.ensureStartWithMoveTo()
	path.append(PathElement{
		Type:   QuadCurveTo,
		Points: [...]Point{c, p, {}},
	})
}

// CubicCurveTo appends a path element that draws a cubic curve from the current
// path position to `p` with centers in `c1` and `c2`.
func (path *Path) CubicCurveTo(c1 Point, c2 Point, p Point) {
	path.ensureStartWithMoveTo()
	path.append(PathElement{
		Type:   CubicCurveTo,
		Points: [...]Point{c1, c2, p},
	})
}

// Close appends a path element that closes the current shape by drawing a line
// between the current path position and the last move-to element added to the
// path.
func (path *Path) Close() {
	path.append(PathElement{
		Type: ClosePath,
	})
}

// Clear erases every element in the path.
func (path *Path) Clear() {
	path.Elements = path.Elements[:0]
}

// Copy creates a copy of the path and returns it, the returned value and the
// receiver do not share the same slice of elements and can be safely modified
// independently.
func (path *Path) Copy() Path {
	if path.Empty() {
		return Path{}
	}

	p := Path{
		Elements: make([]PathElement, len(path.Elements)),
	}

	copy(p.Elements, path.Elements)
	return p
}

// LastPoint returns the 2D coordinates of the current path position.
func (path *Path) LastPoint() Point {
	return path.lastPointAt(len(path.Elements) - 1)
}

func (path *Path) lastPointAt(n int) Point {
	if n < 0 {
		return Point{}
	}

	switch e := path.Elements[n-1]; e.Type {
	case MoveTo, LineTo:
		return e.Points[0]

	case QuadCurveTo:
		return e.Points[1]

	case CubicCurveTo:
		return e.Points[2]

	default:
		return path.lastPointAt(n - 1)
	}
}

// Empty checks if the path is empty, which means it has no element.
func (path *Path) Empty() bool {
	return len(path.Elements) == 0
}

// Path satisfies the Shape interface by returning a copy of the path it is
// called on.
func (path Path) Path() Path {
	return path.Copy()
}

func (path *Path) append(e PathElement) {
	path.Elements = append(path.Elements, e)
}

func (path *Path) ensureStartWithMoveTo() {
	if n := len(path.Elements); n == 0 {
		// Empty paths must start with a MoveTo element.
		path.MoveTo(Point{})

	} else if path.Elements[n-1].Type == ClosePath {
		// When a subpath is closed the path that contains more elements must
		// be restarted with a MoveTo as well.
		path.MoveTo(path.LastPoint())
	}
}
<|endoftext|>


<commit_before>package fweight

import (
	"net/http"
	"strings"
)

var _ PathRouter = new(Path)
var _ Router = new(Path)

//Func PathIsEmpty accounts for the fact that paths like
// a/b///c or a/b// can exist, which would result in final
//strings of // or ///. These are assumed to be the same as
//their single-slashed counterparts.
func PathEmpty(path string) bool {
	if path == "" || strings.TrimLeft(path, "/") == "" {
		return true
	}
	return false
}

//Type Path is a Router which routes by URL path. Files or directories
//with empty names are not allowed. The empty name routes to the terminal
//Router, the one used if the path stops here.
//
//It should be noted that when RouteHTTP is called
//the PathRouter is followed to completion from the
//start to end of the URL, thus using two routers separately
//(i.e. separated by a different Router in the routing tree)
//causes two separate and independant operations on the path;
//a Path that routes a/b followed by one that routes a/b/c does
//not result in a route of a/b/a/c, instead resulting in a route of
//just a/b.
type Path map[string]Router

func (p Path) self() Path {
	if p == nil {
		p = make(Path)
	}
	return p
}

//Function AddChild adds a child PathRouter which is used
//as the next name in the path.
//
//A non PathRouter child will cause the Router to
//be returned to the caller of RouteHTTP, causing the
//path to terminate there if it is a Handler.
func (p Path) AddChild(pR Router, name string) Path {
	p[name] = pR
	return p
}

//Sets the Handler that is used when the path terminates here.
//This is the same as AddChild(r, ""); the empty string routes
//to here.
func (p Path) Handler(r Router) Path {
	return p.AddChild(r, "")
}

func isPathRouter(r Router) (b bool) {
	_, b = r.(PathRouter)
	return
}

//RouteHTTP traverses the tree of Paths until the end of the URL path
//is encountered, returning the terminal router or nil.
func (p Path) RouteHTTP(rq *http.Request) Router {
	remaining := strings.Trim(rq.URL.Path, "/")
	var pRouter Router
	for pRouter, remaining = p.Child(rq.URL.Path); isPathRouter(pRouter); pRouter, remaining = pRouter.(PathRouter).Child(remaining) {
	}
	return pRouter
}

//Function Child returns the next Router associated with the next
//'hop' in the path.
func (p Path) Child(subpath string) (n Router, remainingSubpath string) {
	//If strings.SplitN(subpath, "/", 3)'s length is two, then this
	//is the only item left in the path, and thus we must terminate here.
	if subpath == "" || subpath == "/" {
		return p[""], ""
	}

	//First, check if we have bound a handler for this whole
	//subpath (a/b/c)
	if pR, ok := p[subpath]; ok {
		return pR, ""
	}

	//Check if the next node is present
	splt := strings.SplitN(subpath, "/", 3)
	if pR, ok := p[splt[0]]; ok {
		return pR, splt[1]
	}

	//Check if we have a route that begins with the subpath
	for path, pR := range p {
		if strings.HasPrefix(subpath, path) {
			return pR, strings.TrimLeft(subpath, path)
		}
	}

	//Not Found.
	return nil, subpath
}
<commit_msg>Path bugfix<commit_after>package fweight

import (
	"net/http"
	"strings"
)

var _ PathRouter = new(Path)
var _ Router = new(Path)

//Func PathIsEmpty accounts for the fact that paths like
// a/b///c or a/b// can exist, which would result in final
//strings of // or ///. These are assumed to be the same as
//their single-slashed counterparts.
func PathEmpty(path string) bool {
	if path == "" || strings.TrimLeft(path, "/") == "" {
		return true
	}
	return false
}

//Type Path is a Router which routes by URL path. Files or directories
//with empty names are not allowed. The empty name routes to the terminal
//Router, the one used if the path stops here.
//
//It should be noted that when RouteHTTP is called
//the PathRouter is followed to completion from the
//start to end of the URL, thus using two routers separately
//(i.e. separated by a different Router in the routing tree)
//causes two separate and independant operations on the path;
//a Path that routes a/b followed by one that routes a/b/c does
//not result in a route of a/b/a/c, instead resulting in a route of
//just a/b.
type Path map[string]Router

func (p Path) self() Path {
	if p == nil {
		p = make(Path)
	}
	return p
}

//Function AddChild adds a child PathRouter which is used
//as the next name in the path.
//
//A non PathRouter child will cause the Router to
//be returned to the caller of RouteHTTP, causing the
//path to terminate there if it is a Handler.
func (p Path) AddChild(pR Router, name string) Path {
	p[name] = pR
	return p
}

//Sets the Handler that is used when the path terminates here.
//This is the same as AddChild(r, ""); the empty string routes
//to here.
func (p Path) Handler(r Router) Path {
	return p.AddChild(r, "")
}

func isPathRouter(r Router) (b bool) {
	_, b = r.(PathRouter)
	return
}

//RouteHTTP traverses the tree of Paths until the end of the URL path
//is encountered, returning the terminal router or nil.
func (p Path) RouteHTTP(rq *http.Request) Router {
	remaining := strings.Trim(rq.URL.Path, "/")
	var pRouter Router
	for pRouter, remaining = p.Child(rq.URL.Path); isPathRouter(pRouter); pRouter, remaining = pRouter.(PathRouter).Child(remaining) {
	}
	return pRouter
}

//Function Child returns the next Router associated with the next
//'hop' in the path.
func (p Path) Child(subpath string) (n Router, remainingSubpath string) {
	//If strings.SplitN(subpath, "/", 3)'s length is two, then this
	//is the only item left in the path, and thus we must terminate here.
	if subpath == "" || subpath == "/" {
		return p[""], ""
	}

	//First, check if we have bound a handler for this whole
	//subpath (a/b/c)
	if pR, ok := p[subpath]; ok {
		return pR, ""
	}

	//Check if the next node is present
	splt := strings.SplitN(subpath, "/", 3)
	if pR, ok := p[splt[1]]; ok {
		return pR, splt[1]
	}

	//Check if we have a route that begins with the subpath
	for path, pR := range p {
		if path != "" && strings.HasPrefix(subpath, path) {
			return pR, strings.TrimLeft(subpath, path)
		}
	}

	//Not Found.
	return nil, subpath
}
<|endoftext|>


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

package txtdirect

import (
	"fmt"
	"log"
	"net/http"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

var PathRegex = regexp.MustCompile("\\/([A-Za-z0-9-._~!$'()*+,;=:@]+)")
var FromRegex = regexp.MustCompile("\\/\\$(\\d+)")
var GroupRegex = regexp.MustCompile("P<[a-zA-Z]+[a-zA-Z0-9]*>")
var GroupOrderRegex = regexp.MustCompile("P<([a-zA-Z]+[a-zA-Z0-9]*)>")

// zoneFromPath generates a DNS zone with the given host and path
// It will use custom regex to parse the path if it's provided in
// the given record.
func zoneFromPath(r *http.Request, rec record) (string, int, []string, error) {
	path := fmt.Sprintf("%s?%s", r.URL.Path, r.URL.RawQuery)

	if strings.ContainsAny(path, ".") {
		path = strings.Replace(path, ".", "-", -1)
	}
	pathSubmatchs := PathRegex.FindAllStringSubmatch(path, -1)
	if rec.Re != "" {
		CustomRegex, err := regexp.Compile(rec.Re)
		if err != nil {
			log.Printf("<%s> [txtdirect]: the given regex doesn't work as expected: %s", time.Now().String(), rec.Re)
		}
		pathSubmatchs = CustomRegex.FindAllStringSubmatch(path, -1)
		if GroupRegex.MatchString(rec.Re) {
			pathSlice := []string{}
			unordered := make(map[string]string)
			for _, item := range pathSubmatchs[0] {
				pathSlice = append(pathSlice, item)
			}
			order := GroupOrderRegex.FindAllStringSubmatch(rec.Re, -1)
			for i, group := range order {
				unordered[group[1]] = pathSlice[i+1]
			}
			url := sortMap(unordered)
			*r = *r.WithContext(context.WithValue(r.Context(), "regexMatches", unordered))
			reverse(url)
			from := len(pathSlice)
			url = append(url, r.Host)
			url = append([]string{basezone}, url...)
			return strings.Join(url, "."), from, pathSlice, nil
		}
	}
	pathSlice := []string{}
	for _, v := range pathSubmatchs {
		pathSlice = append(pathSlice, v[1])
	}
	*r = *r.WithContext(context.WithValue(r.Context(), "regexMatches", pathSlice))
	if len(pathSlice) < 1 && rec.Re != "" {
		log.Printf("<%s> [txtdirect]: custom regex doesn't work on %s", time.Now().String(), path)
	}
	from := len(pathSlice)
	if rec.From != "" {
		fromSubmatch := FromRegex.FindAllStringSubmatch(rec.From, -1)
		if len(fromSubmatch) != len(pathSlice) {
			return "", 0, []string{}, fmt.Errorf("length of path doesn't match with length of from= in record")
		}
		fromSlice := make(map[int]string)
		for k, v := range fromSubmatch {
			index, _ := strconv.Atoi(v[1])
			fromSlice[index] = pathSlice[k]
		}

		keys := []int{}
		for k := range fromSlice {
			keys = append(keys, k)
		}
		if len(keys) != len(pathSlice) {
			return "", 0, []string{}, fmt.Errorf("length of path doesn't match with length of from= in record")
		}
		generatedPath := []string{}

		sort.Sort(sort.Reverse(sort.IntSlice(keys)))

		for _, k := range keys {
			generatedPath = append(generatedPath, fromSlice[k])
		}

		url := append(generatedPath, r.Host)
		url = append([]string{basezone}, url...)
		return strings.Join(url, "."), from, pathSlice, nil
	}
	ps := pathSlice
	reverse(pathSlice)
	url := append(pathSlice, r.Host)
	url = append([]string{basezone}, url...)
	return strings.Join(url, "."), from, ps, nil
}

// getFinalRecord finds the final TXT record for the given zone.
// It will try wildcards if the first zone return error
func getFinalRecord(zone string, from int, c Config, w http.ResponseWriter, r *http.Request, pathSlice []string) (record, error) {
	txts, err := query(zone, r.Context(), c)
	if err != nil {
		// if nothing found, jump into wildcards
		for i := 1; i <= from && len(txts) == 0; i++ {
			zoneSlice := strings.Split(zone, ".")
			zoneSlice[i] = "_"
			zone = strings.Join(zoneSlice, ".")
			txts, err = query(zone, r.Context(), c)
		}
	}
	if err != nil || len(txts) == 0 {
		return record{}, fmt.Errorf("could not get TXT record: %s", err)
	}

	txts[0], err = parsePlaceholders(txts[0], r, pathSlice)
	rec := record{}
	if err = rec.Parse(txts[0], w, r, c); err != nil {
		return rec, fmt.Errorf("could not parse record: %s", err)
	}

	if rec.Type == "path" {
		return rec, fmt.Errorf("chaining path is not currently supported")
	}

	return rec, nil
}

// reverse reverses the order of the array
func reverse(input []string) {
	last := len(input) - 1
	for i := 0; i < len(input)/2; i++ {
		input[i], input[last-i] = input[last-i], input[i]
	}
}

func sortMap(m map[string]string) []string {
	var keys []string
	var result []string
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	for _, k := range keys {
		result = append(result, m[k])
	}
	return result
}
<commit_msg>(path): Update comment<commit_after>/*
Copyright 2019 - The TXTDirect Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package txtdirect

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

var PathRegex = regexp.MustCompile("\\/([A-Za-z0-9-._~!$'()*+,;=:@]+)")
var FromRegex = regexp.MustCompile("\\/\\$(\\d+)")
var GroupRegex = regexp.MustCompile("P<[a-zA-Z]+[a-zA-Z0-9]*>")
var GroupOrderRegex = regexp.MustCompile("P<([a-zA-Z]+[a-zA-Z0-9]*)>")

// zoneFromPath generates a DNS zone with the given request's path and host
// It will use custom regex to parse the path if it's provided in
// the given record.
func zoneFromPath(r *http.Request, rec record) (string, int, []string, error) {
	path := fmt.Sprintf("%s?%s", r.URL.Path, r.URL.RawQuery)

	if strings.ContainsAny(path, ".") {
		path = strings.Replace(path, ".", "-", -1)
	}
	pathSubmatchs := PathRegex.FindAllStringSubmatch(path, -1)
	if rec.Re != "" {
		CustomRegex, err := regexp.Compile(rec.Re)
		if err != nil {
			log.Printf("<%s> [txtdirect]: the given regex doesn't work as expected: %s", time.Now().String(), rec.Re)
		}
		pathSubmatchs = CustomRegex.FindAllStringSubmatch(path, -1)
		if GroupRegex.MatchString(rec.Re) {
			pathSlice := []string{}
			unordered := make(map[string]string)
			for _, item := range pathSubmatchs[0] {
				pathSlice = append(pathSlice, item)
			}
			order := GroupOrderRegex.FindAllStringSubmatch(rec.Re, -1)
			for i, group := range order {
				unordered[group[1]] = pathSlice[i+1]
			}
			url := sortMap(unordered)
			*r = *r.WithContext(context.WithValue(r.Context(), "regexMatches", unordered))
			reverse(url)
			from := len(pathSlice)
			url = append(url, r.Host)
			url = append([]string{basezone}, url...)
			return strings.Join(url, "."), from, pathSlice, nil
		}
	}
	pathSlice := []string{}
	for _, v := range pathSubmatchs {
		pathSlice = append(pathSlice, v[1])
	}
	*r = *r.WithContext(context.WithValue(r.Context(), "regexMatches", pathSlice))
	if len(pathSlice) < 1 && rec.Re != "" {
		log.Printf("<%s> [txtdirect]: custom regex doesn't work on %s", time.Now().String(), path)
	}
	from := len(pathSlice)
	if rec.From != "" {
		fromSubmatch := FromRegex.FindAllStringSubmatch(rec.From, -1)
		if len(fromSubmatch) != len(pathSlice) {
			return "", 0, []string{}, fmt.Errorf("length of path doesn't match with length of from= in record")
		}
		fromSlice := make(map[int]string)
		for k, v := range fromSubmatch {
			index, _ := strconv.Atoi(v[1])
			fromSlice[index] = pathSlice[k]
		}

		keys := []int{}
		for k := range fromSlice {
			keys = append(keys, k)
		}
		if len(keys) != len(pathSlice) {
			return "", 0, []string{}, fmt.Errorf("length of path doesn't match with length of from= in record")
		}
		generatedPath := []string{}

		sort.Sort(sort.Reverse(sort.IntSlice(keys)))

		for _, k := range keys {
			generatedPath = append(generatedPath, fromSlice[k])
		}

		url := append(generatedPath, r.Host)
		url = append([]string{basezone}, url...)
		return strings.Join(url, "."), from, pathSlice, nil
	}
	ps := pathSlice
	reverse(pathSlice)
	url := append(pathSlice, r.Host)
	url = append([]string{basezone}, url...)
	return strings.Join(url, "."), from, ps, nil
}

// getFinalRecord finds the final TXT record for the given zone.
// It will try wildcards if the first zone return error
func getFinalRecord(zone string, from int, c Config, w http.ResponseWriter, r *http.Request, pathSlice []string) (record, error) {
	txts, err := query(zone, r.Context(), c)
	if err != nil {
		// if nothing found, jump into wildcards
		for i := 1; i <= from && len(txts) == 0; i++ {
			zoneSlice := strings.Split(zone, ".")
			zoneSlice[i] = "_"
			zone = strings.Join(zoneSlice, ".")
			txts, err = query(zone, r.Context(), c)
		}
	}
	if err != nil || len(txts) == 0 {
		return record{}, fmt.Errorf("could not get TXT record: %s", err)
	}

	txts[0], err = parsePlaceholders(txts[0], r, pathSlice)
	rec := record{}
	if err = rec.Parse(txts[0], w, r, c); err != nil {
		return rec, fmt.Errorf("could not parse record: %s", err)
	}

	if rec.Type == "path" {
		return rec, fmt.Errorf("chaining path is not currently supported")
	}

	return rec, nil
}

// reverse reverses the order of the array
func reverse(input []string) {
	last := len(input) - 1
	for i := 0; i < len(input)/2; i++ {
		input[i], input[last-i] = input[last-i], input[i]
	}
}

func sortMap(m map[string]string) []string {
	var keys []string
	var result []string
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	for _, k := range keys {
		result = append(result, m[k])
	}
	return result
}
<|endoftext|>


<commit_before>package pget

import (
	"bufio"
	"os"
	"runtime"
	"strings"

	"github.com/asaskevich/govalidator"
	"github.com/pkg/errors"
)

const (
	version = "0.0.4"
	msg     = "Pget v" + version + ", parallel file download client\n"
)

// Pget structs
type Pget struct {
	Trace bool
	Utils
	TargetDir  string
	Procs      int
	URLs       []string
	TargetURLs []string
	args       []string
	timeout    int
	useragent  string
}

type ignore struct {
	err error
}

type cause interface {
	Cause() error
}

// New for pget package
func New() *Pget {
	return &Pget{
		Trace:   false,
		Utils:   &Data{},
		Procs:   runtime.NumCPU(), // default
		timeout: 10,
	}
}

// ErrTop get important message from wrapped error message
func (pget Pget) ErrTop(err error) error {
	for e := err; e != nil; {
		switch e.(type) {
		case ignore:
			return nil
		case cause:
			e = e.(cause).Cause()
		default:
			return e
		}
	}

	return nil
}

// Run execute methods in pget package
func (pget *Pget) Run() error {
	if err := pget.Ready(); err != nil {
		return pget.ErrTop(err)
	}

	if err := pget.Checking(); err != nil {
		return errors.Wrap(err, "failed to check header")
	}

	if err := pget.Download(); err != nil {
		return err
	}

	if err := pget.Utils.BindwithFiles(pget.Procs); err != nil {
		return err
	}

	return nil
}

// Ready method define the variables required to Download.
func (pget *Pget) Ready() error {
	if procs := os.Getenv("GOMAXPROCS"); procs == "" {
		runtime.GOMAXPROCS(pget.Procs)
	}

	var opts Options
	if err := pget.parseOptions(&opts, os.Args[1:]); err != nil {
		return errors.Wrap(err, "failed to parse command line args")
	}

	if opts.Trace {
		pget.Trace = opts.Trace
	}

	if opts.Procs > 2 {
		pget.Procs = opts.Procs
	}

	if opts.Timeout > 0 {
		pget.timeout = opts.Timeout
	}

	if err := pget.parseURLs(); err != nil {
		return errors.Wrap(err, "failed to parse of url")
	}

	if opts.UserAgent != "" {
		pget.useragent = opts.UserAgent
	}

	if opts.TargetDir != "" {
		info, err := os.Stat(opts.TargetDir)
		if err != nil {
			if !os.IsNotExist(err) {
				return errors.Wrap(err, "target dir is invalid")
			}

			if err := os.MkdirAll(opts.TargetDir, 0755); err != nil {
				return errors.Wrapf(err, "failed to create diretory at %s", opts.TargetDir)
			}

		} else if !info.IsDir() {
			return errors.New("target dir is not a valid directory")
		}
	}
	opts.TargetDir = strings.TrimSuffix(opts.TargetDir, "/")
	pget.TargetDir = opts.TargetDir

	return nil
}

func (pget Pget) makeIgnoreErr() ignore {
	return ignore{
		err: errors.New("this is ignore message"),
	}
}

// Error for options: version, usage
func (i ignore) Error() string {
	return i.err.Error()
}

func (i ignore) Cause() error {
	return i.err
}

func (pget *Pget) parseOptions(opts *Options, argv []string) error {

	if len(argv) == 0 {
		os.Stdout.Write(opts.usage())
		return pget.makeIgnoreErr()
	}

	o, err := opts.parse(argv)
	if err != nil {
		return errors.Wrap(err, "failed to parse command line options")
	}

	if opts.Help {
		os.Stdout.Write(opts.usage())
		return pget.makeIgnoreErr()
	}

	if opts.Version {
		os.Stdout.Write([]byte(msg))
		return pget.makeIgnoreErr()
	}

	if opts.Update {
		result, err := opts.isupdate()
		if err != nil {
			return errors.Wrap(err, "failed to parse command line options")
		}

		os.Stdout.Write(result)
		return pget.makeIgnoreErr()
	}

	pget.args = o

	return nil
}

func (pget *Pget) parseURLs() error {

	// find url in args
	for _, argv := range pget.args {
		if govalidator.IsURL(argv) {
			pget.URLs = append(pget.URLs, argv)
		}
	}

	if len(pget.URLs) < 1 {
		scanner := bufio.NewScanner(os.Stdin)
		for scanner.Scan() {
			scan := scanner.Text()
			urls := strings.Split(scan, " ")
			for _, url := range urls {
				if govalidator.IsURL(url) {
					pget.URLs = append(pget.URLs, url)
				}
			}
		}

		if err := scanner.Err(); err != nil {
			return errors.Wrap(err, "failed to parse url from stdin")
		}

		if len(pget.URLs) < 1 {
			return errors.New("urls not found in the arguments passed")
		}
	}

	return nil
}
<commit_msg>Added input messages for stdin<commit_after>package pget

import (
	"bufio"
	"fmt"
	"os"
	"runtime"
	"strings"

	"github.com/asaskevich/govalidator"
	"github.com/pkg/errors"
)

const (
	version = "0.0.4"
	msg     = "Pget v" + version + ", parallel file download client\n"
)

// Pget structs
type Pget struct {
	Trace bool
	Utils
	TargetDir  string
	Procs      int
	URLs       []string
	TargetURLs []string
	args       []string
	timeout    int
	useragent  string
}

type ignore struct {
	err error
}

type cause interface {
	Cause() error
}

// New for pget package
func New() *Pget {
	return &Pget{
		Trace:   false,
		Utils:   &Data{},
		Procs:   runtime.NumCPU(), // default
		timeout: 10,
	}
}

// ErrTop get important message from wrapped error message
func (pget Pget) ErrTop(err error) error {
	for e := err; e != nil; {
		switch e.(type) {
		case ignore:
			return nil
		case cause:
			e = e.(cause).Cause()
		default:
			return e
		}
	}

	return nil
}

// Run execute methods in pget package
func (pget *Pget) Run() error {
	if err := pget.Ready(); err != nil {
		return pget.ErrTop(err)
	}

	if err := pget.Checking(); err != nil {
		return errors.Wrap(err, "failed to check header")
	}

	if err := pget.Download(); err != nil {
		return err
	}

	if err := pget.Utils.BindwithFiles(pget.Procs); err != nil {
		return err
	}

	return nil
}

// Ready method define the variables required to Download.
func (pget *Pget) Ready() error {
	if procs := os.Getenv("GOMAXPROCS"); procs == "" {
		runtime.GOMAXPROCS(pget.Procs)
	}

	var opts Options
	if err := pget.parseOptions(&opts, os.Args[1:]); err != nil {
		return errors.Wrap(err, "failed to parse command line args")
	}

	if opts.Trace {
		pget.Trace = opts.Trace
	}

	if opts.Procs > 2 {
		pget.Procs = opts.Procs
	}

	if opts.Timeout > 0 {
		pget.timeout = opts.Timeout
	}

	if err := pget.parseURLs(); err != nil {
		return errors.Wrap(err, "failed to parse of url")
	}

	if opts.UserAgent != "" {
		pget.useragent = opts.UserAgent
	}

	if opts.TargetDir != "" {
		info, err := os.Stat(opts.TargetDir)
		if err != nil {
			if !os.IsNotExist(err) {
				return errors.Wrap(err, "target dir is invalid")
			}

			if err := os.MkdirAll(opts.TargetDir, 0755); err != nil {
				return errors.Wrapf(err, "failed to create diretory at %s", opts.TargetDir)
			}

		} else if !info.IsDir() {
			return errors.New("target dir is not a valid directory")
		}
	}
	opts.TargetDir = strings.TrimSuffix(opts.TargetDir, "/")
	pget.TargetDir = opts.TargetDir

	return nil
}

func (pget Pget) makeIgnoreErr() ignore {
	return ignore{
		err: errors.New("this is ignore message"),
	}
}

// Error for options: version, usage
func (i ignore) Error() string {
	return i.err.Error()
}

func (i ignore) Cause() error {
	return i.err
}

func (pget *Pget) parseOptions(opts *Options, argv []string) error {

	if len(argv) == 0 {
		os.Stdout.Write(opts.usage())
		return pget.makeIgnoreErr()
	}

	o, err := opts.parse(argv)
	if err != nil {
		return errors.Wrap(err, "failed to parse command line options")
	}

	if opts.Help {
		os.Stdout.Write(opts.usage())
		return pget.makeIgnoreErr()
	}

	if opts.Version {
		os.Stdout.Write([]byte(msg))
		return pget.makeIgnoreErr()
	}

	if opts.Update {
		result, err := opts.isupdate()
		if err != nil {
			return errors.Wrap(err, "failed to parse command line options")
		}

		os.Stdout.Write(result)
		return pget.makeIgnoreErr()
	}

	pget.args = o

	return nil
}

func (pget *Pget) parseURLs() error {

	// find url in args
	for _, argv := range pget.args {
		if govalidator.IsURL(argv) {
			pget.URLs = append(pget.URLs, argv)
		}
	}

	if len(pget.URLs) < 1 {
		fmt.Fprintf(os.Stdout, "Please input url separate with space or newline\n")
		fmt.Fprintf(os.Stdout, "Start download at ^D\n")
		scanner := bufio.NewScanner(os.Stdin)
		for scanner.Scan() {
			scan := scanner.Text()
			urls := strings.Split(scan, " ")
			for _, url := range urls {
				if govalidator.IsURL(url) {
					pget.URLs = append(pget.URLs, url)
				}
			}
		}

		if err := scanner.Err(); err != nil {
			return errors.Wrap(err, "failed to parse url from stdin")
		}

		if len(pget.URLs) < 1 {
			return errors.New("urls not found in the arguments passed")
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package pick

import (
	"io"

	"golang.org/x/net/html"
)

type Option struct {
	PageSource io.Reader
	TagName    string
	Attr       *Attr // optional
}

type Attr struct {
	Label string
	Value string
}

func PickAttr(option *Option, AttrLabel string, limit int) (res []string) {
	if option == nil || option.PageSource == nil {
		return
	}

	z := html.NewTokenizer(option.PageSource)

	for {
		tokenType := z.Next()

		switch tokenType {

		// ignore the error token
		// quit on eof
		case html.ErrorToken:
			if z.Err() == io.EOF {
				return
			}

		case html.SelfClosingTagToken:
			fallthrough
		case html.StartTagToken:
			tagName, attr := z.TagName()

			if string(tagName) != option.TagName {
				continue
			}

			var label, value []byte

			matched := false
			tmpRes := []string{}

			// get attr
			for attr {
				label, value, attr = z.TagAttr()

				labelStr := string(label)
				valueStr := string(value)

				// check the attr
				if option.Attr == nil || (option.Attr.Label == labelStr && option.Attr.Value == valueStr) {
					matched = true
				}

				// get the result - even the matched false or true
				if labelStr == AttrLabel {
					tmpRes = append(tmpRes, valueStr)
				}
			}

			// skip the non matched one
			if !matched {
				continue
			}

			// send the result for matched only
			res = append(res, tmpRes...)

			// return when limit
			if limit > 0 && len(res) >= limit {
				return
			}
		}
	}

	return
}

func PickText(option *Option) (res []string) {
	if option == nil || option.PageSource == nil {
		return
	}

	z := html.NewTokenizer(option.PageSource)

	depth := 0

	for {
		tokenType := z.Next()

		switch tokenType {

		// ignore the error token
		// quit on eof
		case html.ErrorToken:
			if z.Err() == io.EOF {
				return
			}

		case html.TextToken:
			if depth > 0 {
				res = append(res, string(z.Text()))
			}

		case html.EndTagToken:
			if depth > 0 {
				depth--
			}

		case html.StartTagToken:
			if depth > 0 {
				depth++
				continue
			}

			tagName, attr := z.TagName()

			if string(tagName) != option.TagName {
				continue
			}

			var label, value []byte

			matched := false

			// get attr
			for attr {
				label, value, attr = z.TagAttr()

				// TODO: break when found
				if option.Attr == nil || (option.Attr.Label == string(label) && option.Attr.Value == string(value)) {
					matched = true
				}
			}

			if !matched {
				continue
			}

			depth++
		}
	}

	return
}
<commit_msg>revise #PickAttr cases<commit_after>package pick

import (
	"io"

	"golang.org/x/net/html"
)

type Option struct {
	PageSource io.Reader
	TagName    string
	Attr       *Attr // optional
}

type Attr struct {
	Label string
	Value string
}

func PickAttr(option *Option, AttrLabel string, limit int) (res []string) {
	if option == nil || option.PageSource == nil {
		return
	}

	z := html.NewTokenizer(option.PageSource)

	for {
		tokenType := z.Next()

		switch tokenType {

		// ignore the error token
		// quit on eof
		case html.ErrorToken:
			if z.Err() == io.EOF {
				return
			}

		case html.StartTagToken, html.SelfClosingTagToken:
			tagName, attr := z.TagName()

			if string(tagName) != option.TagName {
				continue
			}

			var label, value []byte

			matched := false
			tmpRes := []string{}

			// get attr
			for attr {
				label, value, attr = z.TagAttr()

				labelStr := string(label)
				valueStr := string(value)

				// check the attr
				if option.Attr == nil || (option.Attr.Label == labelStr && option.Attr.Value == valueStr) {
					matched = true
				}

				// get the result - even the matched false or true
				if labelStr == AttrLabel {
					tmpRes = append(tmpRes, valueStr)
				}
			}

			// skip the non matched one
			if !matched {
				continue
			}

			// send the result for matched only
			res = append(res, tmpRes...)

			// return when limit
			if limit > 0 && len(res) >= limit {
				return
			}
		}
	}

	return
}

func PickText(option *Option) (res []string) {
	if option == nil || option.PageSource == nil {
		return
	}

	z := html.NewTokenizer(option.PageSource)

	depth := 0

	for {
		tokenType := z.Next()

		switch tokenType {

		// ignore the error token
		// quit on eof
		case html.ErrorToken:
			if z.Err() == io.EOF {
				return
			}

		case html.TextToken:
			if depth > 0 {
				res = append(res, string(z.Text()))
			}

		case html.EndTagToken:
			if depth > 0 {
				depth--
			}

		case html.StartTagToken:
			if depth > 0 {
				depth++
				continue
			}

			tagName, attr := z.TagName()

			if string(tagName) != option.TagName {
				continue
			}

			var label, value []byte

			matched := false

			// get attr
			for attr {
				label, value, attr = z.TagAttr()

				// TODO: break when found
				if option.Attr == nil || (option.Attr.Label == string(label) && option.Attr.Value == string(value)) {
					matched = true
				}
			}

			if !matched {
				continue
			}

			depth++
		}
	}

	return
}
<|endoftext|>