Merge pull request #5950 from tianon/update-deps

Update gorilla/mux, gorilla/context, and kr/pty deps
This commit is contained in:
Tianon Gravi 2014-05-20 16:16:21 -06:00
commit 1f219672fa
30 changed files with 661 additions and 138 deletions

View File

@ -39,11 +39,11 @@ clone() {
echo done
}
clone git github.com/kr/pty 98c7b80083
clone git github.com/kr/pty 67e2db24c8
clone git github.com/gorilla/context 708054d61e5
clone git github.com/gorilla/context b06ed15e1c
clone git github.com/gorilla/mux 9b36453141c
clone git github.com/gorilla/mux 136d54f81f
clone git github.com/syndtr/gocapability 3c85049eae

View File

@ -0,0 +1,7 @@
language: go
go:
- 1.0
- 1.1
- 1.2
- tip

View File

@ -1,5 +1,6 @@
context
=======
[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context)
gorilla/context is a general purpose registry for global request variables.

View File

@ -11,7 +11,7 @@ import (
)
var (
mutex sync.Mutex
mutex sync.RWMutex
data = make(map[*http.Request]map[interface{}]interface{})
datat = make(map[*http.Request]int64)
)
@ -19,42 +19,64 @@ var (
// Set stores a value for a given key in a given request.
func Set(r *http.Request, key, val interface{}) {
mutex.Lock()
defer mutex.Unlock()
if data[r] == nil {
data[r] = make(map[interface{}]interface{})
datat[r] = time.Now().Unix()
}
data[r][key] = val
mutex.Unlock()
}
// Get returns a value stored for a given key in a given request.
func Get(r *http.Request, key interface{}) interface{} {
mutex.Lock()
defer mutex.Unlock()
mutex.RLock()
if data[r] != nil {
mutex.RUnlock()
return data[r][key]
}
mutex.RUnlock()
return nil
}
// GetOk returns stored value and presence state like multi-value return of map access.
func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
mutex.Lock()
defer mutex.Unlock()
mutex.RLock()
if _, ok := data[r]; ok {
value, ok := data[r][key]
mutex.RUnlock()
return value, ok
}
mutex.RUnlock()
return nil, false
}
// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
func GetAll(r *http.Request) map[interface{}]interface{} {
mutex.RLock()
if context, ok := data[r]; ok {
mutex.RUnlock()
return context
}
mutex.RUnlock()
return nil
}
// GetAllOk returns all stored values for the request as a map. It returns not
// ok if the request was never registered.
func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) {
mutex.RLock()
context, ok := data[r]
mutex.RUnlock()
return context, ok
}
// Delete removes a value stored for a given key in a given request.
func Delete(r *http.Request, key interface{}) {
mutex.Lock()
defer mutex.Unlock()
if data[r] != nil {
delete(data[r], key)
}
mutex.Unlock()
}
// Clear removes all values stored for a given request.
@ -63,8 +85,8 @@ func Delete(r *http.Request, key interface{}) {
// variables at the end of a request lifetime. See ClearHandler().
func Clear(r *http.Request) {
mutex.Lock()
defer mutex.Unlock()
clear(r)
mutex.Unlock()
}
// clear is Clear without the lock.
@ -84,7 +106,6 @@ func clear(r *http.Request) {
// periodically until the problem is fixed.
func Purge(maxAge int) int {
mutex.Lock()
defer mutex.Unlock()
count := 0
if maxAge <= 0 {
count = len(data)
@ -92,13 +113,14 @@ func Purge(maxAge int) int {
datat = make(map[*http.Request]int64)
} else {
min := time.Now().Unix() - int64(maxAge)
for r, _ := range data {
for r := range data {
if datat[r] < min {
clear(r)
count++
}
}
}
mutex.Unlock()
return count
}

View File

@ -24,6 +24,7 @@ func TestContext(t *testing.T) {
}
r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
// Get()
assertEqual(Get(r, key1), nil)
@ -51,6 +52,26 @@ func TestContext(t *testing.T) {
assertEqual(value, nil)
assertEqual(ok, true)
// GetAll()
values := GetAll(r)
assertEqual(len(values), 3)
// GetAll() for empty request
values = GetAll(emptyR)
if values != nil {
t.Error("GetAll didn't return nil value for invalid request")
}
// GetAllOk()
values, ok = GetAllOk(r)
assertEqual(len(values), 3)
assertEqual(ok, true)
// GetAllOk() for empty request
values, ok = GetAllOk(emptyR)
assertEqual(value, nil)
assertEqual(ok, false)
// Delete()
Delete(r, key1)
assertEqual(Get(r, key1), nil)
@ -64,3 +85,77 @@ func TestContext(t *testing.T) {
Clear(r)
assertEqual(len(data), 0)
}
func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) {
<-wait
for i := 0; i < iterations; i++ {
Get(r, key)
}
done <- struct{}{}
}
func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) {
<-wait
for i := 0; i < iterations; i++ {
Get(r, key)
}
done <- struct{}{}
}
func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) {
b.StopTimer()
r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
done := make(chan struct{})
b.StartTimer()
for i := 0; i < b.N; i++ {
wait := make(chan struct{})
for i := 0; i < numReaders; i++ {
go parallelReader(r, "test", iterations, wait, done)
}
for i := 0; i < numWriters; i++ {
go parallelWriter(r, "test", "123", iterations, wait, done)
}
close(wait)
for i := 0; i < numReaders+numWriters; i++ {
<-done
}
}
}
func BenchmarkMutexSameReadWrite1(b *testing.B) {
benchmarkMutex(b, 1, 1, 32)
}
func BenchmarkMutexSameReadWrite2(b *testing.B) {
benchmarkMutex(b, 2, 2, 32)
}
func BenchmarkMutexSameReadWrite4(b *testing.B) {
benchmarkMutex(b, 4, 4, 32)
}
func BenchmarkMutex1(b *testing.B) {
benchmarkMutex(b, 2, 8, 32)
}
func BenchmarkMutex2(b *testing.B) {
benchmarkMutex(b, 16, 4, 64)
}
func BenchmarkMutex3(b *testing.B) {
benchmarkMutex(b, 1, 2, 128)
}
func BenchmarkMutex4(b *testing.B) {
benchmarkMutex(b, 128, 32, 256)
}
func BenchmarkMutex5(b *testing.B) {
benchmarkMutex(b, 1024, 2048, 64)
}
func BenchmarkMutex6(b *testing.B) {
benchmarkMutex(b, 2048, 1024, 512)
}

View File

@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
/*
Package gorilla/context stores values shared during a request lifetime.
Package context stores values shared during a request lifetime.
For example, a router can set variables extracted from the URL and later
application handlers can access those values, or it can be used to store

View File

@ -0,0 +1,7 @@
language: go
go:
- 1.0
- 1.1
- 1.2
- tip

View File

@ -1,5 +1,6 @@
mux
===
[![Build Status](https://travis-ci.org/gorilla/mux.png?branch=master)](https://travis-ci.org/gorilla/mux)
gorilla/mux is a powerful URL router and dispatcher.

View File

@ -134,7 +134,7 @@ the inner routes use it as base for their paths:
// "/products/{key}/"
s.HandleFunc("/{key}/", ProductHandler)
// "/products/{key}/details"
s.HandleFunc("/{key}/details"), ProductDetailsHandler)
s.HandleFunc("/{key}/details", ProductDetailsHandler)
Now let's see how to build registered URLs.

View File

@ -14,7 +14,7 @@ import (
// NewRouter returns a new router instance.
func NewRouter() *Router {
return &Router{namedRoutes: make(map[string]*Route)}
return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
}
// Router registers routes to be matched and dispatches a handler.
@ -46,6 +46,8 @@ type Router struct {
namedRoutes map[string]*Route
// See Router.StrictSlash(). This defines the flag for new routes.
strictSlash bool
// If true, do not clear the request context after handling the request
KeepContext bool
}
// Match matches registered routes against the request.
@ -65,6 +67,14 @@ func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Clean path to canonical form and redirect.
if p := cleanPath(req.URL.Path); p != req.URL.Path {
// Added 3 lines (Philip Schlump) - It was droping the query string and #whatever from query.
// This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue:
// http://code.google.com/p/go/issues/detail?id=5252
url := *req.URL
url.Path = p
p = url.String()
w.Header().Set("Location", p)
w.WriteHeader(http.StatusMovedPermanently)
return
@ -82,7 +92,9 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
handler = r.NotFoundHandler
}
defer context.Clear(req)
if !r.KeepContext {
defer context.Clear(req)
}
handler.ServeHTTP(w, req)
}
@ -97,14 +109,20 @@ func (r *Router) GetRoute(name string) *Route {
return r.getNamedRoutes()[name]
}
// StrictSlash defines the slash behavior for new routes.
// StrictSlash defines the trailing slash behavior for new routes. The initial
// value is false.
//
// When true, if the route path is "/path/", accessing "/path" will redirect
// to the former and vice versa.
// to the former and vice versa. In other words, your application will always
// see the path as specified in the route.
//
// Special case: when a route sets a path prefix, strict slash is
// automatically set to false for that route because the redirect behavior
// can't be determined for prefixes.
// When false, if the route path is "/path", accessing "/path/" will not match
// this route and vice versa.
//
// Special case: when a route sets a path prefix using the PathPrefix() method,
// strict slash is ignored for that route because the redirect behavior can't
// be determined from a prefix alone. However, any subrouters created from that
// route inherit the original StrictSlash setting.
func (r *Router) StrictSlash(value bool) *Router {
r.strictSlash = value
return r

View File

@ -8,16 +8,19 @@ import (
"fmt"
"net/http"
"testing"
"github.com/gorilla/context"
)
type routeTest struct {
title string // title of the test
route *Route // the route being tested
request *http.Request // a request to test the route
vars map[string]string // the expected vars of the match
host string // the expected host of the match
path string // the expected path of the match
shouldMatch bool // whether the request is expected to match the route at all
title string // title of the test
route *Route // the route being tested
request *http.Request // a request to test the route
vars map[string]string // the expected vars of the match
host string // the expected host of the match
path string // the expected path of the match
shouldMatch bool // whether the request is expected to match the route at all
shouldRedirect bool // whether the request should result in a redirect
}
func TestHost(t *testing.T) {
@ -149,6 +152,33 @@ func TestPath(t *testing.T) {
path: "/111/222/333",
shouldMatch: true,
},
{
title: "Path route, match with trailing slash in request and path",
route: new(Route).Path("/111/"),
request: newRequest("GET", "http://localhost/111/"),
vars: map[string]string{},
host: "",
path: "/111/",
shouldMatch: true,
},
{
title: "Path route, do not match with trailing slash in path",
route: new(Route).Path("/111/"),
request: newRequest("GET", "http://localhost/111"),
vars: map[string]string{},
host: "",
path: "/111",
shouldMatch: false,
},
{
title: "Path route, do not match with trailing slash in request",
route: new(Route).Path("/111"),
request: newRequest("GET", "http://localhost/111/"),
vars: map[string]string{},
host: "",
path: "/111/",
shouldMatch: false,
},
{
title: "Path route, wrong path in request in request URL",
route: new(Route).Path("/111/222/333"),
@ -212,6 +242,15 @@ func TestPathPrefix(t *testing.T) {
path: "/111",
shouldMatch: true,
},
{
title: "PathPrefix route, match substring",
route: new(Route).PathPrefix("/1"),
request: newRequest("GET", "http://localhost/111/222/333"),
vars: map[string]string{},
host: "",
path: "/1",
shouldMatch: true,
},
{
title: "PathPrefix route, URL prefix in request does not match",
route: new(Route).PathPrefix("/111"),
@ -414,6 +453,15 @@ func TestQueries(t *testing.T) {
path: "",
shouldMatch: true,
},
{
title: "Queries route, match with a query string",
route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
request: newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"),
vars: map[string]string{},
host: "",
path: "",
shouldMatch: true,
},
{
title: "Queries route, bad query",
route: new(Route).Queries("foo", "bar", "baz", "ding"),
@ -568,26 +616,74 @@ func TestNamedRoutes(t *testing.T) {
}
func TestStrictSlash(t *testing.T) {
var r *Router
var req *http.Request
var route *Route
var match *RouteMatch
var matched bool
// StrictSlash should be ignored for path prefix.
// So we register a route ending in slash but it doesn't attempt to add
// the slash for a path not ending in slash.
r = NewRouter()
r := NewRouter()
r.StrictSlash(true)
route = r.NewRoute().PathPrefix("/static/")
req, _ = http.NewRequest("GET", "http://localhost/static/logo.png", nil)
match = new(RouteMatch)
matched = r.Match(req, match)
if !matched {
t.Errorf("Should match request %q -- %v", req.URL.Path, getRouteTemplate(route))
tests := []routeTest{
{
title: "Redirect path without slash",
route: r.NewRoute().Path("/111/"),
request: newRequest("GET", "http://localhost/111"),
vars: map[string]string{},
host: "",
path: "/111/",
shouldMatch: true,
shouldRedirect: true,
},
{
title: "Do not redirect path with slash",
route: r.NewRoute().Path("/111/"),
request: newRequest("GET", "http://localhost/111/"),
vars: map[string]string{},
host: "",
path: "/111/",
shouldMatch: true,
shouldRedirect: false,
},
{
title: "Redirect path with slash",
route: r.NewRoute().Path("/111"),
request: newRequest("GET", "http://localhost/111/"),
vars: map[string]string{},
host: "",
path: "/111",
shouldMatch: true,
shouldRedirect: true,
},
{
title: "Do not redirect path without slash",
route: r.NewRoute().Path("/111"),
request: newRequest("GET", "http://localhost/111"),
vars: map[string]string{},
host: "",
path: "/111",
shouldMatch: true,
shouldRedirect: false,
},
{
title: "Propagate StrictSlash to subrouters",
route: r.NewRoute().PathPrefix("/static/").Subrouter().Path("/images/"),
request: newRequest("GET", "http://localhost/static/images"),
vars: map[string]string{},
host: "",
path: "/static/images/",
shouldMatch: true,
shouldRedirect: true,
},
{
title: "Ignore StrictSlash for path prefix",
route: r.NewRoute().PathPrefix("/static/"),
request: newRequest("GET", "http://localhost/static/logo.png"),
vars: map[string]string{},
host: "",
path: "/static/",
shouldMatch: true,
shouldRedirect: false,
},
}
if match.Handler != nil {
t.Errorf("Should not redirect")
for _, test := range tests {
testRoute(t, test)
}
}
@ -616,6 +712,7 @@ func testRoute(t *testing.T, test routeTest) {
host := test.host
path := test.path
url := test.host + test.path
shouldRedirect := test.shouldRedirect
var match RouteMatch
ok := route.Match(request, &match)
@ -653,6 +750,84 @@ func testRoute(t *testing.T, test routeTest) {
return
}
}
if shouldRedirect && match.Handler == nil {
t.Errorf("(%v) Did not redirect", test.title)
return
}
if !shouldRedirect && match.Handler != nil {
t.Errorf("(%v) Unexpected redirect", test.title)
return
}
}
}
// Tests that the context is cleared or not cleared properly depending on
// the configuration of the router
func TestKeepContext(t *testing.T) {
func1 := func(w http.ResponseWriter, r *http.Request) {}
r := NewRouter()
r.HandleFunc("/", func1).Name("func1")
req, _ := http.NewRequest("GET", "http://localhost/", nil)
context.Set(req, "t", 1)
res := new(http.ResponseWriter)
r.ServeHTTP(*res, req)
if _, ok := context.GetOk(req, "t"); ok {
t.Error("Context should have been cleared at end of request")
}
r.KeepContext = true
req, _ = http.NewRequest("GET", "http://localhost/", nil)
context.Set(req, "t", 1)
r.ServeHTTP(*res, req)
if _, ok := context.GetOk(req, "t"); !ok {
t.Error("Context should NOT have been cleared at end of request")
}
}
type TestA301ResponseWriter struct {
hh http.Header
status int
}
func (ho TestA301ResponseWriter) Header() http.Header {
return http.Header(ho.hh)
}
func (ho TestA301ResponseWriter) Write(b []byte) (int, error) {
return 0, nil
}
func (ho TestA301ResponseWriter) WriteHeader(code int) {
ho.status = code
}
func Test301Redirect(t *testing.T) {
m := make(http.Header)
func1 := func(w http.ResponseWriter, r *http.Request) {}
func2 := func(w http.ResponseWriter, r *http.Request) {}
r := NewRouter()
r.HandleFunc("/api/", func2).Name("func2")
r.HandleFunc("/", func1).Name("func1")
req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil)
res := TestA301ResponseWriter{
hh: m,
status: 0,
}
r.ServeHTTP(&res, req)
if "http://localhost/api/?abc=def" != res.hh["Location"][0] {
t.Errorf("Should have complete URL with query string")
}
}

View File

@ -96,8 +96,8 @@ func TestRouteMatchers(t *testing.T) {
method = "GET"
headers = map[string]string{"X-Requested-With": "XMLHttpRequest"}
resultVars = map[bool]map[string]string{
true: map[string]string{"var1": "www", "var2": "product", "var3": "42"},
false: map[string]string{},
true: {"var1": "www", "var2": "product", "var3": "42"},
false: {},
}
}
@ -110,8 +110,8 @@ func TestRouteMatchers(t *testing.T) {
method = "POST"
headers = map[string]string{"Content-Type": "application/json"}
resultVars = map[bool]map[string]string{
true: map[string]string{"var4": "google", "var5": "product", "var6": "42"},
false: map[string]string{},
true: {"var4": "google", "var5": "product", "var6": "42"},
false: {},
}
}

View File

@ -98,12 +98,13 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, strictSlash bool) (*rout
}
// Done!
return &routeRegexp{
template: template,
matchHost: matchHost,
regexp: reg,
reverse: reverse.String(),
varsN: varsN,
varsR: varsR,
template: template,
matchHost: matchHost,
strictSlash: strictSlash,
regexp: reg,
reverse: reverse.String(),
varsN: varsN,
varsR: varsR,
}, nil
}
@ -114,6 +115,8 @@ type routeRegexp struct {
template string
// True for host match, false for path match.
matchHost bool
// The strictSlash value defined on the route, but disabled if PathPrefix was used.
strictSlash bool
// Expanded regexp.
regexp *regexp.Regexp
// Reverse template.
@ -216,7 +219,7 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
m.Vars[v] = pathVars[k+1]
}
// Check if we should redirect.
if r.strictSlash {
if v.path.strictSlash {
p1 := strings.HasSuffix(req.URL.Path, "/")
p2 := strings.HasSuffix(v.path.template, "/")
if p1 != p2 {

View File

@ -259,7 +259,8 @@ func (r *Route) Methods(methods ...string) *Route {
// Path -----------------------------------------------------------------------
// Path adds a matcher for the URL path.
// It accepts a template with zero or more URL variables enclosed by {}.
// It accepts a template with zero or more URL variables enclosed by {}. The
// template must start with a "/".
// Variables can define an optional regexp pattern to me matched:
//
// - {name} matches anything until the next slash.
@ -283,9 +284,16 @@ func (r *Route) Path(tpl string) *Route {
// PathPrefix -----------------------------------------------------------------
// PathPrefix adds a matcher for the URL path prefix.
// PathPrefix adds a matcher for the URL path prefix. This matches if the given
// template is a prefix of the full URL path. See Route.Path() for details on
// the tpl argument.
//
// Note that it does not treat slashes specially ("/foobar/" will be matched by
// the prefix "/foo") so you may want to use a trailing slash here.
//
// Also note that the setting of Router.StrictSlash() has no effect on routes
// with a PathPrefix matcher.
func (r *Route) PathPrefix(tpl string) *Route {
r.strictSlash = false
r.err = r.addRegexpMatcher(tpl, false, true)
return r
}
@ -328,7 +336,7 @@ func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
}
// Schemes adds a matcher for URL schemes.
// It accepts a sequence schemes to be matched, e.g.: "http", "https".
// It accepts a sequence of schemes to be matched, e.g.: "http", "https".
func (r *Route) Schemes(schemes ...string) *Route {
for k, v := range schemes {
schemes[k] = strings.ToLower(v)

11
vendor/src/github.com/kr/pty/ioctl.go vendored Normal file
View File

@ -0,0 +1,11 @@
package pty
import "syscall"
func ioctl(fd, cmd, ptr uintptr) error {
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
if e != 0 {
return e
}
return nil
}

View File

@ -0,0 +1,39 @@
// +build darwin dragonfly freebsd netbsd openbsd
package pty
// from <sys/ioccom.h>
const (
_IOC_VOID uintptr = 0x20000000
_IOC_OUT uintptr = 0x40000000
_IOC_IN uintptr = 0x80000000
_IOC_IN_OUT uintptr = _IOC_OUT | _IOC_IN
_IOC_DIRMASK = _IOC_VOID | _IOC_OUT | _IOC_IN
_IOC_PARAM_SHIFT = 13
_IOC_PARAM_MASK = (1 << _IOC_PARAM_SHIFT) - 1
)
func _IOC_PARM_LEN(ioctl uintptr) uintptr {
return (ioctl >> 16) & _IOC_PARAM_MASK
}
func _IOC(inout uintptr, group byte, ioctl_num uintptr, param_len uintptr) uintptr {
return inout | (param_len&_IOC_PARAM_MASK)<<16 | uintptr(group)<<8 | ioctl_num
}
func _IO(group byte, ioctl_num uintptr) uintptr {
return _IOC(_IOC_VOID, group, ioctl_num, 0)
}
func _IOR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
return _IOC(_IOC_OUT, group, ioctl_num, param_len)
}
func _IOW(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
return _IOC(_IOC_IN, group, ioctl_num, param_len)
}
func _IOWR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
return _IOC(_IOC_IN_OUT, group, ioctl_num, param_len)
}

View File

@ -0,0 +1,42 @@
package pty
// from <asm-generic/ioctl.h>
const (
_IOC_NRBITS = 8
_IOC_TYPEBITS = 8
_IOC_SIZEBITS = 14
_IOC_DIRBITS = 2
_IOC_NRSHIFT = 0
_IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS
_IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS
_IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS
_IOC_NONE uint = 0
_IOC_WRITE uint = 1
_IOC_READ uint = 2
)
func _IOC(dir uint, ioctl_type byte, nr byte, size uintptr) uintptr {
return (uintptr(dir)<<_IOC_DIRSHIFT |
uintptr(ioctl_type)<<_IOC_TYPESHIFT |
uintptr(nr)<<_IOC_NRSHIFT |
size<<_IOC_SIZESHIFT)
}
func _IO(ioctl_type byte, nr byte) uintptr {
return _IOC(_IOC_NONE, ioctl_type, nr, 0)
}
func _IOR(ioctl_type byte, nr byte, size uintptr) uintptr {
return _IOC(_IOC_READ, ioctl_type, nr, size)
}
func _IOW(ioctl_type byte, nr byte, size uintptr) uintptr {
return _IOC(_IOC_WRITE, ioctl_type, nr, size)
}
func _IOWR(ioctl_type byte, nr byte, size uintptr) uintptr {
return _IOC(_IOC_READ|_IOC_WRITE, ioctl_type, nr, size)
}

19
vendor/src/github.com/kr/pty/mktypes.bash vendored Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env bash
GOOSARCH="${GOOS}_${GOARCH}"
case "$GOOSARCH" in
_* | *_ | _)
echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
exit 1
;;
esac
GODEFS="go tool cgo -godefs"
$GODEFS types.go |gofmt > ztypes_$GOARCH.go
case $GOOS in
freebsd)
$GODEFS types_$GOOS.go |gofmt > ztypes_$GOOSARCH.go
;;
esac

View File

@ -7,9 +7,6 @@ import (
"unsafe"
)
// see ioccom.h
const sys_IOCPARM_MASK = 0x1fff
func open() (pty, tty *os.File, err error) {
p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
if err != nil {
@ -39,9 +36,13 @@ func open() (pty, tty *os.File, err error) {
}
func ptsname(f *os.File) (string, error) {
var n [(syscall.TIOCPTYGNAME >> 16) & sys_IOCPARM_MASK]byte
n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME))
err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0])))
if err != nil {
return "", err
}
ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n)))
for i, c := range n {
if c == 0 {
return string(n[:i]), nil
@ -51,19 +52,9 @@ func ptsname(f *os.File) (string, error) {
}
func grantpt(f *os.File) error {
var u int
return ioctl(f.Fd(), syscall.TIOCPTYGRANT, uintptr(unsafe.Pointer(&u)))
return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0)
}
func unlockpt(f *os.File) error {
var u int
return ioctl(f.Fd(), syscall.TIOCPTYUNLK, uintptr(unsafe.Pointer(&u)))
}
func ioctl(fd, cmd, ptr uintptr) error {
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
if e != 0 {
return syscall.ENOTTY
}
return nil
return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0)
}

View File

@ -1,53 +1,73 @@
package pty
import (
"errors"
"os"
"strconv"
"syscall"
"unsafe"
)
const (
sys_TIOCGPTN = 0x4004740F
sys_TIOCSPTLCK = 0x40045431
)
func posix_openpt(oflag int) (fd int, err error) {
r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0)
fd = int(r0)
if e1 != 0 {
err = e1
}
return
}
func open() (pty, tty *os.File, err error) {
p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
fd, err := posix_openpt(syscall.O_RDWR | syscall.O_CLOEXEC)
if err != nil {
return nil, nil, err
}
p := os.NewFile(uintptr(fd), "/dev/pts")
sname, err := ptsname(p)
if err != nil {
return nil, nil, err
}
t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
t, err := os.OpenFile("/dev/"+sname, os.O_RDWR, 0)
if err != nil {
return nil, nil, err
}
return p, t, nil
}
func isptmaster(fd uintptr) (bool, error) {
err := ioctl(fd, syscall.TIOCPTMASTER, 0)
return err == nil, err
}
var (
emptyFiodgnameArg fiodgnameArg
ioctl_FIODGNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
)
func ptsname(f *os.File) (string, error) {
var n int
err := ioctl(f.Fd(), sys_TIOCGPTN, &n)
master, err := isptmaster(f.Fd())
if err != nil {
return "", err
}
return "/dev/pts/" + strconv.Itoa(n), nil
}
func ioctl(fd uintptr, cmd uintptr, data *int) error {
_, _, e := syscall.Syscall(
syscall.SYS_IOCTL,
fd,
cmd,
uintptr(unsafe.Pointer(data)),
)
if e != 0 {
return syscall.ENOTTY
if !master {
return "", syscall.EINVAL
}
return nil
const n = _C_SPECNAMELEN + 1
var (
buf = make([]byte, n)
arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))}
)
err = ioctl(f.Fd(), ioctl_FIODGNAME, uintptr(unsafe.Pointer(&arg)))
if err != nil {
return "", err
}
for i, c := range buf {
if c == 0 {
return string(buf[:i]), nil
}
}
return "", errors.New("FIODGNAME string not NUL-terminated")
}

View File

@ -7,9 +7,9 @@ import (
"unsafe"
)
const (
sys_TIOCGPTN = 0x80045430
sys_TIOCSPTLCK = 0x40045431
var (
ioctl_TIOCGPTN = _IOR('T', 0x30, unsafe.Sizeof(_C_uint(0))) /* Get Pty Number (of pty-mux device) */
ioctl_TIOCSPTLCK = _IOW('T', 0x31, unsafe.Sizeof(_C_int(0))) /* Lock/unlock Pty */
)
func open() (pty, tty *os.File, err error) {
@ -36,28 +36,16 @@ func open() (pty, tty *os.File, err error) {
}
func ptsname(f *os.File) (string, error) {
var n int
err := ioctl(f.Fd(), sys_TIOCGPTN, &n)
var n _C_uint
err := ioctl(f.Fd(), ioctl_TIOCGPTN, uintptr(unsafe.Pointer(&n)))
if err != nil {
return "", err
}
return "/dev/pts/" + strconv.Itoa(n), nil
return "/dev/pts/" + strconv.Itoa(int(n)), nil
}
func unlockpt(f *os.File) error {
var u int
return ioctl(f.Fd(), sys_TIOCSPTLCK, &u)
}
func ioctl(fd uintptr, cmd uintptr, data *int) error {
_, _, e := syscall.Syscall(
syscall.SYS_IOCTL,
fd,
cmd,
uintptr(unsafe.Pointer(data)),
)
if e != 0 {
return syscall.ENOTTY
}
return nil
var u _C_int
// use TIOCSPTLCK with a zero valued arg to clear the slave pty lock
return ioctl(f.Fd(), ioctl_TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
}

View File

@ -9,19 +9,3 @@ import (
func open() (pty, tty *os.File, err error) {
return nil, nil, ErrUnsupported
}
func ptsname(f *os.File) (string, error) {
return "", ErrUnsupported
}
func grantpt(f *os.File) error {
return ErrUnsupported
}
func unlockpt(f *os.File) error {
return ErrUnsupported
}
func ioctl(fd, cmd, ptr uintptr) error {
return ErrUnsupported
}

10
vendor/src/github.com/kr/pty/types.go vendored Normal file
View File

@ -0,0 +1,10 @@
// +build ignore
package pty
import "C"
type (
_C_int C.int
_C_uint C.uint
)

View File

@ -0,0 +1,15 @@
// +build ignore
package pty
/*
#include <sys/param.h>
#include <sys/filio.h>
*/
import "C"
const (
_C_SPECNAMELEN = C.SPECNAMELEN /* max length of devicename */
)
type fiodgnameArg C.struct_fiodgname_arg

View File

@ -0,0 +1,9 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go
package pty
type (
_C_int int32
_C_uint uint32
)

View File

@ -0,0 +1,9 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go
package pty
type (
_C_int int32
_C_uint uint32
)

View File

@ -0,0 +1,9 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go
package pty
type (
_C_int int32
_C_uint uint32
)

View File

@ -0,0 +1,13 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go
package pty
const (
_C_SPECNAMELEN = 0x3f
)
type fiodgnameArg struct {
Len int32
Buf *byte
}

View File

@ -0,0 +1,14 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go
package pty
const (
_C_SPECNAMELEN = 0x3f
)
type fiodgnameArg struct {
Len int32
Pad_cgo_0 [4]byte
Buf *byte
}

View File

@ -0,0 +1,13 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go
package pty
const (
_C_SPECNAMELEN = 0x3f
)
type fiodgnameArg struct {
Len int32
Buf *byte
}