<commit_before>// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

// 分页管理.
package gpage

import (
    "fmt"
    "math"
    "strings"
    url2 "net/url"
    "gitee.com/johng/gf/g/util/gconv"
)

// 分页对象
type Page struct {
    Url            *url2.URL // 当前页面的URL对象
    Route          string    // 当前页面的路由规则(在静态分页下有效)
    TotalSize      int       // 总共数据条数
    TotalPage      int       // 总页数
    CurrentPage    int       // 当前页码
    PageName       string    // 分页参数名称(GET参数)
    NextPageTag    string    // 下一页标签
    PrevPageTag    string    // 上一页标签
    FirstPageTag   string    // 首页标签
    LastPageTag    string    // 尾页标签
    PrevBar        string    // 上一分页条
    NextBar        string    // 下一分页条
    PageBarNum     int       // 控制分页条的数量
    AjaxActionName string    // AJAX方法名，当该属性有值时，表示使用AJAX分页
}

// 创建一个分页对象，输入参数分别为：
// 总数量、每页数量、当前页码、当前的URL(可以只是URI+QUERY)、(可选)路由规则(例如: /user/list/:page、/order/list/*page)
func New(TotalSize, perPage int,  CurrentPage interface{}, url string, route...string) *Page {
    u, _ := url2.Parse(url)
    page := &Page {
        PageName     : "page",
        PrevPageTag  : "<",
        NextPageTag  : ">",
        FirstPageTag : "|<",
        LastPageTag  : ">|",
        PrevBar      : "<<",
        NextBar      : ">>",
        TotalSize    : TotalSize,
        TotalPage    : int(math.Ceil(float64(TotalSize/perPage))),
        CurrentPage  : 1,
        PageBarNum   : 10,
        Url          : u,
    }
    curPage := gconv.Int(CurrentPage)
    if curPage > 0 {
        page.CurrentPage = curPage
    }
    if len(route) > 0 {
        page.Route = route[0]
    }
    return page
}

// 启用AJAX分页
func (page *Page) EnableAjax(actionName string) {
    page.AjaxActionName = actionName
}

// 获取显示"下一页"的内容.
func (page *Page) NextPage(styles ... string) string {
    var curStyle, style string
    if len(styles) > 0 {
        curStyle = styles[0]
    }
    if len(styles) > 1 {
        style    = styles[0]
    }
    if page.CurrentPage < page.TotalPage {
        return page.GetLink(page.GetUrl(page.CurrentPage + 1), page.NextPageTag, "下一页", style)
    }
    return fmt.Sprintf(`<span class="%s">%s</span>`, curStyle, page.NextPageTag)
}

/// 获取显示“上一页”的内容
func (page *Page) PrevPage(styles ... string) string {
    var curStyle, style string
    if len(styles) > 0 {
        curStyle = styles[0]
    }
    if len(styles) > 1 {
        style    = styles[0]
    }
    if page.CurrentPage > 1 {
        return page.GetLink(page.GetUrl(page.CurrentPage - 1), page.PrevPageTag, "上一页", style)
    }
    return fmt.Sprintf(`<span class="%s">%s</span>`, curStyle, page.PrevPageTag)
}

/**
* 获取显示“首页”的代码
*
* @return string
*/
func (page *Page) FirstPage(styles ... string) string {
    var curStyle, style string
    if len(styles) > 0 {
        curStyle = styles[0]
    }
    if len(styles) > 1 {
        style    = styles[0]
    }
    if page.CurrentPage == 1 {
        return fmt.Sprintf(`<span class="%s">%s</span>`, curStyle, page.FirstPageTag)
    }
    return page.GetLink(page.GetUrl(1), page.FirstPageTag, "第一页", style)
}

// 获取显示“尾页”的内容
func (page *Page) LastPage(styles ... string) string {
    var curStyle, style string
    if len(styles) > 0 {
        curStyle = styles[0]
    }
    if len(styles) > 1 {
        style    = styles[0]
    }
    if page.CurrentPage == page.TotalPage {
        return fmt.Sprintf(`<span class="%s">%s</span>`, curStyle, page.LastPageTag)
    }
    return page.GetLink(page.GetUrl(page.TotalPage), page.LastPageTag, "最后页", style)
}

// 获得分页条列表内容
func (page *Page) PageBar(styles ... string) string {
    var curStyle, style string
    if len(styles) > 0 {
        curStyle = styles[0]
    }
    if len(styles) > 1 {
        style    = styles[0]
    }
    plus := int(math.Ceil(float64(page.PageBarNum / 2)))
    if page.PageBarNum - plus + page.CurrentPage > page.TotalPage {
        plus = page.PageBarNum - page.TotalPage + page.CurrentPage
    }
    begin := page.CurrentPage - plus + 1
    if begin < 1 {
        begin = 1
    }
    ret := ""
    for i := begin; i < begin + page.PageBarNum; i++ {
        if i <= page.TotalPage {
            if i != page.CurrentPage {
                ret += page.GetLink(page.GetUrl(i), gconv.String(i), style, "")
            } else {
                ret += fmt.Sprintf(`<span class="%s">%d</span>`, curStyle, i)
            }
        } else {
            break
        }
    }
    return ret
}
// 获取基于select标签的显示跳转按钮的代码
func (page *Page) SelectBar() string {
    ret := `<select name="gpage_select" onchange="window.location.href=this.value">`
    for i := 1; i <= page.TotalPage; i++ {
        if i == page.CurrentPage {
            ret += fmt.Sprintf(`<option value="%s" selected>%d</option>`, page.GetUrl(i), i)
        } else {
            ret += fmt.Sprintf(`<option value="%s">%d</option>`, page.GetUrl(i), i)
        }
    }
    ret += "</select>"
    return ret
}

// 预定义的分页显示风格内容
func (page *Page) GetContent(mode int) string {
    switch mode {
        case 1:
            page.NextPageTag = "下一页"
            page.PrevPageTag = "上一页"
            return fmt.Sprintf(
                `%s <span class="current">%d</span> %s`,
                page.PrevPage(),
                page.CurrentPage,
                page.NextPage(),
            )

        case 2:
            page.NextPageTag  = "下一页>>"
            page.PrevPageTag  = "<<上一页"
            page.FirstPageTag = "首页"
            page.LastPageTag  = "尾页"
            return fmt.Sprintf(
                `%s%s<span class="current">[第%d页]</span>%s%s第%s页`,
                page.FirstPage(),
                page.PrevPage(),
                page.CurrentPage,
                page.NextPage(),
                page.LastPage(),
                page.SelectBar(),
            )

        case 3:
            page.NextPageTag  = "下一页"
            page.PrevPageTag  = "上一页"
            page.FirstPageTag = "首页"
            page.LastPageTag  = "尾页"
            pageStr := page.FirstPage()
            pageStr += page.PrevPage()
            pageStr += page.PageBar("current")
            pageStr += page.NextPage()
            pageStr += page.LastPage()
            pageStr += fmt.Sprintf(
                `<span>当前页%d/%d</span> <span>共%d条</span>`,
                page.CurrentPage,
                page.TotalPage,
                page.TotalSize,
            )
            return pageStr

        case 4:
            page.NextPageTag  = "下一页"
            page.PrevPageTag  = "上一页"
            page.FirstPageTag = "首页"
            page.LastPageTag  = "尾页"
            pageStr := page.FirstPage()
            pageStr += page.PrevPage()
            pageStr += page.PageBar("current")
            pageStr += page.NextPage()
            pageStr += page.LastPage()
            return pageStr
    }
    return ""
}

// 为指定的页面返回地址值
func (page *Page) GetUrl(pageNo int) string {
    url := *page.Url
    if len(page.Route) > 0 {
        // 这里基于路由匹配的URL页码替换比较简单，但能满足绝大多数场景
        index := -1
        array := strings.Split(page.Route, "/")
        for k, v := range array {
            if strings.EqualFold(v, ":" + page.PageName) || strings.EqualFold(v, "*" + page.PageName) {
                index = k
                break
            }
        }
        // 替换url.Path中的分页码
        if index != -1 {
            pathArray := strings.Split(page.Url.Path, "/")
            for i := 0; i <= index - len(pathArray); i++ {
                pathArray = append(pathArray, "")
            }
            pathArray[index] = gconv.String(pageNo)
            url.Path         = strings.TrimRight(strings.Join(pathArray, "/"), "/")
            return url.String()
        }
    }
    values := page.Url.Query()
    values.Set(page.PageName, gconv.String(pageNo))
    url.RawQuery = values.Encode()
    return url.String()
}

// 获取链接地址
func (page *Page) GetLink(url, text, title, style string) string {
    if len(style) > 0 {
        style = fmt.Sprintf(`class="%s" `, style)
    }
    if len(page.AjaxActionName) > 0 {
        return fmt.Sprintf(`<a %shref='#' onclick="%s('%s')">%s</a>`, style, page.AjaxActionName, url, text)
    } else {
        return fmt.Sprintf(`<a %shref="%s" title="%s">%s</a>`, style, url, title, text)
    }
}

<commit_msg>修复gpage包分页计数问题<commit_after>// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

// 分页管理.
package gpage

import (
    "fmt"
    "math"
    "strings"
    url2 "net/url"
    "gitee.com/johng/gf/g/util/gconv"
)

// 分页对象
type Page struct {
    Url            *url2.URL // 当前页面的URL对象
    Route          string    // 当前页面的路由规则(在静态分页下有效)
    TotalSize      int       // 总共数据条数
    TotalPage      int       // 总页数
    CurrentPage    int       // 当前页码
    PageName       string    // 分页参数名称(GET参数)
    NextPageTag    string    // 下一页标签
    PrevPageTag    string    // 上一页标签
    FirstPageTag   string    // 首页标签
    LastPageTag    string    // 尾页标签
    PrevBar        string    // 上一分页条
    NextBar        string    // 下一分页条
    PageBarNum     int       // 控制分页条的数量
    AjaxActionName string    // AJAX方法名，当该属性有值时，表示使用AJAX分页
}

// 创建一个分页对象，输入参数分别为：
// 总数量、每页数量、当前页码、当前的URL(可以只是URI+QUERY)、(可选)路由规则(例如: /user/list/:page、/order/list/*page)
func New(TotalSize, perPage int,  CurrentPage interface{}, url string, route...string) *Page {
    u, _ := url2.Parse(url)
    page := &Page {
        PageName     : "page",
        PrevPageTag  : "<",
        NextPageTag  : ">",
        FirstPageTag : "|<",
        LastPageTag  : ">|",
        PrevBar      : "<<",
        NextBar      : ">>",
        TotalSize    : TotalSize,
        TotalPage    : int(math.Ceil(float64(TotalSize)/float64(perPage))),
        CurrentPage  : 1,
        PageBarNum   : 10,
        Url          : u,
    }
    curPage := gconv.Int(CurrentPage)
    if curPage > 0 {
        page.CurrentPage = curPage
    }
    if len(route) > 0 {
        page.Route = route[0]
    }
    return page
}

// 启用AJAX分页
func (page *Page) EnableAjax(actionName string) {
    page.AjaxActionName = actionName
}

// 获取显示"下一页"的内容.
func (page *Page) NextPage(styles ... string) string {
    var curStyle, style string
    if len(styles) > 0 {
        curStyle = styles[0]
    }
    if len(styles) > 1 {
        style    = styles[0]
    }
    if page.CurrentPage < page.TotalPage {
        return page.GetLink(page.GetUrl(page.CurrentPage + 1), page.NextPageTag, "下一页", style)
    }
    return fmt.Sprintf(`<span class="%s">%s</span>`, curStyle, page.NextPageTag)
}

/// 获取显示“上一页”的内容
func (page *Page) PrevPage(styles ... string) string {
    var curStyle, style string
    if len(styles) > 0 {
        curStyle = styles[0]
    }
    if len(styles) > 1 {
        style    = styles[0]
    }
    if page.CurrentPage > 1 {
        return page.GetLink(page.GetUrl(page.CurrentPage - 1), page.PrevPageTag, "上一页", style)
    }
    return fmt.Sprintf(`<span class="%s">%s</span>`, curStyle, page.PrevPageTag)
}

/**
* 获取显示“首页”的代码
*
* @return string
*/
func (page *Page) FirstPage(styles ... string) string {
    var curStyle, style string
    if len(styles) > 0 {
        curStyle = styles[0]
    }
    if len(styles) > 1 {
        style    = styles[0]
    }
    if page.CurrentPage == 1 {
        return fmt.Sprintf(`<span class="%s">%s</span>`, curStyle, page.FirstPageTag)
    }
    return page.GetLink(page.GetUrl(1), page.FirstPageTag, "第一页", style)
}

// 获取显示“尾页”的内容
func (page *Page) LastPage(styles ... string) string {
    var curStyle, style string
    if len(styles) > 0 {
        curStyle = styles[0]
    }
    if len(styles) > 1 {
        style    = styles[0]
    }
    if page.CurrentPage == page.TotalPage {
        return fmt.Sprintf(`<span class="%s">%s</span>`, curStyle, page.LastPageTag)
    }
    return page.GetLink(page.GetUrl(page.TotalPage), page.LastPageTag, "最后页", style)
}

// 获得分页条列表内容
func (page *Page) PageBar(styles ... string) string {
    var curStyle, style string
    if len(styles) > 0 {
        curStyle = styles[0]
    }
    if len(styles) > 1 {
        style    = styles[0]
    }
    plus := int(math.Ceil(float64(page.PageBarNum / 2)))
    if page.PageBarNum - plus + page.CurrentPage > page.TotalPage {
        plus = page.PageBarNum - page.TotalPage + page.CurrentPage
    }
    begin := page.CurrentPage - plus + 1
    if begin < 1 {
        begin = 1
    }
    ret := ""
    for i := begin; i < begin + page.PageBarNum; i++ {
        if i <= page.TotalPage {
            if i != page.CurrentPage {
                ret += page.GetLink(page.GetUrl(i), gconv.String(i), style, "")
            } else {
                ret += fmt.Sprintf(`<span class="%s">%d</span>`, curStyle, i)
            }
        } else {
            break
        }
    }
    return ret
}
// 获取基于select标签的显示跳转按钮的代码
func (page *Page) SelectBar() string {
    ret := `<select name="gpage_select" onchange="window.location.href=this.value">`
    for i := 1; i <= page.TotalPage; i++ {
        if i == page.CurrentPage {
            ret += fmt.Sprintf(`<option value="%s" selected>%d</option>`, page.GetUrl(i), i)
        } else {
            ret += fmt.Sprintf(`<option value="%s">%d</option>`, page.GetUrl(i), i)
        }
    }
    ret += "</select>"
    return ret
}

// 预定义的分页显示风格内容
func (page *Page) GetContent(mode int) string {
    switch mode {
        case 1:
            page.NextPageTag = "下一页"
            page.PrevPageTag = "上一页"
            return fmt.Sprintf(
                `%s <span class="current">%d</span> %s`,
                page.PrevPage(),
                page.CurrentPage,
                page.NextPage(),
            )

        case 2:
            page.NextPageTag  = "下一页>>"
            page.PrevPageTag  = "<<上一页"
            page.FirstPageTag = "首页"
            page.LastPageTag  = "尾页"
            return fmt.Sprintf(
                `%s%s<span class="current">[第%d页]</span>%s%s第%s页`,
                page.FirstPage(),
                page.PrevPage(),
                page.CurrentPage,
                page.NextPage(),
                page.LastPage(),
                page.SelectBar(),
            )

        case 3:
            page.NextPageTag  = "下一页"
            page.PrevPageTag  = "上一页"
            page.FirstPageTag = "首页"
            page.LastPageTag  = "尾页"
            pageStr := page.FirstPage()
            pageStr += page.PrevPage()
            pageStr += page.PageBar("current")
            pageStr += page.NextPage()
            pageStr += page.LastPage()
            pageStr += fmt.Sprintf(
                `<span>当前页%d/%d</span> <span>共%d条</span>`,
                page.CurrentPage,
                page.TotalPage,
                page.TotalSize,
            )
            return pageStr

        case 4:
            page.NextPageTag  = "下一页"
            page.PrevPageTag  = "上一页"
            page.FirstPageTag = "首页"
            page.LastPageTag  = "尾页"
            pageStr := page.FirstPage()
            pageStr += page.PrevPage()
            pageStr += page.PageBar("current")
            pageStr += page.NextPage()
            pageStr += page.LastPage()
            return pageStr
    }
    return ""
}

// 为指定的页面返回地址值
func (page *Page) GetUrl(pageNo int) string {
    url := *page.Url
    if len(page.Route) > 0 {
        // 这里基于路由匹配的URL页码替换比较简单，但能满足绝大多数场景
        index := -1
        array := strings.Split(page.Route, "/")
        for k, v := range array {
            if strings.EqualFold(v, ":" + page.PageName) || strings.EqualFold(v, "*" + page.PageName) {
                index = k
                break
            }
        }
        // 替换url.Path中的分页码
        if index != -1 {
            pathArray := strings.Split(page.Url.Path, "/")
            for i := 0; i <= index - len(pathArray); i++ {
                pathArray = append(pathArray, "")
            }
            pathArray[index] = gconv.String(pageNo)
            url.Path         = strings.TrimRight(strings.Join(pathArray, "/"), "/")
            return url.String()
        }
    }
    values := page.Url.Query()
    values.Set(page.PageName, gconv.String(pageNo))
    url.RawQuery = values.Encode()
    return url.String()
}

// 获取链接地址
func (page *Page) GetLink(url, text, title, style string) string {
    if len(style) > 0 {
        style = fmt.Sprintf(`class="%s" `, style)
    }
    if len(page.AjaxActionName) > 0 {
        return fmt.Sprintf(`<a %shref='#' onclick="%s('%s')">%s</a>`, style, page.AjaxActionName, url, text)
    } else {
        return fmt.Sprintf(`<a %shref="%s" title="%s">%s</a>`, style, url, title, text)
    }
}

<|endoftext|>


<commit_before>// Copyright 2013 Jari Takkala. 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 (
	"errors"
	"fmt"
)

type Torrent struct {
	tracker Tracker
	metaInfo MetaInfo
	infoHash []byte
	uploaded int
	downloaded int
	left int
}

// Multiple File Mode
type Files struct {
	Length int
	Md5sum string
	Path   []string
}

// Info dictionary
type Info struct {
	PieceLength int "piece length"
	Pieces      string
	Private     int
	Name        string
	Length      int
	Md5sum      string
	Files       []Files
}

// Metainfo structure
type MetaInfo struct {
	Info         Info
	Announce     string
	AnnounceList [][]string "announce-list"
	CreationDate int        "creation date"
	Comment      string
	CreatedBy    string "created by"
	Encoding     string
}

// Init completes the initalization of the Torrent structure
func (t *Torrent) Init() {
	// Initialize bytes left to download
	if len(t.metaInfo.Info.Files) > 0 {
		for _, file := range(t.metaInfo.Info.Files) {
			t.left += file.Length
		}
	} else {
		t.left = t.metaInfo.Info.Length
	}
	if t.left != 0 {
		err := errors.New("Unable to deterimine bytes left to download")
		fmt.Println(err)
		// TODO: Bail out here
	}
}

// Run starts the Torrent session and orchestrates all the child processes
func (t *Torrent) Run(complete chan bool) {
	t.Init()
	fmt.Printf("%#v\n", t)

	// Spawn the tracker and wait for it to complete
	trackerMonitor := make(chan bool)
	go t.tracker.Run(t, trackerMonitor)
	<-trackerMonitor

	complete <- true
}

<commit_msg>Ensure bytes left to download is initalized correctly, bail if it isn't<commit_after>// Copyright 2013 Jari Takkala. 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"
	"log"
)

type Torrent struct {
	tracker Tracker
	metaInfo MetaInfo
	infoHash []byte
	uploaded int
	downloaded int
	left int
}

// Multiple File Mode
type Files struct {
	Length int
	Md5sum string
	Path   []string
}

// Info dictionary
type Info struct {
	PieceLength int "piece length"
	Pieces      string
	Private     int
	Name        string
	Length      int
	Md5sum      string
	Files       []Files
}

// Metainfo structure
type MetaInfo struct {
	Info         Info
	Announce     string
	AnnounceList [][]string "announce-list"
	CreationDate int        "creation date"
	Comment      string
	CreatedBy    string "created by"
	Encoding     string
}

// Init completes the initalization of the Torrent structure
func (t *Torrent) Init() {
	// Initialize bytes left to download
	if len(t.metaInfo.Info.Files) > 0 {
		for _, file := range(t.metaInfo.Info.Files) {
			t.left += file.Length
		}
	} else {
		t.left = t.metaInfo.Info.Length
	}
	if t.left == 0 {
		log.Fatal("Unable to deterimine bytes left to download")
	}
}

// Run starts the Torrent session and orchestrates all the child processes
func (t *Torrent) Run(complete chan bool) {
	t.Init()
	fmt.Printf("%#v\n", t)

	// Spawn the tracker and wait for it to complete
	trackerMonitor := make(chan bool)
	go t.tracker.Run(t, trackerMonitor)
	<-trackerMonitor

	complete <- true
}

<|endoftext|>


<commit_before>package acomm

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/url"
	"os"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
)

const (
	statusStarted = iota
	statusStopping
	statusStopped
)

// Tracker keeps track of requests waiting on a response.
type Tracker struct {
	status           int
	responseListener *UnixListener
	httpStreamURL    *url.URL
	defaultTimeout   time.Duration
	requestsLock     sync.Mutex // Protects requests
	requests         map[string]*Request
	dsLock           sync.Mutex // Protects dataStreams
	dataStreams      map[string]*UnixListener
	waitgroup        sync.WaitGroup
}

// NewTracker creates and initializes a new Tracker. If a socketPath is not
// provided, the response socket will be created in a temporary directory.
func NewTracker(socketPath string, httpStreamURL *url.URL, defaultTimeout time.Duration) (*Tracker, error) {
	if socketPath == "" {
		var err error
		socketPath, err = generateTempSocketPath("", "acommTrackerResponses-")
		if err != nil {
			return nil, err
		}
	}

	return &Tracker{
		status:           statusStopped,
		responseListener: NewUnixListener(socketPath, 0),
		httpStreamURL:    httpStreamURL,
		dataStreams:      make(map[string]*UnixListener),
		defaultTimeout:   defaultTimeout,
	}, nil
}

func generateTempSocketPath(dir, prefix string) (string, error) {
	// Use TempFile to allocate a uniquely named file in either the specified
	// dir or the default temp dir. It is then removed so that the unix socket
	// can be created with that name.
	// TODO: Decide on permissions
	if dir != "" {
		if err := os.MkdirAll(dir, os.ModePerm); err != nil {
			log.WithFields(log.Fields{
				"directory": dir,
				"perm":      os.ModePerm,
				"error":     err,
			}).Error("failed to create directory for socket")
			return "", err
		}
	}

	f, err := ioutil.TempFile(dir, prefix)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Error("failed to create temp file for response socket")
		return "", err
	}
	_ = f.Close()
	_ = os.Remove(f.Name())

	return fmt.Sprintf("%s.sock", f.Name()), nil
}

// NumRequests returns the number of tracked requests
func (t *Tracker) NumRequests() int {
	t.requestsLock.Lock()
	defer t.requestsLock.Unlock()

	return len(t.requests)
}

// Addr returns the string representation of the Tracker's response listener socket.
func (t *Tracker) Addr() string {
	return t.responseListener.Addr()
}

// URL returns the URL of the Tracker's response listener socket.
func (t *Tracker) URL() *url.URL {
	return t.responseListener.URL()
}

// Start activates the tracker. This allows tracking of requests as well as
// listening for and handling responses.
func (t *Tracker) Start() error {
	if t.status == statusStarted {
		return nil
	}
	if t.status == statusStopping {
		return errors.New("can't start tracker while stopping")
	}

	t.requests = make(map[string]*Request)

	// start the proxy response listener
	if err := t.responseListener.Start(); err != nil {
		return err
	}

	go t.listenForResponses()

	t.status = statusStarted

	return nil
}

// listenForResponse continually accepts new responses on the listener.
func (t *Tracker) listenForResponses() {
	for {
		conn := t.responseListener.NextConn()
		if conn == nil {
			return
		}

		go t.handleConn(conn)
	}
}

// handleConn handles the response connection and parses the data.
func (t *Tracker) handleConn(conn net.Conn) {
	defer t.responseListener.DoneConn(conn)

	resp := &Response{}
	if err := UnmarshalConnData(conn, resp); err != nil {
		return
	}

	_ = SendConnData(conn, &Response{})

	go t.HandleResponse(resp)
}

// HandleResponse associates a response with a request and either forwards the
// response or calls the request's handler.
func (t *Tracker) HandleResponse(resp *Response) {
	req := t.retrieveRequest(resp.ID)
	if req == nil {
		err := errors.New("response does not have tracked request")
		log.WithFields(log.Fields{
			"error":    err,
			"response": resp,
		}).Error(err)
		return
	}
	defer t.waitgroup.Done()

	// Stop the request timeout. The result doesn't matter.
	if req.timeout != nil {
		_ = req.timeout.Stop()
	}

	// If there are handlers, this is the final destination, so handle the
	// response. Otherwise, forward the response along.
	// Known issue: If this is the final destination and there are
	// no handlers, there will be an extra redirects back here. Since the
	// request has already been removed from the tracker, it will only happen
	// once.
	if !req.proxied {
		req.HandleResponse(resp)
		return
	}

	if resp.StreamURL != nil {
		streamURL, err := t.ProxyStreamHTTPURL(resp.StreamURL) // Replace the StreamURL with a proxy stream url
		if err != nil {
			streamURL = nil
		}
		resp.StreamURL = streamURL
	}

	// Forward the response along
	_ = req.Respond(resp)
	return
}

// Stop deactivates the tracker. It blocks until all active connections or tracked requests to finish.
func (t *Tracker) Stop() {
	// Nothing to do if it's not listening.
	if t.responseListener == nil {
		return
	}

	// Prevent new requests from being tracked
	t.status = statusStopping

	// Handle any requests that are expected
	t.waitgroup.Wait()

	// Stop listening for responses
	t.responseListener.Stop(0)

	// Stop any data streamers
	var dsWG sync.WaitGroup
	t.dsLock.Lock()
	for _, ds := range t.dataStreams {
		dsWG.Add(1)
		go func(ds *UnixListener) {
			defer dsWG.Done()
			ds.Stop(0)
		}(ds)
	}
	t.dsLock.Unlock()
	dsWG.Wait()

	t.status = statusStopped
	return
}

// TrackRequest tracks a request. This does not need to be called after using
// ProxyUnix.
func (t *Tracker) TrackRequest(req *Request, timeout time.Duration) error {
	t.requestsLock.Lock()
	defer t.requestsLock.Unlock()

	if t.status == statusStarted {
		if _, ok := t.requests[req.ID]; ok {
			err := errors.New("request id already traacked")
			log.WithFields(log.Fields{
				"request": req,
				"error":   err,
			}).Error(err)
			return err
		}
		t.waitgroup.Add(1)
		t.requests[req.ID] = req

		t.setRequestTimeout(req, timeout)
		return nil
	}

	err := errors.New("failed to track request in unstarted tracker")
	log.WithFields(log.Fields{
		"request":       req,
		"trackerStatus": t.status,
		"error":         err,
	}).Error(err)
	return err
}

// RemoveRequest should be used to remove a tracked request. Use in cases such
// as sending failures, where there is no hope of a response being received.
func (t *Tracker) RemoveRequest(req *Request) bool {
	if r := t.retrieveRequest(req.ID); r != nil {
		t.waitgroup.Done()
		return true
	}
	return false
}

// retrieveRequest returns a tracked Request based on ID and stops tracking it.
func (t *Tracker) retrieveRequest(id string) *Request {
	t.requestsLock.Lock()
	defer t.requestsLock.Unlock()

	if req, ok := t.requests[id]; ok {
		delete(t.requests, id)
		return req
	}

	return nil
}

func (t *Tracker) setRequestTimeout(req *Request, timeout time.Duration) {
	// Fallback to default timeout
	if timeout == 0 {
		timeout = t.defaultTimeout
	}
	// Timeout of nil is no timeout
	if timeout == 0 {
		return
	}

	resp, err := NewResponse(req, nil, nil, errors.New("response timeout"))
	if err != nil {
		return
	}

	req.timeout = time.AfterFunc(timeout, func() {
		t.HandleResponse(resp)
	})
	return
}

// ProxyUnix proxies requests that have response hooks of non-unix sockets
// through one that does. If the response hook is already a unix socket, it
// returns the original request. If not, it tracks the original request and
// returns a new request with a unix socket response hook. The purpose of this
// is so that there can be a single entry and exit point for external
// communication, while local services can reply directly to each other.
func (t *Tracker) ProxyUnix(req *Request, timeout time.Duration) (*Request, error) {
	if t.responseListener == nil {
		err := errors.New("request tracker's response listener not active")
		log.WithFields(log.Fields{
			"error": err,
		}).Error(err)
		return nil, err
	}

	unixReq := req

	if req.ResponseHook.Scheme != "unix" {
		unixReq = &Request{
			ID:           req.ID,
			Task:         req.Task,
			ResponseHook: t.responseListener.URL(),
			Args:         req.Args,
			// Success and ErrorHandler are unnecessary here and intentionally
			// omitted.
		}
		if err := t.TrackRequest(req, timeout); err != nil {
			return nil, err
		}
		req.proxied = true
	}

	return unixReq, nil
}
<commit_msg>Stop the timeout when removing request<commit_after>package acomm

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/url"
	"os"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
)

const (
	statusStarted = iota
	statusStopping
	statusStopped
)

// Tracker keeps track of requests waiting on a response.
type Tracker struct {
	status           int
	responseListener *UnixListener
	httpStreamURL    *url.URL
	defaultTimeout   time.Duration
	requestsLock     sync.Mutex // Protects requests
	requests         map[string]*Request
	dsLock           sync.Mutex // Protects dataStreams
	dataStreams      map[string]*UnixListener
	waitgroup        sync.WaitGroup
}

// NewTracker creates and initializes a new Tracker. If a socketPath is not
// provided, the response socket will be created in a temporary directory.
func NewTracker(socketPath string, httpStreamURL *url.URL, defaultTimeout time.Duration) (*Tracker, error) {
	if socketPath == "" {
		var err error
		socketPath, err = generateTempSocketPath("", "acommTrackerResponses-")
		if err != nil {
			return nil, err
		}
	}

	return &Tracker{
		status:           statusStopped,
		responseListener: NewUnixListener(socketPath, 0),
		httpStreamURL:    httpStreamURL,
		dataStreams:      make(map[string]*UnixListener),
		defaultTimeout:   defaultTimeout,
	}, nil
}

func generateTempSocketPath(dir, prefix string) (string, error) {
	// Use TempFile to allocate a uniquely named file in either the specified
	// dir or the default temp dir. It is then removed so that the unix socket
	// can be created with that name.
	// TODO: Decide on permissions
	if dir != "" {
		if err := os.MkdirAll(dir, os.ModePerm); err != nil {
			log.WithFields(log.Fields{
				"directory": dir,
				"perm":      os.ModePerm,
				"error":     err,
			}).Error("failed to create directory for socket")
			return "", err
		}
	}

	f, err := ioutil.TempFile(dir, prefix)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Error("failed to create temp file for response socket")
		return "", err
	}
	_ = f.Close()
	_ = os.Remove(f.Name())

	return fmt.Sprintf("%s.sock", f.Name()), nil
}

// NumRequests returns the number of tracked requests
func (t *Tracker) NumRequests() int {
	t.requestsLock.Lock()
	defer t.requestsLock.Unlock()

	return len(t.requests)
}

// Addr returns the string representation of the Tracker's response listener socket.
func (t *Tracker) Addr() string {
	return t.responseListener.Addr()
}

// URL returns the URL of the Tracker's response listener socket.
func (t *Tracker) URL() *url.URL {
	return t.responseListener.URL()
}

// Start activates the tracker. This allows tracking of requests as well as
// listening for and handling responses.
func (t *Tracker) Start() error {
	if t.status == statusStarted {
		return nil
	}
	if t.status == statusStopping {
		return errors.New("can't start tracker while stopping")
	}

	t.requests = make(map[string]*Request)

	// start the proxy response listener
	if err := t.responseListener.Start(); err != nil {
		return err
	}

	go t.listenForResponses()

	t.status = statusStarted

	return nil
}

// listenForResponse continually accepts new responses on the listener.
func (t *Tracker) listenForResponses() {
	for {
		conn := t.responseListener.NextConn()
		if conn == nil {
			return
		}

		go t.handleConn(conn)
	}
}

// handleConn handles the response connection and parses the data.
func (t *Tracker) handleConn(conn net.Conn) {
	defer t.responseListener.DoneConn(conn)

	resp := &Response{}
	if err := UnmarshalConnData(conn, resp); err != nil {
		return
	}

	_ = SendConnData(conn, &Response{})

	go t.HandleResponse(resp)
}

// HandleResponse associates a response with a request and either forwards the
// response or calls the request's handler.
func (t *Tracker) HandleResponse(resp *Response) {
	req := t.retrieveRequest(resp.ID)
	if req == nil {
		err := errors.New("response does not have tracked request")
		log.WithFields(log.Fields{
			"error":    err,
			"response": resp,
		}).Error(err)
		return
	}
	defer t.waitgroup.Done()

	// Stop the request timeout. The result doesn't matter.
	if req.timeout != nil {
		_ = req.timeout.Stop()
	}

	// If there are handlers, this is the final destination, so handle the
	// response. Otherwise, forward the response along.
	// Known issue: If this is the final destination and there are
	// no handlers, there will be an extra redirects back here. Since the
	// request has already been removed from the tracker, it will only happen
	// once.
	if !req.proxied {
		req.HandleResponse(resp)
		return
	}

	if resp.StreamURL != nil {
		streamURL, err := t.ProxyStreamHTTPURL(resp.StreamURL) // Replace the StreamURL with a proxy stream url
		if err != nil {
			streamURL = nil
		}
		resp.StreamURL = streamURL
	}

	// Forward the response along
	_ = req.Respond(resp)
	return
}

// Stop deactivates the tracker. It blocks until all active connections or tracked requests to finish.
func (t *Tracker) Stop() {
	// Nothing to do if it's not listening.
	if t.responseListener == nil {
		return
	}

	// Prevent new requests from being tracked
	t.status = statusStopping

	// Handle any requests that are expected
	t.waitgroup.Wait()

	// Stop listening for responses
	t.responseListener.Stop(0)

	// Stop any data streamers
	var dsWG sync.WaitGroup
	t.dsLock.Lock()
	for _, ds := range t.dataStreams {
		dsWG.Add(1)
		go func(ds *UnixListener) {
			defer dsWG.Done()
			ds.Stop(0)
		}(ds)
	}
	t.dsLock.Unlock()
	dsWG.Wait()

	t.status = statusStopped
	return
}

// TrackRequest tracks a request. This does not need to be called after using
// ProxyUnix.
func (t *Tracker) TrackRequest(req *Request, timeout time.Duration) error {
	t.requestsLock.Lock()
	defer t.requestsLock.Unlock()

	if t.status == statusStarted {
		if _, ok := t.requests[req.ID]; ok {
			err := errors.New("request id already traacked")
			log.WithFields(log.Fields{
				"request": req,
				"error":   err,
			}).Error(err)
			return err
		}
		t.waitgroup.Add(1)
		t.requests[req.ID] = req

		t.setRequestTimeout(req, timeout)
		return nil
	}

	err := errors.New("failed to track request in unstarted tracker")
	log.WithFields(log.Fields{
		"request":       req,
		"trackerStatus": t.status,
		"error":         err,
	}).Error(err)
	return err
}

// RemoveRequest should be used to remove a tracked request. Use in cases such
// as sending failures, where there is no hope of a response being received.
func (t *Tracker) RemoveRequest(req *Request) bool {
	if r := t.retrieveRequest(req.ID); r != nil {
		if r.timeout != nil {
			_ = r.timeout.Stop()
		}
		t.waitgroup.Done()
		return true
	}
	return false
}

// retrieveRequest returns a tracked Request based on ID and stops tracking it.
func (t *Tracker) retrieveRequest(id string) *Request {
	t.requestsLock.Lock()
	defer t.requestsLock.Unlock()

	if req, ok := t.requests[id]; ok {
		delete(t.requests, id)
		return req
	}

	return nil
}

func (t *Tracker) setRequestTimeout(req *Request, timeout time.Duration) {
	// Fallback to default timeout
	if timeout == 0 {
		timeout = t.defaultTimeout
	}
	// Timeout of nil is no timeout
	if timeout == 0 {
		return
	}

	resp, err := NewResponse(req, nil, nil, errors.New("response timeout"))
	if err != nil {
		return
	}

	req.timeout = time.AfterFunc(timeout, func() {
		t.HandleResponse(resp)
	})
	return
}

// ProxyUnix proxies requests that have response hooks of non-unix sockets
// through one that does. If the response hook is already a unix socket, it
// returns the original request. If not, it tracks the original request and
// returns a new request with a unix socket response hook. The purpose of this
// is so that there can be a single entry and exit point for external
// communication, while local services can reply directly to each other.
func (t *Tracker) ProxyUnix(req *Request, timeout time.Duration) (*Request, error) {
	if t.responseListener == nil {
		err := errors.New("request tracker's response listener not active")
		log.WithFields(log.Fields{
			"error": err,
		}).Error(err)
		return nil, err
	}

	unixReq := req

	if req.ResponseHook.Scheme != "unix" {
		unixReq = &Request{
			ID:           req.ID,
			Task:         req.Task,
			ResponseHook: t.responseListener.URL(),
			Args:         req.Args,
			// Success and ErrorHandler are unnecessary here and intentionally
			// omitted.
		}
		if err := t.TrackRequest(req, timeout); err != nil {
			return nil, err
		}
		req.proxied = true
	}

	return unixReq, nil
}
<|endoftext|>


<commit_before>// Copyright 2012, 2013, 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package jujuc

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/juju/errors"
	"github.com/juju/names"
	"gopkg.in/juju/charm.v5"

	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/network"
	"github.com/juju/juju/storage"
)

// RebootPriority is the type used for reboot requests.
type RebootPriority int

const (
	// RebootSkip is a noop.
	RebootSkip RebootPriority = iota
	// RebootAfterHook means wait for current hook to finish before
	// rebooting.
	RebootAfterHook
	// RebootNow means reboot immediately, killing and requeueing the
	// calling hook
	RebootNow
)

// Context is the interface that all hook helper commands
// depend on to interact with the rest of the system.
type Context interface {
	HookContext
	relationHookContext
	actionHookContext
}

// HookContext represents the information and functionality that is
// common to all charm hooks.
type HookContext interface {
	ContextUnit
	ContextStatus
	ContextInstance
	ContextNetworking
	ContextLeadership
	ContextMetrics
	ContextStorage
	ContextComponents
	ContextRelations
}

// UnitHookContext is the context for a unit hook.
type UnitHookContext interface {
	HookContext
}

// RelationHookContext is the context for a relation hook.
type RelationHookContext interface {
	HookContext
	relationHookContext
}

type relationHookContext interface {
	// HookRelation returns the ContextRelation associated with the executing
	// hook if it was found, and whether it was found.
	HookRelation() (ContextRelation, bool)

	// RemoteUnitName returns the name of the remote unit the hook execution
	// is associated with if it was found, and whether it was found.
	RemoteUnitName() (string, bool)
}

// ActionHookContext is the context for an action hook.
type ActionHookContext interface {
	HookContext
	actionHookContext
}

type actionHookContext interface {
	// ActionParams returns the map of params passed with an Action.
	ActionParams() (map[string]interface{}, error)

	// UpdateActionResults inserts new values for use with action-set.
	// The results struct will be delivered to the state server upon
	// completion of the Action.
	UpdateActionResults(keys []string, value string) error

	// SetActionMessage sets a message for the Action.
	SetActionMessage(string) error

	// SetActionFailed sets a failure state for the Action.
	SetActionFailed() error
}

// ContextUnit is the part of a hook context related to the unit.
type ContextUnit interface {
	// UnitName returns the executing unit's name.
	UnitName() string

	// OwnerTag returns the user tag of the service the executing
	// units belongs to.
	OwnerTag() string

	// Config returns the current service configuration of the executing unit.
	ConfigSettings() (charm.Settings, error)
}

// ContextStatus is the part of a hook context related to the unit's status.
type ContextStatus interface {
	// UnitStatus returns the executing unit's current status.
	UnitStatus() (*StatusInfo, error)

	// SetUnitStatus updates the unit's status.
	SetUnitStatus(StatusInfo) error

	// ServiceStatus returns the executing unit's service status
	// (including all units).
	ServiceStatus() (ServiceStatusInfo, error)

	// SetServiceStatus updates the status for the unit's service.
	SetServiceStatus(StatusInfo) error
}

// ContextInstance is the part of a hook context related to the unit's intance.
type ContextInstance interface {
	// AvailabilityZone returns the executing unit's availablilty zone.
	AvailabilityZone() (string, bool)

	// RequestReboot will set the reboot flag to true on the machine agent
	RequestReboot(prio RebootPriority) error
}

// ContextNetworking is the part of a hook context related to network
// interface of the unit's instance.
type ContextNetworking interface {
	// PublicAddress returns the executing unit's public address.
	PublicAddress() (string, bool)

	// PrivateAddress returns the executing unit's private address.
	PrivateAddress() (string, bool)

	// OpenPorts marks the supplied port range for opening when the
	// executing unit's service is exposed.
	OpenPorts(protocol string, fromPort, toPort int) error

	// ClosePorts ensures the supplied port range is closed even when
	// the executing unit's service is exposed (unless it is opened
	// separately by a co- located unit).
	ClosePorts(protocol string, fromPort, toPort int) error

	// OpenedPorts returns all port ranges currently opened by this
	// unit on its assigned machine. The result is sorted first by
	// protocol, then by number.
	OpenedPorts() []network.PortRange
}

// ContextLeadership is the part of a hook context related to the
// unit leadership.
type ContextLeadership interface {
	// IsLeader returns true if the local unit is known to be leader for at
	// least the next 30s.
	IsLeader() (bool, error)

	// LeaderSettings returns the current leader settings. Once leader settings
	// have been read in a given context, they will not be updated other than
	// via successful calls to WriteLeaderSettings.
	LeaderSettings() (map[string]string, error)

	// WriteLeaderSettings writes the supplied settings directly to state, or
	// fails if the local unit is not the service's leader.
	WriteLeaderSettings(map[string]string) error
}

// ContextMetrics is the part of a hook context related to metrics.
type ContextMetrics interface {
	// AddMetric records a metric to return after hook execution.
	AddMetric(string, string, time.Time) error
}

// ContextStorage is the part of a hook context related to storage
// resources associated with the unit.
type ContextStorage interface {
	// Storage returns the ContextStorageAttachment with the supplied
	// tag if it was found, and whether it was found.
	Storage(names.StorageTag) (ContextStorageAttachment, bool)

	// HookStorage returns the storage attachment associated
	// the executing hook if it was found, and whether it was found.
	HookStorage() (ContextStorageAttachment, bool)

	// AddUnitStorage saves storage constraints in the context.
	AddUnitStorage(map[string]params.StorageConstraints)
}

// ContextComponents exposes modular Juju components as they relate to
// the unit in the context of the hook.
type ContextComponents interface {
	// Component returns the ContextComponent with the supplied name if
	// it was found.
	Component(name string) (ContextComponent, error)
}

// ContextRelations exposes the relations associated with the unit.
type ContextRelations interface {
	// Relation returns the relation with the supplied id if it was found, and
	// whether it was found.
	Relation(id int) (ContextRelation, bool)

	// RelationIds returns the ids of all relations the executing unit is
	// currently participating in.
	RelationIds() []int
}

// ContextComponent is a single modular Juju component as it relates to
// the current unit and hook.
type ContextComponent interface {
	// Get populates result with the value corresponding to the given ID.
	Get(id string, result interface{}) error

	// Set records the value for the given ID.
	Set(id string, value interface{}) error

	// Flush pushes the component's data to Juju state.
	Flush() error
}

// ContextRelation expresses the capabilities of a hook with respect to a relation.
type ContextRelation interface {

	// Id returns an integer which uniquely identifies the relation.
	Id() int

	// Name returns the name the locally executing charm assigned to this relation.
	Name() string

	// FakeId returns a string of the form "relation-name:123", which uniquely
	// identifies the relation to the hook. In reality, the identification
	// of the relation is the integer following the colon, but the composed
	// name is useful to humans observing it.
	FakeId() string

	// Settings allows read/write access to the local unit's settings in
	// this relation.
	Settings() (Settings, error)

	// UnitNames returns a list of the remote units in the relation.
	UnitNames() []string

	// ReadSettings returns the settings of any remote unit in the relation.
	ReadSettings(unit string) (params.Settings, error)
}

// ContextStorageAttachment expresses the capabilities of a hook with
// respect to a storage attachment.
type ContextStorageAttachment interface {

	// Tag returns a tag which uniquely identifies the storage attachment
	// in the context of the unit.
	Tag() names.StorageTag

	// Kind returns the kind of the storage.
	Kind() storage.StorageKind

	// Location returns the location of the storage: the mount point for
	// filesystem-kind stores, and the device path for block-kind stores.
	Location() string
}

// Settings is implemented by types that manipulate unit settings.
type Settings interface {
	Map() params.Settings
	Set(string, string)
	Delete(string)
}

// newRelationIdValue returns a gnuflag.Value for convenient parsing of relation
// ids in ctx.
func newRelationIdValue(ctx Context, result *int) *relationIdValue {
	v := &relationIdValue{result: result, ctx: ctx}
	id := -1
	if r, found := ctx.HookRelation(); found {
		id = r.Id()
		v.value = r.FakeId()
	}
	*result = id
	return v
}

// relationIdValue implements gnuflag.Value for use in relation commands.
type relationIdValue struct {
	result *int
	ctx    Context
	value  string
}

// String returns the current value.
func (v *relationIdValue) String() string {
	return v.value
}

// Set interprets value as a relation id, if possible, and returns an error
// if it is not known to the system. The parsed relation id will be written
// to v.result.
func (v *relationIdValue) Set(value string) error {
	trim := value
	if idx := strings.LastIndex(trim, ":"); idx != -1 {
		trim = trim[idx+1:]
	}
	id, err := strconv.Atoi(trim)
	if err != nil {
		return fmt.Errorf("invalid relation id")
	}
	if _, found := v.ctx.Relation(id); !found {
		return fmt.Errorf("unknown relation id")
	}
	*v.result = id
	v.value = value
	return nil
}

// newStorageIdValue returns a gnuflag.Value for convenient parsing of storage
// ids in ctx.
func newStorageIdValue(ctx Context, result *names.StorageTag) *storageIdValue {
	v := &storageIdValue{result: result, ctx: ctx}
	if s, found := ctx.HookStorage(); found {
		*v.result = s.Tag()
	}
	return v
}

// storageIdValue implements gnuflag.Value for use in storage commands.
type storageIdValue struct {
	result *names.StorageTag
	ctx    Context
}

// String returns the current value.
func (v *storageIdValue) String() string {
	if *v.result == (names.StorageTag{}) {
		return ""
	}
	return v.result.Id()
}

// Set interprets value as a storage id, if possible, and returns an error
// if it is not known to the system. The parsed storage id will be written
// to v.result.
func (v *storageIdValue) Set(value string) error {
	if !names.IsValidStorage(value) {
		return errors.Errorf("invalid storage ID %q", value)
	}
	tag := names.NewStorageTag(value)
	if _, found := v.ctx.Storage(tag); !found {
		return fmt.Errorf("unknown storage ID")
	}
	*v.result = tag
	return nil
}
<commit_msg>extended the ContextComponent documentation<commit_after>// Copyright 2012, 2013, 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package jujuc

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/juju/errors"
	"github.com/juju/names"
	"gopkg.in/juju/charm.v5"

	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/network"
	"github.com/juju/juju/storage"
)

// RebootPriority is the type used for reboot requests.
type RebootPriority int

const (
	// RebootSkip is a noop.
	RebootSkip RebootPriority = iota
	// RebootAfterHook means wait for current hook to finish before
	// rebooting.
	RebootAfterHook
	// RebootNow means reboot immediately, killing and requeueing the
	// calling hook
	RebootNow
)

// Context is the interface that all hook helper commands
// depend on to interact with the rest of the system.
type Context interface {
	HookContext
	relationHookContext
	actionHookContext
}

// HookContext represents the information and functionality that is
// common to all charm hooks.
type HookContext interface {
	ContextUnit
	ContextStatus
	ContextInstance
	ContextNetworking
	ContextLeadership
	ContextMetrics
	ContextStorage
	ContextComponents
	ContextRelations
}

// UnitHookContext is the context for a unit hook.
type UnitHookContext interface {
	HookContext
}

// RelationHookContext is the context for a relation hook.
type RelationHookContext interface {
	HookContext
	relationHookContext
}

type relationHookContext interface {
	// HookRelation returns the ContextRelation associated with the executing
	// hook if it was found, and whether it was found.
	HookRelation() (ContextRelation, bool)

	// RemoteUnitName returns the name of the remote unit the hook execution
	// is associated with if it was found, and whether it was found.
	RemoteUnitName() (string, bool)
}

// ActionHookContext is the context for an action hook.
type ActionHookContext interface {
	HookContext
	actionHookContext
}

type actionHookContext interface {
	// ActionParams returns the map of params passed with an Action.
	ActionParams() (map[string]interface{}, error)

	// UpdateActionResults inserts new values for use with action-set.
	// The results struct will be delivered to the state server upon
	// completion of the Action.
	UpdateActionResults(keys []string, value string) error

	// SetActionMessage sets a message for the Action.
	SetActionMessage(string) error

	// SetActionFailed sets a failure state for the Action.
	SetActionFailed() error
}

// ContextUnit is the part of a hook context related to the unit.
type ContextUnit interface {
	// UnitName returns the executing unit's name.
	UnitName() string

	// OwnerTag returns the user tag of the service the executing
	// units belongs to.
	OwnerTag() string

	// Config returns the current service configuration of the executing unit.
	ConfigSettings() (charm.Settings, error)
}

// ContextStatus is the part of a hook context related to the unit's status.
type ContextStatus interface {
	// UnitStatus returns the executing unit's current status.
	UnitStatus() (*StatusInfo, error)

	// SetUnitStatus updates the unit's status.
	SetUnitStatus(StatusInfo) error

	// ServiceStatus returns the executing unit's service status
	// (including all units).
	ServiceStatus() (ServiceStatusInfo, error)

	// SetServiceStatus updates the status for the unit's service.
	SetServiceStatus(StatusInfo) error
}

// ContextInstance is the part of a hook context related to the unit's intance.
type ContextInstance interface {
	// AvailabilityZone returns the executing unit's availablilty zone.
	AvailabilityZone() (string, bool)

	// RequestReboot will set the reboot flag to true on the machine agent
	RequestReboot(prio RebootPriority) error
}

// ContextNetworking is the part of a hook context related to network
// interface of the unit's instance.
type ContextNetworking interface {
	// PublicAddress returns the executing unit's public address.
	PublicAddress() (string, bool)

	// PrivateAddress returns the executing unit's private address.
	PrivateAddress() (string, bool)

	// OpenPorts marks the supplied port range for opening when the
	// executing unit's service is exposed.
	OpenPorts(protocol string, fromPort, toPort int) error

	// ClosePorts ensures the supplied port range is closed even when
	// the executing unit's service is exposed (unless it is opened
	// separately by a co- located unit).
	ClosePorts(protocol string, fromPort, toPort int) error

	// OpenedPorts returns all port ranges currently opened by this
	// unit on its assigned machine. The result is sorted first by
	// protocol, then by number.
	OpenedPorts() []network.PortRange
}

// ContextLeadership is the part of a hook context related to the
// unit leadership.
type ContextLeadership interface {
	// IsLeader returns true if the local unit is known to be leader for at
	// least the next 30s.
	IsLeader() (bool, error)

	// LeaderSettings returns the current leader settings. Once leader settings
	// have been read in a given context, they will not be updated other than
	// via successful calls to WriteLeaderSettings.
	LeaderSettings() (map[string]string, error)

	// WriteLeaderSettings writes the supplied settings directly to state, or
	// fails if the local unit is not the service's leader.
	WriteLeaderSettings(map[string]string) error
}

// ContextMetrics is the part of a hook context related to metrics.
type ContextMetrics interface {
	// AddMetric records a metric to return after hook execution.
	AddMetric(string, string, time.Time) error
}

// ContextStorage is the part of a hook context related to storage
// resources associated with the unit.
type ContextStorage interface {
	// Storage returns the ContextStorageAttachment with the supplied
	// tag if it was found, and whether it was found.
	Storage(names.StorageTag) (ContextStorageAttachment, bool)

	// HookStorage returns the storage attachment associated
	// the executing hook if it was found, and whether it was found.
	HookStorage() (ContextStorageAttachment, bool)

	// AddUnitStorage saves storage constraints in the context.
	AddUnitStorage(map[string]params.StorageConstraints)
}

// ContextComponents exposes modular Juju components as they relate to
// the unit in the context of the hook.
type ContextComponents interface {
	// Component returns the ContextComponent with the supplied name if
	// it was found.
	Component(name string) (ContextComponent, error)
}

// ContextRelations exposes the relations associated with the unit.
type ContextRelations interface {
	// Relation returns the relation with the supplied id if it was found, and
	// whether it was found.
	Relation(id int) (ContextRelation, bool)

	// RelationIds returns the ids of all relations the executing unit is
	// currently participating in.
	RelationIds() []int
}

// ContextComponent is a single modular Juju component as it relates to
// the current unit and hook. Components should implement this interfaces
// in a type-safe way. Ensuring checked type-conversions are preformed on
// the result and value interfaces. You will use the runner.RegisterComponentFunc
// to register a your components concrete ContextComponent implementation.
//
// See: process/context/context.go for an implementation example.
//
type ContextComponent interface {
	// Get populates result with the value corresponding to the given ID.
	// In the Get implementation, result should have a checked type-conversion
	// to your componenets concreate type. Your implementation is also
	// responsible for associating the id with the result.
	Get(id string, result interface{}) error

	// Set records the value for the given ID.
	// In the Set implementation, value should have a checked type-conversion
	// to your components concrete type. You implementation is also responsible
	// for associating the id with the value.
	Set(id string, value interface{}) error

	// Flush pushes the component's data to Juju state.
	// In the Flush implementation, call your components API.
	Flush() error
}

// ContextRelation expresses the capabilities of a hook with respect to a relation.
type ContextRelation interface {

	// Id returns an integer which uniquely identifies the relation.
	Id() int

	// Name returns the name the locally executing charm assigned to this relation.
	Name() string

	// FakeId returns a string of the form "relation-name:123", which uniquely
	// identifies the relation to the hook. In reality, the identification
	// of the relation is the integer following the colon, but the composed
	// name is useful to humans observing it.
	FakeId() string

	// Settings allows read/write access to the local unit's settings in
	// this relation.
	Settings() (Settings, error)

	// UnitNames returns a list of the remote units in the relation.
	UnitNames() []string

	// ReadSettings returns the settings of any remote unit in the relation.
	ReadSettings(unit string) (params.Settings, error)
}

// ContextStorageAttachment expresses the capabilities of a hook with
// respect to a storage attachment.
type ContextStorageAttachment interface {

	// Tag returns a tag which uniquely identifies the storage attachment
	// in the context of the unit.
	Tag() names.StorageTag

	// Kind returns the kind of the storage.
	Kind() storage.StorageKind

	// Location returns the location of the storage: the mount point for
	// filesystem-kind stores, and the device path for block-kind stores.
	Location() string
}

// Settings is implemented by types that manipulate unit settings.
type Settings interface {
	Map() params.Settings
	Set(string, string)
	Delete(string)
}

// newRelationIdValue returns a gnuflag.Value for convenient parsing of relation
// ids in ctx.
func newRelationIdValue(ctx Context, result *int) *relationIdValue {
	v := &relationIdValue{result: result, ctx: ctx}
	id := -1
	if r, found := ctx.HookRelation(); found {
		id = r.Id()
		v.value = r.FakeId()
	}
	*result = id
	return v
}

// relationIdValue implements gnuflag.Value for use in relation commands.
type relationIdValue struct {
	result *int
	ctx    Context
	value  string
}

// String returns the current value.
func (v *relationIdValue) String() string {
	return v.value
}

// Set interprets value as a relation id, if possible, and returns an error
// if it is not known to the system. The parsed relation id will be written
// to v.result.
func (v *relationIdValue) Set(value string) error {
	trim := value
	if idx := strings.LastIndex(trim, ":"); idx != -1 {
		trim = trim[idx+1:]
	}
	id, err := strconv.Atoi(trim)
	if err != nil {
		return fmt.Errorf("invalid relation id")
	}
	if _, found := v.ctx.Relation(id); !found {
		return fmt.Errorf("unknown relation id")
	}
	*v.result = id
	v.value = value
	return nil
}

// newStorageIdValue returns a gnuflag.Value for convenient parsing of storage
// ids in ctx.
func newStorageIdValue(ctx Context, result *names.StorageTag) *storageIdValue {
	v := &storageIdValue{result: result, ctx: ctx}
	if s, found := ctx.HookStorage(); found {
		*v.result = s.Tag()
	}
	return v
}

// storageIdValue implements gnuflag.Value for use in storage commands.
type storageIdValue struct {
	result *names.StorageTag
	ctx    Context
}

// String returns the current value.
func (v *storageIdValue) String() string {
	if *v.result == (names.StorageTag{}) {
		return ""
	}
	return v.result.Id()
}

// Set interprets value as a storage id, if possible, and returns an error
// if it is not known to the system. The parsed storage id will be written
// to v.result.
func (v *storageIdValue) Set(value string) error {
	if !names.IsValidStorage(value) {
		return errors.Errorf("invalid storage ID %q", value)
	}
	tag := names.NewStorageTag(value)
	if _, found := v.ctx.Storage(tag); !found {
		return fmt.Errorf("unknown storage ID")
	}
	*v.result = tag
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	log "github.com/Sirupsen/logrus"
	"github.com/jmoiron/sqlx"
	"github.com/mopsalarm/go-pr0gramm-tags/store"
	"time"
)

type tagInfo struct {
	Id     int    `db:"id"`
	ItemId int    `db:"item_id"`
	Tag    string `db:"tag"`
}

func queryTags(db *sqlx.DB, firstTagId, count int, consumer func(tagInfo)) error {
	var tagInfos []tagInfo
	err := db.Select(&tagInfos,
		"SELECT id, item_id, lower(tag) as tag FROM tags WHERE id >= $1 ORDER BY id ASC LIMIT $2",
		firstTagId, count)

	if err == nil {
		for _, tagInfo := range tagInfos {
			consumer(tagInfo)
		}
	}

	return err
}

type postInfo struct {
	Id            int    `db:"id"`
	Flags         int    `db:"flags"`
	Score         int    `db:"score"`
	CreatedEpoch  int    `db:"created"`
	Promoted      bool   `db:"promoted"`
	Username      string `db:"username"`
	HasText       bool   `db:"has_text"`
	HasAudio      bool   `db:"audio"`
	Controversial bool   `db:"is_controversial"`
}

func queryItems(db *sqlx.DB, firstItemId, itemCount int, consumer func(postInfo)) error {
	var postInfos []postInfo

	err := db.Select(&postInfos, `
		SELECT
			items.id,
			items.flags,
			items.created,
			items.audio,
			items.up - items.down as score,
			items.promoted != 0 as promoted,
			lower(items.username) AS username,
			COALESCE(texts.has_text, FALSE) AS has_text,
			up>60 AND down>60 AND least(up, down)::float/greatest(up, down)>=0.7 as is_controversial
		FROM
			items
			LEFT JOIN items_text texts ON (items.id = texts.item_id)
		WHERE items.id >= $1 OR to_timestamp(items.created) > CURRENT_TIMESTAMP - interval '1day'
		ORDER BY items.id ASC LIMIT $2`, firstItemId, itemCount)

	if err == nil {
		for _, postInfo := range postInfos {
			consumer(postInfo)
		}
	}

	return err
}

func FetchUpdates(db *sqlx.DB, state store.StoreState) (store.IterStore, store.StoreState, bool) {
	builder := store.NewStoreBuilder(HashWord)

	itemCount := 20000
	{
		err := queryItems(db, state.LastItemId, itemCount, func(postInfo postInfo) {
			itemId := int32(-postInfo.Id)

			builder.Push("u:"+CleanString(postInfo.Username), itemId)

			switch {
			case postInfo.Flags&1 != 0:
				builder.Push("f:sfw", itemId)
			case postInfo.Flags&2 != 0:
				builder.Push("f:nsfw", itemId)
			case postInfo.Flags&4 != 0:
				builder.Push("f:nsfl", itemId)
			}

			if postInfo.Promoted {
				builder.Push("f:top", itemId)
			}

			if postInfo.HasText {
				builder.Push("f:text", itemId)
			}

			if postInfo.HasAudio {
				builder.Push("f:sound", itemId)
			}

			if postInfo.Controversial {
				builder.Push("f:controversial", itemId)
			}

			created := time.Unix(int64(postInfo.CreatedEpoch), 0)
			builder.Push(fmt.Sprintf("d:%04d", created.Year()), itemId)
			builder.Push(fmt.Sprintf("d:%04d:%02d", created.Year(), created.Month()), itemId)

			// sort posts into bins (size 500) by score.
			// a post with score 1100 will be put into bins 500 and 1000
			for bin := 1; bin <= postInfo.Score/500; bin++ {
				label := fmt.Sprintf("s:%d", (500 * bin))
				builder.Push(label, itemId)
			}

			itemCount -= 1
			state.LastItemId = postInfo.Id
		})

		if err != nil {
			log.WithError(err).Warn("Could not fetch the list of post items")
			metricsUpdaterError.Inc(1)
		}
	}

	tagCount := 100000
	{
		err := queryTags(db, state.LastTagId, tagCount, func(info tagInfo) {
			for _, word := range ExtractWords(info.Tag) {
				builder.Push(word, int32(-info.ItemId))
			}

			tagCount -= 1
			state.LastTagId = info.Id
		})

		if err != nil {
			log.WithError(err).Warn("Error while streaming from postgres")
			metricsUpdaterError.Inc(1)
		}
	}

	expectMore := tagCount == 0 || itemCount == 0
	return builder.Build(), state, expectMore
}
<commit_msg>Add tags about quality<commit_after>package main

import (
	"fmt"

	log "github.com/Sirupsen/logrus"
	"github.com/jmoiron/sqlx"
	"github.com/mopsalarm/go-pr0gramm-tags/store"
	"strings"
	"time"
)

type tagInfo struct {
	Id     int    `db:"id"`
	ItemId int    `db:"item_id"`
	Tag    string `db:"tag"`
}

func queryTags(db *sqlx.DB, firstTagId, count int, consumer func(tagInfo)) error {
	var tagInfos []tagInfo
	err := db.Select(&tagInfos,
		"SELECT id, item_id, lower(tag) as tag FROM tags WHERE id >= $1 ORDER BY id ASC LIMIT $2",
		firstTagId, count)

	if err == nil {
		for _, tagInfo := range tagInfos {
			consumer(tagInfo)
		}
	}

	return err
}

type postInfo struct {
	Id            int    `db:"id"`
	Flags         int    `db:"flags"`
	Score         int    `db:"score"`
	CreatedEpoch  int    `db:"created"`
	Promoted      bool   `db:"promoted"`
	Username      string `db:"username"`
	HasText       bool   `db:"has_text"`
	HasAudio      bool   `db:"audio"`
	Width         int    `db:"width"`
	Controversial bool   `db:"is_controversial"`
}

func queryItems(db *sqlx.DB, firstItemId, itemCount int, consumer func(postInfo)) error {
	var postInfos []postInfo

	err := db.Select(&postInfos, `
		SELECT
			items.id,
			items.flags,
			items.created,
			items.audio,
			items.width,
			items.up - items.down as score,
			items.promoted != 0 as promoted,
			lower(items.username) AS username,
			COALESCE(texts.has_text, FALSE) AS has_text,
			up>60 AND down>60 AND least(up, down)::float/greatest(up, down)>=0.7 as is_controversial
		FROM
			items
			LEFT JOIN items_text texts ON (items.id = texts.item_id)
		WHERE items.id >= $1 OR to_timestamp(items.created) > CURRENT_TIMESTAMP - interval '1day'
		ORDER BY items.id ASC LIMIT $2`, firstItemId, itemCount)

	if err == nil {
		for _, postInfo := range postInfos {
			consumer(postInfo)
		}
	}

	return err
}

func sizeCategories(width int) []string {
	switch {
	case width > 3800:
		return []string{"2160p", "4k"}

	case width > 1900:
		return []string{"1080p", "hd"}

	case width > 1200:
		return []string{"720p", "hd"}

	case width > 600:
		return []string{"sd"}

	default:
		return []string{"kartoffel"}
	}
}

func FetchUpdates(db *sqlx.DB, state store.StoreState) (store.IterStore, store.StoreState, bool) {
	builder := store.NewStoreBuilder(HashWord)

	itemCount := 10000
	{
		err := queryItems(db, state.LastItemId, itemCount, func(postInfo postInfo) {
			itemId := int32(-postInfo.Id)

			// Prefixes currently in-use:
			//  d: date
			//  f: flags
			//  s: score
			//  u: user
			//  q: quality

			builder.Push("u:"+CleanString(postInfo.Username), itemId)

			switch {
			case postInfo.Flags&1 != 0:
				builder.Push("f:sfw", itemId)
			case postInfo.Flags&2 != 0:
				builder.Push("f:nsfw", itemId)
			case postInfo.Flags&4 != 0:
				builder.Push("f:nsfl", itemId)
			}

			if postInfo.Promoted {
				builder.Push("f:top", itemId)
			}

			if postInfo.HasText {
				builder.Push("f:text", itemId)
			}

			if postInfo.HasAudio {
				builder.Push("f:sound", itemId)
			}

			if postInfo.Controversial {
				builder.Push("f:controversial", itemId)
			}

			// add quality-tag
			for _, sizeCategory := range sizeCategories(postInfo.Width) {
				builder.Push("q:"+sizeCategory, itemId)
			}

			created := time.Unix(int64(postInfo.CreatedEpoch), 0)
			builder.Push(fmt.Sprintf("d:%04d", created.Year()), itemId)
			builder.Push(fmt.Sprintf("d:%04d:%02d", created.Year(), created.Month()), itemId)

			// sort posts into bins (size 500) by score.
			// a post with score 1100 will be put into bins 500 and 1000
			for bin := 1; bin <= postInfo.Score/500; bin++ {
				label := fmt.Sprintf("s:%d", (500 * bin))
				builder.Push(label, itemId)
			}

			itemCount -= 1
			state.LastItemId = postInfo.Id
		})

		if err != nil {
			log.WithError(err).Warn("Could not fetch the list of post items")
			metricsUpdaterError.Inc(1)
		}
	}

	tagCount := 50000
	{
		err := queryTags(db, state.LastTagId, tagCount, func(info tagInfo) {
			itemId := int32(-info.ItemId)
			for _, word := range ExtractWords(info.Tag) {
				builder.Push(word, itemId)
			}

			if strings.ToLower(info.Tag) == "repost" {
				builder.Push("f:repost", itemId)
			}

			tagCount -= 1
			state.LastTagId = info.Id
		})

		if err != nil {
			log.WithError(err).Warn("Error while streaming from postgres")
			metricsUpdaterError.Inc(1)
		}
	}

	expectMore := tagCount == 0 || itemCount == 0
	return builder.Build(), state, expectMore
}
<|endoftext|>


<commit_before>// Copyright 2013 Google Inc.  All rights reserved.
// Copyright 2016 the gousb Authors.  All rights reserved.
//
// 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 usb

/*
#include <libusb-1.0/libusb.h>
*/
import "C"

import (
	"log"
	"time"
)

func isochronous_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) {
	t, err := e.newUSBTransfer(TRANSFER_TYPE_ISOCHRONOUS, buf)
	if err != nil {
		return 0, err
	}
	defer t.free()

	if err := t.submit(timeout); err != nil {
		log.Printf("iso: xfer failed to submit: %s", err)
		return 0, err
	}

	n, err := t.wait(buf)
	if err != nil {
		log.Printf("iso: xfer failed: %s", err)
		return 0, err
	}
	return n, err
}
<commit_msg>iso.go is no longer needed, the only iso-specific part lives in transfer.c<commit_after><|endoftext|>


<commit_before>package geom

// A GeometryCollection is a collection of arbitrary geometries with the same
// SRID.
type GeometryCollection struct {
	geoms []T
	srid  int
}

// NewGeometryCollection returns a new GeometryCollection with the specified
// geometries.
func NewGeometryCollection() *GeometryCollection {
	return &GeometryCollection{}
}

// Geom returns the ith geometry in gc.
func (gc *GeometryCollection) Geom(i int) T {
	return gc.geoms[i]
}

// Geoms returns the geometries in gc.
func (gc *GeometryCollection) Geoms() []T {
	return gc.geoms
}

// Layout returns the smallest layout that covers all of the layouts in gc's
// geometries.
func (gc *GeometryCollection) Layout() Layout {
	maxLayout := NoLayout
	for _, g := range gc.geoms {
		switch l := g.Layout(); l {
		case XYZ:
			if maxLayout == XYM {
				maxLayout = XYZM
			} else if l > maxLayout {
				maxLayout = l
			}
		case XYM:
			if maxLayout == XYZ {
				maxLayout = XYZM
			} else if l > maxLayout {
				maxLayout = l
			}
		default:
			if l > maxLayout {
				maxLayout = l
			}
		}
	}
	return maxLayout
}

// NumGeoms returns the number of geometries in gc.
func (gc *GeometryCollection) NumGeoms() int {
	return len(gc.geoms)
}

// Stride returns the stride of gc's layout.
func (gc *GeometryCollection) Stride() int {
	return gc.Layout().Stride()
}

// Bounds returns the bounds of all the geometries in gc.
func (gc *GeometryCollection) Bounds() *Bounds {
	// FIXME this needs work for mixing layouts, e.g. XYZ and XYM
	b := NewBounds(gc.Layout())
	for _, g := range gc.geoms {
		b = b.Extend(g)
	}
	return b
}

// Empty returns true if the collection is empty.
func (gc *GeometryCollection) Empty() bool {
	return len(gc.geoms) == 0
}

// FlatCoords panics.
func (*GeometryCollection) FlatCoords() []float64 {
	panic("FlatCoords() called on a GeometryCollection")
}

// Ends panics.
func (*GeometryCollection) Ends() []int {
	panic("Ends() called on a GeometryCollection")
}

// Endss panics.
func (*GeometryCollection) Endss() [][]int {
	panic("Endss() called on a GeometryCollection")
}

// SRID returns gc's SRID.
func (gc *GeometryCollection) SRID() int {
	return gc.srid
}

// MustPush pushes gs to gc. It panics on any error.
func (gc *GeometryCollection) MustPush(gs ...T) *GeometryCollection {
	if err := gc.Push(gs...); err != nil {
		panic(err)
	}
	return gc
}

// Push appends geometries.
func (gc *GeometryCollection) Push(gs ...T) error {
	gc.geoms = append(gc.geoms, gs...)
	return nil
}

// SetSRID sets gc's SRID and the SRID of all its elements.
func (gc *GeometryCollection) SetSRID(srid int) *GeometryCollection {
	gc.srid = srid
	return gc
}
<commit_msg>Use g as receiver name in GeometryCollection<commit_after>package geom

// A GeometryCollection is a collection of arbitrary geometries with the same
// SRID.
type GeometryCollection struct {
	geoms []T
	srid  int
}

// NewGeometryCollection returns a new GeometryCollection with the specified
// geometries.
func NewGeometryCollection() *GeometryCollection {
	return &GeometryCollection{}
}

// Geom returns the ith geometry in g.
func (g *GeometryCollection) Geom(i int) T {
	return g.geoms[i]
}

// Geoms returns the geometries in g.
func (g *GeometryCollection) Geoms() []T {
	return g.geoms
}

// Layout returns the smallest layout that covers all of the layouts in g's
// geometries.
func (g *GeometryCollection) Layout() Layout {
	maxLayout := NoLayout
	for _, g := range g.geoms {
		switch l := g.Layout(); l {
		case XYZ:
			if maxLayout == XYM {
				maxLayout = XYZM
			} else if l > maxLayout {
				maxLayout = l
			}
		case XYM:
			if maxLayout == XYZ {
				maxLayout = XYZM
			} else if l > maxLayout {
				maxLayout = l
			}
		default:
			if l > maxLayout {
				maxLayout = l
			}
		}
	}
	return maxLayout
}

// NumGeoms returns the number of geometries in g.
func (g *GeometryCollection) NumGeoms() int {
	return len(g.geoms)
}

// Stride returns the stride of g's layout.
func (g *GeometryCollection) Stride() int {
	return g.Layout().Stride()
}

// Bounds returns the bounds of all the geometries in g.
func (g *GeometryCollection) Bounds() *Bounds {
	// FIXME this needs work for mixing layouts, e.g. XYZ and XYM
	b := NewBounds(g.Layout())
	for _, g := range g.geoms {
		b = b.Extend(g)
	}
	return b
}

// Empty returns true if the collection is empty.
func (g *GeometryCollection) Empty() bool {
	return len(g.geoms) == 0
}

// FlatCoords panics.
func (*GeometryCollection) FlatCoords() []float64 {
	panic("FlatCoords() called on a GeometryCollection")
}

// Ends panics.
func (*GeometryCollection) Ends() []int {
	panic("Ends() called on a GeometryCollection")
}

// Endss panics.
func (*GeometryCollection) Endss() [][]int {
	panic("Endss() called on a GeometryCollection")
}

// SRID returns g's SRID.
func (g *GeometryCollection) SRID() int {
	return g.srid
}

// MustPush pushes gs to g. It panics on any error.
func (g *GeometryCollection) MustPush(gs ...T) *GeometryCollection {
	if err := g.Push(gs...); err != nil {
		panic(err)
	}
	return g
}

// Push appends geometries.
func (g *GeometryCollection) Push(gs ...T) error {
	g.geoms = append(g.geoms, gs...)
	return nil
}

// SetSRID sets g's SRID and the SRID of all its elements.
func (g *GeometryCollection) SetSRID(srid int) *GeometryCollection {
	g.srid = srid
	return g
}
<|endoftext|>


<commit_before>package util

import (
	"bufio"
	"io"
	"os"
	"path/filepath"
	"strings"
)

func ReadLines(r io.Reader) []string {
	buf := bufio.NewReader(r)
	lines := make([]string, 0)
	for {
		line, err := buf.ReadString('\n')
		if err != nil && err != io.EOF {
			Fatalf("Could not read line: %s.", err)
		}
		lines = append(lines, strings.TrimSpace(line))
		if err == io.EOF {
			break
		}
	}
	return lines
}

func CopyFile(src, dest string) {
	_, err := io.Copy(CreateFile(dest), OpenFile(src))
	Assert(err, "Could not copy '%s' to '%s'", src, dest)
}

func IsDir(path string) bool {
	fi, err := os.Stat(path)
	return err == nil && fi.IsDir()
}

func Exists(path string) bool {
	_, err := os.Stat(path)
	return err == nil || !os.IsNotExist(err)
}

func AssertOverwritable(path string, overwritable bool) {
	if Exists(path) {
		if overwritable {
			Assert(os.RemoveAll(path), "Could not remove %s", path)
		} else {
			Fatalf("%s already exists.", path)
		}
	}
}

func AllFilesFromArgs(fileArgs []string) []string {
	files := make([]string, 0)
	for _, fordir := range fileArgs {
		var more []string
		if IsDir(fordir) {
			more = RecursiveFiles(fordir)
		} else {
			more = []string{fordir}
		}
		files = append(files, more...)
	}
	return files
}

func RecursiveFiles(dir string) []string {
	if !strings.HasSuffix(dir, "/") {
		dir = dir + "/"
	}
	files := make([]string, 0)
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			Warnf("Could not read '%s' because: %s\n", path, err)
			return nil
		}
		if info.IsDir() {
			return nil
		}
		files = append(files, path)
		return nil
	})
	return files
}
<commit_msg>Switch ReadLines to use scanner.<commit_after>package util

import (
	"bufio"
	"io"
	"os"
	"path/filepath"
	"strings"
)

func ReadLines(r io.Reader) []string {
	scanner := bufio.NewScanner(r)
	lines := make([]string, 0)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	Assert(scanner.Err())
	return lines
}

func CopyFile(src, dest string) {
	_, err := io.Copy(CreateFile(dest), OpenFile(src))
	Assert(err, "Could not copy '%s' to '%s'", src, dest)
}

func IsDir(path string) bool {
	fi, err := os.Stat(path)
	return err == nil && fi.IsDir()
}

func Exists(path string) bool {
	_, err := os.Stat(path)
	return err == nil || !os.IsNotExist(err)
}

func AssertOverwritable(path string, overwritable bool) {
	if Exists(path) {
		if overwritable {
			Assert(os.RemoveAll(path), "Could not remove %s", path)
		} else {
			Fatalf("%s already exists.", path)
		}
	}
}

func AllFilesFromArgs(fileArgs []string) []string {
	files := make([]string, 0)
	for _, fordir := range fileArgs {
		var more []string
		if IsDir(fordir) {
			more = RecursiveFiles(fordir)
		} else {
			more = []string{fordir}
		}
		files = append(files, more...)
	}
	return files
}

func RecursiveFiles(dir string) []string {
	if !strings.HasSuffix(dir, "/") {
		dir = dir + "/"
	}
	files := make([]string, 0)
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			Warnf("Could not read '%s' because: %s\n", path, err)
			return nil
		}
		if info.IsDir() {
			return nil
		}
		files = append(files, path)
		return nil
	})
	return files
}
<|endoftext|>


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

// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris

package os

import (
	"runtime"
	"sync/atomic"
	"syscall"
)

func rename(oldname, newname string) error {
	e := syscall.Rename(oldname, newname)
	if e != nil {
		return &LinkError{"rename", oldname, newname, e}
	}
	return nil
}

// File represents an open file descriptor.
type File struct {
	*file
}

// file is the real representation of *File.
// The extra level of indirection ensures that no clients of os
// can overwrite this data, which could cause the finalizer
// to close the wrong file descriptor.
type file struct {
	fd      int
	name    string
	dirinfo *dirInfo // nil unless directory being read
	nepipe  int32    // number of consecutive EPIPE in Write
}

// Fd returns the integer Unix file descriptor referencing the open file.
// The file descriptor is valid only until f.Close is called or f is garbage collected.
func (f *File) Fd() uintptr {
	if f == nil {
		return ^(uintptr(0))
	}
	return uintptr(f.fd)
}

// NewFile returns a new File with the given file descriptor and name.
func NewFile(fd uintptr, name string) *File {
	fdi := int(fd)
	if fdi < 0 {
		return nil
	}
	f := &File{&file{fd: fdi, name: name}}
	runtime.SetFinalizer(f.file, (*file).close)
	return f
}

// Auxiliary information if the File describes a directory
type dirInfo struct {
	buf  []byte // buffer for directory I/O
	nbuf int    // length of buf; return value from Getdirentries
	bufp int    // location of next record in buf.
}

func epipecheck(file *File, e error) {
	if e == syscall.EPIPE {
		if atomic.AddInt32(&file.nepipe, 1) >= 10 {
			sigpipe()
		}
	} else {
		atomic.StoreInt32(&file.nepipe, 0)
	}
}

// DevNull is the name of the operating system's ``null device.''
// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
const DevNull = "/dev/null"

// OpenFile is the generalized open call; most users will use Open
// or Create instead.  It opens the named file with specified flag
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
// methods on the returned File can be used for I/O.
// If there is an error, it will be of type *PathError.
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
	chmod := false
	if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 {
		if _, err := Stat(name); IsNotExist(err) {
			chmod = true
		}
	}

retry:
	r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
	if e != nil {
		// On OS X, sigaction(2) doesn't guarantee that SA_RESTART will cause
		// open(2) to be restarted for regular files. This is easy to reproduce on
		// fuse file systems (see http://golang.org/issue/11180).
		if e == syscall.EINTR {
			goto retry
		}

		return nil, &PathError{"open", name, e}
	}

	// open(2) itself won't handle the sticky bit on *BSD and Solaris
	if chmod {
		Chmod(name, perm)
	}

	// There's a race here with fork/exec, which we are
	// content to live with.  See ../syscall/exec_unix.go.
	if !supportsCloseOnExec {
		syscall.CloseOnExec(r)
	}

	return NewFile(uintptr(r), name), nil
}

// Close closes the File, rendering it unusable for I/O.
// It returns an error, if any.
func (f *File) Close() error {
	if f == nil {
		return ErrInvalid
	}
	return f.file.close()
}

func (file *file) close() error {
	if file == nil || file.fd < 0 {
		return syscall.EINVAL
	}
	var err error
	if e := syscall.Close(file.fd); e != nil {
		err = &PathError{"close", file.name, e}
	}
	file.fd = -1 // so it can't be closed again

	// no need for a finalizer anymore
	runtime.SetFinalizer(file, nil)
	return err
}

// Stat returns the FileInfo structure describing file.
// If there is an error, it will be of type *PathError.
func (f *File) Stat() (FileInfo, error) {
	if f == nil {
		return nil, ErrInvalid
	}
	var stat syscall.Stat_t
	err := syscall.Fstat(f.fd, &stat)
	if err != nil {
		return nil, &PathError{"stat", f.name, err}
	}
	return fileInfoFromStat(&stat, f.name), nil
}

// Stat returns a FileInfo describing the named file.
// If there is an error, it will be of type *PathError.
func Stat(name string) (FileInfo, error) {
	var stat syscall.Stat_t
	err := syscall.Stat(name, &stat)
	if err != nil {
		return nil, &PathError{"stat", name, err}
	}
	return fileInfoFromStat(&stat, name), nil
}

// Lstat returns a FileInfo describing the named file.
// If the file is a symbolic link, the returned FileInfo
// describes the symbolic link.  Lstat makes no attempt to follow the link.
// If there is an error, it will be of type *PathError.
func Lstat(name string) (FileInfo, error) {
	var stat syscall.Stat_t
	err := syscall.Lstat(name, &stat)
	if err != nil {
		return nil, &PathError{"lstat", name, err}
	}
	return fileInfoFromStat(&stat, name), nil
}

func (f *File) readdir(n int) (fi []FileInfo, err error) {
	dirname := f.name
	if dirname == "" {
		dirname = "."
	}
	names, err := f.Readdirnames(n)
	fi = make([]FileInfo, 0, len(names))
	for _, filename := range names {
		fip, lerr := lstat(dirname + "/" + filename)
		if IsNotExist(lerr) {
			// File disappeared between readdir + stat.
			// Just treat it as if it didn't exist.
			continue
		}
		if lerr != nil {
			return fi, lerr
		}
		fi = append(fi, fip)
	}
	return fi, err
}

// Darwin and FreeBSD can't read or write 2GB+ at a time,
// even on 64-bit systems. See golang.org/issue/7812.
// Use 1GB instead of, say, 2GB-1, to keep subsequent
// reads aligned.
const (
	needsMaxRW = runtime.GOOS == "darwin" || runtime.GOOS == "freebsd"
	maxRW      = 1 << 30
)

// read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
func (f *File) read(b []byte) (n int, err error) {
	if needsMaxRW && len(b) > maxRW {
		b = b[:maxRW]
	}
	return fixCount(syscall.Read(f.fd, b))
}

// pread reads len(b) bytes from the File starting at byte offset off.
// It returns the number of bytes read and the error, if any.
// EOF is signaled by a zero count with err set to nil.
func (f *File) pread(b []byte, off int64) (n int, err error) {
	if needsMaxRW && len(b) > maxRW {
		b = b[:maxRW]
	}
	return fixCount(syscall.Pread(f.fd, b, off))
}

// write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
func (f *File) write(b []byte) (n int, err error) {
	for {
		bcap := b
		if needsMaxRW && len(bcap) > maxRW {
			bcap = bcap[:maxRW]
		}
		m, err := fixCount(syscall.Write(f.fd, bcap))
		n += m

		// If the syscall wrote some data but not all (short write)
		// or it returned EINTR, then assume it stopped early for
		// reasons that are uninteresting to the caller, and try again.
		if 0 < m && m < len(bcap) || err == syscall.EINTR {
			b = b[m:]
			continue
		}

		if needsMaxRW && len(bcap) != len(b) && err == nil {
			b = b[m:]
			continue
		}

		return n, err
	}
}

// pwrite writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
func (f *File) pwrite(b []byte, off int64) (n int, err error) {
	if needsMaxRW && len(b) > maxRW {
		b = b[:maxRW]
	}
	return fixCount(syscall.Pwrite(f.fd, b, off))
}

// seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
func (f *File) seek(offset int64, whence int) (ret int64, err error) {
	return syscall.Seek(f.fd, offset, whence)
}

// Truncate changes the size of the named file.
// If the file is a symbolic link, it changes the size of the link's target.
// If there is an error, it will be of type *PathError.
func Truncate(name string, size int64) error {
	if e := syscall.Truncate(name, size); e != nil {
		return &PathError{"truncate", name, e}
	}
	return nil
}

// Remove removes the named file or directory.
// If there is an error, it will be of type *PathError.
func Remove(name string) error {
	// System call interface forces us to know
	// whether name is a file or directory.
	// Try both: it is cheaper on average than
	// doing a Stat plus the right one.
	e := syscall.Unlink(name)
	if e == nil {
		return nil
	}
	e1 := syscall.Rmdir(name)
	if e1 == nil {
		return nil
	}

	// Both failed: figure out which error to return.
	// OS X and Linux differ on whether unlink(dir)
	// returns EISDIR, so can't use that.  However,
	// both agree that rmdir(file) returns ENOTDIR,
	// so we can use that to decide which error is real.
	// Rmdir might also return ENOTDIR if given a bad
	// file path, like /etc/passwd/foo, but in that case,
	// both errors will be ENOTDIR, so it's okay to
	// use the error from unlink.
	if e1 != syscall.ENOTDIR {
		e = e1
	}
	return &PathError{"remove", name, e}
}

// basename removes trailing slashes and the leading directory name from path name
func basename(name string) string {
	i := len(name) - 1
	// Remove trailing slashes
	for ; i > 0 && name[i] == '/'; i-- {
		name = name[:i]
	}
	// Remove leading directory name
	for i--; i >= 0; i-- {
		if name[i] == '/' {
			name = name[i+1:]
			break
		}
	}

	return name
}

// TempDir returns the default directory to use for temporary files.
func TempDir() string {
	dir := Getenv("TMPDIR")
	if dir == "" {
		if runtime.GOOS == "android" {
			dir = "/data/local/tmp"
		} else {
			dir = "/tmp"
		}
	}
	return dir
}

// Link creates newname as a hard link to the oldname file.
// If there is an error, it will be of type *LinkError.
func Link(oldname, newname string) error {
	e := syscall.Link(oldname, newname)
	if e != nil {
		return &LinkError{"link", oldname, newname, e}
	}
	return nil
}

// Symlink creates newname as a symbolic link to oldname.
// If there is an error, it will be of type *LinkError.
func Symlink(oldname, newname string) error {
	e := syscall.Symlink(oldname, newname)
	if e != nil {
		return &LinkError{"symlink", oldname, newname, e}
	}
	return nil
}
<commit_msg>os: touch up the EINTR retry loop in OpenFile<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris

package os

import (
	"runtime"
	"sync/atomic"
	"syscall"
)

func rename(oldname, newname string) error {
	e := syscall.Rename(oldname, newname)
	if e != nil {
		return &LinkError{"rename", oldname, newname, e}
	}
	return nil
}

// File represents an open file descriptor.
type File struct {
	*file
}

// file is the real representation of *File.
// The extra level of indirection ensures that no clients of os
// can overwrite this data, which could cause the finalizer
// to close the wrong file descriptor.
type file struct {
	fd      int
	name    string
	dirinfo *dirInfo // nil unless directory being read
	nepipe  int32    // number of consecutive EPIPE in Write
}

// Fd returns the integer Unix file descriptor referencing the open file.
// The file descriptor is valid only until f.Close is called or f is garbage collected.
func (f *File) Fd() uintptr {
	if f == nil {
		return ^(uintptr(0))
	}
	return uintptr(f.fd)
}

// NewFile returns a new File with the given file descriptor and name.
func NewFile(fd uintptr, name string) *File {
	fdi := int(fd)
	if fdi < 0 {
		return nil
	}
	f := &File{&file{fd: fdi, name: name}}
	runtime.SetFinalizer(f.file, (*file).close)
	return f
}

// Auxiliary information if the File describes a directory
type dirInfo struct {
	buf  []byte // buffer for directory I/O
	nbuf int    // length of buf; return value from Getdirentries
	bufp int    // location of next record in buf.
}

func epipecheck(file *File, e error) {
	if e == syscall.EPIPE {
		if atomic.AddInt32(&file.nepipe, 1) >= 10 {
			sigpipe()
		}
	} else {
		atomic.StoreInt32(&file.nepipe, 0)
	}
}

// DevNull is the name of the operating system's ``null device.''
// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
const DevNull = "/dev/null"

// OpenFile is the generalized open call; most users will use Open
// or Create instead.  It opens the named file with specified flag
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
// methods on the returned File can be used for I/O.
// If there is an error, it will be of type *PathError.
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
	chmod := false
	if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 {
		if _, err := Stat(name); IsNotExist(err) {
			chmod = true
		}
	}

	var r int
	for {
		var e error
		r, e = syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
		if e == nil {
			break
		}

		// On OS X, sigaction(2) doesn't guarantee that SA_RESTART will cause
		// open(2) to be restarted for regular files. This is easy to reproduce on
		// fuse file systems (see http://golang.org/issue/11180).
		if runtime.GOOS == "darwin" && e == syscall.EINTR {
			continue
		}

		return nil, &PathError{"open", name, e}
	}

	// open(2) itself won't handle the sticky bit on *BSD and Solaris
	if chmod {
		Chmod(name, perm)
	}

	// There's a race here with fork/exec, which we are
	// content to live with.  See ../syscall/exec_unix.go.
	if !supportsCloseOnExec {
		syscall.CloseOnExec(r)
	}

	return NewFile(uintptr(r), name), nil
}

// Close closes the File, rendering it unusable for I/O.
// It returns an error, if any.
func (f *File) Close() error {
	if f == nil {
		return ErrInvalid
	}
	return f.file.close()
}

func (file *file) close() error {
	if file == nil || file.fd < 0 {
		return syscall.EINVAL
	}
	var err error
	if e := syscall.Close(file.fd); e != nil {
		err = &PathError{"close", file.name, e}
	}
	file.fd = -1 // so it can't be closed again

	// no need for a finalizer anymore
	runtime.SetFinalizer(file, nil)
	return err
}

// Stat returns the FileInfo structure describing file.
// If there is an error, it will be of type *PathError.
func (f *File) Stat() (FileInfo, error) {
	if f == nil {
		return nil, ErrInvalid
	}
	var stat syscall.Stat_t
	err := syscall.Fstat(f.fd, &stat)
	if err != nil {
		return nil, &PathError{"stat", f.name, err}
	}
	return fileInfoFromStat(&stat, f.name), nil
}

// Stat returns a FileInfo describing the named file.
// If there is an error, it will be of type *PathError.
func Stat(name string) (FileInfo, error) {
	var stat syscall.Stat_t
	err := syscall.Stat(name, &stat)
	if err != nil {
		return nil, &PathError{"stat", name, err}
	}
	return fileInfoFromStat(&stat, name), nil
}

// Lstat returns a FileInfo describing the named file.
// If the file is a symbolic link, the returned FileInfo
// describes the symbolic link.  Lstat makes no attempt to follow the link.
// If there is an error, it will be of type *PathError.
func Lstat(name string) (FileInfo, error) {
	var stat syscall.Stat_t
	err := syscall.Lstat(name, &stat)
	if err != nil {
		return nil, &PathError{"lstat", name, err}
	}
	return fileInfoFromStat(&stat, name), nil
}

func (f *File) readdir(n int) (fi []FileInfo, err error) {
	dirname := f.name
	if dirname == "" {
		dirname = "."
	}
	names, err := f.Readdirnames(n)
	fi = make([]FileInfo, 0, len(names))
	for _, filename := range names {
		fip, lerr := lstat(dirname + "/" + filename)
		if IsNotExist(lerr) {
			// File disappeared between readdir + stat.
			// Just treat it as if it didn't exist.
			continue
		}
		if lerr != nil {
			return fi, lerr
		}
		fi = append(fi, fip)
	}
	return fi, err
}

// Darwin and FreeBSD can't read or write 2GB+ at a time,
// even on 64-bit systems. See golang.org/issue/7812.
// Use 1GB instead of, say, 2GB-1, to keep subsequent
// reads aligned.
const (
	needsMaxRW = runtime.GOOS == "darwin" || runtime.GOOS == "freebsd"
	maxRW      = 1 << 30
)

// read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
func (f *File) read(b []byte) (n int, err error) {
	if needsMaxRW && len(b) > maxRW {
		b = b[:maxRW]
	}
	return fixCount(syscall.Read(f.fd, b))
}

// pread reads len(b) bytes from the File starting at byte offset off.
// It returns the number of bytes read and the error, if any.
// EOF is signaled by a zero count with err set to nil.
func (f *File) pread(b []byte, off int64) (n int, err error) {
	if needsMaxRW && len(b) > maxRW {
		b = b[:maxRW]
	}
	return fixCount(syscall.Pread(f.fd, b, off))
}

// write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
func (f *File) write(b []byte) (n int, err error) {
	for {
		bcap := b
		if needsMaxRW && len(bcap) > maxRW {
			bcap = bcap[:maxRW]
		}
		m, err := fixCount(syscall.Write(f.fd, bcap))
		n += m

		// If the syscall wrote some data but not all (short write)
		// or it returned EINTR, then assume it stopped early for
		// reasons that are uninteresting to the caller, and try again.
		if 0 < m && m < len(bcap) || err == syscall.EINTR {
			b = b[m:]
			continue
		}

		if needsMaxRW && len(bcap) != len(b) && err == nil {
			b = b[m:]
			continue
		}

		return n, err
	}
}

// pwrite writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
func (f *File) pwrite(b []byte, off int64) (n int, err error) {
	if needsMaxRW && len(b) > maxRW {
		b = b[:maxRW]
	}
	return fixCount(syscall.Pwrite(f.fd, b, off))
}

// seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
func (f *File) seek(offset int64, whence int) (ret int64, err error) {
	return syscall.Seek(f.fd, offset, whence)
}

// Truncate changes the size of the named file.
// If the file is a symbolic link, it changes the size of the link's target.
// If there is an error, it will be of type *PathError.
func Truncate(name string, size int64) error {
	if e := syscall.Truncate(name, size); e != nil {
		return &PathError{"truncate", name, e}
	}
	return nil
}

// Remove removes the named file or directory.
// If there is an error, it will be of type *PathError.
func Remove(name string) error {
	// System call interface forces us to know
	// whether name is a file or directory.
	// Try both: it is cheaper on average than
	// doing a Stat plus the right one.
	e := syscall.Unlink(name)
	if e == nil {
		return nil
	}
	e1 := syscall.Rmdir(name)
	if e1 == nil {
		return nil
	}

	// Both failed: figure out which error to return.
	// OS X and Linux differ on whether unlink(dir)
	// returns EISDIR, so can't use that.  However,
	// both agree that rmdir(file) returns ENOTDIR,
	// so we can use that to decide which error is real.
	// Rmdir might also return ENOTDIR if given a bad
	// file path, like /etc/passwd/foo, but in that case,
	// both errors will be ENOTDIR, so it's okay to
	// use the error from unlink.
	if e1 != syscall.ENOTDIR {
		e = e1
	}
	return &PathError{"remove", name, e}
}

// basename removes trailing slashes and the leading directory name from path name
func basename(name string) string {
	i := len(name) - 1
	// Remove trailing slashes
	for ; i > 0 && name[i] == '/'; i-- {
		name = name[:i]
	}
	// Remove leading directory name
	for i--; i >= 0; i-- {
		if name[i] == '/' {
			name = name[i+1:]
			break
		}
	}

	return name
}

// TempDir returns the default directory to use for temporary files.
func TempDir() string {
	dir := Getenv("TMPDIR")
	if dir == "" {
		if runtime.GOOS == "android" {
			dir = "/data/local/tmp"
		} else {
			dir = "/tmp"
		}
	}
	return dir
}

// Link creates newname as a hard link to the oldname file.
// If there is an error, it will be of type *LinkError.
func Link(oldname, newname string) error {
	e := syscall.Link(oldname, newname)
	if e != nil {
		return &LinkError{"link", oldname, newname, e}
	}
	return nil
}

// Symlink creates newname as a symbolic link to oldname.
// If there is an error, it will be of type *LinkError.
func Symlink(oldname, newname string) error {
	e := syscall.Symlink(oldname, newname)
	if e != nil {
		return &LinkError{"symlink", oldname, newname, e}
	}
	return nil
}
<|endoftext|>