@@ -17,9 +17,7 @@ require ( | |||
github.com/writeas/go-writeas/v2 v2.0.2 | |||
github.com/writeas/saturday v0.0.0-20170402010311-f455b05c043f // indirect | |||
github.com/writeas/web-core v0.0.0-20181111165528-05f387ffa1b3 | |||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 // indirect | |||
golang.org/x/net v0.0.0-20181217023233-e147a9138326 // indirect | |||
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 // indirect | |||
golang.org/x/net v0.7.0 // indirect | |||
gopkg.in/ini.v1 v1.39.3 | |||
gopkg.in/urfave/cli.v1 v1.20.0 | |||
) | |||
@@ -1,5 +1,3 @@ | |||
code.as/core/socks v0.0.0-20180906144846-5be269b4e664 h1:zWSFbwkYSuZ2PjvHqYDE/dhd9CCcsbSvUIRx8hIed3I= | |||
code.as/core/socks v0.0.0-20180906144846-5be269b4e664/go.mod h1:BAXBy5O9s2gmw6UxLqNJcVbWY7C/UPs+801CcSsfWOY= | |||
code.as/core/socks v1.0.0 h1:SPQXNp4SbEwjOAP9VzUahLHak8SDqy5n+9cm9tpjZOs= | |||
code.as/core/socks v1.0.0/go.mod h1:BAXBy5O9s2gmw6UxLqNJcVbWY7C/UPs+801CcSsfWOY= | |||
github.com/atotto/clipboard v0.1.1 h1:WSoEbAS70E5gw8FbiqFlp69MGsB6dUb4l+0AGGLiVGw= | |||
@@ -39,26 +37,48 @@ github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpke | |||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= | |||
github.com/writeas/go-writeas/v2 v2.0.2 h1:akvdMg89U5oBJiCkBwOXljVLTqP354uN6qnG2oOMrbk= | |||
github.com/writeas/go-writeas/v2 v2.0.2/go.mod h1:9sjczQJKmru925fLzg0usrU1R1tE4vBmQtGnItUMR0M= | |||
github.com/writeas/impart v0.0.0-20180808220913-fef51864677b h1:vsZIsYneuNwXMsnh0lKviEVc8WeIqBG4RTmGWU86HpI= | |||
github.com/writeas/impart v0.0.0-20180808220913-fef51864677b/go.mod h1:sUkQZZHJfrVNsoD4QbkrYrRSQtCN3SaUPWKdohmFKT8= | |||
github.com/writeas/impart v1.1.0 h1:nPnoO211VscNkp/gnzir5UwCDEvdHThL5uELU60NFSE= | |||
github.com/writeas/impart v1.1.0/go.mod h1:g0MpxdnTOHHrl+Ca/2oMXUHJ0PcRAEWtkCzYCJUXC9Y= | |||
github.com/writeas/saturday v0.0.0-20170402010311-f455b05c043f h1:yyFguE0EopK8e7I7/AB1JWM925OFOI1uFhTM/SwXAnQ= | |||
github.com/writeas/saturday v0.0.0-20170402010311-f455b05c043f/go.mod h1:ETE1EK6ogxptJpAgUbcJD0prAtX48bSloie80+tvnzQ= | |||
github.com/writeas/web-core v0.0.0-20181111165528-05f387ffa1b3 h1:mKD4DMZuiZWrn1k/f+1wLmBu9SYMrydy9om+eeo9kjA= | |||
github.com/writeas/web-core v0.0.0-20181111165528-05f387ffa1b3/go.mod h1:Si3chV7VWgY8CsV+3gRolMXSO2Vx1ZFAQ/mkrpvmyEE= | |||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0= | |||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | |||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= | |||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | |||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= | |||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | |||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= | |||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |||
golang.org/x/net v0.0.0-20181217023233-e147a9138326 h1:iCzOf0xz39Tstp+Tu/WwyGjUXCk34QhQORRxBeXXTA4= | |||
golang.org/x/net v0.0.0-20181217023233-e147a9138326/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= | |||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | |||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | |||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= | |||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | |||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= | |||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |||
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 h1:0oC8rFnE+74kEmuHZ46F6KHsMr5Gx2gUQPuNz28iQZM= | |||
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= | |||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= | |||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | |||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | |||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= | |||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= | |||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | |||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | |||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= | |||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | |||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | |||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | |||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | |||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | |||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= | |||
@@ -2,950 +2,75 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// Package terminal provides support functions for dealing with terminals, as | |||
// commonly found on UNIX systems. | |||
// | |||
// Deprecated: this package moved to golang.org/x/term. | |||
package terminal | |||
import ( | |||
"bytes" | |||
"io" | |||
"sync" | |||
"unicode/utf8" | |||
"golang.org/x/term" | |||
) | |||
// EscapeCodes contains escape sequences that can be written to the terminal in | |||
// order to achieve different styles of text. | |||
type EscapeCodes struct { | |||
// Foreground colors | |||
Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte | |||
// Reset all attributes | |||
Reset []byte | |||
} | |||
var vt100EscapeCodes = EscapeCodes{ | |||
Black: []byte{keyEscape, '[', '3', '0', 'm'}, | |||
Red: []byte{keyEscape, '[', '3', '1', 'm'}, | |||
Green: []byte{keyEscape, '[', '3', '2', 'm'}, | |||
Yellow: []byte{keyEscape, '[', '3', '3', 'm'}, | |||
Blue: []byte{keyEscape, '[', '3', '4', 'm'}, | |||
Magenta: []byte{keyEscape, '[', '3', '5', 'm'}, | |||
Cyan: []byte{keyEscape, '[', '3', '6', 'm'}, | |||
White: []byte{keyEscape, '[', '3', '7', 'm'}, | |||
Reset: []byte{keyEscape, '[', '0', 'm'}, | |||
} | |||
type EscapeCodes = term.EscapeCodes | |||
// Terminal contains the state for running a VT100 terminal that is capable of | |||
// reading lines of input. | |||
type Terminal struct { | |||
// AutoCompleteCallback, if non-null, is called for each keypress with | |||
// the full input line and the current position of the cursor (in | |||
// bytes, as an index into |line|). If it returns ok=false, the key | |||
// press is processed normally. Otherwise it returns a replacement line | |||
// and the new cursor position. | |||
AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool) | |||
// Escape contains a pointer to the escape codes for this terminal. | |||
// It's always a valid pointer, although the escape codes themselves | |||
// may be empty if the terminal doesn't support them. | |||
Escape *EscapeCodes | |||
// lock protects the terminal and the state in this object from | |||
// concurrent processing of a key press and a Write() call. | |||
lock sync.Mutex | |||
c io.ReadWriter | |||
prompt []rune | |||
// line is the current line being entered. | |||
line []rune | |||
// pos is the logical position of the cursor in line | |||
pos int | |||
// echo is true if local echo is enabled | |||
echo bool | |||
// pasteActive is true iff there is a bracketed paste operation in | |||
// progress. | |||
pasteActive bool | |||
// cursorX contains the current X value of the cursor where the left | |||
// edge is 0. cursorY contains the row number where the first row of | |||
// the current line is 0. | |||
cursorX, cursorY int | |||
// maxLine is the greatest value of cursorY so far. | |||
maxLine int | |||
termWidth, termHeight int | |||
// outBuf contains the terminal data to be sent. | |||
outBuf []byte | |||
// remainder contains the remainder of any partial key sequences after | |||
// a read. It aliases into inBuf. | |||
remainder []byte | |||
inBuf [256]byte | |||
// history contains previously entered commands so that they can be | |||
// accessed with the up and down keys. | |||
history stRingBuffer | |||
// historyIndex stores the currently accessed history entry, where zero | |||
// means the immediately previous entry. | |||
historyIndex int | |||
// When navigating up and down the history it's possible to return to | |||
// the incomplete, initial line. That value is stored in | |||
// historyPending. | |||
historyPending string | |||
} | |||
type Terminal = term.Terminal | |||
// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is | |||
// a local terminal, that terminal must first have been put into raw mode. | |||
// prompt is a string that is written at the start of each input line (i.e. | |||
// "> "). | |||
func NewTerminal(c io.ReadWriter, prompt string) *Terminal { | |||
return &Terminal{ | |||
Escape: &vt100EscapeCodes, | |||
c: c, | |||
prompt: []rune(prompt), | |||
termWidth: 80, | |||
termHeight: 24, | |||
echo: true, | |||
historyIndex: -1, | |||
} | |||
} | |||
const ( | |||
keyCtrlD = 4 | |||
keyCtrlU = 21 | |||
keyEnter = '\r' | |||
keyEscape = 27 | |||
keyBackspace = 127 | |||
keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota | |||
keyUp | |||
keyDown | |||
keyLeft | |||
keyRight | |||
keyAltLeft | |||
keyAltRight | |||
keyHome | |||
keyEnd | |||
keyDeleteWord | |||
keyDeleteLine | |||
keyClearScreen | |||
keyPasteStart | |||
keyPasteEnd | |||
) | |||
var ( | |||
crlf = []byte{'\r', '\n'} | |||
pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'} | |||
pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'} | |||
) | |||
// bytesToKey tries to parse a key sequence from b. If successful, it returns | |||
// the key and the remainder of the input. Otherwise it returns utf8.RuneError. | |||
func bytesToKey(b []byte, pasteActive bool) (rune, []byte) { | |||
if len(b) == 0 { | |||
return utf8.RuneError, nil | |||
} | |||
if !pasteActive { | |||
switch b[0] { | |||
case 1: // ^A | |||
return keyHome, b[1:] | |||
case 5: // ^E | |||
return keyEnd, b[1:] | |||
case 8: // ^H | |||
return keyBackspace, b[1:] | |||
case 11: // ^K | |||
return keyDeleteLine, b[1:] | |||
case 12: // ^L | |||
return keyClearScreen, b[1:] | |||
case 23: // ^W | |||
return keyDeleteWord, b[1:] | |||
} | |||
} | |||
if b[0] != keyEscape { | |||
if !utf8.FullRune(b) { | |||
return utf8.RuneError, b | |||
} | |||
r, l := utf8.DecodeRune(b) | |||
return r, b[l:] | |||
} | |||
if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' { | |||
switch b[2] { | |||
case 'A': | |||
return keyUp, b[3:] | |||
case 'B': | |||
return keyDown, b[3:] | |||
case 'C': | |||
return keyRight, b[3:] | |||
case 'D': | |||
return keyLeft, b[3:] | |||
case 'H': | |||
return keyHome, b[3:] | |||
case 'F': | |||
return keyEnd, b[3:] | |||
} | |||
} | |||
if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' { | |||
switch b[5] { | |||
case 'C': | |||
return keyAltRight, b[6:] | |||
case 'D': | |||
return keyAltLeft, b[6:] | |||
} | |||
} | |||
if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) { | |||
return keyPasteStart, b[6:] | |||
} | |||
if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) { | |||
return keyPasteEnd, b[6:] | |||
} | |||
// If we get here then we have a key that we don't recognise, or a | |||
// partial sequence. It's not clear how one should find the end of a | |||
// sequence without knowing them all, but it seems that [a-zA-Z~] only | |||
// appears at the end of a sequence. | |||
for i, c := range b[0:] { | |||
if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' { | |||
return keyUnknown, b[i+1:] | |||
} | |||
} | |||
return utf8.RuneError, b | |||
} | |||
// queue appends data to the end of t.outBuf | |||
func (t *Terminal) queue(data []rune) { | |||
t.outBuf = append(t.outBuf, []byte(string(data))...) | |||
} | |||
var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'} | |||
var space = []rune{' '} | |||
func isPrintable(key rune) bool { | |||
isInSurrogateArea := key >= 0xd800 && key <= 0xdbff | |||
return key >= 32 && !isInSurrogateArea | |||
} | |||
// moveCursorToPos appends data to t.outBuf which will move the cursor to the | |||
// given, logical position in the text. | |||
func (t *Terminal) moveCursorToPos(pos int) { | |||
if !t.echo { | |||
return | |||
} | |||
x := visualLength(t.prompt) + pos | |||
y := x / t.termWidth | |||
x = x % t.termWidth | |||
up := 0 | |||
if y < t.cursorY { | |||
up = t.cursorY - y | |||
} | |||
down := 0 | |||
if y > t.cursorY { | |||
down = y - t.cursorY | |||
} | |||
left := 0 | |||
if x < t.cursorX { | |||
left = t.cursorX - x | |||
} | |||
right := 0 | |||
if x > t.cursorX { | |||
right = x - t.cursorX | |||
} | |||
t.cursorX = x | |||
t.cursorY = y | |||
t.move(up, down, left, right) | |||
} | |||
func (t *Terminal) move(up, down, left, right int) { | |||
movement := make([]rune, 3*(up+down+left+right)) | |||
m := movement | |||
for i := 0; i < up; i++ { | |||
m[0] = keyEscape | |||
m[1] = '[' | |||
m[2] = 'A' | |||
m = m[3:] | |||
} | |||
for i := 0; i < down; i++ { | |||
m[0] = keyEscape | |||
m[1] = '[' | |||
m[2] = 'B' | |||
m = m[3:] | |||
} | |||
for i := 0; i < left; i++ { | |||
m[0] = keyEscape | |||
m[1] = '[' | |||
m[2] = 'D' | |||
m = m[3:] | |||
} | |||
for i := 0; i < right; i++ { | |||
m[0] = keyEscape | |||
m[1] = '[' | |||
m[2] = 'C' | |||
m = m[3:] | |||
} | |||
t.queue(movement) | |||
} | |||
func (t *Terminal) clearLineToRight() { | |||
op := []rune{keyEscape, '[', 'K'} | |||
t.queue(op) | |||
} | |||
const maxLineLength = 4096 | |||
func (t *Terminal) setLine(newLine []rune, newPos int) { | |||
if t.echo { | |||
t.moveCursorToPos(0) | |||
t.writeLine(newLine) | |||
for i := len(newLine); i < len(t.line); i++ { | |||
t.writeLine(space) | |||
} | |||
t.moveCursorToPos(newPos) | |||
} | |||
t.line = newLine | |||
t.pos = newPos | |||
} | |||
func (t *Terminal) advanceCursor(places int) { | |||
t.cursorX += places | |||
t.cursorY += t.cursorX / t.termWidth | |||
if t.cursorY > t.maxLine { | |||
t.maxLine = t.cursorY | |||
} | |||
t.cursorX = t.cursorX % t.termWidth | |||
if places > 0 && t.cursorX == 0 { | |||
// Normally terminals will advance the current position | |||
// when writing a character. But that doesn't happen | |||
// for the last character in a line. However, when | |||
// writing a character (except a new line) that causes | |||
// a line wrap, the position will be advanced two | |||
// places. | |||
// | |||
// So, if we are stopping at the end of a line, we | |||
// need to write a newline so that our cursor can be | |||
// advanced to the next line. | |||
t.outBuf = append(t.outBuf, '\r', '\n') | |||
} | |||
} | |||
func (t *Terminal) eraseNPreviousChars(n int) { | |||
if n == 0 { | |||
return | |||
} | |||
if t.pos < n { | |||
n = t.pos | |||
} | |||
t.pos -= n | |||
t.moveCursorToPos(t.pos) | |||
copy(t.line[t.pos:], t.line[n+t.pos:]) | |||
t.line = t.line[:len(t.line)-n] | |||
if t.echo { | |||
t.writeLine(t.line[t.pos:]) | |||
for i := 0; i < n; i++ { | |||
t.queue(space) | |||
} | |||
t.advanceCursor(n) | |||
t.moveCursorToPos(t.pos) | |||
} | |||
} | |||
// countToLeftWord returns then number of characters from the cursor to the | |||
// start of the previous word. | |||
func (t *Terminal) countToLeftWord() int { | |||
if t.pos == 0 { | |||
return 0 | |||
} | |||
pos := t.pos - 1 | |||
for pos > 0 { | |||
if t.line[pos] != ' ' { | |||
break | |||
} | |||
pos-- | |||
} | |||
for pos > 0 { | |||
if t.line[pos] == ' ' { | |||
pos++ | |||
break | |||
} | |||
pos-- | |||
} | |||
return t.pos - pos | |||
} | |||
// countToRightWord returns then number of characters from the cursor to the | |||
// start of the next word. | |||
func (t *Terminal) countToRightWord() int { | |||
pos := t.pos | |||
for pos < len(t.line) { | |||
if t.line[pos] == ' ' { | |||
break | |||
} | |||
pos++ | |||
} | |||
for pos < len(t.line) { | |||
if t.line[pos] != ' ' { | |||
break | |||
} | |||
pos++ | |||
} | |||
return pos - t.pos | |||
} | |||
// visualLength returns the number of visible glyphs in s. | |||
func visualLength(runes []rune) int { | |||
inEscapeSeq := false | |||
length := 0 | |||
for _, r := range runes { | |||
switch { | |||
case inEscapeSeq: | |||
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') { | |||
inEscapeSeq = false | |||
} | |||
case r == '\x1b': | |||
inEscapeSeq = true | |||
default: | |||
length++ | |||
} | |||
} | |||
return length | |||
} | |||
// handleKey processes the given key and, optionally, returns a line of text | |||
// that the user has entered. | |||
func (t *Terminal) handleKey(key rune) (line string, ok bool) { | |||
if t.pasteActive && key != keyEnter { | |||
t.addKeyToLine(key) | |||
return | |||
} | |||
switch key { | |||
case keyBackspace: | |||
if t.pos == 0 { | |||
return | |||
} | |||
t.eraseNPreviousChars(1) | |||
case keyAltLeft: | |||
// move left by a word. | |||
t.pos -= t.countToLeftWord() | |||
t.moveCursorToPos(t.pos) | |||
case keyAltRight: | |||
// move right by a word. | |||
t.pos += t.countToRightWord() | |||
t.moveCursorToPos(t.pos) | |||
case keyLeft: | |||
if t.pos == 0 { | |||
return | |||
} | |||
t.pos-- | |||
t.moveCursorToPos(t.pos) | |||
case keyRight: | |||
if t.pos == len(t.line) { | |||
return | |||
} | |||
t.pos++ | |||
t.moveCursorToPos(t.pos) | |||
case keyHome: | |||
if t.pos == 0 { | |||
return | |||
} | |||
t.pos = 0 | |||
t.moveCursorToPos(t.pos) | |||
case keyEnd: | |||
if t.pos == len(t.line) { | |||
return | |||
} | |||
t.pos = len(t.line) | |||
t.moveCursorToPos(t.pos) | |||
case keyUp: | |||
entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1) | |||
if !ok { | |||
return "", false | |||
} | |||
if t.historyIndex == -1 { | |||
t.historyPending = string(t.line) | |||
} | |||
t.historyIndex++ | |||
runes := []rune(entry) | |||
t.setLine(runes, len(runes)) | |||
case keyDown: | |||
switch t.historyIndex { | |||
case -1: | |||
return | |||
case 0: | |||
runes := []rune(t.historyPending) | |||
t.setLine(runes, len(runes)) | |||
t.historyIndex-- | |||
default: | |||
entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1) | |||
if ok { | |||
t.historyIndex-- | |||
runes := []rune(entry) | |||
t.setLine(runes, len(runes)) | |||
} | |||
} | |||
case keyEnter: | |||
t.moveCursorToPos(len(t.line)) | |||
t.queue([]rune("\r\n")) | |||
line = string(t.line) | |||
ok = true | |||
t.line = t.line[:0] | |||
t.pos = 0 | |||
t.cursorX = 0 | |||
t.cursorY = 0 | |||
t.maxLine = 0 | |||
case keyDeleteWord: | |||
// Delete zero or more spaces and then one or more characters. | |||
t.eraseNPreviousChars(t.countToLeftWord()) | |||
case keyDeleteLine: | |||
// Delete everything from the current cursor position to the | |||
// end of line. | |||
for i := t.pos; i < len(t.line); i++ { | |||
t.queue(space) | |||
t.advanceCursor(1) | |||
} | |||
t.line = t.line[:t.pos] | |||
t.moveCursorToPos(t.pos) | |||
case keyCtrlD: | |||
// Erase the character under the current position. | |||
// The EOF case when the line is empty is handled in | |||
// readLine(). | |||
if t.pos < len(t.line) { | |||
t.pos++ | |||
t.eraseNPreviousChars(1) | |||
} | |||
case keyCtrlU: | |||
t.eraseNPreviousChars(t.pos) | |||
case keyClearScreen: | |||
// Erases the screen and moves the cursor to the home position. | |||
t.queue([]rune("\x1b[2J\x1b[H")) | |||
t.queue(t.prompt) | |||
t.cursorX, t.cursorY = 0, 0 | |||
t.advanceCursor(visualLength(t.prompt)) | |||
t.setLine(t.line, t.pos) | |||
default: | |||
if t.AutoCompleteCallback != nil { | |||
prefix := string(t.line[:t.pos]) | |||
suffix := string(t.line[t.pos:]) | |||
t.lock.Unlock() | |||
newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key) | |||
t.lock.Lock() | |||
if completeOk { | |||
t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos])) | |||
return | |||
} | |||
} | |||
if !isPrintable(key) { | |||
return | |||
} | |||
if len(t.line) == maxLineLength { | |||
return | |||
} | |||
t.addKeyToLine(key) | |||
} | |||
return | |||
} | |||
// addKeyToLine inserts the given key at the current position in the current | |||
// line. | |||
func (t *Terminal) addKeyToLine(key rune) { | |||
if len(t.line) == cap(t.line) { | |||
newLine := make([]rune, len(t.line), 2*(1+len(t.line))) | |||
copy(newLine, t.line) | |||
t.line = newLine | |||
} | |||
t.line = t.line[:len(t.line)+1] | |||
copy(t.line[t.pos+1:], t.line[t.pos:]) | |||
t.line[t.pos] = key | |||
if t.echo { | |||
t.writeLine(t.line[t.pos:]) | |||
} | |||
t.pos++ | |||
t.moveCursorToPos(t.pos) | |||
} | |||
func (t *Terminal) writeLine(line []rune) { | |||
for len(line) != 0 { | |||
remainingOnLine := t.termWidth - t.cursorX | |||
todo := len(line) | |||
if todo > remainingOnLine { | |||
todo = remainingOnLine | |||
} | |||
t.queue(line[:todo]) | |||
t.advanceCursor(visualLength(line[:todo])) | |||
line = line[todo:] | |||
} | |||
} | |||
// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n. | |||
func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) { | |||
for len(buf) > 0 { | |||
i := bytes.IndexByte(buf, '\n') | |||
todo := len(buf) | |||
if i >= 0 { | |||
todo = i | |||
} | |||
var nn int | |||
nn, err = w.Write(buf[:todo]) | |||
n += nn | |||
if err != nil { | |||
return n, err | |||
} | |||
buf = buf[todo:] | |||
if i >= 0 { | |||
if _, err = w.Write(crlf); err != nil { | |||
return n, err | |||
} | |||
n++ | |||
buf = buf[1:] | |||
} | |||
} | |||
return n, nil | |||
} | |||
func (t *Terminal) Write(buf []byte) (n int, err error) { | |||
t.lock.Lock() | |||
defer t.lock.Unlock() | |||
if t.cursorX == 0 && t.cursorY == 0 { | |||
// This is the easy case: there's nothing on the screen that we | |||
// have to move out of the way. | |||
return writeWithCRLF(t.c, buf) | |||
} | |||
// We have a prompt and possibly user input on the screen. We | |||
// have to clear it first. | |||
t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */) | |||
t.cursorX = 0 | |||
t.clearLineToRight() | |||
for t.cursorY > 0 { | |||
t.move(1 /* up */, 0, 0, 0) | |||
t.cursorY-- | |||
t.clearLineToRight() | |||
} | |||
if _, err = t.c.Write(t.outBuf); err != nil { | |||
return | |||
} | |||
t.outBuf = t.outBuf[:0] | |||
if n, err = writeWithCRLF(t.c, buf); err != nil { | |||
return | |||
} | |||
t.writeLine(t.prompt) | |||
if t.echo { | |||
t.writeLine(t.line) | |||
} | |||
t.moveCursorToPos(t.pos) | |||
if _, err = t.c.Write(t.outBuf); err != nil { | |||
return | |||
} | |||
t.outBuf = t.outBuf[:0] | |||
return | |||
} | |||
// ReadPassword temporarily changes the prompt and reads a password, without | |||
// echo, from the terminal. | |||
func (t *Terminal) ReadPassword(prompt string) (line string, err error) { | |||
t.lock.Lock() | |||
defer t.lock.Unlock() | |||
oldPrompt := t.prompt | |||
t.prompt = []rune(prompt) | |||
t.echo = false | |||
line, err = t.readLine() | |||
t.prompt = oldPrompt | |||
t.echo = true | |||
return | |||
} | |||
// ReadLine returns a line of input from the terminal. | |||
func (t *Terminal) ReadLine() (line string, err error) { | |||
t.lock.Lock() | |||
defer t.lock.Unlock() | |||
return t.readLine() | |||
} | |||
func (t *Terminal) readLine() (line string, err error) { | |||
// t.lock must be held at this point | |||
if t.cursorX == 0 && t.cursorY == 0 { | |||
t.writeLine(t.prompt) | |||
t.c.Write(t.outBuf) | |||
t.outBuf = t.outBuf[:0] | |||
} | |||
lineIsPasted := t.pasteActive | |||
for { | |||
rest := t.remainder | |||
lineOk := false | |||
for !lineOk { | |||
var key rune | |||
key, rest = bytesToKey(rest, t.pasteActive) | |||
if key == utf8.RuneError { | |||
break | |||
} | |||
if !t.pasteActive { | |||
if key == keyCtrlD { | |||
if len(t.line) == 0 { | |||
return "", io.EOF | |||
} | |||
} | |||
if key == keyPasteStart { | |||
t.pasteActive = true | |||
if len(t.line) == 0 { | |||
lineIsPasted = true | |||
} | |||
continue | |||
} | |||
} else if key == keyPasteEnd { | |||
t.pasteActive = false | |||
continue | |||
} | |||
if !t.pasteActive { | |||
lineIsPasted = false | |||
} | |||
line, lineOk = t.handleKey(key) | |||
} | |||
if len(rest) > 0 { | |||
n := copy(t.inBuf[:], rest) | |||
t.remainder = t.inBuf[:n] | |||
} else { | |||
t.remainder = nil | |||
} | |||
t.c.Write(t.outBuf) | |||
t.outBuf = t.outBuf[:0] | |||
if lineOk { | |||
if t.echo { | |||
t.historyIndex = -1 | |||
t.history.Add(line) | |||
} | |||
if lineIsPasted { | |||
err = ErrPasteIndicator | |||
} | |||
return | |||
} | |||
// t.remainder is a slice at the beginning of t.inBuf | |||
// containing a partial key sequence | |||
readBuf := t.inBuf[len(t.remainder):] | |||
var n int | |||
t.lock.Unlock() | |||
n, err = t.c.Read(readBuf) | |||
t.lock.Lock() | |||
if err != nil { | |||
return | |||
} | |||
t.remainder = t.inBuf[:n+len(t.remainder)] | |||
} | |||
} | |||
// SetPrompt sets the prompt to be used when reading subsequent lines. | |||
func (t *Terminal) SetPrompt(prompt string) { | |||
t.lock.Lock() | |||
defer t.lock.Unlock() | |||
t.prompt = []rune(prompt) | |||
} | |||
func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) { | |||
// Move cursor to column zero at the start of the line. | |||
t.move(t.cursorY, 0, t.cursorX, 0) | |||
t.cursorX, t.cursorY = 0, 0 | |||
t.clearLineToRight() | |||
for t.cursorY < numPrevLines { | |||
// Move down a line | |||
t.move(0, 1, 0, 0) | |||
t.cursorY++ | |||
t.clearLineToRight() | |||
} | |||
// Move back to beginning. | |||
t.move(t.cursorY, 0, 0, 0) | |||
t.cursorX, t.cursorY = 0, 0 | |||
t.queue(t.prompt) | |||
t.advanceCursor(visualLength(t.prompt)) | |||
t.writeLine(t.line) | |||
t.moveCursorToPos(t.pos) | |||
} | |||
func (t *Terminal) SetSize(width, height int) error { | |||
t.lock.Lock() | |||
defer t.lock.Unlock() | |||
if width == 0 { | |||
width = 1 | |||
} | |||
oldWidth := t.termWidth | |||
t.termWidth, t.termHeight = width, height | |||
switch { | |||
case width == oldWidth: | |||
// If the width didn't change then nothing else needs to be | |||
// done. | |||
return nil | |||
case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0: | |||
// If there is nothing on current line and no prompt printed, | |||
// just do nothing | |||
return nil | |||
case width < oldWidth: | |||
// Some terminals (e.g. xterm) will truncate lines that were | |||
// too long when shinking. Others, (e.g. gnome-terminal) will | |||
// attempt to wrap them. For the former, repainting t.maxLine | |||
// works great, but that behaviour goes badly wrong in the case | |||
// of the latter because they have doubled every full line. | |||
// We assume that we are working on a terminal that wraps lines | |||
// and adjust the cursor position based on every previous line | |||
// wrapping and turning into two. This causes the prompt on | |||
// xterms to move upwards, which isn't great, but it avoids a | |||
// huge mess with gnome-terminal. | |||
if t.cursorX >= t.termWidth { | |||
t.cursorX = t.termWidth - 1 | |||
} | |||
t.cursorY *= 2 | |||
t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2) | |||
case width > oldWidth: | |||
// If the terminal expands then our position calculations will | |||
// be wrong in the future because we think the cursor is | |||
// |t.pos| chars into the string, but there will be a gap at | |||
// the end of any wrapped line. | |||
// | |||
// But the position will actually be correct until we move, so | |||
// we can move back to the beginning and repaint everything. | |||
t.clearAndRepaintLinePlusNPrevious(t.maxLine) | |||
} | |||
_, err := t.c.Write(t.outBuf) | |||
t.outBuf = t.outBuf[:0] | |||
return err | |||
} | |||
type pasteIndicatorError struct{} | |||
func (pasteIndicatorError) Error() string { | |||
return "terminal: ErrPasteIndicator not correctly handled" | |||
return term.NewTerminal(c, prompt) | |||
} | |||
// ErrPasteIndicator may be returned from ReadLine as the error, in addition | |||
// to valid line data. It indicates that bracketed paste mode is enabled and | |||
// that the returned line consists only of pasted data. Programs may wish to | |||
// interpret pasted data more literally than typed data. | |||
var ErrPasteIndicator = pasteIndicatorError{} | |||
var ErrPasteIndicator = term.ErrPasteIndicator | |||
// SetBracketedPasteMode requests that the terminal bracket paste operations | |||
// with markers. Not all terminals support this but, if it is supported, then | |||
// enabling this mode will stop any autocomplete callback from running due to | |||
// pastes. Additionally, any lines that are completely pasted will be returned | |||
// from ReadLine with the error set to ErrPasteIndicator. | |||
func (t *Terminal) SetBracketedPasteMode(on bool) { | |||
if on { | |||
io.WriteString(t.c, "\x1b[?2004h") | |||
} else { | |||
io.WriteString(t.c, "\x1b[?2004l") | |||
} | |||
} | |||
// State contains the state of a terminal. | |||
type State = term.State | |||
// stRingBuffer is a ring buffer of strings. | |||
type stRingBuffer struct { | |||
// entries contains max elements. | |||
entries []string | |||
max int | |||
// head contains the index of the element most recently added to the ring. | |||
head int | |||
// size contains the number of elements in the ring. | |||
size int | |||
// IsTerminal returns whether the given file descriptor is a terminal. | |||
func IsTerminal(fd int) bool { | |||
return term.IsTerminal(fd) | |||
} | |||
func (s *stRingBuffer) Add(a string) { | |||
if s.entries == nil { | |||
const defaultNumEntries = 100 | |||
s.entries = make([]string, defaultNumEntries) | |||
s.max = defaultNumEntries | |||
} | |||
// ReadPassword reads a line of input from a terminal without local echo. This | |||
// is commonly used for inputting passwords and other sensitive data. The slice | |||
// returned does not include the \n. | |||
func ReadPassword(fd int) ([]byte, error) { | |||
return term.ReadPassword(fd) | |||
} | |||
s.head = (s.head + 1) % s.max | |||
s.entries[s.head] = a | |||
if s.size < s.max { | |||
s.size++ | |||
} | |||
// MakeRaw puts the terminal connected to the given file descriptor into raw | |||
// mode and returns the previous state of the terminal so that it can be | |||
// restored. | |||
func MakeRaw(fd int) (*State, error) { | |||
return term.MakeRaw(fd) | |||
} | |||
// NthPreviousEntry returns the value passed to the nth previous call to Add. | |||
// If n is zero then the immediately prior value is returned, if one, then the | |||
// next most recent, and so on. If such an element doesn't exist then ok is | |||
// false. | |||
func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) { | |||
if n >= s.size { | |||
return "", false | |||
} | |||
index := s.head - n | |||
if index < 0 { | |||
index += s.max | |||
} | |||
return s.entries[index], true | |||
// Restore restores the terminal connected to the given file descriptor to a | |||
// previous state. | |||
func Restore(fd int, oldState *State) error { | |||
return term.Restore(fd, oldState) | |||
} | |||
// readPasswordLine reads from reader until it finds \n or io.EOF. | |||
// The slice returned does not include the \n. | |||
// readPasswordLine also ignores any \r it finds. | |||
func readPasswordLine(reader io.Reader) ([]byte, error) { | |||
var buf [1]byte | |||
var ret []byte | |||
// GetState returns the current state of a terminal which may be useful to | |||
// restore the terminal after a signal. | |||
func GetState(fd int) (*State, error) { | |||
return term.GetState(fd) | |||
} | |||
for { | |||
n, err := reader.Read(buf[:]) | |||
if n > 0 { | |||
switch buf[0] { | |||
case '\n': | |||
return ret, nil | |||
case '\r': | |||
// remove \r from passwords on Windows | |||
default: | |||
ret = append(ret, buf[0]) | |||
} | |||
continue | |||
} | |||
if err != nil { | |||
if err == io.EOF && len(ret) > 0 { | |||
return ret, nil | |||
} | |||
return ret, err | |||
} | |||
} | |||
// GetSize returns the dimensions of the given terminal. | |||
func GetSize(fd int) (width, height int, err error) { | |||
return term.GetSize(fd) | |||
} |
@@ -1,10 +0,0 @@ | |||
// Copyright 2013 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. | |||
package terminal | |||
import "golang.org/x/sys/unix" | |||
const ioctlReadTermios = unix.TCGETS | |||
const ioctlWriteTermios = unix.TCSETS |
@@ -1,58 +0,0 @@ | |||
// Copyright 2016 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. | |||
// Package terminal provides support functions for dealing with terminals, as | |||
// commonly found on UNIX systems. | |||
// | |||
// Putting a terminal into raw mode is the most common requirement: | |||
// | |||
// oldState, err := terminal.MakeRaw(0) | |||
// if err != nil { | |||
// panic(err) | |||
// } | |||
// defer terminal.Restore(0, oldState) | |||
package terminal | |||
import ( | |||
"fmt" | |||
"runtime" | |||
) | |||
type State struct{} | |||
// IsTerminal returns whether the given file descriptor is a terminal. | |||
func IsTerminal(fd int) bool { | |||
return false | |||
} | |||
// MakeRaw put the terminal connected to the given file descriptor into raw | |||
// mode and returns the previous state of the terminal so that it can be | |||
// restored. | |||
func MakeRaw(fd int) (*State, error) { | |||
return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) | |||
} | |||
// GetState returns the current state of a terminal which may be useful to | |||
// restore the terminal after a signal. | |||
func GetState(fd int) (*State, error) { | |||
return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) | |||
} | |||
// Restore restores the terminal connected to the given file descriptor to a | |||
// previous state. | |||
func Restore(fd int, state *State) error { | |||
return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) | |||
} | |||
// GetSize returns the dimensions of the given terminal. | |||
func GetSize(fd int) (width, height int, err error) { | |||
return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) | |||
} | |||
// ReadPassword reads a line of input from a terminal without local echo. This | |||
// is commonly used for inputting passwords and other sensitive data. The slice | |||
// returned does not include the \n. | |||
func ReadPassword(fd int) ([]byte, error) { | |||
return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) | |||
} |
@@ -1,124 +0,0 @@ | |||
// Copyright 2015 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 solaris | |||
package terminal // import "golang.org/x/crypto/ssh/terminal" | |||
import ( | |||
"golang.org/x/sys/unix" | |||
"io" | |||
"syscall" | |||
) | |||
// State contains the state of a terminal. | |||
type State struct { | |||
termios unix.Termios | |||
} | |||
// IsTerminal returns whether the given file descriptor is a terminal. | |||
func IsTerminal(fd int) bool { | |||
_, err := unix.IoctlGetTermio(fd, unix.TCGETA) | |||
return err == nil | |||
} | |||
// ReadPassword reads a line of input from a terminal without local echo. This | |||
// is commonly used for inputting passwords and other sensitive data. The slice | |||
// returned does not include the \n. | |||
func ReadPassword(fd int) ([]byte, error) { | |||
// see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c | |||
val, err := unix.IoctlGetTermios(fd, unix.TCGETS) | |||
if err != nil { | |||
return nil, err | |||
} | |||
oldState := *val | |||
newState := oldState | |||
newState.Lflag &^= syscall.ECHO | |||
newState.Lflag |= syscall.ICANON | syscall.ISIG | |||
newState.Iflag |= syscall.ICRNL | |||
err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState) | |||
if err != nil { | |||
return nil, err | |||
} | |||
defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState) | |||
var buf [16]byte | |||
var ret []byte | |||
for { | |||
n, err := syscall.Read(fd, buf[:]) | |||
if err != nil { | |||
return nil, err | |||
} | |||
if n == 0 { | |||
if len(ret) == 0 { | |||
return nil, io.EOF | |||
} | |||
break | |||
} | |||
if buf[n-1] == '\n' { | |||
n-- | |||
} | |||
ret = append(ret, buf[:n]...) | |||
if n < len(buf) { | |||
break | |||
} | |||
} | |||
return ret, nil | |||
} | |||
// MakeRaw puts the terminal connected to the given file descriptor into raw | |||
// mode and returns the previous state of the terminal so that it can be | |||
// restored. | |||
// see http://cr.illumos.org/~webrev/andy_js/1060/ | |||
func MakeRaw(fd int) (*State, error) { | |||
termios, err := unix.IoctlGetTermios(fd, unix.TCGETS) | |||
if err != nil { | |||
return nil, err | |||
} | |||
oldState := State{termios: *termios} | |||
termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON | |||
termios.Oflag &^= unix.OPOST | |||
termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN | |||
termios.Cflag &^= unix.CSIZE | unix.PARENB | |||
termios.Cflag |= unix.CS8 | |||
termios.Cc[unix.VMIN] = 1 | |||
termios.Cc[unix.VTIME] = 0 | |||
if err := unix.IoctlSetTermios(fd, unix.TCSETS, termios); err != nil { | |||
return nil, err | |||
} | |||
return &oldState, nil | |||
} | |||
// Restore restores the terminal connected to the given file descriptor to a | |||
// previous state. | |||
func Restore(fd int, oldState *State) error { | |||
return unix.IoctlSetTermios(fd, unix.TCSETS, &oldState.termios) | |||
} | |||
// GetState returns the current state of a terminal which may be useful to | |||
// restore the terminal after a signal. | |||
func GetState(fd int) (*State, error) { | |||
termios, err := unix.IoctlGetTermios(fd, unix.TCGETS) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return &State{termios: *termios}, nil | |||
} | |||
// GetSize returns the dimensions of the given terminal. | |||
func GetSize(fd int) (width, height int, err error) { | |||
ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) | |||
if err != nil { | |||
return 0, 0, err | |||
} | |||
return int(ws.Col), int(ws.Row), nil | |||
} |
@@ -1,103 +0,0 @@ | |||
// Copyright 2011 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 windows | |||
// Package terminal provides support functions for dealing with terminals, as | |||
// commonly found on UNIX systems. | |||
// | |||
// Putting a terminal into raw mode is the most common requirement: | |||
// | |||
// oldState, err := terminal.MakeRaw(0) | |||
// if err != nil { | |||
// panic(err) | |||
// } | |||
// defer terminal.Restore(0, oldState) | |||
package terminal | |||
import ( | |||
"os" | |||
"golang.org/x/sys/windows" | |||
) | |||
type State struct { | |||
mode uint32 | |||
} | |||
// IsTerminal returns whether the given file descriptor is a terminal. | |||
func IsTerminal(fd int) bool { | |||
var st uint32 | |||
err := windows.GetConsoleMode(windows.Handle(fd), &st) | |||
return err == nil | |||
} | |||
// MakeRaw put the terminal connected to the given file descriptor into raw | |||
// mode and returns the previous state of the terminal so that it can be | |||
// restored. | |||
func MakeRaw(fd int) (*State, error) { | |||
var st uint32 | |||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil { | |||
return nil, err | |||
} | |||
raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT) | |||
if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil { | |||
return nil, err | |||
} | |||
return &State{st}, nil | |||
} | |||
// GetState returns the current state of a terminal which may be useful to | |||
// restore the terminal after a signal. | |||
func GetState(fd int) (*State, error) { | |||
var st uint32 | |||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil { | |||
return nil, err | |||
} | |||
return &State{st}, nil | |||
} | |||
// Restore restores the terminal connected to the given file descriptor to a | |||
// previous state. | |||
func Restore(fd int, state *State) error { | |||
return windows.SetConsoleMode(windows.Handle(fd), state.mode) | |||
} | |||
// GetSize returns the dimensions of the given terminal. | |||
func GetSize(fd int) (width, height int, err error) { | |||
var info windows.ConsoleScreenBufferInfo | |||
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil { | |||
return 0, 0, err | |||
} | |||
return int(info.Size.X), int(info.Size.Y), nil | |||
} | |||
// ReadPassword reads a line of input from a terminal without local echo. This | |||
// is commonly used for inputting passwords and other sensitive data. The slice | |||
// returned does not include the \n. | |||
func ReadPassword(fd int) ([]byte, error) { | |||
var st uint32 | |||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil { | |||
return nil, err | |||
} | |||
old := st | |||
st &^= (windows.ENABLE_ECHO_INPUT) | |||
st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT) | |||
if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil { | |||
return nil, err | |||
} | |||
defer windows.SetConsoleMode(windows.Handle(fd), old) | |||
var h windows.Handle | |||
p, _ := windows.GetCurrentProcess() | |||
if err := windows.DuplicateHandle(p, windows.Handle(fd), p, &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil { | |||
return nil, err | |||
} | |||
f := os.NewFile(uintptr(h), "stdin") | |||
defer f.Close() | |||
return readPasswordLine(f) | |||
} |
@@ -1,3 +0,0 @@ | |||
# This source code refers to The Go Authors for copyright purposes. | |||
# The master list of authors is in the main Go distribution, | |||
# visible at http://tip.golang.org/AUTHORS. |
@@ -1,3 +0,0 @@ | |||
# This source code was written by the Go contributors. | |||
# The master list of contributors is in the main Go distribution, | |||
# visible at http://tip.golang.org/CONTRIBUTORS. |
@@ -52,8 +52,7 @@ var isSpecialElementMap = map[string]bool{ | |||
"iframe": true, | |||
"img": true, | |||
"input": true, | |||
"isindex": true, // The 'isindex' element has been removed, but keep it for backwards compatibility. | |||
"keygen": true, | |||
"keygen": true, // "keygen" has been removed from the spec, but are kept here for backwards compatibility. | |||
"li": true, | |||
"link": true, | |||
"listing": true, | |||
@@ -161,66 +161,62 @@ var mathMLAttributeAdjustments = map[string]string{ | |||
} | |||
var svgAttributeAdjustments = map[string]string{ | |||
"attributename": "attributeName", | |||
"attributetype": "attributeType", | |||
"basefrequency": "baseFrequency", | |||
"baseprofile": "baseProfile", | |||
"calcmode": "calcMode", | |||
"clippathunits": "clipPathUnits", | |||
"contentscripttype": "contentScriptType", | |||
"contentstyletype": "contentStyleType", | |||
"diffuseconstant": "diffuseConstant", | |||
"edgemode": "edgeMode", | |||
"externalresourcesrequired": "externalResourcesRequired", | |||
"filterres": "filterRes", | |||
"filterunits": "filterUnits", | |||
"glyphref": "glyphRef", | |||
"gradienttransform": "gradientTransform", | |||
"gradientunits": "gradientUnits", | |||
"kernelmatrix": "kernelMatrix", | |||
"kernelunitlength": "kernelUnitLength", | |||
"keypoints": "keyPoints", | |||
"keysplines": "keySplines", | |||
"keytimes": "keyTimes", | |||
"lengthadjust": "lengthAdjust", | |||
"limitingconeangle": "limitingConeAngle", | |||
"markerheight": "markerHeight", | |||
"markerunits": "markerUnits", | |||
"markerwidth": "markerWidth", | |||
"maskcontentunits": "maskContentUnits", | |||
"maskunits": "maskUnits", | |||
"numoctaves": "numOctaves", | |||
"pathlength": "pathLength", | |||
"patterncontentunits": "patternContentUnits", | |||
"patterntransform": "patternTransform", | |||
"patternunits": "patternUnits", | |||
"pointsatx": "pointsAtX", | |||
"pointsaty": "pointsAtY", | |||
"pointsatz": "pointsAtZ", | |||
"preservealpha": "preserveAlpha", | |||
"preserveaspectratio": "preserveAspectRatio", | |||
"primitiveunits": "primitiveUnits", | |||
"refx": "refX", | |||
"refy": "refY", | |||
"repeatcount": "repeatCount", | |||
"repeatdur": "repeatDur", | |||
"requiredextensions": "requiredExtensions", | |||
"requiredfeatures": "requiredFeatures", | |||
"specularconstant": "specularConstant", | |||
"specularexponent": "specularExponent", | |||
"spreadmethod": "spreadMethod", | |||
"startoffset": "startOffset", | |||
"stddeviation": "stdDeviation", | |||
"stitchtiles": "stitchTiles", | |||
"surfacescale": "surfaceScale", | |||
"systemlanguage": "systemLanguage", | |||
"tablevalues": "tableValues", | |||
"targetx": "targetX", | |||
"targety": "targetY", | |||
"textlength": "textLength", | |||
"viewbox": "viewBox", | |||
"viewtarget": "viewTarget", | |||
"xchannelselector": "xChannelSelector", | |||
"ychannelselector": "yChannelSelector", | |||
"zoomandpan": "zoomAndPan", | |||
"attributename": "attributeName", | |||
"attributetype": "attributeType", | |||
"basefrequency": "baseFrequency", | |||
"baseprofile": "baseProfile", | |||
"calcmode": "calcMode", | |||
"clippathunits": "clipPathUnits", | |||
"diffuseconstant": "diffuseConstant", | |||
"edgemode": "edgeMode", | |||
"filterunits": "filterUnits", | |||
"glyphref": "glyphRef", | |||
"gradienttransform": "gradientTransform", | |||
"gradientunits": "gradientUnits", | |||
"kernelmatrix": "kernelMatrix", | |||
"kernelunitlength": "kernelUnitLength", | |||
"keypoints": "keyPoints", | |||
"keysplines": "keySplines", | |||
"keytimes": "keyTimes", | |||
"lengthadjust": "lengthAdjust", | |||
"limitingconeangle": "limitingConeAngle", | |||
"markerheight": "markerHeight", | |||
"markerunits": "markerUnits", | |||
"markerwidth": "markerWidth", | |||
"maskcontentunits": "maskContentUnits", | |||
"maskunits": "maskUnits", | |||
"numoctaves": "numOctaves", | |||
"pathlength": "pathLength", | |||
"patterncontentunits": "patternContentUnits", | |||
"patterntransform": "patternTransform", | |||
"patternunits": "patternUnits", | |||
"pointsatx": "pointsAtX", | |||
"pointsaty": "pointsAtY", | |||
"pointsatz": "pointsAtZ", | |||
"preservealpha": "preserveAlpha", | |||
"preserveaspectratio": "preserveAspectRatio", | |||
"primitiveunits": "primitiveUnits", | |||
"refx": "refX", | |||
"refy": "refY", | |||
"repeatcount": "repeatCount", | |||
"repeatdur": "repeatDur", | |||
"requiredextensions": "requiredExtensions", | |||
"requiredfeatures": "requiredFeatures", | |||
"specularconstant": "specularConstant", | |||
"specularexponent": "specularExponent", | |||
"spreadmethod": "spreadMethod", | |||
"startoffset": "startOffset", | |||
"stddeviation": "stdDeviation", | |||
"stitchtiles": "stitchTiles", | |||
"surfacescale": "surfaceScale", | |||
"systemlanguage": "systemLanguage", | |||
"tablevalues": "tableValues", | |||
"targetx": "targetX", | |||
"targety": "targetY", | |||
"textlength": "textLength", | |||
"viewbox": "viewBox", | |||
"viewtarget": "viewTarget", | |||
"xchannelselector": "xChannelSelector", | |||
"ychannelselector": "yChannelSelector", | |||
"zoomandpan": "zoomAndPan", | |||
} |
@@ -18,6 +18,11 @@ const ( | |||
ElementNode | |||
CommentNode | |||
DoctypeNode | |||
// RawNode nodes are not returned by the parser, but can be part of the | |||
// Node tree passed to func Render to insert raw HTML (without escaping). | |||
// If so, this package makes no guarantee that the rendered HTML is secure | |||
// (from e.g. Cross Site Scripting attacks) or well-formed. | |||
RawNode | |||
scopeMarkerNode | |||
) | |||
@@ -177,7 +182,7 @@ func (s *nodeStack) index(n *Node) int { | |||
// contains returns whether a is within s. | |||
func (s *nodeStack) contains(a atom.Atom) bool { | |||
for _, n := range *s { | |||
if n.DataAtom == a { | |||
if n.DataAtom == a && n.Namespace == "" { | |||
return true | |||
} | |||
} | |||
@@ -184,6 +184,17 @@ func (p *parser) clearStackToContext(s scope) { | |||
} | |||
} | |||
// parseGenericRawTextElement implements the generic raw text element parsing | |||
// algorithm defined in 12.2.6.2. | |||
// https://html.spec.whatwg.org/multipage/parsing.html#parsing-elements-that-contain-only-text | |||
// TODO: Since both RAWTEXT and RCDATA states are treated as tokenizer's part | |||
// officially, need to make tokenizer consider both states. | |||
func (p *parser) parseGenericRawTextElement() { | |||
p.addElement() | |||
p.originalIM = p.im | |||
p.im = textIM | |||
} | |||
// generateImpliedEndTags pops nodes off the stack of open elements as long as | |||
// the top node has a tag name of dd, dt, li, optgroup, option, p, rb, rp, rt or rtc. | |||
// If exceptions are specified, nodes with that name will not be popped off. | |||
@@ -192,16 +203,17 @@ func (p *parser) generateImpliedEndTags(exceptions ...string) { | |||
loop: | |||
for i = len(p.oe) - 1; i >= 0; i-- { | |||
n := p.oe[i] | |||
if n.Type == ElementNode { | |||
switch n.DataAtom { | |||
case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc: | |||
for _, except := range exceptions { | |||
if n.Data == except { | |||
break loop | |||
} | |||
if n.Type != ElementNode { | |||
break | |||
} | |||
switch n.DataAtom { | |||
case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc: | |||
for _, except := range exceptions { | |||
if n.Data == except { | |||
break loop | |||
} | |||
continue | |||
} | |||
continue | |||
} | |||
break | |||
} | |||
@@ -369,8 +381,7 @@ findIdenticalElements: | |||
// Section 12.2.4.3. | |||
func (p *parser) clearActiveFormattingElements() { | |||
for { | |||
n := p.afe.pop() | |||
if len(p.afe) == 0 || n.Type == scopeMarkerNode { | |||
if n := p.afe.pop(); len(p.afe) == 0 || n.Type == scopeMarkerNode { | |||
return | |||
} | |||
} | |||
@@ -439,9 +450,6 @@ func (p *parser) resetInsertionMode() { | |||
case a.Select: | |||
if !last { | |||
for ancestor, first := n, p.oe[0]; ancestor != first; { | |||
if ancestor == first { | |||
break | |||
} | |||
ancestor = p.oe[p.oe.index(ancestor)-1] | |||
switch ancestor.DataAtom { | |||
case a.Template: | |||
@@ -628,20 +636,51 @@ func inHeadIM(p *parser) bool { | |||
switch p.tok.DataAtom { | |||
case a.Html: | |||
return inBodyIM(p) | |||
case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta: | |||
case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta: | |||
p.addElement() | |||
p.oe.pop() | |||
p.acknowledgeSelfClosingTag() | |||
return true | |||
case a.Script, a.Title, a.Noscript, a.Noframes, a.Style: | |||
case a.Noscript: | |||
if p.scripting { | |||
p.parseGenericRawTextElement() | |||
return true | |||
} | |||
p.addElement() | |||
p.im = inHeadNoscriptIM | |||
// Don't let the tokenizer go into raw text mode when scripting is disabled. | |||
p.tokenizer.NextIsNotRawText() | |||
return true | |||
case a.Script, a.Title: | |||
p.addElement() | |||
p.setOriginalIM() | |||
p.im = textIM | |||
return true | |||
case a.Noframes, a.Style: | |||
p.parseGenericRawTextElement() | |||
return true | |||
case a.Head: | |||
// Ignore the token. | |||
return true | |||
case a.Template: | |||
// TODO: remove this divergence from the HTML5 spec. | |||
// | |||
// We don't handle all of the corner cases when mixing foreign | |||
// content (i.e. <math> or <svg>) with <template>. Without this | |||
// early return, we can get into an infinite loop, possibly because | |||
// of the "TODO... further divergence" a little below. | |||
// | |||
// As a workaround, if we are mixing foreign content and templates, | |||
// just ignore the rest of the HTML. Foreign content is rare and a | |||
// relatively old HTML feature. Templates are also rare and a | |||
// relatively new HTML feature. Their combination is very rare. | |||
for _, e := range p.oe { | |||
if e.Namespace != "" { | |||
p.im = ignoreTheRemainingTokens | |||
return true | |||
} | |||
} | |||
p.addElement() | |||
p.afe = append(p.afe, &scopeMarker) | |||
p.framesetOK = false | |||
@@ -662,7 +701,7 @@ func inHeadIM(p *parser) bool { | |||
if !p.oe.contains(a.Template) { | |||
return true | |||
} | |||
// TODO: remove this divergence from the HTML5 spec. | |||
// TODO: remove this further divergence from the HTML5 spec. | |||
// | |||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 | |||
p.generateImpliedEndTags() | |||
@@ -695,6 +734,55 @@ func inHeadIM(p *parser) bool { | |||
return false | |||
} | |||
// Section 12.2.6.4.5. | |||
func inHeadNoscriptIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case DoctypeToken: | |||
// Ignore the token. | |||
return true | |||
case StartTagToken: | |||
switch p.tok.DataAtom { | |||
case a.Html: | |||
return inBodyIM(p) | |||
case a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Style: | |||
return inHeadIM(p) | |||
case a.Head: | |||
// Ignore the token. | |||
return true | |||
case a.Noscript: | |||
// Don't let the tokenizer go into raw text mode even when a <noscript> | |||
// tag is in "in head noscript" insertion mode. | |||
p.tokenizer.NextIsNotRawText() | |||
// Ignore the token. | |||
return true | |||
} | |||
case EndTagToken: | |||
switch p.tok.DataAtom { | |||
case a.Noscript, a.Br: | |||
default: | |||
// Ignore the token. | |||
return true | |||
} | |||
case TextToken: | |||
s := strings.TrimLeft(p.tok.Data, whitespace) | |||
if len(s) == 0 { | |||
// It was all whitespace. | |||
return inHeadIM(p) | |||
} | |||
case CommentToken: | |||
return inHeadIM(p) | |||
} | |||
p.oe.pop() | |||
if p.top().DataAtom != a.Head { | |||
panic("html: the new current node will be a head element.") | |||
} | |||
p.im = inHeadIM | |||
if p.tok.DataAtom == a.Noscript { | |||
return true | |||
} | |||
return false | |||
} | |||
// Section 12.2.6.4.6. | |||
func afterHeadIM(p *parser) bool { | |||
switch p.tok.Type { | |||
@@ -806,7 +894,7 @@ func inBodyIM(p *parser) bool { | |||
return true | |||
} | |||
copyAttributes(p.oe[0], p.tok) | |||
case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title: | |||
case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title: | |||
return inHeadIM(p) | |||
case a.Body: | |||
if p.oe.contains(a.Template) { | |||
@@ -832,7 +920,7 @@ func inBodyIM(p *parser) bool { | |||
p.addElement() | |||
p.im = inFramesetIM | |||
return true | |||
case a.Address, a.Article, a.Aside, a.Blockquote, a.Center, a.Details, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Menu, a.Nav, a.Ol, a.P, a.Section, a.Summary, a.Ul: | |||
case a.Address, a.Article, a.Aside, a.Blockquote, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Main, a.Menu, a.Nav, a.Ol, a.P, a.Section, a.Summary, a.Ul: | |||
p.popUntil(buttonScope, a.P) | |||
p.addElement() | |||
case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6: | |||
@@ -904,7 +992,7 @@ func inBodyIM(p *parser) bool { | |||
case a.A: | |||
for i := len(p.afe) - 1; i >= 0 && p.afe[i].Type != scopeMarkerNode; i-- { | |||
if n := p.afe[i]; n.Type == ElementNode && n.DataAtom == a.A { | |||
p.inBodyEndTagFormatting(a.A) | |||
p.inBodyEndTagFormatting(a.A, "a") | |||
p.oe.remove(n) | |||
p.afe.remove(n) | |||
break | |||
@@ -918,7 +1006,7 @@ func inBodyIM(p *parser) bool { | |||
case a.Nobr: | |||
p.reconstructActiveFormattingElements() | |||
if p.elementInScope(defaultScope, a.Nobr) { | |||
p.inBodyEndTagFormatting(a.Nobr) | |||
p.inBodyEndTagFormatting(a.Nobr, "nobr") | |||
p.reconstructActiveFormattingElements() | |||
} | |||
p.addFormattingElement() | |||
@@ -965,53 +1053,6 @@ func inBodyIM(p *parser) bool { | |||
p.tok.DataAtom = a.Img | |||
p.tok.Data = a.Img.String() | |||
return false | |||
case a.Isindex: | |||
if p.form != nil { | |||
// Ignore the token. | |||
return true | |||
} | |||
action := "" | |||
prompt := "This is a searchable index. Enter search keywords: " | |||
attr := []Attribute{{Key: "name", Val: "isindex"}} | |||
for _, t := range p.tok.Attr { | |||
switch t.Key { | |||
case "action": | |||
action = t.Val | |||
case "name": | |||
// Ignore the attribute. | |||
case "prompt": | |||
prompt = t.Val | |||
default: | |||
attr = append(attr, t) | |||
} | |||
} | |||
p.acknowledgeSelfClosingTag() | |||
p.popUntil(buttonScope, a.P) | |||
p.parseImpliedToken(StartTagToken, a.Form, a.Form.String()) | |||
if p.form == nil { | |||
// NOTE: The 'isindex' element has been removed, | |||
// and the 'template' element has not been designed to be | |||
// collaborative with the index element. | |||
// | |||
// Ignore the token. | |||
return true | |||
} | |||
if action != "" { | |||
p.form.Attr = []Attribute{{Key: "action", Val: action}} | |||
} | |||
p.parseImpliedToken(StartTagToken, a.Hr, a.Hr.String()) | |||
p.parseImpliedToken(StartTagToken, a.Label, a.Label.String()) | |||
p.addText(prompt) | |||
p.addChild(&Node{ | |||
Type: ElementNode, | |||
DataAtom: a.Input, | |||
Data: a.Input.String(), | |||
Attr: attr, | |||
}) | |||
p.oe.pop() | |||
p.parseImpliedToken(EndTagToken, a.Label, a.Label.String()) | |||
p.parseImpliedToken(StartTagToken, a.Hr, a.Hr.String()) | |||
p.parseImpliedToken(EndTagToken, a.Form, a.Form.String()) | |||
case a.Textarea: | |||
p.addElement() | |||
p.setOriginalIM() | |||
@@ -1021,18 +1062,21 @@ func inBodyIM(p *parser) bool { | |||
p.popUntil(buttonScope, a.P) | |||
p.reconstructActiveFormattingElements() | |||
p.framesetOK = false | |||
p.addElement() | |||
p.setOriginalIM() | |||
p.im = textIM | |||
p.parseGenericRawTextElement() | |||
case a.Iframe: | |||
p.framesetOK = false | |||
p.parseGenericRawTextElement() | |||
case a.Noembed: | |||
p.parseGenericRawTextElement() | |||
case a.Noscript: | |||
if p.scripting { | |||
p.parseGenericRawTextElement() | |||
return true | |||
} | |||
p.reconstructActiveFormattingElements() | |||
p.addElement() | |||
p.setOriginalIM() | |||
p.im = textIM | |||
case a.Noembed, a.Noscript: | |||
p.addElement() | |||
p.setOriginalIM() | |||
p.im = textIM | |||
// Don't let the tokenizer go into raw text mode when scripting is disabled. | |||
p.tokenizer.NextIsNotRawText() | |||
case a.Select: | |||
p.reconstructActiveFormattingElements() | |||
p.addElement() | |||
@@ -1088,7 +1132,7 @@ func inBodyIM(p *parser) bool { | |||
return false | |||
} | |||
return true | |||
case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Menu, a.Nav, a.Ol, a.Pre, a.Section, a.Summary, a.Ul: | |||
case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Section, a.Summary, a.Ul: | |||
p.popUntil(defaultScope, p.tok.DataAtom) | |||
case a.Form: | |||
if p.oe.contains(a.Template) { | |||
@@ -1126,7 +1170,7 @@ func inBodyIM(p *parser) bool { | |||
case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6: | |||
p.popUntil(defaultScope, a.H1, a.H2, a.H3, a.H4, a.H5, a.H6) | |||
case a.A, a.B, a.Big, a.Code, a.Em, a.Font, a.I, a.Nobr, a.S, a.Small, a.Strike, a.Strong, a.Tt, a.U: | |||
p.inBodyEndTagFormatting(p.tok.DataAtom) | |||
p.inBodyEndTagFormatting(p.tok.DataAtom, p.tok.Data) | |||
case a.Applet, a.Marquee, a.Object: | |||
if p.popUntil(defaultScope, p.tok.DataAtom) { | |||
p.clearActiveFormattingElements() | |||
@@ -1137,7 +1181,7 @@ func inBodyIM(p *parser) bool { | |||
case a.Template: | |||
return inHeadIM(p) | |||
default: | |||
p.inBodyEndTagOther(p.tok.DataAtom) | |||
p.inBodyEndTagOther(p.tok.DataAtom, p.tok.Data) | |||
} | |||
case CommentToken: | |||
p.addChild(&Node{ | |||
@@ -1149,14 +1193,13 @@ func inBodyIM(p *parser) bool { | |||
if len(p.templateStack) > 0 { | |||
p.im = inTemplateIM | |||
return false | |||
} else { | |||
for _, e := range p.oe { | |||
switch e.DataAtom { | |||
case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc, a.Tbody, a.Td, a.Tfoot, a.Th, | |||
a.Thead, a.Tr, a.Body, a.Html: | |||
default: | |||
return true | |||
} | |||
} | |||
for _, e := range p.oe { | |||
switch e.DataAtom { | |||
case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc, a.Tbody, a.Td, a.Tfoot, a.Th, | |||
a.Thead, a.Tr, a.Body, a.Html: | |||
default: | |||
return true | |||
} | |||
} | |||
} | |||
@@ -1164,7 +1207,7 @@ func inBodyIM(p *parser) bool { | |||
return true | |||
} | |||
func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) { | |||
func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom, tagName string) { | |||
// This is the "adoption agency" algorithm, described at | |||
// https://html.spec.whatwg.org/multipage/syntax.html#adoptionAgency | |||
@@ -1172,9 +1215,15 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) { | |||
// Once the code successfully parses the comprehensive test suite, we should | |||
// refactor this code to be more idiomatic. | |||
// Steps 1-4. The outer loop. | |||
// Steps 1-2 | |||
if current := p.oe.top(); current.Data == tagName && p.afe.index(current) == -1 { | |||
p.oe.pop() | |||
return | |||
} | |||
// Steps 3-5. The outer loop. | |||
for i := 0; i < 8; i++ { | |||
// Step 5. Find the formatting element. | |||
// Step 6. Find the formatting element. | |||
var formattingElement *Node | |||
for j := len(p.afe) - 1; j >= 0; j-- { | |||
if p.afe[j].Type == scopeMarkerNode { | |||
@@ -1186,20 +1235,25 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) { | |||
} | |||
} | |||
if formattingElement == nil { | |||
p.inBodyEndTagOther(tagAtom) | |||
p.inBodyEndTagOther(tagAtom, tagName) | |||
return | |||
} | |||
// Step 7. Ignore the tag if formatting element is not in the stack of open elements. | |||
feIndex := p.oe.index(formattingElement) | |||
if feIndex == -1 { | |||
p.afe.remove(formattingElement) | |||
return | |||
} | |||
// Step 8. Ignore the tag if formatting element is not in the scope. | |||
if !p.elementInScope(defaultScope, tagAtom) { | |||
// Ignore the tag. | |||
return | |||
} | |||
// Steps 9-10. Find the furthest block. | |||
// Step 9. This step is omitted because it's just a parse error but no need to return. | |||
// Steps 10-11. Find the furthest block. | |||
var furthestBlock *Node | |||
for _, e := range p.oe[feIndex:] { | |||
if isSpecialElement(e) { | |||
@@ -1216,47 +1270,65 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) { | |||
return | |||
} | |||
// Steps 11-12. Find the common ancestor and bookmark node. | |||
// Steps 12-13. Find the common ancestor and bookmark node. | |||
commonAncestor := p.oe[feIndex-1] | |||
bookmark := p.afe.index(formattingElement) | |||
// Step 13. The inner loop. Find the lastNode to reparent. | |||
// Step 14. The inner loop. Find the lastNode to reparent. | |||
lastNode := furthestBlock | |||
node := furthestBlock | |||
x := p.oe.index(node) | |||
// Steps 13.1-13.2 | |||
for j := 0; j < 3; j++ { | |||
// Step 13.3. | |||
// Step 14.1. | |||
j := 0 | |||
for { | |||
// Step 14.2. | |||
j++ | |||
// Step. 14.3. | |||
x-- | |||
node = p.oe[x] | |||
// Step 13.4 - 13.5. | |||
// Step 14.4. Go to the next step if node is formatting element. | |||
if node == formattingElement { | |||
break | |||
} | |||
// Step 14.5. Remove node from the list of active formatting elements if | |||
// inner loop counter is greater than three and node is in the list of | |||
// active formatting elements. | |||
if ni := p.afe.index(node); j > 3 && ni > -1 { | |||
p.afe.remove(node) | |||
// If any element of the list of active formatting elements is removed, | |||
// we need to take care whether bookmark should be decremented or not. | |||
// This is because the value of bookmark may exceed the size of the | |||
// list by removing elements from the list. | |||
if ni <= bookmark { | |||
bookmark-- | |||
} | |||
continue | |||
} | |||
// Step 14.6. Continue the next inner loop if node is not in the list of | |||
// active formatting elements. | |||
if p.afe.index(node) == -1 { | |||
p.oe.remove(node) | |||
continue | |||
} | |||
// Step 13.6. | |||
if node == formattingElement { | |||
break | |||
} | |||
// Step 13.7. | |||
// Step 14.7. | |||
clone := node.clone() | |||
p.afe[p.afe.index(node)] = clone | |||
p.oe[p.oe.index(node)] = clone | |||
node = clone | |||
// Step 13.8. | |||
// Step 14.8. | |||
if lastNode == furthestBlock { | |||
bookmark = p.afe.index(node) + 1 | |||
} | |||
// Step 13.9. | |||
// Step 14.9. | |||
if lastNode.Parent != nil { | |||
lastNode.Parent.RemoveChild(lastNode) | |||
} | |||
node.AppendChild(lastNode) | |||
// Step 13.10. | |||
// Step 14.10. | |||
lastNode = node | |||
} | |||
// Step 14. Reparent lastNode to the common ancestor, | |||
// Step 15. Reparent lastNode to the common ancestor, | |||
// or for misnested table nodes, to the foster parent. | |||
if lastNode.Parent != nil { | |||
lastNode.Parent.RemoveChild(lastNode) | |||
@@ -1268,13 +1340,13 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) { | |||
commonAncestor.AppendChild(lastNode) | |||
} | |||
// Steps 15-17. Reparent nodes from the furthest block's children | |||
// Steps 16-18. Reparent nodes from the furthest block's children | |||
// to a clone of the formatting element. | |||
clone := formattingElement.clone() | |||
reparentChildren(clone, furthestBlock) | |||
furthestBlock.AppendChild(clone) | |||
// Step 18. Fix up the list of active formatting elements. | |||
// Step 19. Fix up the list of active formatting elements. | |||
if oldLoc := p.afe.index(formattingElement); oldLoc != -1 && oldLoc < bookmark { | |||
// Move the bookmark with the rest of the list. | |||
bookmark-- | |||
@@ -1282,7 +1354,7 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) { | |||
p.afe.remove(formattingElement) | |||
p.afe.insert(bookmark, clone) | |||
// Step 19. Fix up the stack of open elements. | |||
// Step 20. Fix up the stack of open elements. | |||
p.oe.remove(formattingElement) | |||
p.oe.insert(p.oe.index(furthestBlock)+1, clone) | |||
} | |||
@@ -1291,9 +1363,17 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) { | |||
// inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM. | |||
// "Any other end tag" handling from 12.2.6.5 The rules for parsing tokens in foreign content | |||
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign | |||
func (p *parser) inBodyEndTagOther(tagAtom a.Atom) { | |||
func (p *parser) inBodyEndTagOther(tagAtom a.Atom, tagName string) { | |||
for i := len(p.oe) - 1; i >= 0; i-- { | |||
if p.oe[i].DataAtom == tagAtom { | |||
// Two element nodes have the same tag if they have the same Data (a | |||
// string-typed field). As an optimization, for common HTML tags, each | |||
// Data string is assigned a unique, non-zero DataAtom (a uint32-typed | |||
// field), since integer comparison is faster than string comparison. | |||
// Uncommon (custom) tags get a zero DataAtom. | |||
// | |||
// The if condition here is equivalent to (p.oe[i].Data == tagName). | |||
if (p.oe[i].DataAtom == tagAtom) && | |||
((tagAtom != 0) || (p.oe[i].Data == tagName)) { | |||
p.oe = p.oe[:i] | |||
break | |||
} | |||
@@ -1445,14 +1525,13 @@ func inCaptionIM(p *parser) bool { | |||
case StartTagToken: | |||
switch p.tok.DataAtom { | |||
case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Td, a.Tfoot, a.Thead, a.Tr: | |||
if p.popUntil(tableScope, a.Caption) { | |||
p.clearActiveFormattingElements() | |||
p.im = inTableIM | |||
return false | |||
} else { | |||
if !p.popUntil(tableScope, a.Caption) { | |||
// Ignore the token. | |||
return true | |||
} | |||
p.clearActiveFormattingElements() | |||
p.im = inTableIM | |||
return false | |||
case a.Select: | |||
p.reconstructActiveFormattingElements() | |||
p.addElement() | |||
@@ -1469,14 +1548,13 @@ func inCaptionIM(p *parser) bool { | |||
} | |||
return true | |||
case a.Table: | |||
if p.popUntil(tableScope, a.Caption) { | |||
p.clearActiveFormattingElements() | |||
p.im = inTableIM | |||
return false | |||
} else { | |||
if !p.popUntil(tableScope, a.Caption) { | |||
// Ignore the token. | |||
return true | |||
} | |||
p.clearActiveFormattingElements() | |||
p.im = inTableIM | |||
return false | |||
case a.Body, a.Col, a.Colgroup, a.Html, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr: | |||
// Ignore the token. | |||
return true | |||
@@ -1687,8 +1765,9 @@ func inCellIM(p *parser) bool { | |||
return true | |||
} | |||
// Close the cell and reprocess. | |||
p.popUntil(tableScope, a.Td, a.Th) | |||
p.clearActiveFormattingElements() | |||
if p.popUntil(tableScope, a.Td, a.Th) { | |||
p.clearActiveFormattingElements() | |||
} | |||
p.im = inRowIM | |||
return false | |||
} | |||
@@ -1719,8 +1798,11 @@ func inSelectIM(p *parser) bool { | |||
} | |||
p.addElement() | |||
case a.Select: | |||
p.tok.Type = EndTagToken | |||
return false | |||
if !p.popUntil(selectScope, a.Select) { | |||
// Ignore the token. | |||
return true | |||
} | |||
p.resetInsertionMode() | |||
case a.Input, a.Keygen, a.Textarea: | |||
if p.elementInScope(selectScope, a.Select) { | |||
p.parseImpliedToken(EndTagToken, a.Select, a.Select.String()) | |||
@@ -1732,6 +1814,13 @@ func inSelectIM(p *parser) bool { | |||
return true | |||
case a.Script, a.Template: | |||
return inHeadIM(p) | |||
case a.Iframe, a.Noembed, a.Noframes, a.Noscript, a.Plaintext, a.Style, a.Title, a.Xmp: | |||
// Don't let the tokenizer go into raw text mode when there are raw tags | |||
// to be ignored. These tags should be ignored from the tokenizer | |||
// properly. | |||
p.tokenizer.NextIsNotRawText() | |||
// Ignore the token. | |||
return true | |||
} | |||
case EndTagToken: | |||
switch p.tok.DataAtom { | |||
@@ -1748,9 +1837,11 @@ func inSelectIM(p *parser) bool { | |||
p.oe = p.oe[:i] | |||
} | |||
case a.Select: | |||
if p.popUntil(selectScope, a.Select) { | |||
p.resetInsertionMode() | |||
if !p.popUntil(selectScope, a.Select) { | |||
// Ignore the token. | |||
return true | |||
} | |||
p.resetInsertionMode() | |||
case a.Template: | |||
return inHeadIM(p) | |||
} | |||
@@ -1775,13 +1866,22 @@ func inSelectInTableIM(p *parser) bool { | |||
case StartTagToken, EndTagToken: | |||
switch p.tok.DataAtom { | |||
case a.Caption, a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr, a.Td, a.Th: | |||
if p.tok.Type == StartTagToken || p.elementInScope(tableScope, p.tok.DataAtom) { | |||
p.parseImpliedToken(EndTagToken, a.Select, a.Select.String()) | |||
return false | |||
} else { | |||
if p.tok.Type == EndTagToken && !p.elementInScope(tableScope, p.tok.DataAtom) { | |||
// Ignore the token. | |||
return true | |||
} | |||
// This is like p.popUntil(selectScope, a.Select), but it also | |||
// matches <math select>, not just <select>. Matching the MathML | |||
// tag is arguably incorrect (conceptually), but it mimics what | |||
// Chromium does. | |||
for i := len(p.oe) - 1; i >= 0; i-- { | |||
if n := p.oe[i]; n.DataAtom == a.Select { | |||
p.oe = p.oe[:i] | |||
break | |||
} | |||
} | |||
p.resetInsertionMode() | |||
return false | |||
} | |||
} | |||
return inSelectIM(p) | |||
@@ -2045,6 +2145,10 @@ func afterAfterFramesetIM(p *parser) bool { | |||
return true | |||
} | |||
func ignoreTheRemainingTokens(p *parser) bool { | |||
return true | |||
} | |||
const whitespaceOrNUL = whitespace + "\x00" | |||
// Section 12.2.6.5 | |||
@@ -2062,28 +2166,31 @@ func parseForeignContent(p *parser) bool { | |||
Data: p.tok.Data, | |||
}) | |||
case StartTagToken: | |||
b := breakout[p.tok.Data] | |||
if p.tok.DataAtom == a.Font { | |||
loop: | |||
for _, attr := range p.tok.Attr { | |||
switch attr.Key { | |||
case "color", "face", "size": | |||
b = true | |||
break loop | |||
if !p.fragment { | |||
b := breakout[p.tok.Data] | |||
if p.tok.DataAtom == a.Font { | |||
loop: | |||
for _, attr := range p.tok.Attr { | |||
switch attr.Key { | |||
case "color", "face", "size": | |||
b = true | |||
break loop | |||
} | |||
} | |||
} | |||
} | |||
if b { | |||
for i := len(p.oe) - 1; i >= 0; i-- { | |||
n := p.oe[i] | |||
if n.Namespace == "" || htmlIntegrationPoint(n) || mathMLTextIntegrationPoint(n) { | |||
p.oe = p.oe[:i+1] | |||
break | |||
if b { | |||
for i := len(p.oe) - 1; i >= 0; i-- { | |||
n := p.oe[i] | |||
if n.Namespace == "" || htmlIntegrationPoint(n) || mathMLTextIntegrationPoint(n) { | |||
p.oe = p.oe[:i+1] | |||
break | |||
} | |||
} | |||
return false | |||
} | |||
return false | |||
} | |||
switch p.top().Namespace { | |||
current := p.adjustedCurrentNode() | |||
switch current.Namespace { | |||
case "math": | |||
adjustAttributeNames(p.tok.Attr, mathMLAttributeAdjustments) | |||
case "svg": | |||
@@ -2098,7 +2205,7 @@ func parseForeignContent(p *parser) bool { | |||
panic("html: bad parser state: unexpected namespace") | |||
} | |||
adjustForeignAttributes(p.tok.Attr) | |||
namespace := p.top().Namespace | |||
namespace := current.Namespace | |||
p.addElement() | |||
p.top().Namespace = namespace | |||
if namespace != "" { | |||
@@ -2127,12 +2234,20 @@ func parseForeignContent(p *parser) bool { | |||
return true | |||
} | |||
// Section 12.2.4.2. | |||
func (p *parser) adjustedCurrentNode() *Node { | |||
if len(p.oe) == 1 && p.fragment && p.context != nil { | |||
return p.context | |||
} | |||
return p.oe.top() | |||
} | |||
// Section 12.2.6. | |||
func (p *parser) inForeignContent() bool { | |||
if len(p.oe) == 0 { | |||
return false | |||
} | |||
n := p.oe[len(p.oe)-1] | |||
n := p.adjustedCurrentNode() | |||
if n.Namespace == "" { | |||
return false | |||
} | |||
@@ -2226,6 +2341,33 @@ func (p *parser) parse() error { | |||
// | |||
// The input is assumed to be UTF-8 encoded. | |||
func Parse(r io.Reader) (*Node, error) { | |||
return ParseWithOptions(r) | |||
} | |||
// ParseFragment parses a fragment of HTML and returns the nodes that were | |||
// found. If the fragment is the InnerHTML for an existing element, pass that | |||
// element in context. | |||
// | |||
// It has the same intricacies as Parse. | |||
func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { | |||
return ParseFragmentWithOptions(r, context) | |||
} | |||
// ParseOption configures a parser. | |||
type ParseOption func(p *parser) | |||
// ParseOptionEnableScripting configures the scripting flag. | |||
// https://html.spec.whatwg.org/multipage/webappapis.html#enabling-and-disabling-scripting | |||
// | |||
// By default, scripting is enabled. | |||
func ParseOptionEnableScripting(enable bool) ParseOption { | |||
return func(p *parser) { | |||
p.scripting = enable | |||
} | |||
} | |||
// ParseWithOptions is like Parse, with options. | |||
func ParseWithOptions(r io.Reader, opts ...ParseOption) (*Node, error) { | |||
p := &parser{ | |||
tokenizer: NewTokenizer(r), | |||
doc: &Node{ | |||
@@ -2235,19 +2377,19 @@ func Parse(r io.Reader) (*Node, error) { | |||
framesetOK: true, | |||
im: initialIM, | |||
} | |||
err := p.parse() | |||
if err != nil { | |||
for _, f := range opts { | |||
f(p) | |||
} | |||
if err := p.parse(); err != nil { | |||
return nil, err | |||
} | |||
return p.doc, nil | |||
} | |||
// ParseFragment parses a fragment of HTML and returns the nodes that were | |||
// found. If the fragment is the InnerHTML for an existing element, pass that | |||
// element in context. | |||
// | |||
// It has the same intricacies as Parse. | |||
func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { | |||
// ParseFragmentWithOptions is like ParseFragment, with options. | |||
func ParseFragmentWithOptions(r io.Reader, context *Node, opts ...ParseOption) ([]*Node, error) { | |||
contextTag := "" | |||
if context != nil { | |||
if context.Type != ElementNode { | |||
@@ -2262,7 +2404,6 @@ func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { | |||
contextTag = context.DataAtom.String() | |||
} | |||
p := &parser{ | |||
tokenizer: NewTokenizerFragment(r, contextTag), | |||
doc: &Node{ | |||
Type: DocumentNode, | |||
}, | |||
@@ -2270,6 +2411,15 @@ func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { | |||
fragment: true, | |||
context: context, | |||
} | |||
if context != nil && context.Namespace != "" { | |||
p.tokenizer = NewTokenizer(r) | |||
} else { | |||
p.tokenizer = NewTokenizerFragment(r, contextTag) | |||
} | |||
for _, f := range opts { | |||
f(p) | |||
} | |||
root := &Node{ | |||
Type: ElementNode, | |||
@@ -2290,8 +2440,7 @@ func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { | |||
} | |||
} | |||
err := p.parse() | |||
if err != nil { | |||
if err := p.parse(); err != nil { | |||
return nil, err | |||
} | |||
@@ -85,7 +85,7 @@ func render1(w writer, n *Node) error { | |||
if _, err := w.WriteString("<!--"); err != nil { | |||
return err | |||
} | |||
if _, err := w.WriteString(n.Data); err != nil { | |||
if err := escape(w, n.Data); err != nil { | |||
return err | |||
} | |||
if _, err := w.WriteString("-->"); err != nil { | |||
@@ -96,7 +96,7 @@ func render1(w writer, n *Node) error { | |||
if _, err := w.WriteString("<!DOCTYPE "); err != nil { | |||
return err | |||
} | |||
if _, err := w.WriteString(n.Data); err != nil { | |||
if err := escape(w, n.Data); err != nil { | |||
return err | |||
} | |||
if n.Attr != nil { | |||
@@ -134,6 +134,9 @@ func render1(w writer, n *Node) error { | |||
} | |||
} | |||
return w.WriteByte('>') | |||
case RawNode: | |||
_, err := w.WriteString(n.Data) | |||
return err | |||
default: | |||
return errors.New("html: unknown node type") | |||
} | |||
@@ -252,20 +255,19 @@ func writeQuoted(w writer, s string) error { | |||
// Section 12.1.2, "Elements", gives this list of void elements. Void elements | |||
// are those that can't have any contents. | |||
var voidElements = map[string]bool{ | |||
"area": true, | |||
"base": true, | |||
"br": true, | |||
"col": true, | |||
"command": true, | |||
"embed": true, | |||
"hr": true, | |||
"img": true, | |||
"input": true, | |||
"keygen": true, | |||
"link": true, | |||
"meta": true, | |||
"param": true, | |||
"source": true, | |||
"track": true, | |||
"wbr": true, | |||
"area": true, | |||
"base": true, | |||
"br": true, | |||
"col": true, | |||
"embed": true, | |||
"hr": true, | |||
"img": true, | |||
"input": true, | |||
"keygen": true, // "keygen" has been removed from the spec, but are kept here for backwards compatibility. | |||
"link": true, | |||
"meta": true, | |||
"param": true, | |||
"source": true, | |||
"track": true, | |||
"wbr": true, | |||
} |
@@ -110,9 +110,9 @@ func (t Token) String() string { | |||
case SelfClosingTagToken: | |||
return "<" + t.tagString() + "/>" | |||
case CommentToken: | |||
return "<!--" + t.Data + "-->" | |||
return "<!--" + EscapeString(t.Data) + "-->" | |||
case DoctypeToken: | |||
return "<!DOCTYPE " + t.Data + ">" | |||
return "<!DOCTYPE " + EscapeString(t.Data) + ">" | |||
} | |||
return "Invalid(" + strconv.Itoa(int(t.Type)) + ")" | |||
} | |||
@@ -296,8 +296,7 @@ func (z *Tokenizer) Buffered() []byte { | |||
// too many times in succession. | |||
func readAtLeastOneByte(r io.Reader, b []byte) (int, error) { | |||
for i := 0; i < 100; i++ { | |||
n, err := r.Read(b) | |||
if n != 0 || err != nil { | |||
if n, err := r.Read(b); n != 0 || err != nil { | |||
return n, err | |||
} | |||
} | |||
@@ -347,6 +346,7 @@ loop: | |||
break loop | |||
} | |||
if c != '/' { | |||
z.raw.end-- | |||
continue loop | |||
} | |||
if z.readRawEndTag() || z.err != nil { | |||
@@ -598,6 +598,11 @@ scriptDataDoubleEscapeEnd: | |||
// readComment reads the next comment token starting with "<!--". The opening | |||
// "<!--" has already been consumed. | |||
func (z *Tokenizer) readComment() { | |||
// When modifying this function, consider manually increasing the suffixLen | |||
// constant in func TestComments, from 6 to e.g. 9 or more. That increase | |||
// should only be temporary, not committed, as it exponentially affects the | |||
// test running time. | |||
z.data.start = z.raw.end | |||
defer func() { | |||
if z.data.end < z.data.start { | |||
@@ -605,14 +610,13 @@ func (z *Tokenizer) readComment() { | |||
z.data.end = z.data.start | |||
} | |||
}() | |||
for dashCount := 2; ; { | |||
var dashCount int | |||
beginning := true | |||
for { | |||
c := z.readByte() | |||
if z.err != nil { | |||
// Ignore up to two dashes at EOF. | |||
if dashCount > 2 { | |||
dashCount = 2 | |||
} | |||
z.data.end = z.raw.end - dashCount | |||
z.data.end = z.calculateAbruptCommentDataEnd() | |||
return | |||
} | |||
switch c { | |||
@@ -620,7 +624,7 @@ func (z *Tokenizer) readComment() { | |||
dashCount++ | |||
continue | |||
case '>': | |||
if dashCount >= 2 { | |||
if dashCount >= 2 || beginning { | |||
z.data.end = z.raw.end - len("-->") | |||
return | |||
} | |||
@@ -628,17 +632,50 @@ func (z *Tokenizer) readComment() { | |||
if dashCount >= 2 { | |||
c = z.readByte() | |||
if z.err != nil { | |||
z.data.end = z.raw.end | |||
z.data.end = z.calculateAbruptCommentDataEnd() | |||
return | |||
} | |||
if c == '>' { | |||
} else if c == '>' { | |||
z.data.end = z.raw.end - len("--!>") | |||
return | |||
} else if c == '-' { | |||
dashCount = 1 | |||
beginning = false | |||
continue | |||
} | |||
} | |||
} | |||
dashCount = 0 | |||
beginning = false | |||
} | |||
} | |||
func (z *Tokenizer) calculateAbruptCommentDataEnd() int { | |||
raw := z.Raw() | |||
const prefixLen = len("<!--") | |||
if len(raw) >= prefixLen { | |||
raw = raw[prefixLen:] | |||
if hasSuffix(raw, "--!") { | |||
return z.raw.end - 3 | |||
} else if hasSuffix(raw, "--") { | |||
return z.raw.end - 2 | |||
} else if hasSuffix(raw, "-") { | |||
return z.raw.end - 1 | |||
} | |||
} | |||
return z.raw.end | |||
} | |||
func hasSuffix(b []byte, suffix string) bool { | |||
if len(b) < len(suffix) { | |||
return false | |||
} | |||
b = b[len(b)-len(suffix):] | |||
for i := range b { | |||
if b[i] != suffix[i] { | |||
return false | |||
} | |||
} | |||
return true | |||
} | |||
// readUntilCloseAngle reads until the next ">". | |||
@@ -1067,6 +1104,11 @@ loop: | |||
// Raw returns the unmodified text of the current token. Calling Next, Token, | |||
// Text, TagName or TagAttr may change the contents of the returned slice. | |||
// | |||
// The token stream's raw bytes partition the byte stream (up until an | |||
// ErrorToken). There are no overlaps or gaps between two consecutive token's | |||
// raw bytes. One implication is that the byte offset of the current token is | |||
// the sum of the lengths of all previous tokens' raw bytes. | |||
func (z *Tokenizer) Raw() []byte { | |||
return z.buf[z.raw.start:z.raw.end] | |||
} | |||
@@ -1,3 +0,0 @@ | |||
# This source code refers to The Go Authors for copyright purposes. | |||
# The master list of authors is in the main Go distribution, | |||
# visible at http://tip.golang.org/AUTHORS. |
@@ -1,3 +0,0 @@ | |||
# This source code was written by the Go contributors. | |||
# The master list of contributors is in the main Go distribution, | |||
# visible at http://tip.golang.org/CONTRIBUTORS. |
@@ -0,0 +1,30 @@ | |||
// Copyright 2020 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. | |||
// Package unsafeheader contains header declarations for the Go runtime's | |||
// slice and string implementations. | |||
// | |||
// This package allows x/sys to use types equivalent to | |||
// reflect.SliceHeader and reflect.StringHeader without introducing | |||
// a dependency on the (relatively heavy) "reflect" package. | |||
package unsafeheader | |||
import ( | |||
"unsafe" | |||
) | |||
// Slice is the runtime representation of a slice. | |||
// It cannot be used safely or portably and its representation may change in a later release. | |||
type Slice struct { | |||
Data unsafe.Pointer | |||
Len int | |||
Cap int | |||
} | |||
// String is the runtime representation of a string. | |||
// It cannot be used safely or portably and its representation may change in a later release. | |||
type String struct { | |||
Data unsafe.Pointer | |||
Len int | |||
} |
@@ -0,0 +1,8 @@ | |||
// Copyright 2014 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. | |||
#include "textflag.h" | |||
TEXT ·use(SB),NOSPLIT,$0 | |||
RET |
@@ -2,28 +2,29 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build !gccgo | |||
#include "textflag.h" | |||
// | |||
// System call support for 386, OpenBSD | |||
// System call support for 386, Plan 9 | |||
// | |||
// Just jump to package syscall's implementation for all these functions. | |||
// The runtime may know about them. | |||
TEXT ·Syscall(SB),NOSPLIT,$0-28 | |||
TEXT ·Syscall(SB),NOSPLIT,$0-32 | |||
JMP syscall·Syscall(SB) | |||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 | |||
TEXT ·Syscall6(SB),NOSPLIT,$0-44 | |||
JMP syscall·Syscall6(SB) | |||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 | |||
JMP syscall·Syscall9(SB) | |||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||
JMP syscall·RawSyscall(SB) | |||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 | |||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 | |||
JMP syscall·RawSyscall6(SB) | |||
TEXT ·seek(SB),NOSPLIT,$0-36 | |||
JMP syscall·seek(SB) | |||
TEXT ·exit(SB),NOSPLIT,$4-4 | |||
JMP syscall·exit(SB) |
@@ -2,28 +2,29 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build !gccgo | |||
#include "textflag.h" | |||
// | |||
// System call support for AMD64, FreeBSD | |||
// System call support for amd64, Plan 9 | |||
// | |||
// Just jump to package syscall's implementation for all these functions. | |||
// The runtime may know about them. | |||
TEXT ·Syscall(SB),NOSPLIT,$0-56 | |||
TEXT ·Syscall(SB),NOSPLIT,$0-64 | |||
JMP syscall·Syscall(SB) | |||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 | |||
TEXT ·Syscall6(SB),NOSPLIT,$0-88 | |||
JMP syscall·Syscall6(SB) | |||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 | |||
JMP syscall·Syscall9(SB) | |||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 | |||
JMP syscall·RawSyscall(SB) | |||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 | |||
JMP syscall·RawSyscall6(SB) | |||
TEXT ·seek(SB),NOSPLIT,$0-56 | |||
JMP syscall·seek(SB) | |||
TEXT ·exit(SB),NOSPLIT,$8-8 | |||
JMP syscall·exit(SB) |
@@ -2,28 +2,24 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build !gccgo | |||
#include "textflag.h" | |||
// | |||
// System call support for 386, NetBSD | |||
// | |||
// System call support for plan9 on arm | |||
// Just jump to package syscall's implementation for all these functions. | |||
// The runtime may know about them. | |||
TEXT ·Syscall(SB),NOSPLIT,$0-28 | |||
TEXT ·Syscall(SB),NOSPLIT,$0-32 | |||
JMP syscall·Syscall(SB) | |||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 | |||
TEXT ·Syscall6(SB),NOSPLIT,$0-44 | |||
JMP syscall·Syscall6(SB) | |||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 | |||
JMP syscall·Syscall9(SB) | |||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||
JMP syscall·RawSyscall(SB) | |||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 | |||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 | |||
JMP syscall·RawSyscall6(SB) | |||
TEXT ·seek(SB),NOSPLIT,$0-36 | |||
JMP syscall·exit(SB) |
@@ -0,0 +1,70 @@ | |||
package plan9 | |||
// Plan 9 Constants | |||
// Open modes | |||
const ( | |||
O_RDONLY = 0 | |||
O_WRONLY = 1 | |||
O_RDWR = 2 | |||
O_TRUNC = 16 | |||
O_CLOEXEC = 32 | |||
O_EXCL = 0x1000 | |||
) | |||
// Rfork flags | |||
const ( | |||
RFNAMEG = 1 << 0 | |||
RFENVG = 1 << 1 | |||
RFFDG = 1 << 2 | |||
RFNOTEG = 1 << 3 | |||
RFPROC = 1 << 4 | |||
RFMEM = 1 << 5 | |||
RFNOWAIT = 1 << 6 | |||
RFCNAMEG = 1 << 10 | |||
RFCENVG = 1 << 11 | |||
RFCFDG = 1 << 12 | |||
RFREND = 1 << 13 | |||
RFNOMNT = 1 << 14 | |||
) | |||
// Qid.Type bits | |||
const ( | |||
QTDIR = 0x80 | |||
QTAPPEND = 0x40 | |||
QTEXCL = 0x20 | |||
QTMOUNT = 0x10 | |||
QTAUTH = 0x08 | |||
QTTMP = 0x04 | |||
QTFILE = 0x00 | |||
) | |||
// Dir.Mode bits | |||
const ( | |||
DMDIR = 0x80000000 | |||
DMAPPEND = 0x40000000 | |||
DMEXCL = 0x20000000 | |||
DMMOUNT = 0x10000000 | |||
DMAUTH = 0x08000000 | |||
DMTMP = 0x04000000 | |||
DMREAD = 0x4 | |||
DMWRITE = 0x2 | |||
DMEXEC = 0x1 | |||
) | |||
const ( | |||
STATMAX = 65535 | |||
ERRMAX = 128 | |||
STATFIXLEN = 49 | |||
) | |||
// Mount and bind flags | |||
const ( | |||
MREPL = 0x0000 | |||
MBEFORE = 0x0001 | |||
MAFTER = 0x0002 | |||
MORDER = 0x0003 | |||
MCREATE = 0x0004 | |||
MCACHE = 0x0010 | |||
MMASK = 0x0017 | |||
) |
@@ -0,0 +1,212 @@ | |||
// Copyright 2012 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. | |||
// Plan 9 directory marshalling. See intro(5). | |||
package plan9 | |||
import "errors" | |||
var ( | |||
ErrShortStat = errors.New("stat buffer too short") | |||
ErrBadStat = errors.New("malformed stat buffer") | |||
ErrBadName = errors.New("bad character in file name") | |||
) | |||
// A Qid represents a 9P server's unique identification for a file. | |||
type Qid struct { | |||
Path uint64 // the file server's unique identification for the file | |||
Vers uint32 // version number for given Path | |||
Type uint8 // the type of the file (plan9.QTDIR for example) | |||
} | |||
// A Dir contains the metadata for a file. | |||
type Dir struct { | |||
// system-modified data | |||
Type uint16 // server type | |||
Dev uint32 // server subtype | |||
// file data | |||
Qid Qid // unique id from server | |||
Mode uint32 // permissions | |||
Atime uint32 // last read time | |||
Mtime uint32 // last write time | |||
Length int64 // file length | |||
Name string // last element of path | |||
Uid string // owner name | |||
Gid string // group name | |||
Muid string // last modifier name | |||
} | |||
var nullDir = Dir{ | |||
Type: ^uint16(0), | |||
Dev: ^uint32(0), | |||
Qid: Qid{ | |||
Path: ^uint64(0), | |||
Vers: ^uint32(0), | |||
Type: ^uint8(0), | |||
}, | |||
Mode: ^uint32(0), | |||
Atime: ^uint32(0), | |||
Mtime: ^uint32(0), | |||
Length: ^int64(0), | |||
} | |||
// Null assigns special "don't touch" values to members of d to | |||
// avoid modifying them during plan9.Wstat. | |||
func (d *Dir) Null() { *d = nullDir } | |||
// Marshal encodes a 9P stat message corresponding to d into b | |||
// | |||
// If there isn't enough space in b for a stat message, ErrShortStat is returned. | |||
func (d *Dir) Marshal(b []byte) (n int, err error) { | |||
n = STATFIXLEN + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid) | |||
if n > len(b) { | |||
return n, ErrShortStat | |||
} | |||
for _, c := range d.Name { | |||
if c == '/' { | |||
return n, ErrBadName | |||
} | |||
} | |||
b = pbit16(b, uint16(n)-2) | |||
b = pbit16(b, d.Type) | |||
b = pbit32(b, d.Dev) | |||
b = pbit8(b, d.Qid.Type) | |||
b = pbit32(b, d.Qid.Vers) | |||
b = pbit64(b, d.Qid.Path) | |||
b = pbit32(b, d.Mode) | |||
b = pbit32(b, d.Atime) | |||
b = pbit32(b, d.Mtime) | |||
b = pbit64(b, uint64(d.Length)) | |||
b = pstring(b, d.Name) | |||
b = pstring(b, d.Uid) | |||
b = pstring(b, d.Gid) | |||
b = pstring(b, d.Muid) | |||
return n, nil | |||
} | |||
// UnmarshalDir decodes a single 9P stat message from b and returns the resulting Dir. | |||
// | |||
// If b is too small to hold a valid stat message, ErrShortStat is returned. | |||
// | |||
// If the stat message itself is invalid, ErrBadStat is returned. | |||
func UnmarshalDir(b []byte) (*Dir, error) { | |||
if len(b) < STATFIXLEN { | |||
return nil, ErrShortStat | |||
} | |||
size, buf := gbit16(b) | |||
if len(b) != int(size)+2 { | |||
return nil, ErrBadStat | |||
} | |||
b = buf | |||
var d Dir | |||
d.Type, b = gbit16(b) | |||
d.Dev, b = gbit32(b) | |||
d.Qid.Type, b = gbit8(b) | |||
d.Qid.Vers, b = gbit32(b) | |||
d.Qid.Path, b = gbit64(b) | |||
d.Mode, b = gbit32(b) | |||
d.Atime, b = gbit32(b) | |||
d.Mtime, b = gbit32(b) | |||
n, b := gbit64(b) | |||
d.Length = int64(n) | |||
var ok bool | |||
if d.Name, b, ok = gstring(b); !ok { | |||
return nil, ErrBadStat | |||
} | |||
if d.Uid, b, ok = gstring(b); !ok { | |||
return nil, ErrBadStat | |||
} | |||
if d.Gid, b, ok = gstring(b); !ok { | |||
return nil, ErrBadStat | |||
} | |||
if d.Muid, b, ok = gstring(b); !ok { | |||
return nil, ErrBadStat | |||
} | |||
return &d, nil | |||
} | |||
// pbit8 copies the 8-bit number v to b and returns the remaining slice of b. | |||
func pbit8(b []byte, v uint8) []byte { | |||
b[0] = byte(v) | |||
return b[1:] | |||
} | |||
// pbit16 copies the 16-bit number v to b in little-endian order and returns the remaining slice of b. | |||
func pbit16(b []byte, v uint16) []byte { | |||
b[0] = byte(v) | |||
b[1] = byte(v >> 8) | |||
return b[2:] | |||
} | |||
// pbit32 copies the 32-bit number v to b in little-endian order and returns the remaining slice of b. | |||
func pbit32(b []byte, v uint32) []byte { | |||
b[0] = byte(v) | |||
b[1] = byte(v >> 8) | |||
b[2] = byte(v >> 16) | |||
b[3] = byte(v >> 24) | |||
return b[4:] | |||
} | |||
// pbit64 copies the 64-bit number v to b in little-endian order and returns the remaining slice of b. | |||
func pbit64(b []byte, v uint64) []byte { | |||
b[0] = byte(v) | |||
b[1] = byte(v >> 8) | |||
b[2] = byte(v >> 16) | |||
b[3] = byte(v >> 24) | |||
b[4] = byte(v >> 32) | |||
b[5] = byte(v >> 40) | |||
b[6] = byte(v >> 48) | |||
b[7] = byte(v >> 56) | |||
return b[8:] | |||
} | |||
// pstring copies the string s to b, prepending it with a 16-bit length in little-endian order, and | |||
// returning the remaining slice of b.. | |||
func pstring(b []byte, s string) []byte { | |||
b = pbit16(b, uint16(len(s))) | |||
n := copy(b, s) | |||
return b[n:] | |||
} | |||
// gbit8 reads an 8-bit number from b and returns it with the remaining slice of b. | |||
func gbit8(b []byte) (uint8, []byte) { | |||
return uint8(b[0]), b[1:] | |||
} | |||
// gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b. | |||
func gbit16(b []byte) (uint16, []byte) { | |||
return uint16(b[0]) | uint16(b[1])<<8, b[2:] | |||
} | |||
// gbit32 reads a 32-bit number in little-endian order from b and returns it with the remaining slice of b. | |||
func gbit32(b []byte) (uint32, []byte) { | |||
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:] | |||
} | |||
// gbit64 reads a 64-bit number in little-endian order from b and returns it with the remaining slice of b. | |||
func gbit64(b []byte) (uint64, []byte) { | |||
lo := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 | |||
hi := uint32(b[4]) | uint32(b[5])<<8 | uint32(b[6])<<16 | uint32(b[7])<<24 | |||
return uint64(lo) | uint64(hi)<<32, b[8:] | |||
} | |||
// gstring reads a string from b, prefixed with a 16-bit length in little-endian order. | |||
// It returns the string with the remaining slice of b and a boolean. If the length is | |||
// greater than the number of bytes in b, the boolean will be false. | |||
func gstring(b []byte) (string, []byte, bool) { | |||
n, b := gbit16(b) | |||
if int(n) > len(b) { | |||
return "", b, false | |||
} | |||
return string(b[:n]), b[n:], true | |||
} |
@@ -0,0 +1,31 @@ | |||
// Copyright 2011 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. | |||
// Plan 9 environment variables. | |||
package plan9 | |||
import ( | |||
"syscall" | |||
) | |||
func Getenv(key string) (value string, found bool) { | |||
return syscall.Getenv(key) | |||
} | |||
func Setenv(key, value string) error { | |||
return syscall.Setenv(key, value) | |||
} | |||
func Clearenv() { | |||
syscall.Clearenv() | |||
} | |||
func Environ() []string { | |||
return syscall.Environ() | |||
} | |||
func Unsetenv(key string) error { | |||
return syscall.Unsetenv(key) | |||
} |
@@ -0,0 +1,50 @@ | |||
// Copyright 2011 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. | |||
package plan9 | |||
import "syscall" | |||
// Constants | |||
const ( | |||
// Invented values to support what package os expects. | |||
O_CREAT = 0x02000 | |||
O_APPEND = 0x00400 | |||
O_NOCTTY = 0x00000 | |||
O_NONBLOCK = 0x00000 | |||
O_SYNC = 0x00000 | |||
O_ASYNC = 0x00000 | |||
S_IFMT = 0x1f000 | |||
S_IFIFO = 0x1000 | |||
S_IFCHR = 0x2000 | |||
S_IFDIR = 0x4000 | |||
S_IFBLK = 0x6000 | |||
S_IFREG = 0x8000 | |||
S_IFLNK = 0xa000 | |||
S_IFSOCK = 0xc000 | |||
) | |||
// Errors | |||
var ( | |||
EINVAL = syscall.NewError("bad arg in system call") | |||
ENOTDIR = syscall.NewError("not a directory") | |||
EISDIR = syscall.NewError("file is a directory") | |||
ENOENT = syscall.NewError("file does not exist") | |||
EEXIST = syscall.NewError("file already exists") | |||
EMFILE = syscall.NewError("no free file descriptors") | |||
EIO = syscall.NewError("i/o error") | |||
ENAMETOOLONG = syscall.NewError("file name too long") | |||
EINTR = syscall.NewError("interrupted") | |||
EPERM = syscall.NewError("permission denied") | |||
EBUSY = syscall.NewError("no free devices") | |||
ETIMEDOUT = syscall.NewError("connection timed out") | |||
EPLAN9 = syscall.NewError("not supported by plan 9") | |||
// The following errors do not correspond to any | |||
// Plan 9 system messages. Invented to support | |||
// what package os and others expect. | |||
EACCES = syscall.NewError("access permission denied") | |||
EAFNOSUPPORT = syscall.NewError("address family not supported by protocol") | |||
) |
@@ -0,0 +1,150 @@ | |||
#!/usr/bin/env bash | |||
# 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. | |||
# The plan9 package provides access to the raw system call | |||
# interface of the underlying operating system. Porting Go to | |||
# a new architecture/operating system combination requires | |||
# some manual effort, though there are tools that automate | |||
# much of the process. The auto-generated files have names | |||
# beginning with z. | |||
# | |||
# This script runs or (given -n) prints suggested commands to generate z files | |||
# for the current system. Running those commands is not automatic. | |||
# This script is documentation more than anything else. | |||
# | |||
# * asm_${GOOS}_${GOARCH}.s | |||
# | |||
# This hand-written assembly file implements system call dispatch. | |||
# There are three entry points: | |||
# | |||
# func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr); | |||
# func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr); | |||
# func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr); | |||
# | |||
# The first and second are the standard ones; they differ only in | |||
# how many arguments can be passed to the kernel. | |||
# The third is for low-level use by the ForkExec wrapper; | |||
# unlike the first two, it does not call into the scheduler to | |||
# let it know that a system call is running. | |||
# | |||
# * syscall_${GOOS}.go | |||
# | |||
# This hand-written Go file implements system calls that need | |||
# special handling and lists "//sys" comments giving prototypes | |||
# for ones that can be auto-generated. Mksyscall reads those | |||
# comments to generate the stubs. | |||
# | |||
# * syscall_${GOOS}_${GOARCH}.go | |||
# | |||
# Same as syscall_${GOOS}.go except that it contains code specific | |||
# to ${GOOS} on one particular architecture. | |||
# | |||
# * types_${GOOS}.c | |||
# | |||
# This hand-written C file includes standard C headers and then | |||
# creates typedef or enum names beginning with a dollar sign | |||
# (use of $ in variable names is a gcc extension). The hardest | |||
# part about preparing this file is figuring out which headers to | |||
# include and which symbols need to be #defined to get the | |||
# actual data structures that pass through to the kernel system calls. | |||
# Some C libraries present alternate versions for binary compatibility | |||
# and translate them on the way in and out of system calls, but | |||
# there is almost always a #define that can get the real ones. | |||
# See types_darwin.c and types_linux.c for examples. | |||
# | |||
# * zerror_${GOOS}_${GOARCH}.go | |||
# | |||
# This machine-generated file defines the system's error numbers, | |||
# error strings, and signal numbers. The generator is "mkerrors.sh". | |||
# Usually no arguments are needed, but mkerrors.sh will pass its | |||
# arguments on to godefs. | |||
# | |||
# * zsyscall_${GOOS}_${GOARCH}.go | |||
# | |||
# Generated by mksyscall.pl; see syscall_${GOOS}.go above. | |||
# | |||
# * zsysnum_${GOOS}_${GOARCH}.go | |||
# | |||
# Generated by mksysnum_${GOOS}. | |||
# | |||
# * ztypes_${GOOS}_${GOARCH}.go | |||
# | |||
# Generated by godefs; see types_${GOOS}.c above. | |||
GOOSARCH="${GOOS}_${GOARCH}" | |||
# defaults | |||
mksyscall="go run mksyscall.go" | |||
mkerrors="./mkerrors.sh" | |||
zerrors="zerrors_$GOOSARCH.go" | |||
mksysctl="" | |||
zsysctl="zsysctl_$GOOSARCH.go" | |||
mksysnum= | |||
mktypes= | |||
run="sh" | |||
case "$1" in | |||
-syscalls) | |||
for i in zsyscall*go | |||
do | |||
sed 1q $i | sed 's;^// ;;' | sh > _$i && gofmt < _$i > $i | |||
rm _$i | |||
done | |||
exit 0 | |||
;; | |||
-n) | |||
run="cat" | |||
shift | |||
esac | |||
case "$#" in | |||
0) | |||
;; | |||
*) | |||
echo 'usage: mkall.sh [-n]' 1>&2 | |||
exit 2 | |||
esac | |||
case "$GOOSARCH" in | |||
_* | *_ | _) | |||
echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2 | |||
exit 1 | |||
;; | |||
plan9_386) | |||
mkerrors= | |||
mksyscall="go run mksyscall.go -l32 -plan9 -tags plan9,386" | |||
mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h" | |||
mktypes="XXX" | |||
;; | |||
plan9_amd64) | |||
mkerrors= | |||
mksyscall="go run mksyscall.go -l32 -plan9 -tags plan9,amd64" | |||
mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h" | |||
mktypes="XXX" | |||
;; | |||
plan9_arm) | |||
mkerrors= | |||
mksyscall="go run mksyscall.go -l32 -plan9 -tags plan9,arm" | |||
mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h" | |||
mktypes="XXX" | |||
;; | |||
*) | |||
echo 'unrecognized $GOOS_$GOARCH: ' "$GOOSARCH" 1>&2 | |||
exit 1 | |||
;; | |||
esac | |||
( | |||
if [ -n "$mkerrors" ]; then echo "$mkerrors |gofmt >$zerrors"; fi | |||
case "$GOOS" in | |||
plan9) | |||
syscall_goos="syscall_$GOOS.go" | |||
if [ -n "$mksyscall" ]; then echo "$mksyscall $syscall_goos |gofmt >zsyscall_$GOOSARCH.go"; fi | |||
;; | |||
esac | |||
if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi | |||
if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi | |||
if [ -n "$mktypes" ]; then echo "$mktypes types_$GOOS.go |gofmt >ztypes_$GOOSARCH.go"; fi | |||
) | $run |
@@ -0,0 +1,246 @@ | |||
#!/usr/bin/env bash | |||
# 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. | |||
# Generate Go code listing errors and other #defined constant | |||
# values (ENAMETOOLONG etc.), by asking the preprocessor | |||
# about the definitions. | |||
unset LANG | |||
export LC_ALL=C | |||
export LC_CTYPE=C | |||
CC=${CC:-gcc} | |||
uname=$(uname) | |||
includes=' | |||
#include <sys/types.h> | |||
#include <sys/file.h> | |||
#include <fcntl.h> | |||
#include <dirent.h> | |||
#include <sys/socket.h> | |||
#include <netinet/in.h> | |||
#include <netinet/ip.h> | |||
#include <netinet/ip6.h> | |||
#include <netinet/tcp.h> | |||
#include <errno.h> | |||
#include <sys/signal.h> | |||
#include <signal.h> | |||
#include <sys/resource.h> | |||
' | |||
ccflags="$@" | |||
# Write go tool cgo -godefs input. | |||
( | |||
echo package plan9 | |||
echo | |||
echo '/*' | |||
indirect="includes_$(uname)" | |||
echo "${!indirect} $includes" | |||
echo '*/' | |||
echo 'import "C"' | |||
echo | |||
echo 'const (' | |||
# The gcc command line prints all the #defines | |||
# it encounters while processing the input | |||
echo "${!indirect} $includes" | $CC -x c - -E -dM $ccflags | | |||
awk ' | |||
$1 != "#define" || $2 ~ /\(/ || $3 == "" {next} | |||
$2 ~ /^E([ABCD]X|[BIS]P|[SD]I|S|FL)$/ {next} # 386 registers | |||
$2 ~ /^(SIGEV_|SIGSTKSZ|SIGRT(MIN|MAX))/ {next} | |||
$2 ~ /^(SCM_SRCRT)$/ {next} | |||
$2 ~ /^(MAP_FAILED)$/ {next} | |||
$2 !~ /^ETH_/ && | |||
$2 !~ /^EPROC_/ && | |||
$2 !~ /^EQUIV_/ && | |||
$2 !~ /^EXPR_/ && | |||
$2 ~ /^E[A-Z0-9_]+$/ || | |||
$2 ~ /^B[0-9_]+$/ || | |||
$2 ~ /^V[A-Z0-9]+$/ || | |||
$2 ~ /^CS[A-Z0-9]/ || | |||
$2 ~ /^I(SIG|CANON|CRNL|EXTEN|MAXBEL|STRIP|UTF8)$/ || | |||
$2 ~ /^IGN/ || | |||
$2 ~ /^IX(ON|ANY|OFF)$/ || | |||
$2 ~ /^IN(LCR|PCK)$/ || | |||
$2 ~ /(^FLU?SH)|(FLU?SH$)/ || | |||
$2 ~ /^C(LOCAL|READ)$/ || | |||
$2 == "BRKINT" || | |||
$2 == "HUPCL" || | |||
$2 == "PENDIN" || | |||
$2 == "TOSTOP" || | |||
$2 ~ /^PAR/ || | |||
$2 ~ /^SIG[^_]/ || | |||
$2 ~ /^O[CNPFP][A-Z]+[^_][A-Z]+$/ || | |||
$2 ~ /^IN_/ || | |||
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ || | |||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ || | |||
$2 == "ICMPV6_FILTER" || | |||
$2 == "SOMAXCONN" || | |||
$2 == "NAME_MAX" || | |||
$2 == "IFNAMSIZ" || | |||
$2 ~ /^CTL_(MAXNAME|NET|QUERY)$/ || | |||
$2 ~ /^SYSCTL_VERS/ || | |||
$2 ~ /^(MS|MNT)_/ || | |||
$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ || | |||
$2 ~ /^(O|F|FD|NAME|S|PTRACE|PT)_/ || | |||
$2 ~ /^LINUX_REBOOT_CMD_/ || | |||
$2 ~ /^LINUX_REBOOT_MAGIC[12]$/ || | |||
$2 !~ "NLA_TYPE_MASK" && | |||
$2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P)_/ || | |||
$2 ~ /^SIOC/ || | |||
$2 ~ /^TIOC/ || | |||
$2 !~ "RTF_BITS" && | |||
$2 ~ /^(IFF|IFT|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ || | |||
$2 ~ /^BIOC/ || | |||
$2 ~ /^RUSAGE_(SELF|CHILDREN|THREAD)/ || | |||
$2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|NOFILE|STACK)|RLIM_INFINITY/ || | |||
$2 ~ /^PRIO_(PROCESS|PGRP|USER)/ || | |||
$2 ~ /^CLONE_[A-Z_]+/ || | |||
$2 !~ /^(BPF_TIMEVAL)$/ && | |||
$2 ~ /^(BPF|DLT)_/ || | |||
$2 !~ "WMESGLEN" && | |||
$2 ~ /^W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", $2, $2)} | |||
$2 ~ /^__WCOREFLAG$/ {next} | |||
$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)} | |||
{next} | |||
' | sort | |||
echo ')' | |||
) >_const.go | |||
# Pull out the error names for later. | |||
errors=$( | |||
echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags | | |||
awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print $2 }' | | |||
sort | |||
) | |||
# Pull out the signal names for later. | |||
signals=$( | |||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags | | |||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print $2 }' | | |||
grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT' | | |||
sort | |||
) | |||
# Again, writing regexps to a file. | |||
echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags | | |||
awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print "^\t" $2 "[ \t]*=" }' | | |||
sort >_error.grep | |||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags | | |||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print "^\t" $2 "[ \t]*=" }' | | |||
grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT' | | |||
sort >_signal.grep | |||
echo '// mkerrors.sh' "$@" | |||
echo '// Code generated by the command above; DO NOT EDIT.' | |||
echo | |||
go tool cgo -godefs -- "$@" _const.go >_error.out | |||
cat _error.out | grep -vf _error.grep | grep -vf _signal.grep | |||
echo | |||
echo '// Errors' | |||
echo 'const (' | |||
cat _error.out | grep -f _error.grep | sed 's/=\(.*\)/= Errno(\1)/' | |||
echo ')' | |||
echo | |||
echo '// Signals' | |||
echo 'const (' | |||
cat _error.out | grep -f _signal.grep | sed 's/=\(.*\)/= Signal(\1)/' | |||
echo ')' | |||
# Run C program to print error and syscall strings. | |||
( | |||
echo -E " | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <errno.h> | |||
#include <ctype.h> | |||
#include <string.h> | |||
#include <signal.h> | |||
#define nelem(x) (sizeof(x)/sizeof((x)[0])) | |||
enum { A = 'A', Z = 'Z', a = 'a', z = 'z' }; // avoid need for single quotes below | |||
int errors[] = { | |||
" | |||
for i in $errors | |||
do | |||
echo -E ' '$i, | |||
done | |||
echo -E " | |||
}; | |||
int signals[] = { | |||
" | |||
for i in $signals | |||
do | |||
echo -E ' '$i, | |||
done | |||
# Use -E because on some systems bash builtin interprets \n itself. | |||
echo -E ' | |||
}; | |||
static int | |||
intcmp(const void *a, const void *b) | |||
{ | |||
return *(int*)a - *(int*)b; | |||
} | |||
int | |||
main(void) | |||
{ | |||
int i, j, e; | |||
char buf[1024], *p; | |||
printf("\n\n// Error table\n"); | |||
printf("var errors = [...]string {\n"); | |||
qsort(errors, nelem(errors), sizeof errors[0], intcmp); | |||
for(i=0; i<nelem(errors); i++) { | |||
e = errors[i]; | |||
if(i > 0 && errors[i-1] == e) | |||
continue; | |||
strcpy(buf, strerror(e)); | |||
// lowercase first letter: Bad -> bad, but STREAM -> STREAM. | |||
if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z) | |||
buf[0] += a - A; | |||
printf("\t%d: \"%s\",\n", e, buf); | |||
} | |||
printf("}\n\n"); | |||
printf("\n\n// Signal table\n"); | |||
printf("var signals = [...]string {\n"); | |||
qsort(signals, nelem(signals), sizeof signals[0], intcmp); | |||
for(i=0; i<nelem(signals); i++) { | |||
e = signals[i]; | |||
if(i > 0 && signals[i-1] == e) | |||
continue; | |||
strcpy(buf, strsignal(e)); | |||
// lowercase first letter: Bad -> bad, but STREAM -> STREAM. | |||
if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z) | |||
buf[0] += a - A; | |||
// cut trailing : number. | |||
p = strrchr(buf, ":"[0]); | |||
if(p) | |||
*p = '\0'; | |||
printf("\t%d: \"%s\",\n", e, buf); | |||
} | |||
printf("}\n\n"); | |||
return 0; | |||
} | |||
' | |||
) >_errors.c | |||
$CC $ccflags -o _errors _errors.c && $GORUN ./_errors && rm -f _errors.c _errors _const.go _error.grep _signal.grep _error.out |
@@ -0,0 +1,23 @@ | |||
#!/bin/sh | |||
# 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. | |||
COMMAND="mksysnum_plan9.sh $@" | |||
cat <<EOF | |||
// $COMMAND | |||
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT | |||
package plan9 | |||
const( | |||
EOF | |||
SP='[ ]' # space or tab | |||
sed "s/^#define${SP}\\([A-Z0-9_][A-Z0-9_]*\\)${SP}${SP}*\\([0-9][0-9]*\\)/SYS_\\1=\\2/g" \ | |||
< $1 | grep -v SYS__ | |||
cat <<EOF | |||
) | |||
EOF |
@@ -0,0 +1,22 @@ | |||
// Copyright 2015 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. | |||
//go:build go1.5 | |||
// +build go1.5 | |||
package plan9 | |||
import "syscall" | |||
func fixwd() { | |||
syscall.Fixwd() | |||
} | |||
func Getwd() (wd string, err error) { | |||
return syscall.Getwd() | |||
} | |||
func Chdir(path string) error { | |||
return syscall.Chdir(path) | |||
} |
@@ -0,0 +1,24 @@ | |||
// Copyright 2015 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. | |||
//go:build !go1.5 | |||
// +build !go1.5 | |||
package plan9 | |||
func fixwd() { | |||
} | |||
func Getwd() (wd string, err error) { | |||
fd, err := open(".", O_RDONLY) | |||
if err != nil { | |||
return "", err | |||
} | |||
defer Close(fd) | |||
return Fd2path(fd) | |||
} | |||
func Chdir(path string) error { | |||
return chdir(path) | |||
} |
@@ -0,0 +1,31 @@ | |||
// Copyright 2012 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. | |||
//go:build plan9 && race | |||
// +build plan9,race | |||
package plan9 | |||
import ( | |||
"runtime" | |||
"unsafe" | |||
) | |||
const raceenabled = true | |||
func raceAcquire(addr unsafe.Pointer) { | |||
runtime.RaceAcquire(addr) | |||
} | |||
func raceReleaseMerge(addr unsafe.Pointer) { | |||
runtime.RaceReleaseMerge(addr) | |||
} | |||
func raceReadRange(addr unsafe.Pointer, len int) { | |||
runtime.RaceReadRange(addr, len) | |||
} | |||
func raceWriteRange(addr unsafe.Pointer, len int) { | |||
runtime.RaceWriteRange(addr, len) | |||
} |
@@ -0,0 +1,26 @@ | |||
// Copyright 2012 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. | |||
//go:build plan9 && !race | |||
// +build plan9,!race | |||
package plan9 | |||
import ( | |||
"unsafe" | |||
) | |||
const raceenabled = false | |||
func raceAcquire(addr unsafe.Pointer) { | |||
} | |||
func raceReleaseMerge(addr unsafe.Pointer) { | |||
} | |||
func raceReadRange(addr unsafe.Pointer, len int) { | |||
} | |||
func raceWriteRange(addr unsafe.Pointer, len int) { | |||
} |
@@ -2,18 +2,15 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris | |||
//go:build plan9 | |||
// +build plan9 | |||
package unix | |||
package plan9 | |||
func itoa(val int) string { // do it here rather than with fmt to avoid dependency | |||
if val < 0 { | |||
return "-" + uitoa(uint(-val)) | |||
return "-" + itoa(-val) | |||
} | |||
return uitoa(uint(val)) | |||
} | |||
func uitoa(val uint) string { | |||
var buf [32]byte // big enough for int64 | |||
i := len(buf) - 1 | |||
for val >= 10 { |
@@ -0,0 +1,110 @@ | |||
// 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. | |||
//go:build plan9 | |||
// +build plan9 | |||
// Package plan9 contains an interface to the low-level operating system | |||
// primitives. OS details vary depending on the underlying system, and | |||
// by default, godoc will display the OS-specific documentation for the current | |||
// system. If you want godoc to display documentation for another | |||
// system, set $GOOS and $GOARCH to the desired system. For example, if | |||
// you want to view documentation for freebsd/arm on linux/amd64, set $GOOS | |||
// to freebsd and $GOARCH to arm. | |||
// | |||
// The primary use of this package is inside other packages that provide a more | |||
// portable interface to the system, such as "os", "time" and "net". Use | |||
// those packages rather than this one if you can. | |||
// | |||
// For details of the functions and data types in this package consult | |||
// the manuals for the appropriate operating system. | |||
// | |||
// These calls return err == nil to indicate success; otherwise | |||
// err represents an operating system error describing the failure and | |||
// holds a value of type syscall.ErrorString. | |||
package plan9 // import "golang.org/x/sys/plan9" | |||
import ( | |||
"bytes" | |||
"strings" | |||
"unsafe" | |||
) | |||
// ByteSliceFromString returns a NUL-terminated slice of bytes | |||
// containing the text of s. If s contains a NUL byte at any | |||
// location, it returns (nil, EINVAL). | |||
func ByteSliceFromString(s string) ([]byte, error) { | |||
if strings.IndexByte(s, 0) != -1 { | |||
return nil, EINVAL | |||
} | |||
a := make([]byte, len(s)+1) | |||
copy(a, s) | |||
return a, nil | |||
} | |||
// BytePtrFromString returns a pointer to a NUL-terminated array of | |||
// bytes containing the text of s. If s contains a NUL byte at any | |||
// location, it returns (nil, EINVAL). | |||
func BytePtrFromString(s string) (*byte, error) { | |||
a, err := ByteSliceFromString(s) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return &a[0], nil | |||
} | |||
// ByteSliceToString returns a string form of the text represented by the slice s, with a terminating NUL and any | |||
// bytes after the NUL removed. | |||
func ByteSliceToString(s []byte) string { | |||
if i := bytes.IndexByte(s, 0); i != -1 { | |||
s = s[:i] | |||
} | |||
return string(s) | |||
} | |||
// BytePtrToString takes a pointer to a sequence of text and returns the corresponding string. | |||
// If the pointer is nil, it returns the empty string. It assumes that the text sequence is terminated | |||
// at a zero byte; if the zero byte is not present, the program may crash. | |||
func BytePtrToString(p *byte) string { | |||
if p == nil { | |||
return "" | |||
} | |||
if *p == 0 { | |||
return "" | |||
} | |||
// Find NUL terminator. | |||
n := 0 | |||
for ptr := unsafe.Pointer(p); *(*byte)(ptr) != 0; n++ { | |||
ptr = unsafe.Pointer(uintptr(ptr) + 1) | |||
} | |||
return string(unsafe.Slice(p, n)) | |||
} | |||
// Single-word zero for use when we need a valid pointer to 0 bytes. | |||
// See mksyscall.pl. | |||
var _zero uintptr | |||
func (ts *Timespec) Unix() (sec int64, nsec int64) { | |||
return int64(ts.Sec), int64(ts.Nsec) | |||
} | |||
func (tv *Timeval) Unix() (sec int64, nsec int64) { | |||
return int64(tv.Sec), int64(tv.Usec) * 1000 | |||
} | |||
func (ts *Timespec) Nano() int64 { | |||
return int64(ts.Sec)*1e9 + int64(ts.Nsec) | |||
} | |||
func (tv *Timeval) Nano() int64 { | |||
return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000 | |||
} | |||
// use is a no-op, but the compiler cannot see that it is. | |||
// Calling use(p) ensures that p is kept live until that point. | |||
// | |||
//go:noescape | |||
func use(p unsafe.Pointer) |
@@ -0,0 +1,361 @@ | |||
// Copyright 2011 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. | |||
// Plan 9 system calls. | |||
// This file is compiled as ordinary Go code, | |||
// but it is also input to mksyscall, | |||
// which parses the //sys lines and generates system call stubs. | |||
// Note that sometimes we use a lowercase //sys name and | |||
// wrap it in our own nicer implementation. | |||
package plan9 | |||
import ( | |||
"bytes" | |||
"syscall" | |||
"unsafe" | |||
) | |||
// A Note is a string describing a process note. | |||
// It implements the os.Signal interface. | |||
type Note string | |||
func (n Note) Signal() {} | |||
func (n Note) String() string { | |||
return string(n) | |||
} | |||
var ( | |||
Stdin = 0 | |||
Stdout = 1 | |||
Stderr = 2 | |||
) | |||
// For testing: clients can set this flag to force | |||
// creation of IPv6 sockets to return EAFNOSUPPORT. | |||
var SocketDisableIPv6 bool | |||
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.ErrorString) | |||
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.ErrorString) | |||
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) | |||
func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) | |||
func atoi(b []byte) (n uint) { | |||
n = 0 | |||
for i := 0; i < len(b); i++ { | |||
n = n*10 + uint(b[i]-'0') | |||
} | |||
return | |||
} | |||
func cstring(s []byte) string { | |||
i := bytes.IndexByte(s, 0) | |||
if i == -1 { | |||
i = len(s) | |||
} | |||
return string(s[:i]) | |||
} | |||
func errstr() string { | |||
var buf [ERRMAX]byte | |||
RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0) | |||
buf[len(buf)-1] = 0 | |||
return cstring(buf[:]) | |||
} | |||
// Implemented in assembly to import from runtime. | |||
func exit(code int) | |||
func Exit(code int) { exit(code) } | |||
func readnum(path string) (uint, error) { | |||
var b [12]byte | |||
fd, e := Open(path, O_RDONLY) | |||
if e != nil { | |||
return 0, e | |||
} | |||
defer Close(fd) | |||
n, e := Pread(fd, b[:], 0) | |||
if e != nil { | |||
return 0, e | |||
} | |||
m := 0 | |||
for ; m < n && b[m] == ' '; m++ { | |||
} | |||
return atoi(b[m : n-1]), nil | |||
} | |||
func Getpid() (pid int) { | |||
n, _ := readnum("#c/pid") | |||
return int(n) | |||
} | |||
func Getppid() (ppid int) { | |||
n, _ := readnum("#c/ppid") | |||
return int(n) | |||
} | |||
func Read(fd int, p []byte) (n int, err error) { | |||
return Pread(fd, p, -1) | |||
} | |||
func Write(fd int, p []byte) (n int, err error) { | |||
return Pwrite(fd, p, -1) | |||
} | |||
var ioSync int64 | |||
//sys fd2path(fd int, buf []byte) (err error) | |||
func Fd2path(fd int) (path string, err error) { | |||
var buf [512]byte | |||
e := fd2path(fd, buf[:]) | |||
if e != nil { | |||
return "", e | |||
} | |||
return cstring(buf[:]), nil | |||
} | |||
//sys pipe(p *[2]int32) (err error) | |||
func Pipe(p []int) (err error) { | |||
if len(p) != 2 { | |||
return syscall.ErrorString("bad arg in system call") | |||
} | |||
var pp [2]int32 | |||
err = pipe(&pp) | |||
if err == nil { | |||
p[0] = int(pp[0]) | |||
p[1] = int(pp[1]) | |||
} | |||
return | |||
} | |||
// Underlying system call writes to newoffset via pointer. | |||
// Implemented in assembly to avoid allocation. | |||
func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string) | |||
func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { | |||
newoffset, e := seek(0, fd, offset, whence) | |||
if newoffset == -1 { | |||
err = syscall.ErrorString(e) | |||
} | |||
return | |||
} | |||
func Mkdir(path string, mode uint32) (err error) { | |||
fd, err := Create(path, O_RDONLY, DMDIR|mode) | |||
if fd != -1 { | |||
Close(fd) | |||
} | |||
return | |||
} | |||
type Waitmsg struct { | |||
Pid int | |||
Time [3]uint32 | |||
Msg string | |||
} | |||
func (w Waitmsg) Exited() bool { return true } | |||
func (w Waitmsg) Signaled() bool { return false } | |||
func (w Waitmsg) ExitStatus() int { | |||
if len(w.Msg) == 0 { | |||
// a normal exit returns no message | |||
return 0 | |||
} | |||
return 1 | |||
} | |||
//sys await(s []byte) (n int, err error) | |||
func Await(w *Waitmsg) (err error) { | |||
var buf [512]byte | |||
var f [5][]byte | |||
n, err := await(buf[:]) | |||
if err != nil || w == nil { | |||
return | |||
} | |||
nf := 0 | |||
p := 0 | |||
for i := 0; i < n && nf < len(f)-1; i++ { | |||
if buf[i] == ' ' { | |||
f[nf] = buf[p:i] | |||
p = i + 1 | |||
nf++ | |||
} | |||
} | |||
f[nf] = buf[p:] | |||
nf++ | |||
if nf != len(f) { | |||
return syscall.ErrorString("invalid wait message") | |||
} | |||
w.Pid = int(atoi(f[0])) | |||
w.Time[0] = uint32(atoi(f[1])) | |||
w.Time[1] = uint32(atoi(f[2])) | |||
w.Time[2] = uint32(atoi(f[3])) | |||
w.Msg = cstring(f[4]) | |||
if w.Msg == "''" { | |||
// await() returns '' for no error | |||
w.Msg = "" | |||
} | |||
return | |||
} | |||
func Unmount(name, old string) (err error) { | |||
fixwd() | |||
oldp, err := BytePtrFromString(old) | |||
if err != nil { | |||
return err | |||
} | |||
oldptr := uintptr(unsafe.Pointer(oldp)) | |||
var r0 uintptr | |||
var e syscall.ErrorString | |||
// bind(2) man page: If name is zero, everything bound or mounted upon old is unbound or unmounted. | |||
if name == "" { | |||
r0, _, e = Syscall(SYS_UNMOUNT, _zero, oldptr, 0) | |||
} else { | |||
namep, err := BytePtrFromString(name) | |||
if err != nil { | |||
return err | |||
} | |||
r0, _, e = Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(namep)), oldptr, 0) | |||
} | |||
if int32(r0) == -1 { | |||
err = e | |||
} | |||
return | |||
} | |||
func Fchdir(fd int) (err error) { | |||
path, err := Fd2path(fd) | |||
if err != nil { | |||
return | |||
} | |||
return Chdir(path) | |||
} | |||
type Timespec struct { | |||
Sec int32 | |||
Nsec int32 | |||
} | |||
type Timeval struct { | |||
Sec int32 | |||
Usec int32 | |||
} | |||
func NsecToTimeval(nsec int64) (tv Timeval) { | |||
nsec += 999 // round up to microsecond | |||
tv.Usec = int32(nsec % 1e9 / 1e3) | |||
tv.Sec = int32(nsec / 1e9) | |||
return | |||
} | |||
func nsec() int64 { | |||
var scratch int64 | |||
r0, _, _ := Syscall(SYS_NSEC, uintptr(unsafe.Pointer(&scratch)), 0, 0) | |||
// TODO(aram): remove hack after I fix _nsec in the pc64 kernel. | |||
if r0 == 0 { | |||
return scratch | |||
} | |||
return int64(r0) | |||
} | |||
func Gettimeofday(tv *Timeval) error { | |||
nsec := nsec() | |||
*tv = NsecToTimeval(nsec) | |||
return nil | |||
} | |||
func Getpagesize() int { return 0x1000 } | |||
func Getegid() (egid int) { return -1 } | |||
func Geteuid() (euid int) { return -1 } | |||
func Getgid() (gid int) { return -1 } | |||
func Getuid() (uid int) { return -1 } | |||
func Getgroups() (gids []int, err error) { | |||
return make([]int, 0), nil | |||
} | |||
//sys open(path string, mode int) (fd int, err error) | |||
func Open(path string, mode int) (fd int, err error) { | |||
fixwd() | |||
return open(path, mode) | |||
} | |||
//sys create(path string, mode int, perm uint32) (fd int, err error) | |||
func Create(path string, mode int, perm uint32) (fd int, err error) { | |||
fixwd() | |||
return create(path, mode, perm) | |||
} | |||
//sys remove(path string) (err error) | |||
func Remove(path string) error { | |||
fixwd() | |||
return remove(path) | |||
} | |||
//sys stat(path string, edir []byte) (n int, err error) | |||
func Stat(path string, edir []byte) (n int, err error) { | |||
fixwd() | |||
return stat(path, edir) | |||
} | |||
//sys bind(name string, old string, flag int) (err error) | |||
func Bind(name string, old string, flag int) (err error) { | |||
fixwd() | |||
return bind(name, old, flag) | |||
} | |||
//sys mount(fd int, afd int, old string, flag int, aname string) (err error) | |||
func Mount(fd int, afd int, old string, flag int, aname string) (err error) { | |||
fixwd() | |||
return mount(fd, afd, old, flag, aname) | |||
} | |||
//sys wstat(path string, edir []byte) (err error) | |||
func Wstat(path string, edir []byte) (err error) { | |||
fixwd() | |||
return wstat(path, edir) | |||
} | |||
//sys chdir(path string) (err error) | |||
//sys Dup(oldfd int, newfd int) (fd int, err error) | |||
//sys Pread(fd int, p []byte, offset int64) (n int, err error) | |||
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) | |||
//sys Close(fd int) (err error) | |||
//sys Fstat(fd int, edir []byte) (n int, err error) | |||
//sys Fwstat(fd int, edir []byte) (err error) |
@@ -0,0 +1,285 @@ | |||
// go run mksyscall.go -l32 -plan9 -tags plan9,386 syscall_plan9.go | |||
// Code generated by the command above; see README.md. DO NOT EDIT. | |||
//go:build plan9 && 386 | |||
// +build plan9,386 | |||
package plan9 | |||
import "unsafe" | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func fd2path(fd int, buf []byte) (err error) { | |||
var _p0 unsafe.Pointer | |||
if len(buf) > 0 { | |||
_p0 = unsafe.Pointer(&buf[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf))) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func pipe(p *[2]int32) (err error) { | |||
r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func await(s []byte) (n int, err error) { | |||
var _p0 unsafe.Pointer | |||
if len(s) > 0 { | |||
_p0 = unsafe.Pointer(&s[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0) | |||
n = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func open(path string, mode int) (fd int, err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) | |||
fd = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func create(path string, mode int, perm uint32) (fd int, err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) | |||
fd = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func remove(path string) (err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func stat(path string, edir []byte) (n int, err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
var _p1 unsafe.Pointer | |||
if len(edir) > 0 { | |||
_p1 = unsafe.Pointer(&edir[0]) | |||
} else { | |||
_p1 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) | |||
n = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func bind(name string, old string, flag int) (err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(name) | |||
if err != nil { | |||
return | |||
} | |||
var _p1 *byte | |||
_p1, err = BytePtrFromString(old) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag)) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func mount(fd int, afd int, old string, flag int, aname string) (err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(old) | |||
if err != nil { | |||
return | |||
} | |||
var _p1 *byte | |||
_p1, err = BytePtrFromString(aname) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func wstat(path string, edir []byte) (err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
var _p1 unsafe.Pointer | |||
if len(edir) > 0 { | |||
_p1 = unsafe.Pointer(&edir[0]) | |||
} else { | |||
_p1 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func chdir(path string) (err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Dup(oldfd int, newfd int) (fd int, err error) { | |||
r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0) | |||
fd = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Pread(fd int, p []byte, offset int64) (n int, err error) { | |||
var _p0 unsafe.Pointer | |||
if len(p) > 0 { | |||
_p0 = unsafe.Pointer(&p[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) | |||
n = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Pwrite(fd int, p []byte, offset int64) (n int, err error) { | |||
var _p0 unsafe.Pointer | |||
if len(p) > 0 { | |||
_p0 = unsafe.Pointer(&p[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) | |||
n = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Close(fd int) (err error) { | |||
r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Fstat(fd int, edir []byte) (n int, err error) { | |||
var _p0 unsafe.Pointer | |||
if len(edir) > 0 { | |||
_p0 = unsafe.Pointer(&edir[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) | |||
n = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Fwstat(fd int, edir []byte) (err error) { | |||
var _p0 unsafe.Pointer | |||
if len(edir) > 0 { | |||
_p0 = unsafe.Pointer(&edir[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} |
@@ -0,0 +1,285 @@ | |||
// go run mksyscall.go -l32 -plan9 -tags plan9,amd64 syscall_plan9.go | |||
// Code generated by the command above; see README.md. DO NOT EDIT. | |||
//go:build plan9 && amd64 | |||
// +build plan9,amd64 | |||
package plan9 | |||
import "unsafe" | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func fd2path(fd int, buf []byte) (err error) { | |||
var _p0 unsafe.Pointer | |||
if len(buf) > 0 { | |||
_p0 = unsafe.Pointer(&buf[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf))) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func pipe(p *[2]int32) (err error) { | |||
r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func await(s []byte) (n int, err error) { | |||
var _p0 unsafe.Pointer | |||
if len(s) > 0 { | |||
_p0 = unsafe.Pointer(&s[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0) | |||
n = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func open(path string, mode int) (fd int, err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) | |||
fd = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func create(path string, mode int, perm uint32) (fd int, err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) | |||
fd = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func remove(path string) (err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func stat(path string, edir []byte) (n int, err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
var _p1 unsafe.Pointer | |||
if len(edir) > 0 { | |||
_p1 = unsafe.Pointer(&edir[0]) | |||
} else { | |||
_p1 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) | |||
n = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func bind(name string, old string, flag int) (err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(name) | |||
if err != nil { | |||
return | |||
} | |||
var _p1 *byte | |||
_p1, err = BytePtrFromString(old) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag)) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func mount(fd int, afd int, old string, flag int, aname string) (err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(old) | |||
if err != nil { | |||
return | |||
} | |||
var _p1 *byte | |||
_p1, err = BytePtrFromString(aname) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func wstat(path string, edir []byte) (err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
var _p1 unsafe.Pointer | |||
if len(edir) > 0 { | |||
_p1 = unsafe.Pointer(&edir[0]) | |||
} else { | |||
_p1 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func chdir(path string) (err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Dup(oldfd int, newfd int) (fd int, err error) { | |||
r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0) | |||
fd = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Pread(fd int, p []byte, offset int64) (n int, err error) { | |||
var _p0 unsafe.Pointer | |||
if len(p) > 0 { | |||
_p0 = unsafe.Pointer(&p[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) | |||
n = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Pwrite(fd int, p []byte, offset int64) (n int, err error) { | |||
var _p0 unsafe.Pointer | |||
if len(p) > 0 { | |||
_p0 = unsafe.Pointer(&p[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) | |||
n = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Close(fd int) (err error) { | |||
r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Fstat(fd int, edir []byte) (n int, err error) { | |||
var _p0 unsafe.Pointer | |||
if len(edir) > 0 { | |||
_p0 = unsafe.Pointer(&edir[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) | |||
n = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Fwstat(fd int, edir []byte) (err error) { | |||
var _p0 unsafe.Pointer | |||
if len(edir) > 0 { | |||
_p0 = unsafe.Pointer(&edir[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} |
@@ -0,0 +1,285 @@ | |||
// go run mksyscall.go -l32 -plan9 -tags plan9,arm syscall_plan9.go | |||
// Code generated by the command above; see README.md. DO NOT EDIT. | |||
//go:build plan9 && arm | |||
// +build plan9,arm | |||
package plan9 | |||
import "unsafe" | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func fd2path(fd int, buf []byte) (err error) { | |||
var _p0 unsafe.Pointer | |||
if len(buf) > 0 { | |||
_p0 = unsafe.Pointer(&buf[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf))) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func pipe(p *[2]int32) (err error) { | |||
r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func await(s []byte) (n int, err error) { | |||
var _p0 unsafe.Pointer | |||
if len(s) > 0 { | |||
_p0 = unsafe.Pointer(&s[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0) | |||
n = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func open(path string, mode int) (fd int, err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) | |||
fd = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func create(path string, mode int, perm uint32) (fd int, err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) | |||
fd = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func remove(path string) (err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func stat(path string, edir []byte) (n int, err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
var _p1 unsafe.Pointer | |||
if len(edir) > 0 { | |||
_p1 = unsafe.Pointer(&edir[0]) | |||
} else { | |||
_p1 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) | |||
n = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func bind(name string, old string, flag int) (err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(name) | |||
if err != nil { | |||
return | |||
} | |||
var _p1 *byte | |||
_p1, err = BytePtrFromString(old) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag)) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func mount(fd int, afd int, old string, flag int, aname string) (err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(old) | |||
if err != nil { | |||
return | |||
} | |||
var _p1 *byte | |||
_p1, err = BytePtrFromString(aname) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func wstat(path string, edir []byte) (err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
var _p1 unsafe.Pointer | |||
if len(edir) > 0 { | |||
_p1 = unsafe.Pointer(&edir[0]) | |||
} else { | |||
_p1 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func chdir(path string) (err error) { | |||
var _p0 *byte | |||
_p0, err = BytePtrFromString(path) | |||
if err != nil { | |||
return | |||
} | |||
r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Dup(oldfd int, newfd int) (fd int, err error) { | |||
r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0) | |||
fd = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Pread(fd int, p []byte, offset int64) (n int, err error) { | |||
var _p0 unsafe.Pointer | |||
if len(p) > 0 { | |||
_p0 = unsafe.Pointer(&p[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) | |||
n = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Pwrite(fd int, p []byte, offset int64) (n int, err error) { | |||
var _p0 unsafe.Pointer | |||
if len(p) > 0 { | |||
_p0 = unsafe.Pointer(&p[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) | |||
n = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Close(fd int) (err error) { | |||
r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Fstat(fd int, edir []byte) (n int, err error) { | |||
var _p0 unsafe.Pointer | |||
if len(edir) > 0 { | |||
_p0 = unsafe.Pointer(&edir[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) | |||
n = int(r0) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} | |||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT | |||
func Fwstat(fd int, edir []byte) (err error) { | |||
var _p0 unsafe.Pointer | |||
if len(edir) > 0 { | |||
_p0 = unsafe.Pointer(&edir[0]) | |||
} else { | |||
_p0 = unsafe.Pointer(&_zero) | |||
} | |||
r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) | |||
if int32(r0) == -1 { | |||
err = e1 | |||
} | |||
return | |||
} |
@@ -0,0 +1,49 @@ | |||
// mksysnum_plan9.sh /opt/plan9/sys/src/libc/9syscall/sys.h | |||
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT | |||
package plan9 | |||
const ( | |||
SYS_SYSR1 = 0 | |||
SYS_BIND = 2 | |||
SYS_CHDIR = 3 | |||
SYS_CLOSE = 4 | |||
SYS_DUP = 5 | |||
SYS_ALARM = 6 | |||
SYS_EXEC = 7 | |||
SYS_EXITS = 8 | |||
SYS_FAUTH = 10 | |||
SYS_SEGBRK = 12 | |||
SYS_OPEN = 14 | |||
SYS_OSEEK = 16 | |||
SYS_SLEEP = 17 | |||
SYS_RFORK = 19 | |||
SYS_PIPE = 21 | |||
SYS_CREATE = 22 | |||
SYS_FD2PATH = 23 | |||
SYS_BRK_ = 24 | |||
SYS_REMOVE = 25 | |||
SYS_NOTIFY = 28 | |||
SYS_NOTED = 29 | |||
SYS_SEGATTACH = 30 | |||
SYS_SEGDETACH = 31 | |||
SYS_SEGFREE = 32 | |||
SYS_SEGFLUSH = 33 | |||
SYS_RENDEZVOUS = 34 | |||
SYS_UNMOUNT = 35 | |||
SYS_SEMACQUIRE = 37 | |||
SYS_SEMRELEASE = 38 | |||
SYS_SEEK = 39 | |||
SYS_FVERSION = 40 | |||
SYS_ERRSTR = 41 | |||
SYS_STAT = 42 | |||
SYS_FSTAT = 43 | |||
SYS_WSTAT = 44 | |||
SYS_FWSTAT = 45 | |||
SYS_MOUNT = 46 | |||
SYS_AWAIT = 47 | |||
SYS_PREAD = 50 | |||
SYS_PWRITE = 51 | |||
SYS_TSEMACQUIRE = 52 | |||
SYS_NSEC = 53 | |||
) |
@@ -14,7 +14,7 @@ migrating the build system to use containers so the builds are reproducible. | |||
This is being done on an OS-by-OS basis. Please update this documentation as | |||
components of the build system change. | |||
### Old Build System (currently for `GOOS != "Linux" || GOARCH == "sparc64"`) | |||
### Old Build System (currently for `GOOS != "linux"`) | |||
The old build system generates the Go files based on the C header files | |||
present on your system. This means that files | |||
@@ -32,9 +32,9 @@ To build the files for your current OS and architecture, make sure GOOS and | |||
GOARCH are set correctly and run `mkall.sh`. This will generate the files for | |||
your specific system. Running `mkall.sh -n` shows the commands that will be run. | |||
Requirements: bash, perl, go | |||
Requirements: bash, go | |||
### New Build System (currently for `GOOS == "Linux" && GOARCH != "sparc64"`) | |||
### New Build System (currently for `GOOS == "linux"`) | |||
The new build system uses a Docker container to generate the go files directly | |||
from source checkouts of the kernel and various system libraries. This means | |||
@@ -52,14 +52,14 @@ system and have your GOOS and GOARCH set accordingly. Running `mkall.sh` will | |||
then generate all of the files for all of the GOOS/GOARCH pairs in the new build | |||
system. Running `mkall.sh -n` shows the commands that will be run. | |||
Requirements: bash, perl, go, docker | |||
Requirements: bash, go, docker | |||
## Component files | |||
This section describes the various files used in the code generation process. | |||
It also contains instructions on how to modify these files to add a new | |||
architecture/OS or to add additional syscalls, types, or constants. Note that | |||
if you are using the new build system, the scripts cannot be called normally. | |||
if you are using the new build system, the scripts/programs cannot be called normally. | |||
They must be called from within the docker container. | |||
### asm files | |||
@@ -76,30 +76,30 @@ arguments can be passed to the kernel. The third is for low-level use by the | |||
ForkExec wrapper. Unlike the first two, it does not call into the scheduler to | |||
let it know that a system call is running. | |||
When porting Go to an new architecture/OS, this file must be implemented for | |||
When porting Go to a new architecture/OS, this file must be implemented for | |||
each GOOS/GOARCH pair. | |||
### mksysnum | |||
Mksysnum is a script located at `${GOOS}/mksysnum.pl` (or `mksysnum_${GOOS}.pl` | |||
for the old system). This script takes in a list of header files containing the | |||
Mksysnum is a Go program located at `${GOOS}/mksysnum.go` (or `mksysnum_${GOOS}.go` | |||
for the old system). This program takes in a list of header files containing the | |||
syscall number declarations and parses them to produce the corresponding list of | |||
Go numeric constants. See `zsysnum_${GOOS}_${GOARCH}.go` for the generated | |||
constants. | |||
Adding new syscall numbers is mostly done by running the build on a sufficiently | |||
new installation of the target OS (or updating the source checkouts for the | |||
new build system). However, depending on the OS, you make need to update the | |||
new build system). However, depending on the OS, you may need to update the | |||
parsing in mksysnum. | |||
### mksyscall.pl | |||
### mksyscall.go | |||
The `syscall.go`, `syscall_${GOOS}.go`, `syscall_${GOOS}_${GOARCH}.go` are | |||
hand-written Go files which implement system calls (for unix, the specific OS, | |||
or the specific OS/Architecture pair respectively) that need special handling | |||
and list `//sys` comments giving prototypes for ones that can be generated. | |||
The mksyscall.pl script takes the `//sys` and `//sysnb` comments and converts | |||
The mksyscall.go program takes the `//sys` and `//sysnb` comments and converts | |||
them into syscalls. This requires the name of the prototype in the comment to | |||
match a syscall number in the `zsysnum_${GOOS}_${GOARCH}.go` file. The function | |||
prototype can be exported (capitalized) or not. | |||
@@ -107,7 +107,7 @@ prototype can be exported (capitalized) or not. | |||
Adding a new syscall often just requires adding a new `//sys` function prototype | |||
with the desired arguments and a capitalized name so it is exported. However, if | |||
you want the interface to the syscall to be different, often one will make an | |||
unexported `//sys` prototype, an then write a custom wrapper in | |||
unexported `//sys` prototype, and then write a custom wrapper in | |||
`syscall_${GOOS}.go`. | |||
### types files | |||
@@ -137,7 +137,7 @@ some `#if/#elif` macros in your include statements. | |||
This script is used to generate the system's various constants. This doesn't | |||
just include the error numbers and error strings, but also the signal numbers | |||
an a wide variety of miscellaneous constants. The constants come from the list | |||
and a wide variety of miscellaneous constants. The constants come from the list | |||
of include files in the `includes_${uname}` variable. A regex then picks out | |||
the desired `#define` statements, and generates the corresponding Go constants. | |||
The error numbers and strings are generated from `#include <errno.h>`, and the | |||
@@ -149,10 +149,21 @@ To add a constant, add the header that includes it to the appropriate variable. | |||
Then, edit the regex (if necessary) to match the desired constant. Avoid making | |||
the regex too broad to avoid matching unintended constants. | |||
### internal/mkmerge | |||
This program is used to extract duplicate const, func, and type declarations | |||
from the generated architecture-specific files listed below, and merge these | |||
into a common file for each OS. | |||
The merge is performed in the following steps: | |||
1. Construct the set of common code that is idential in all architecture-specific files. | |||
2. Write this common code to the merged file. | |||
3. Remove the common code from all architecture-specific files. | |||
## Generated files | |||
### `zerror_${GOOS}_${GOARCH}.go` | |||
### `zerrors_${GOOS}_${GOARCH}.go` | |||
A file containing all of the system's generated error numbers, error strings, | |||
signal numbers, and constants. Generated by `mkerrors.sh` (see above). | |||
@@ -160,7 +171,7 @@ signal numbers, and constants. Generated by `mkerrors.sh` (see above). | |||
### `zsyscall_${GOOS}_${GOARCH}.go` | |||
A file containing all the generated syscalls for a specific GOOS and GOARCH. | |||
Generated by `mksyscall.pl` (see above). | |||
Generated by `mksyscall.go` (see above). | |||
### `zsysnum_${GOOS}_${GOARCH}.go` | |||
@@ -7,6 +7,7 @@ | |||
package unix | |||
import ( | |||
"math/bits" | |||
"unsafe" | |||
) | |||
@@ -79,46 +80,7 @@ func (s *CPUSet) IsSet(cpu int) bool { | |||
func (s *CPUSet) Count() int { | |||
c := 0 | |||
for _, b := range s { | |||
c += onesCount64(uint64(b)) | |||
c += bits.OnesCount64(uint64(b)) | |||
} | |||
return c | |||
} | |||
// onesCount64 is a copy of Go 1.9's math/bits.OnesCount64. | |||
// Once this package can require Go 1.9, we can delete this | |||
// and update the caller to use bits.OnesCount64. | |||
func onesCount64(x uint64) int { | |||
const m0 = 0x5555555555555555 // 01010101 ... | |||
const m1 = 0x3333333333333333 // 00110011 ... | |||
const m2 = 0x0f0f0f0f0f0f0f0f // 00001111 ... | |||
const m3 = 0x00ff00ff00ff00ff // etc. | |||
const m4 = 0x0000ffff0000ffff | |||
// Implementation: Parallel summing of adjacent bits. | |||
// See "Hacker's Delight", Chap. 5: Counting Bits. | |||
// The following pattern shows the general approach: | |||
// | |||
// x = x>>1&(m0&m) + x&(m0&m) | |||
// x = x>>2&(m1&m) + x&(m1&m) | |||
// x = x>>4&(m2&m) + x&(m2&m) | |||
// x = x>>8&(m3&m) + x&(m3&m) | |||
// x = x>>16&(m4&m) + x&(m4&m) | |||
// x = x>>32&(m5&m) + x&(m5&m) | |||
// return int(x) | |||
// | |||
// Masking (& operations) can be left away when there's no | |||
// danger that a field's sum will carry over into the next | |||
// field: Since the result cannot be > 64, 8 bits is enough | |||
// and we can ignore the masks for the shifts by 8 and up. | |||
// Per "Hacker's Delight", the first line can be simplified | |||
// more, but it saves at best one instruction, so we leave | |||
// it alone for clarity. | |||
const m = 1<<64 - 1 | |||
x = x>>1&(m0&m) + x&(m0&m) | |||
x = x>>2&(m1&m) + x&(m1&m) | |||
x = (x>>4 + x) & (m2 & m) | |||
x += x >> 8 | |||
x += x >> 16 | |||
x += x >> 32 | |||
return int(x) & (1<<7 - 1) | |||
} |
@@ -2,7 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris | |||
//go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos) && go1.9 | |||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos | |||
// +build go1.9 | |||
package unix | |||
@@ -2,7 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build !gccgo | |||
//go:build gc | |||
// +build gc | |||
#include "textflag.h" | |||
@@ -1,14 +1,14 @@ | |||
// Copyright 2009 The Go Authors. All rights reserved. | |||
// Copyright 2021 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 !gccgo | |||
//go:build (freebsd || netbsd || openbsd) && gc | |||
// +build freebsd netbsd openbsd | |||
// +build gc | |||
#include "textflag.h" | |||
// | |||
// System call support for 386, Darwin | |||
// | |||
// System call support for 386 BSD | |||
// Just jump to package syscall's implementation for all these functions. | |||
// The runtime may know about them. | |||
@@ -22,7 +22,7 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-40 | |||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 | |||
JMP syscall·Syscall9(SB) | |||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||
JMP syscall·RawSyscall(SB) | |||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 |
@@ -0,0 +1,29 @@ | |||
// Copyright 2021 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. | |||
//go:build (darwin || dragonfly || freebsd || netbsd || openbsd) && gc | |||
// +build darwin dragonfly freebsd netbsd openbsd | |||
// +build gc | |||
#include "textflag.h" | |||
// System call support for AMD64 BSD | |||
// Just jump to package syscall's implementation for all these functions. | |||
// The runtime may know about them. | |||
TEXT ·Syscall(SB),NOSPLIT,$0-56 | |||
JMP syscall·Syscall(SB) | |||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 | |||
JMP syscall·Syscall6(SB) | |||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 | |||
JMP syscall·Syscall9(SB) | |||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 | |||
JMP syscall·RawSyscall(SB) | |||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 | |||
JMP syscall·RawSyscall6(SB) |
@@ -1,14 +1,14 @@ | |||
// Copyright 2013 The Go Authors. All rights reserved. | |||
// Copyright 2021 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 !gccgo | |||
//go:build (freebsd || netbsd || openbsd) && gc | |||
// +build freebsd netbsd openbsd | |||
// +build gc | |||
#include "textflag.h" | |||
// | |||
// System call support for ARM, NetBSD | |||
// | |||
// System call support for ARM BSD | |||
// Just jump to package syscall's implementation for all these functions. | |||
// The runtime may know about them. |
@@ -1,14 +1,14 @@ | |||
// Copyright 2009 The Go Authors. All rights reserved. | |||
// Copyright 2021 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 !gccgo | |||
//go:build (darwin || freebsd || netbsd || openbsd) && gc | |||
// +build darwin freebsd netbsd openbsd | |||
// +build gc | |||
#include "textflag.h" | |||
// | |||
// System call support for AMD64, NetBSD | |||
// | |||
// System call support for ARM64 BSD | |||
// Just jump to package syscall's implementation for all these functions. | |||
// The runtime may know about them. |
@@ -1,13 +1,15 @@ | |||
// Copyright 2009 The Go Authors. All rights reserved. | |||
// Copyright 2022 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 !gccgo | |||
//go:build (darwin || freebsd || netbsd || openbsd) && gc | |||
// +build darwin freebsd netbsd openbsd | |||
// +build gc | |||
#include "textflag.h" | |||
// | |||
// System call support for AMD64, Darwin | |||
// System call support for ppc64, BSD | |||
// | |||
// Just jump to package syscall's implementation for all these functions. |
@@ -1,14 +1,14 @@ | |||
// Copyright 2009 The Go Authors. All rights reserved. | |||
// Copyright 2021 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 !gccgo | |||
//go:build (darwin || freebsd || netbsd || openbsd) && gc | |||
// +build darwin freebsd netbsd openbsd | |||
// +build gc | |||
#include "textflag.h" | |||
// | |||
// System call support for AMD64, DragonFly | |||
// | |||
// System call support for RISCV64 BSD | |||
// Just jump to package syscall's implementation for all these functions. | |||
// The runtime may know about them. | |||
@@ -22,7 +22,7 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-80 | |||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 | |||
JMP syscall·Syscall9(SB) | |||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 | |||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 | |||
JMP syscall·RawSyscall(SB) | |||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 |
@@ -1,30 +0,0 @@ | |||
// Copyright 2015 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 !gccgo | |||
// +build arm,darwin | |||
#include "textflag.h" | |||
// | |||
// System call support for ARM, Darwin | |||
// | |||
// Just jump to package syscall's implementation for all these functions. | |||
// The runtime may know about them. | |||
TEXT ·Syscall(SB),NOSPLIT,$0-28 | |||
B syscall·Syscall(SB) | |||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 | |||
B syscall·Syscall6(SB) | |||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 | |||
B syscall·Syscall9(SB) | |||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||
B syscall·RawSyscall(SB) | |||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 | |||
B syscall·RawSyscall6(SB) |
@@ -1,30 +0,0 @@ | |||
// Copyright 2015 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 !gccgo | |||
// +build arm64,darwin | |||
#include "textflag.h" | |||
// | |||
// System call support for AMD64, Darwin | |||
// | |||
// Just jump to package syscall's implementation for all these functions. | |||
// The runtime may know about them. | |||
TEXT ·Syscall(SB),NOSPLIT,$0-56 | |||
B syscall·Syscall(SB) | |||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 | |||
B syscall·Syscall6(SB) | |||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 | |||
B syscall·Syscall9(SB) | |||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 | |||
B syscall·RawSyscall(SB) | |||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 | |||
B syscall·RawSyscall6(SB) |
@@ -1,29 +0,0 @@ | |||
// 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 !gccgo | |||
#include "textflag.h" | |||
// | |||
// System call support for 386, FreeBSD | |||
// | |||
// Just jump to package syscall's implementation for all these functions. | |||
// The runtime may know about them. | |||
TEXT ·Syscall(SB),NOSPLIT,$0-28 | |||
JMP syscall·Syscall(SB) | |||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 | |||
JMP syscall·Syscall6(SB) | |||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 | |||
JMP syscall·Syscall9(SB) | |||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||
JMP syscall·RawSyscall(SB) | |||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 | |||
JMP syscall·RawSyscall6(SB) |
@@ -1,29 +0,0 @@ | |||
// Copyright 2012 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 !gccgo | |||
#include "textflag.h" | |||
// | |||
// System call support for ARM, FreeBSD | |||
// | |||
// Just jump to package syscall's implementation for all these functions. | |||
// The runtime may know about them. | |||
TEXT ·Syscall(SB),NOSPLIT,$0-28 | |||
B syscall·Syscall(SB) | |||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 | |||
B syscall·Syscall6(SB) | |||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 | |||
B syscall·Syscall9(SB) | |||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||
B syscall·RawSyscall(SB) | |||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 | |||
B syscall·RawSyscall6(SB) |
@@ -2,7 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build !gccgo | |||
//go:build gc | |||
// +build gc | |||
#include "textflag.h" | |||
@@ -2,7 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build !gccgo | |||
//go:build gc | |||
// +build gc | |||
#include "textflag.h" | |||
@@ -2,7 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build !gccgo | |||
//go:build gc | |||
// +build gc | |||
#include "textflag.h" | |||
@@ -2,9 +2,10 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
//go:build linux && arm64 && gc | |||
// +build linux | |||
// +build arm64 | |||
// +build !gccgo | |||
// +build gc | |||
#include "textflag.h" | |||
@@ -0,0 +1,54 @@ | |||
// Copyright 2022 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. | |||
//go:build linux && loong64 && gc | |||
// +build linux | |||
// +build loong64 | |||
// +build gc | |||
#include "textflag.h" | |||
// Just jump to package syscall's implementation for all these functions. | |||
// The runtime may know about them. | |||
TEXT ·Syscall(SB),NOSPLIT,$0-56 | |||
JMP syscall·Syscall(SB) | |||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 | |||
JMP syscall·Syscall6(SB) | |||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48 | |||
JAL runtime·entersyscall(SB) | |||
MOVV a1+8(FP), R4 | |||
MOVV a2+16(FP), R5 | |||
MOVV a3+24(FP), R6 | |||
MOVV R0, R7 | |||
MOVV R0, R8 | |||
MOVV R0, R9 | |||
MOVV trap+0(FP), R11 // syscall entry | |||
SYSCALL | |||
MOVV R4, r1+32(FP) | |||
MOVV R0, r2+40(FP) // r2 is not used. Always set to 0 | |||
JAL runtime·exitsyscall(SB) | |||
RET | |||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 | |||
JMP syscall·RawSyscall(SB) | |||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 | |||
JMP syscall·RawSyscall6(SB) | |||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48 | |||
MOVV a1+8(FP), R4 | |||
MOVV a2+16(FP), R5 | |||
MOVV a3+24(FP), R6 | |||
MOVV R0, R7 | |||
MOVV R0, R8 | |||
MOVV R0, R9 | |||
MOVV trap+0(FP), R11 // syscall entry | |||
SYSCALL | |||
MOVV R4, r1+32(FP) | |||
MOVV R0, r2+40(FP) // r2 is not used. Always set to 0 | |||
RET |
@@ -2,9 +2,10 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
//go:build linux && (mips64 || mips64le) && gc | |||
// +build linux | |||
// +build mips64 mips64le | |||
// +build !gccgo | |||
// +build gc | |||
#include "textflag.h" | |||
@@ -2,9 +2,10 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
//go:build linux && (mips || mipsle) && gc | |||
// +build linux | |||
// +build mips mipsle | |||
// +build !gccgo | |||
// +build gc | |||
#include "textflag.h" | |||
@@ -2,9 +2,10 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
//go:build linux && (ppc64 || ppc64le) && gc | |||
// +build linux | |||
// +build ppc64 ppc64le | |||
// +build !gccgo | |||
// +build gc | |||
#include "textflag.h" | |||
@@ -0,0 +1,49 @@ | |||
// Copyright 2019 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. | |||
//go:build riscv64 && gc | |||
// +build riscv64 | |||
// +build gc | |||
#include "textflag.h" | |||
// | |||
// System calls for linux/riscv64. | |||
// | |||
// Where available, just jump to package syscall's implementation of | |||
// these functions. | |||
TEXT ·Syscall(SB),NOSPLIT,$0-56 | |||
JMP syscall·Syscall(SB) | |||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 | |||
JMP syscall·Syscall6(SB) | |||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48 | |||
CALL runtime·entersyscall(SB) | |||
MOV a1+8(FP), A0 | |||
MOV a2+16(FP), A1 | |||
MOV a3+24(FP), A2 | |||
MOV trap+0(FP), A7 // syscall entry | |||
ECALL | |||
MOV A0, r1+32(FP) // r1 | |||
MOV A1, r2+40(FP) // r2 | |||
CALL runtime·exitsyscall(SB) | |||
RET | |||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 | |||
JMP syscall·RawSyscall(SB) | |||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 | |||
JMP syscall·RawSyscall6(SB) | |||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48 | |||
MOV a1+8(FP), A0 | |||
MOV a2+16(FP), A1 | |||
MOV a3+24(FP), A2 | |||
MOV trap+0(FP), A7 // syscall entry | |||
ECALL | |||
MOV A0, r1+32(FP) | |||
MOV A1, r2+40(FP) | |||
RET |
@@ -2,9 +2,10 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build s390x | |||
//go:build linux && s390x && gc | |||
// +build linux | |||
// +build !gccgo | |||
// +build s390x | |||
// +build gc | |||
#include "textflag.h" | |||
@@ -1,29 +0,0 @@ | |||
// Copyright 2017 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 !gccgo | |||
#include "textflag.h" | |||
// | |||
// System call support for ARM, OpenBSD | |||
// | |||
// Just jump to package syscall's implementation for all these functions. | |||
// The runtime may know about them. | |||
TEXT ·Syscall(SB),NOSPLIT,$0-28 | |||
B syscall·Syscall(SB) | |||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 | |||
B syscall·Syscall6(SB) | |||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 | |||
B syscall·Syscall9(SB) | |||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 | |||
B syscall·RawSyscall(SB) | |||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 | |||
B syscall·RawSyscall6(SB) |
@@ -1,13 +1,14 @@ | |||
// Copyright 2009 The Go Authors. All rights reserved. | |||
// Copyright 2019 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 !gccgo | |||
//go:build gc | |||
// +build gc | |||
#include "textflag.h" | |||
// | |||
// System call support for AMD64, OpenBSD | |||
// System call support for mips64, OpenBSD | |||
// | |||
// Just jump to package syscall's implementation for all these functions. |
@@ -2,7 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build !gccgo | |||
//go:build gc | |||
// +build gc | |||
#include "textflag.h" | |||
@@ -0,0 +1,426 @@ | |||
// Copyright 2020 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. | |||
//go:build zos && s390x && gc | |||
// +build zos | |||
// +build s390x | |||
// +build gc | |||
#include "textflag.h" | |||
#define PSALAA 1208(R0) | |||
#define GTAB64(x) 80(x) | |||
#define LCA64(x) 88(x) | |||
#define CAA(x) 8(x) | |||
#define EDCHPXV(x) 1016(x) // in the CAA | |||
#define SAVSTACK_ASYNC(x) 336(x) // in the LCA | |||
// SS_*, where x=SAVSTACK_ASYNC | |||
#define SS_LE(x) 0(x) | |||
#define SS_GO(x) 8(x) | |||
#define SS_ERRNO(x) 16(x) | |||
#define SS_ERRNOJR(x) 20(x) | |||
#define LE_CALL BYTE $0x0D; BYTE $0x76; // BL R7, R6 | |||
TEXT ·clearErrno(SB),NOSPLIT,$0-0 | |||
BL addrerrno<>(SB) | |||
MOVD $0, 0(R3) | |||
RET | |||
// Returns the address of errno in R3. | |||
TEXT addrerrno<>(SB),NOSPLIT|NOFRAME,$0-0 | |||
// Get library control area (LCA). | |||
MOVW PSALAA, R8 | |||
MOVD LCA64(R8), R8 | |||
// Get __errno FuncDesc. | |||
MOVD CAA(R8), R9 | |||
MOVD EDCHPXV(R9), R9 | |||
ADD $(0x156*16), R9 | |||
LMG 0(R9), R5, R6 | |||
// Switch to saved LE stack. | |||
MOVD SAVSTACK_ASYNC(R8), R9 | |||
MOVD 0(R9), R4 | |||
MOVD $0, 0(R9) | |||
// Call __errno function. | |||
LE_CALL | |||
NOPH | |||
// Switch back to Go stack. | |||
XOR R0, R0 // Restore R0 to $0. | |||
MOVD R4, 0(R9) // Save stack pointer. | |||
RET | |||
TEXT ·syscall_syscall(SB),NOSPLIT,$0-56 | |||
BL runtime·entersyscall(SB) | |||
MOVD a1+8(FP), R1 | |||
MOVD a2+16(FP), R2 | |||
MOVD a3+24(FP), R3 | |||
// Get library control area (LCA). | |||
MOVW PSALAA, R8 | |||
MOVD LCA64(R8), R8 | |||
// Get function. | |||
MOVD CAA(R8), R9 | |||
MOVD EDCHPXV(R9), R9 | |||
MOVD trap+0(FP), R5 | |||
SLD $4, R5 | |||
ADD R5, R9 | |||
LMG 0(R9), R5, R6 | |||
// Restore LE stack. | |||
MOVD SAVSTACK_ASYNC(R8), R9 | |||
MOVD 0(R9), R4 | |||
MOVD $0, 0(R9) | |||
// Call function. | |||
LE_CALL | |||
NOPH | |||
XOR R0, R0 // Restore R0 to $0. | |||
MOVD R4, 0(R9) // Save stack pointer. | |||
MOVD R3, r1+32(FP) | |||
MOVD R0, r2+40(FP) | |||
MOVD R0, err+48(FP) | |||
MOVW R3, R4 | |||
CMP R4, $-1 | |||
BNE done | |||
BL addrerrno<>(SB) | |||
MOVWZ 0(R3), R3 | |||
MOVD R3, err+48(FP) | |||
done: | |||
BL runtime·exitsyscall(SB) | |||
RET | |||
TEXT ·syscall_rawsyscall(SB),NOSPLIT,$0-56 | |||
MOVD a1+8(FP), R1 | |||
MOVD a2+16(FP), R2 | |||
MOVD a3+24(FP), R3 | |||
// Get library control area (LCA). | |||
MOVW PSALAA, R8 | |||
MOVD LCA64(R8), R8 | |||
// Get function. | |||
MOVD CAA(R8), R9 | |||
MOVD EDCHPXV(R9), R9 | |||
MOVD trap+0(FP), R5 | |||
SLD $4, R5 | |||
ADD R5, R9 | |||
LMG 0(R9), R5, R6 | |||
// Restore LE stack. | |||
MOVD SAVSTACK_ASYNC(R8), R9 | |||
MOVD 0(R9), R4 | |||
MOVD $0, 0(R9) | |||
// Call function. | |||
LE_CALL | |||
NOPH | |||
XOR R0, R0 // Restore R0 to $0. | |||
MOVD R4, 0(R9) // Save stack pointer. | |||
MOVD R3, r1+32(FP) | |||
MOVD R0, r2+40(FP) | |||
MOVD R0, err+48(FP) | |||
MOVW R3, R4 | |||
CMP R4, $-1 | |||
BNE done | |||
BL addrerrno<>(SB) | |||
MOVWZ 0(R3), R3 | |||
MOVD R3, err+48(FP) | |||
done: | |||
RET | |||
TEXT ·syscall_syscall6(SB),NOSPLIT,$0-80 | |||
BL runtime·entersyscall(SB) | |||
MOVD a1+8(FP), R1 | |||
MOVD a2+16(FP), R2 | |||
MOVD a3+24(FP), R3 | |||
// Get library control area (LCA). | |||
MOVW PSALAA, R8 | |||
MOVD LCA64(R8), R8 | |||
// Get function. | |||
MOVD CAA(R8), R9 | |||
MOVD EDCHPXV(R9), R9 | |||
MOVD trap+0(FP), R5 | |||
SLD $4, R5 | |||
ADD R5, R9 | |||
LMG 0(R9), R5, R6 | |||
// Restore LE stack. | |||
MOVD SAVSTACK_ASYNC(R8), R9 | |||
MOVD 0(R9), R4 | |||
MOVD $0, 0(R9) | |||
// Fill in parameter list. | |||
MOVD a4+32(FP), R12 | |||
MOVD R12, (2176+24)(R4) | |||
MOVD a5+40(FP), R12 | |||
MOVD R12, (2176+32)(R4) | |||
MOVD a6+48(FP), R12 | |||
MOVD R12, (2176+40)(R4) | |||
// Call function. | |||
LE_CALL | |||
NOPH | |||
XOR R0, R0 // Restore R0 to $0. | |||
MOVD R4, 0(R9) // Save stack pointer. | |||
MOVD R3, r1+56(FP) | |||
MOVD R0, r2+64(FP) | |||
MOVD R0, err+72(FP) | |||
MOVW R3, R4 | |||
CMP R4, $-1 | |||
BNE done | |||
BL addrerrno<>(SB) | |||
MOVWZ 0(R3), R3 | |||
MOVD R3, err+72(FP) | |||
done: | |||
BL runtime·exitsyscall(SB) | |||
RET | |||
TEXT ·syscall_rawsyscall6(SB),NOSPLIT,$0-80 | |||
MOVD a1+8(FP), R1 | |||
MOVD a2+16(FP), R2 | |||
MOVD a3+24(FP), R3 | |||
// Get library control area (LCA). | |||
MOVW PSALAA, R8 | |||
MOVD LCA64(R8), R8 | |||
// Get function. | |||
MOVD CAA(R8), R9 | |||
MOVD EDCHPXV(R9), R9 | |||
MOVD trap+0(FP), R5 | |||
SLD $4, R5 | |||
ADD R5, R9 | |||
LMG 0(R9), R5, R6 | |||
// Restore LE stack. | |||
MOVD SAVSTACK_ASYNC(R8), R9 | |||
MOVD 0(R9), R4 | |||
MOVD $0, 0(R9) | |||
// Fill in parameter list. | |||
MOVD a4+32(FP), R12 | |||
MOVD R12, (2176+24)(R4) | |||
MOVD a5+40(FP), R12 | |||
MOVD R12, (2176+32)(R4) | |||
MOVD a6+48(FP), R12 | |||
MOVD R12, (2176+40)(R4) | |||
// Call function. | |||
LE_CALL | |||
NOPH | |||
XOR R0, R0 // Restore R0 to $0. | |||
MOVD R4, 0(R9) // Save stack pointer. | |||
MOVD R3, r1+56(FP) | |||
MOVD R0, r2+64(FP) | |||
MOVD R0, err+72(FP) | |||
MOVW R3, R4 | |||
CMP R4, $-1 | |||
BNE done | |||
BL ·rrno<>(SB) | |||
MOVWZ 0(R3), R3 | |||
MOVD R3, err+72(FP) | |||
done: | |||
RET | |||
TEXT ·syscall_syscall9(SB),NOSPLIT,$0 | |||
BL runtime·entersyscall(SB) | |||
MOVD a1+8(FP), R1 | |||
MOVD a2+16(FP), R2 | |||
MOVD a3+24(FP), R3 | |||
// Get library control area (LCA). | |||
MOVW PSALAA, R8 | |||
MOVD LCA64(R8), R8 | |||
// Get function. | |||
MOVD CAA(R8), R9 | |||
MOVD EDCHPXV(R9), R9 | |||
MOVD trap+0(FP), R5 | |||
SLD $4, R5 | |||
ADD R5, R9 | |||
LMG 0(R9), R5, R6 | |||
// Restore LE stack. | |||
MOVD SAVSTACK_ASYNC(R8), R9 | |||
MOVD 0(R9), R4 | |||
MOVD $0, 0(R9) | |||
// Fill in parameter list. | |||
MOVD a4+32(FP), R12 | |||
MOVD R12, (2176+24)(R4) | |||
MOVD a5+40(FP), R12 | |||
MOVD R12, (2176+32)(R4) | |||
MOVD a6+48(FP), R12 | |||
MOVD R12, (2176+40)(R4) | |||
MOVD a7+56(FP), R12 | |||
MOVD R12, (2176+48)(R4) | |||
MOVD a8+64(FP), R12 | |||
MOVD R12, (2176+56)(R4) | |||
MOVD a9+72(FP), R12 | |||
MOVD R12, (2176+64)(R4) | |||
// Call function. | |||
LE_CALL | |||
NOPH | |||
XOR R0, R0 // Restore R0 to $0. | |||
MOVD R4, 0(R9) // Save stack pointer. | |||
MOVD R3, r1+80(FP) | |||
MOVD R0, r2+88(FP) | |||
MOVD R0, err+96(FP) | |||
MOVW R3, R4 | |||
CMP R4, $-1 | |||
BNE done | |||
BL addrerrno<>(SB) | |||
MOVWZ 0(R3), R3 | |||
MOVD R3, err+96(FP) | |||
done: | |||
BL runtime·exitsyscall(SB) | |||
RET | |||
TEXT ·syscall_rawsyscall9(SB),NOSPLIT,$0 | |||
MOVD a1+8(FP), R1 | |||
MOVD a2+16(FP), R2 | |||
MOVD a3+24(FP), R3 | |||
// Get library control area (LCA). | |||
MOVW PSALAA, R8 | |||
MOVD LCA64(R8), R8 | |||
// Get function. | |||
MOVD CAA(R8), R9 | |||
MOVD EDCHPXV(R9), R9 | |||
MOVD trap+0(FP), R5 | |||
SLD $4, R5 | |||
ADD R5, R9 | |||
LMG 0(R9), R5, R6 | |||
// Restore LE stack. | |||
MOVD SAVSTACK_ASYNC(R8), R9 | |||
MOVD 0(R9), R4 | |||
MOVD $0, 0(R9) | |||
// Fill in parameter list. | |||
MOVD a4+32(FP), R12 | |||
MOVD R12, (2176+24)(R4) | |||
MOVD a5+40(FP), R12 | |||
MOVD R12, (2176+32)(R4) | |||
MOVD a6+48(FP), R12 | |||
MOVD R12, (2176+40)(R4) | |||
MOVD a7+56(FP), R12 | |||
MOVD R12, (2176+48)(R4) | |||
MOVD a8+64(FP), R12 | |||
MOVD R12, (2176+56)(R4) | |||
MOVD a9+72(FP), R12 | |||
MOVD R12, (2176+64)(R4) | |||
// Call function. | |||
LE_CALL | |||
NOPH | |||
XOR R0, R0 // Restore R0 to $0. | |||
MOVD R4, 0(R9) // Save stack pointer. | |||
MOVD R3, r1+80(FP) | |||
MOVD R0, r2+88(FP) | |||
MOVD R0, err+96(FP) | |||
MOVW R3, R4 | |||
CMP R4, $-1 | |||
BNE done | |||
BL addrerrno<>(SB) | |||
MOVWZ 0(R3), R3 | |||
MOVD R3, err+96(FP) | |||
done: | |||
RET | |||
// func svcCall(fnptr unsafe.Pointer, argv *unsafe.Pointer, dsa *uint64) | |||
TEXT ·svcCall(SB),NOSPLIT,$0 | |||
BL runtime·save_g(SB) // Save g and stack pointer | |||
MOVW PSALAA, R8 | |||
MOVD LCA64(R8), R8 | |||
MOVD SAVSTACK_ASYNC(R8), R9 | |||
MOVD R15, 0(R9) | |||
MOVD argv+8(FP), R1 // Move function arguments into registers | |||
MOVD dsa+16(FP), g | |||
MOVD fnptr+0(FP), R15 | |||
BYTE $0x0D // Branch to function | |||
BYTE $0xEF | |||
BL runtime·load_g(SB) // Restore g and stack pointer | |||
MOVW PSALAA, R8 | |||
MOVD LCA64(R8), R8 | |||
MOVD SAVSTACK_ASYNC(R8), R9 | |||
MOVD 0(R9), R15 | |||
RET | |||
// func svcLoad(name *byte) unsafe.Pointer | |||
TEXT ·svcLoad(SB),NOSPLIT,$0 | |||
MOVD R15, R2 // Save go stack pointer | |||
MOVD name+0(FP), R0 // Move SVC args into registers | |||
MOVD $0x80000000, R1 | |||
MOVD $0, R15 | |||
BYTE $0x0A // SVC 08 LOAD | |||
BYTE $0x08 | |||
MOVW R15, R3 // Save return code from SVC | |||
MOVD R2, R15 // Restore go stack pointer | |||
CMP R3, $0 // Check SVC return code | |||
BNE error | |||
MOVD $-2, R3 // Reset last bit of entry point to zero | |||
AND R0, R3 | |||
MOVD R3, addr+8(FP) // Return entry point returned by SVC | |||
CMP R0, R3 // Check if last bit of entry point was set | |||
BNE done | |||
MOVD R15, R2 // Save go stack pointer | |||
MOVD $0, R15 // Move SVC args into registers (entry point still in r0 from SVC 08) | |||
BYTE $0x0A // SVC 09 DELETE | |||
BYTE $0x09 | |||
MOVD R2, R15 // Restore go stack pointer | |||
error: | |||
MOVD $0, addr+8(FP) // Return 0 on failure | |||
done: | |||
XOR R0, R0 // Reset r0 to 0 | |||
RET | |||
// func svcUnload(name *byte, fnptr unsafe.Pointer) int64 | |||
TEXT ·svcUnload(SB),NOSPLIT,$0 | |||
MOVD R15, R2 // Save go stack pointer | |||
MOVD name+0(FP), R0 // Move SVC args into registers | |||
MOVD addr+8(FP), R15 | |||
BYTE $0x0A // SVC 09 | |||
BYTE $0x09 | |||
XOR R0, R0 // Reset r0 to 0 | |||
MOVD R15, R1 // Save SVC return code | |||
MOVD R2, R15 // Restore go stack pointer | |||
MOVD R1, rc+0(FP) // Return SVC return code | |||
RET | |||
// func gettid() uint64 | |||
TEXT ·gettid(SB), NOSPLIT, $0 | |||
// Get library control area (LCA). | |||
MOVW PSALAA, R8 | |||
MOVD LCA64(R8), R8 | |||
// Get CEECAATHDID | |||
MOVD CAA(R8), R9 | |||
MOVD 0x3D0(R9), R9 | |||
MOVD R9, ret+0(FP) | |||
RET |
@@ -23,6 +23,7 @@ const ( | |||
HCI_CHANNEL_USER = 1 | |||
HCI_CHANNEL_MONITOR = 2 | |||
HCI_CHANNEL_CONTROL = 3 | |||
HCI_CHANNEL_LOGGING = 4 | |||
) | |||
// Socketoption Level | |||
@@ -2,6 +2,7 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
//go:build freebsd | |||
// +build freebsd | |||
package unix | |||
@@ -2,7 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris | |||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos | |||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos | |||
package unix | |||
@@ -2,8 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build aix | |||
// +build ppc | |||
//go:build aix && ppc | |||
// +build aix,ppc | |||
// Functions to access/create device major and minor numbers matching the | |||
// encoding used by AIX. | |||
@@ -2,8 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build aix | |||
// +build ppc64 | |||
//go:build aix && ppc64 | |||
// +build aix,ppc64 | |||
// Functions to access/create device major and minor numbers matching the | |||
// encoding used AIX. | |||
@@ -0,0 +1,29 @@ | |||
// Copyright 2020 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. | |||
//go:build zos && s390x | |||
// +build zos,s390x | |||
// Functions to access/create device major and minor numbers matching the | |||
// encoding used by z/OS. | |||
// | |||
// The information below is extracted and adapted from <sys/stat.h> macros. | |||
package unix | |||
// Major returns the major component of a z/OS device number. | |||
func Major(dev uint64) uint32 { | |||
return uint32((dev >> 16) & 0x0000FFFF) | |||
} | |||
// Minor returns the minor component of a z/OS device number. | |||
func Minor(dev uint64) uint32 { | |||
return uint32(dev & 0x0000FFFF) | |||
} | |||
// Mkdev returns a z/OS device number generated from the given major and minor | |||
// components. | |||
func Mkdev(major, minor uint32) uint64 { | |||
return (uint64(major) << 16) | uint64(minor) | |||
} |
@@ -2,16 +2,102 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris | |||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos | |||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos | |||
package unix | |||
import "syscall" | |||
import "unsafe" | |||
// readInt returns the size-bytes unsigned integer in native byte order at offset off. | |||
func readInt(b []byte, off, size uintptr) (u uint64, ok bool) { | |||
if len(b) < int(off+size) { | |||
return 0, false | |||
} | |||
if isBigEndian { | |||
return readIntBE(b[off:], size), true | |||
} | |||
return readIntLE(b[off:], size), true | |||
} | |||
func readIntBE(b []byte, size uintptr) uint64 { | |||
switch size { | |||
case 1: | |||
return uint64(b[0]) | |||
case 2: | |||
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 | |||
return uint64(b[1]) | uint64(b[0])<<8 | |||
case 4: | |||
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 | |||
return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24 | |||
case 8: | |||
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 | |||
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | | |||
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 | |||
default: | |||
panic("syscall: readInt with unsupported size") | |||
} | |||
} | |||
func readIntLE(b []byte, size uintptr) uint64 { | |||
switch size { | |||
case 1: | |||
return uint64(b[0]) | |||
case 2: | |||
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 | |||
return uint64(b[0]) | uint64(b[1])<<8 | |||
case 4: | |||
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 | |||
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | |||
case 8: | |||
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 | |||
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | | |||
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 | |||
default: | |||
panic("syscall: readInt with unsupported size") | |||
} | |||
} | |||
// ParseDirent parses up to max directory entries in buf, | |||
// appending the names to names. It returns the number of | |||
// bytes consumed from buf, the number of entries added | |||
// to names, and the new names slice. | |||
func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) { | |||
return syscall.ParseDirent(buf, max, names) | |||
origlen := len(buf) | |||
count = 0 | |||
for max != 0 && len(buf) > 0 { | |||
reclen, ok := direntReclen(buf) | |||
if !ok || reclen > uint64(len(buf)) { | |||
return origlen, count, names | |||
} | |||
rec := buf[:reclen] | |||
buf = buf[reclen:] | |||
ino, ok := direntIno(rec) | |||
if !ok { | |||
break | |||
} | |||
if ino == 0 { // File absent in directory. | |||
continue | |||
} | |||
const namoff = uint64(unsafe.Offsetof(Dirent{}.Name)) | |||
namlen, ok := direntNamlen(rec) | |||
if !ok || namoff+namlen > uint64(len(rec)) { | |||
break | |||
} | |||
name := rec[namoff : namoff+namlen] | |||
for i, c := range name { | |||
if c == 0 { | |||
name = name[:i] | |||
break | |||
} | |||
} | |||
// Check for useless names before allocating a string. | |||
if string(name) == "." || string(name) == ".." { | |||
continue | |||
} | |||
max-- | |||
count++ | |||
names = append(names, string(name)) | |||
} | |||
return origlen - len(buf), count, names | |||
} |
@@ -2,7 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// | |||
// +build ppc64 s390x mips mips64 | |||
//go:build armbe || arm64be || m68k || mips || mips64 || mips64p32 || ppc || ppc64 || s390 || s390x || shbe || sparc || sparc64 | |||
// +build armbe arm64be m68k mips mips64 mips64p32 ppc ppc64 s390 s390x shbe sparc sparc64 | |||
package unix | |||
@@ -2,7 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// | |||
// +build 386 amd64 amd64p32 arm arm64 ppc64le mipsle mips64le | |||
//go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh | |||
// +build 386 amd64 amd64p32 alpha arm arm64 loong64 mipsle mips64le mips64p32le nios2 ppc64le riscv riscv64 sh | |||
package unix | |||
@@ -2,7 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris | |||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos | |||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos | |||
// Unix environment variables. | |||
@@ -0,0 +1,221 @@ | |||
// Copyright 2020 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. | |||
//go:build zos && s390x | |||
// +build zos,s390x | |||
package unix | |||
import ( | |||
"sync" | |||
) | |||
// This file simulates epoll on z/OS using poll. | |||
// Analogous to epoll_event on Linux. | |||
// TODO(neeilan): Pad is because the Linux kernel expects a 96-bit struct. We never pass this to the kernel; remove? | |||
type EpollEvent struct { | |||
Events uint32 | |||
Fd int32 | |||
Pad int32 | |||
} | |||
const ( | |||
EPOLLERR = 0x8 | |||
EPOLLHUP = 0x10 | |||
EPOLLIN = 0x1 | |||
EPOLLMSG = 0x400 | |||
EPOLLOUT = 0x4 | |||
EPOLLPRI = 0x2 | |||
EPOLLRDBAND = 0x80 | |||
EPOLLRDNORM = 0x40 | |||
EPOLLWRBAND = 0x200 | |||
EPOLLWRNORM = 0x100 | |||
EPOLL_CTL_ADD = 0x1 | |||
EPOLL_CTL_DEL = 0x2 | |||
EPOLL_CTL_MOD = 0x3 | |||
// The following constants are part of the epoll API, but represent | |||
// currently unsupported functionality on z/OS. | |||
// EPOLL_CLOEXEC = 0x80000 | |||
// EPOLLET = 0x80000000 | |||
// EPOLLONESHOT = 0x40000000 | |||
// EPOLLRDHUP = 0x2000 // Typically used with edge-triggered notis | |||
// EPOLLEXCLUSIVE = 0x10000000 // Exclusive wake-up mode | |||
// EPOLLWAKEUP = 0x20000000 // Relies on Linux's BLOCK_SUSPEND capability | |||
) | |||
// TODO(neeilan): We can eliminate these epToPoll / pToEpoll calls by using identical mask values for POLL/EPOLL | |||
// constants where possible The lower 16 bits of epoll events (uint32) can fit any system poll event (int16). | |||
// epToPollEvt converts epoll event field to poll equivalent. | |||
// In epoll, Events is a 32-bit field, while poll uses 16 bits. | |||
func epToPollEvt(events uint32) int16 { | |||
var ep2p = map[uint32]int16{ | |||
EPOLLIN: POLLIN, | |||
EPOLLOUT: POLLOUT, | |||
EPOLLHUP: POLLHUP, | |||
EPOLLPRI: POLLPRI, | |||
EPOLLERR: POLLERR, | |||
} | |||
var pollEvts int16 = 0 | |||
for epEvt, pEvt := range ep2p { | |||
if (events & epEvt) != 0 { | |||
pollEvts |= pEvt | |||
} | |||
} | |||
return pollEvts | |||
} | |||
// pToEpollEvt converts 16 bit poll event bitfields to 32-bit epoll event fields. | |||
func pToEpollEvt(revents int16) uint32 { | |||
var p2ep = map[int16]uint32{ | |||
POLLIN: EPOLLIN, | |||
POLLOUT: EPOLLOUT, | |||
POLLHUP: EPOLLHUP, | |||
POLLPRI: EPOLLPRI, | |||
POLLERR: EPOLLERR, | |||
} | |||
var epollEvts uint32 = 0 | |||
for pEvt, epEvt := range p2ep { | |||
if (revents & pEvt) != 0 { | |||
epollEvts |= epEvt | |||
} | |||
} | |||
return epollEvts | |||
} | |||
// Per-process epoll implementation. | |||
type epollImpl struct { | |||
mu sync.Mutex | |||
epfd2ep map[int]*eventPoll | |||
nextEpfd int | |||
} | |||
// eventPoll holds a set of file descriptors being watched by the process. A process can have multiple epoll instances. | |||
// On Linux, this is an in-kernel data structure accessed through a fd. | |||
type eventPoll struct { | |||
mu sync.Mutex | |||
fds map[int]*EpollEvent | |||
} | |||
// epoll impl for this process. | |||
var impl epollImpl = epollImpl{ | |||
epfd2ep: make(map[int]*eventPoll), | |||
nextEpfd: 0, | |||
} | |||
func (e *epollImpl) epollcreate(size int) (epfd int, err error) { | |||
e.mu.Lock() | |||
defer e.mu.Unlock() | |||
epfd = e.nextEpfd | |||
e.nextEpfd++ | |||
e.epfd2ep[epfd] = &eventPoll{ | |||
fds: make(map[int]*EpollEvent), | |||
} | |||
return epfd, nil | |||
} | |||
func (e *epollImpl) epollcreate1(flag int) (fd int, err error) { | |||
return e.epollcreate(4) | |||
} | |||
func (e *epollImpl) epollctl(epfd int, op int, fd int, event *EpollEvent) (err error) { | |||
e.mu.Lock() | |||
defer e.mu.Unlock() | |||
ep, ok := e.epfd2ep[epfd] | |||
if !ok { | |||
return EBADF | |||
} | |||
switch op { | |||
case EPOLL_CTL_ADD: | |||
// TODO(neeilan): When we make epfds and fds disjoint, detect epoll | |||
// loops here (instances watching each other) and return ELOOP. | |||
if _, ok := ep.fds[fd]; ok { | |||
return EEXIST | |||
} | |||
ep.fds[fd] = event | |||
case EPOLL_CTL_MOD: | |||
if _, ok := ep.fds[fd]; !ok { | |||
return ENOENT | |||
} | |||
ep.fds[fd] = event | |||
case EPOLL_CTL_DEL: | |||
if _, ok := ep.fds[fd]; !ok { | |||
return ENOENT | |||
} | |||
delete(ep.fds, fd) | |||
} | |||
return nil | |||
} | |||
// Must be called while holding ep.mu | |||
func (ep *eventPoll) getFds() []int { | |||
fds := make([]int, len(ep.fds)) | |||
for fd := range ep.fds { | |||
fds = append(fds, fd) | |||
} | |||
return fds | |||
} | |||
func (e *epollImpl) epollwait(epfd int, events []EpollEvent, msec int) (n int, err error) { | |||
e.mu.Lock() // in [rare] case of concurrent epollcreate + epollwait | |||
ep, ok := e.epfd2ep[epfd] | |||
if !ok { | |||
e.mu.Unlock() | |||
return 0, EBADF | |||
} | |||
pollfds := make([]PollFd, 4) | |||
for fd, epollevt := range ep.fds { | |||
pollfds = append(pollfds, PollFd{Fd: int32(fd), Events: epToPollEvt(epollevt.Events)}) | |||
} | |||
e.mu.Unlock() | |||
n, err = Poll(pollfds, msec) | |||
if err != nil { | |||
return n, err | |||
} | |||
i := 0 | |||
for _, pFd := range pollfds { | |||
if pFd.Revents != 0 { | |||
events[i] = EpollEvent{Fd: pFd.Fd, Events: pToEpollEvt(pFd.Revents)} | |||
i++ | |||
} | |||
if i == n { | |||
break | |||
} | |||
} | |||
return n, nil | |||
} | |||
func EpollCreate(size int) (fd int, err error) { | |||
return impl.epollcreate(size) | |||
} | |||
func EpollCreate1(flag int) (fd int, err error) { | |||
return impl.epollcreate1(flag) | |||
} | |||
func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { | |||
return impl.epollctl(epfd, op, fd, event) | |||
} | |||
// Because EpollWait mutates events, the caller is expected to coordinate | |||
// concurrent access if calling with the same epfd from multiple goroutines. | |||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { | |||
return impl.epollwait(epfd, events, msec) | |||
} |
@@ -1,227 +0,0 @@ | |||
// Copyright 2017 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. | |||
// Constants that were deprecated or moved to enums in the FreeBSD headers. Keep | |||
// them here for backwards compatibility. | |||
package unix | |||
const ( | |||
IFF_SMART = 0x20 | |||
IFT_1822 = 0x2 | |||
IFT_A12MPPSWITCH = 0x82 | |||
IFT_AAL2 = 0xbb | |||
IFT_AAL5 = 0x31 | |||
IFT_ADSL = 0x5e | |||
IFT_AFLANE8023 = 0x3b | |||
IFT_AFLANE8025 = 0x3c | |||
IFT_ARAP = 0x58 | |||
IFT_ARCNET = 0x23 | |||
IFT_ARCNETPLUS = 0x24 | |||
IFT_ASYNC = 0x54 | |||
IFT_ATM = 0x25 | |||
IFT_ATMDXI = 0x69 | |||
IFT_ATMFUNI = 0x6a | |||
IFT_ATMIMA = 0x6b | |||
IFT_ATMLOGICAL = 0x50 | |||
IFT_ATMRADIO = 0xbd | |||
IFT_ATMSUBINTERFACE = 0x86 | |||
IFT_ATMVCIENDPT = 0xc2 | |||
IFT_ATMVIRTUAL = 0x95 | |||
IFT_BGPPOLICYACCOUNTING = 0xa2 | |||
IFT_BSC = 0x53 | |||
IFT_CCTEMUL = 0x3d | |||
IFT_CEPT = 0x13 | |||
IFT_CES = 0x85 | |||
IFT_CHANNEL = 0x46 | |||
IFT_CNR = 0x55 | |||
IFT_COFFEE = 0x84 | |||
IFT_COMPOSITELINK = 0x9b | |||
IFT_DCN = 0x8d | |||
IFT_DIGITALPOWERLINE = 0x8a | |||
IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba | |||
IFT_DLSW = 0x4a | |||
IFT_DOCSCABLEDOWNSTREAM = 0x80 | |||
IFT_DOCSCABLEMACLAYER = 0x7f | |||
IFT_DOCSCABLEUPSTREAM = 0x81 | |||
IFT_DS0 = 0x51 | |||
IFT_DS0BUNDLE = 0x52 | |||
IFT_DS1FDL = 0xaa | |||
IFT_DS3 = 0x1e | |||
IFT_DTM = 0x8c | |||
IFT_DVBASILN = 0xac | |||
IFT_DVBASIOUT = 0xad | |||
IFT_DVBRCCDOWNSTREAM = 0x93 | |||
IFT_DVBRCCMACLAYER = 0x92 | |||
IFT_DVBRCCUPSTREAM = 0x94 | |||
IFT_ENC = 0xf4 | |||
IFT_EON = 0x19 | |||
IFT_EPLRS = 0x57 | |||
IFT_ESCON = 0x49 | |||
IFT_ETHER = 0x6 | |||
IFT_FAITH = 0xf2 | |||
IFT_FAST = 0x7d | |||
IFT_FASTETHER = 0x3e | |||
IFT_FASTETHERFX = 0x45 | |||
IFT_FDDI = 0xf | |||
IFT_FIBRECHANNEL = 0x38 | |||
IFT_FRAMERELAYINTERCONNECT = 0x3a | |||
IFT_FRAMERELAYMPI = 0x5c | |||
IFT_FRDLCIENDPT = 0xc1 | |||
IFT_FRELAY = 0x20 | |||
IFT_FRELAYDCE = 0x2c | |||
IFT_FRF16MFRBUNDLE = 0xa3 | |||
IFT_FRFORWARD = 0x9e | |||
IFT_G703AT2MB = 0x43 | |||
IFT_G703AT64K = 0x42 | |||
IFT_GIF = 0xf0 | |||
IFT_GIGABITETHERNET = 0x75 | |||
IFT_GR303IDT = 0xb2 | |||
IFT_GR303RDT = 0xb1 | |||
IFT_H323GATEKEEPER = 0xa4 | |||
IFT_H323PROXY = 0xa5 | |||
IFT_HDH1822 = 0x3 | |||
IFT_HDLC = 0x76 | |||
IFT_HDSL2 = 0xa8 | |||
IFT_HIPERLAN2 = 0xb7 | |||
IFT_HIPPI = 0x2f | |||
IFT_HIPPIINTERFACE = 0x39 | |||
IFT_HOSTPAD = 0x5a | |||
IFT_HSSI = 0x2e | |||
IFT_HY = 0xe | |||
IFT_IBM370PARCHAN = 0x48 | |||
IFT_IDSL = 0x9a | |||
IFT_IEEE80211 = 0x47 | |||
IFT_IEEE80212 = 0x37 | |||
IFT_IEEE8023ADLAG = 0xa1 | |||
IFT_IFGSN = 0x91 | |||
IFT_IMT = 0xbe | |||
IFT_INTERLEAVE = 0x7c | |||
IFT_IP = 0x7e | |||
IFT_IPFORWARD = 0x8e | |||
IFT_IPOVERATM = 0x72 | |||
IFT_IPOVERCDLC = 0x6d | |||
IFT_IPOVERCLAW = 0x6e | |||
IFT_IPSWITCH = 0x4e | |||
IFT_IPXIP = 0xf9 | |||
IFT_ISDN = 0x3f | |||
IFT_ISDNBASIC = 0x14 | |||
IFT_ISDNPRIMARY = 0x15 | |||
IFT_ISDNS = 0x4b | |||
IFT_ISDNU = 0x4c | |||
IFT_ISO88022LLC = 0x29 | |||
IFT_ISO88023 = 0x7 | |||
IFT_ISO88024 = 0x8 | |||
IFT_ISO88025 = 0x9 | |||
IFT_ISO88025CRFPINT = 0x62 | |||
IFT_ISO88025DTR = 0x56 | |||
IFT_ISO88025FIBER = 0x73 | |||
IFT_ISO88026 = 0xa | |||
IFT_ISUP = 0xb3 | |||
IFT_L3IPXVLAN = 0x89 | |||
IFT_LAPB = 0x10 | |||
IFT_LAPD = 0x4d | |||
IFT_LAPF = 0x77 | |||
IFT_LOCALTALK = 0x2a | |||
IFT_LOOP = 0x18 | |||
IFT_MEDIAMAILOVERIP = 0x8b | |||
IFT_MFSIGLINK = 0xa7 | |||
IFT_MIOX25 = 0x26 | |||
IFT_MODEM = 0x30 | |||
IFT_MPC = 0x71 | |||
IFT_MPLS = 0xa6 | |||
IFT_MPLSTUNNEL = 0x96 | |||
IFT_MSDSL = 0x8f | |||
IFT_MVL = 0xbf | |||
IFT_MYRINET = 0x63 | |||
IFT_NFAS = 0xaf | |||
IFT_NSIP = 0x1b | |||
IFT_OPTICALCHANNEL = 0xc3 | |||
IFT_OPTICALTRANSPORT = 0xc4 | |||
IFT_OTHER = 0x1 | |||
IFT_P10 = 0xc | |||
IFT_P80 = 0xd | |||
IFT_PARA = 0x22 | |||
IFT_PFLOG = 0xf6 | |||
IFT_PFSYNC = 0xf7 | |||
IFT_PLC = 0xae | |||
IFT_POS = 0xab | |||
IFT_PPPMULTILINKBUNDLE = 0x6c | |||
IFT_PROPBWAP2MP = 0xb8 | |||
IFT_PROPCNLS = 0x59 | |||
IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5 | |||
IFT_PROPDOCSWIRELESSMACLAYER = 0xb4 | |||
IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6 | |||
IFT_PROPMUX = 0x36 | |||
IFT_PROPWIRELESSP2P = 0x9d | |||
IFT_PTPSERIAL = 0x16 | |||
IFT_PVC = 0xf1 | |||
IFT_QLLC = 0x44 | |||
IFT_RADIOMAC = 0xbc | |||
IFT_RADSL = 0x5f | |||
IFT_REACHDSL = 0xc0 | |||
IFT_RFC1483 = 0x9f | |||
IFT_RS232 = 0x21 | |||
IFT_RSRB = 0x4f | |||
IFT_SDLC = 0x11 | |||
IFT_SDSL = 0x60 | |||
IFT_SHDSL = 0xa9 | |||
IFT_SIP = 0x1f | |||
IFT_SLIP = 0x1c | |||
IFT_SMDSDXI = 0x2b | |||
IFT_SMDSICIP = 0x34 | |||
IFT_SONET = 0x27 | |||
IFT_SONETOVERHEADCHANNEL = 0xb9 | |||
IFT_SONETPATH = 0x32 | |||
IFT_SONETVT = 0x33 | |||
IFT_SRP = 0x97 | |||
IFT_SS7SIGLINK = 0x9c | |||
IFT_STACKTOSTACK = 0x6f | |||
IFT_STARLAN = 0xb | |||
IFT_STF = 0xd7 | |||
IFT_T1 = 0x12 | |||
IFT_TDLC = 0x74 | |||
IFT_TERMPAD = 0x5b | |||
IFT_TR008 = 0xb0 | |||
IFT_TRANSPHDLC = 0x7b | |||
IFT_TUNNEL = 0x83 | |||
IFT_ULTRA = 0x1d | |||
IFT_USB = 0xa0 | |||
IFT_V11 = 0x40 | |||
IFT_V35 = 0x2d | |||
IFT_V36 = 0x41 | |||
IFT_V37 = 0x78 | |||
IFT_VDSL = 0x61 | |||
IFT_VIRTUALIPADDRESS = 0x70 | |||
IFT_VOICEEM = 0x64 | |||
IFT_VOICEENCAP = 0x67 | |||
IFT_VOICEFXO = 0x65 | |||
IFT_VOICEFXS = 0x66 | |||
IFT_VOICEOVERATM = 0x98 | |||
IFT_VOICEOVERFRAMERELAY = 0x99 | |||
IFT_VOICEOVERIP = 0x68 | |||
IFT_X213 = 0x5d | |||
IFT_X25 = 0x5 | |||
IFT_X25DDN = 0x4 | |||
IFT_X25HUNTGROUP = 0x7a | |||
IFT_X25MLP = 0x79 | |||
IFT_X25PLE = 0x28 | |||
IFT_XETHER = 0x1a | |||
IPPROTO_MAXID = 0x34 | |||
IPV6_FAITH = 0x1d | |||
IP_FAITH = 0x16 | |||
MAP_NORESERVE = 0x40 | |||
MAP_RENAME = 0x20 | |||
NET_RT_MAXID = 0x6 | |||
RTF_PRCLONING = 0x10000 | |||
RTM_OLDADD = 0x9 | |||
RTM_OLDDEL = 0xa | |||
SIOCADDRT = 0x8030720a | |||
SIOCALIFADDR = 0x8118691b | |||
SIOCDELRT = 0x8030720b | |||
SIOCDLIFADDR = 0x8118691d | |||
SIOCGLIFADDR = 0xc118691c | |||
SIOCGLIFPHYADDR = 0xc118694b | |||
SIOCSLIFPHYADDR = 0x8118694a | |||
) |
@@ -1,227 +0,0 @@ | |||
// Copyright 2017 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. | |||
// Constants that were deprecated or moved to enums in the FreeBSD headers. Keep | |||
// them here for backwards compatibility. | |||
package unix | |||
const ( | |||
IFF_SMART = 0x20 | |||
IFT_1822 = 0x2 | |||
IFT_A12MPPSWITCH = 0x82 | |||
IFT_AAL2 = 0xbb | |||
IFT_AAL5 = 0x31 | |||
IFT_ADSL = 0x5e | |||
IFT_AFLANE8023 = 0x3b | |||
IFT_AFLANE8025 = 0x3c | |||
IFT_ARAP = 0x58 | |||
IFT_ARCNET = 0x23 | |||
IFT_ARCNETPLUS = 0x24 | |||
IFT_ASYNC = 0x54 | |||
IFT_ATM = 0x25 | |||
IFT_ATMDXI = 0x69 | |||
IFT_ATMFUNI = 0x6a | |||
IFT_ATMIMA = 0x6b | |||
IFT_ATMLOGICAL = 0x50 | |||
IFT_ATMRADIO = 0xbd | |||
IFT_ATMSUBINTERFACE = 0x86 | |||
IFT_ATMVCIENDPT = 0xc2 | |||
IFT_ATMVIRTUAL = 0x95 | |||
IFT_BGPPOLICYACCOUNTING = 0xa2 | |||
IFT_BSC = 0x53 | |||
IFT_CCTEMUL = 0x3d | |||
IFT_CEPT = 0x13 | |||
IFT_CES = 0x85 | |||
IFT_CHANNEL = 0x46 | |||
IFT_CNR = 0x55 | |||
IFT_COFFEE = 0x84 | |||
IFT_COMPOSITELINK = 0x9b | |||
IFT_DCN = 0x8d | |||
IFT_DIGITALPOWERLINE = 0x8a | |||
IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba | |||
IFT_DLSW = 0x4a | |||
IFT_DOCSCABLEDOWNSTREAM = 0x80 | |||
IFT_DOCSCABLEMACLAYER = 0x7f | |||
IFT_DOCSCABLEUPSTREAM = 0x81 | |||
IFT_DS0 = 0x51 | |||
IFT_DS0BUNDLE = 0x52 | |||
IFT_DS1FDL = 0xaa | |||
IFT_DS3 = 0x1e | |||
IFT_DTM = 0x8c | |||
IFT_DVBASILN = 0xac | |||
IFT_DVBASIOUT = 0xad | |||
IFT_DVBRCCDOWNSTREAM = 0x93 | |||
IFT_DVBRCCMACLAYER = 0x92 | |||
IFT_DVBRCCUPSTREAM = 0x94 | |||
IFT_ENC = 0xf4 | |||
IFT_EON = 0x19 | |||
IFT_EPLRS = 0x57 | |||
IFT_ESCON = 0x49 | |||
IFT_ETHER = 0x6 | |||
IFT_FAITH = 0xf2 | |||
IFT_FAST = 0x7d | |||
IFT_FASTETHER = 0x3e | |||
IFT_FASTETHERFX = 0x45 | |||
IFT_FDDI = 0xf | |||
IFT_FIBRECHANNEL = 0x38 | |||
IFT_FRAMERELAYINTERCONNECT = 0x3a | |||
IFT_FRAMERELAYMPI = 0x5c | |||
IFT_FRDLCIENDPT = 0xc1 | |||
IFT_FRELAY = 0x20 | |||
IFT_FRELAYDCE = 0x2c | |||
IFT_FRF16MFRBUNDLE = 0xa3 | |||
IFT_FRFORWARD = 0x9e | |||
IFT_G703AT2MB = 0x43 | |||
IFT_G703AT64K = 0x42 | |||
IFT_GIF = 0xf0 | |||
IFT_GIGABITETHERNET = 0x75 | |||
IFT_GR303IDT = 0xb2 | |||
IFT_GR303RDT = 0xb1 | |||
IFT_H323GATEKEEPER = 0xa4 | |||
IFT_H323PROXY = 0xa5 | |||
IFT_HDH1822 = 0x3 | |||
IFT_HDLC = 0x76 | |||
IFT_HDSL2 = 0xa8 | |||
IFT_HIPERLAN2 = 0xb7 | |||
IFT_HIPPI = 0x2f | |||
IFT_HIPPIINTERFACE = 0x39 | |||
IFT_HOSTPAD = 0x5a | |||
IFT_HSSI = 0x2e | |||
IFT_HY = 0xe | |||
IFT_IBM370PARCHAN = 0x48 | |||
IFT_IDSL = 0x9a | |||
IFT_IEEE80211 = 0x47 | |||
IFT_IEEE80212 = 0x37 | |||
IFT_IEEE8023ADLAG = 0xa1 | |||
IFT_IFGSN = 0x91 | |||
IFT_IMT = 0xbe | |||
IFT_INTERLEAVE = 0x7c | |||
IFT_IP = 0x7e | |||
IFT_IPFORWARD = 0x8e | |||
IFT_IPOVERATM = 0x72 | |||
IFT_IPOVERCDLC = 0x6d | |||
IFT_IPOVERCLAW = 0x6e | |||
IFT_IPSWITCH = 0x4e | |||
IFT_IPXIP = 0xf9 | |||
IFT_ISDN = 0x3f | |||
IFT_ISDNBASIC = 0x14 | |||
IFT_ISDNPRIMARY = 0x15 | |||
IFT_ISDNS = 0x4b | |||
IFT_ISDNU = 0x4c | |||
IFT_ISO88022LLC = 0x29 | |||
IFT_ISO88023 = 0x7 | |||
IFT_ISO88024 = 0x8 | |||
IFT_ISO88025 = 0x9 | |||
IFT_ISO88025CRFPINT = 0x62 | |||
IFT_ISO88025DTR = 0x56 | |||
IFT_ISO88025FIBER = 0x73 | |||
IFT_ISO88026 = 0xa | |||
IFT_ISUP = 0xb3 | |||
IFT_L3IPXVLAN = 0x89 | |||
IFT_LAPB = 0x10 | |||
IFT_LAPD = 0x4d | |||
IFT_LAPF = 0x77 | |||
IFT_LOCALTALK = 0x2a | |||
IFT_LOOP = 0x18 | |||
IFT_MEDIAMAILOVERIP = 0x8b | |||
IFT_MFSIGLINK = 0xa7 | |||
IFT_MIOX25 = 0x26 | |||
IFT_MODEM = 0x30 | |||
IFT_MPC = 0x71 | |||
IFT_MPLS = 0xa6 | |||
IFT_MPLSTUNNEL = 0x96 | |||
IFT_MSDSL = 0x8f | |||
IFT_MVL = 0xbf | |||
IFT_MYRINET = 0x63 | |||
IFT_NFAS = 0xaf | |||
IFT_NSIP = 0x1b | |||
IFT_OPTICALCHANNEL = 0xc3 | |||
IFT_OPTICALTRANSPORT = 0xc4 | |||
IFT_OTHER = 0x1 | |||
IFT_P10 = 0xc | |||
IFT_P80 = 0xd | |||
IFT_PARA = 0x22 | |||
IFT_PFLOG = 0xf6 | |||
IFT_PFSYNC = 0xf7 | |||
IFT_PLC = 0xae | |||
IFT_POS = 0xab | |||
IFT_PPPMULTILINKBUNDLE = 0x6c | |||
IFT_PROPBWAP2MP = 0xb8 | |||
IFT_PROPCNLS = 0x59 | |||
IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5 | |||
IFT_PROPDOCSWIRELESSMACLAYER = 0xb4 | |||
IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6 | |||
IFT_PROPMUX = 0x36 | |||
IFT_PROPWIRELESSP2P = 0x9d | |||
IFT_PTPSERIAL = 0x16 | |||
IFT_PVC = 0xf1 | |||
IFT_QLLC = 0x44 | |||
IFT_RADIOMAC = 0xbc | |||
IFT_RADSL = 0x5f | |||
IFT_REACHDSL = 0xc0 | |||
IFT_RFC1483 = 0x9f | |||
IFT_RS232 = 0x21 | |||
IFT_RSRB = 0x4f | |||
IFT_SDLC = 0x11 | |||
IFT_SDSL = 0x60 | |||
IFT_SHDSL = 0xa9 | |||
IFT_SIP = 0x1f | |||
IFT_SLIP = 0x1c | |||
IFT_SMDSDXI = 0x2b | |||
IFT_SMDSICIP = 0x34 | |||
IFT_SONET = 0x27 | |||
IFT_SONETOVERHEADCHANNEL = 0xb9 | |||
IFT_SONETPATH = 0x32 | |||
IFT_SONETVT = 0x33 | |||
IFT_SRP = 0x97 | |||
IFT_SS7SIGLINK = 0x9c | |||
IFT_STACKTOSTACK = 0x6f | |||
IFT_STARLAN = 0xb | |||
IFT_STF = 0xd7 | |||
IFT_T1 = 0x12 | |||
IFT_TDLC = 0x74 | |||
IFT_TERMPAD = 0x5b | |||
IFT_TR008 = 0xb0 | |||
IFT_TRANSPHDLC = 0x7b | |||
IFT_TUNNEL = 0x83 | |||
IFT_ULTRA = 0x1d | |||
IFT_USB = 0xa0 | |||
IFT_V11 = 0x40 | |||
IFT_V35 = 0x2d | |||
IFT_V36 = 0x41 | |||
IFT_V37 = 0x78 | |||
IFT_VDSL = 0x61 | |||
IFT_VIRTUALIPADDRESS = 0x70 | |||
IFT_VOICEEM = 0x64 | |||
IFT_VOICEENCAP = 0x67 | |||
IFT_VOICEFXO = 0x65 | |||
IFT_VOICEFXS = 0x66 | |||
IFT_VOICEOVERATM = 0x98 | |||
IFT_VOICEOVERFRAMERELAY = 0x99 | |||
IFT_VOICEOVERIP = 0x68 | |||
IFT_X213 = 0x5d | |||
IFT_X25 = 0x5 | |||
IFT_X25DDN = 0x4 | |||
IFT_X25HUNTGROUP = 0x7a | |||
IFT_X25MLP = 0x79 | |||
IFT_X25PLE = 0x28 | |||
IFT_XETHER = 0x1a | |||
IPPROTO_MAXID = 0x34 | |||
IPV6_FAITH = 0x1d | |||
IP_FAITH = 0x16 | |||
MAP_NORESERVE = 0x40 | |||
MAP_RENAME = 0x20 | |||
NET_RT_MAXID = 0x6 | |||
RTF_PRCLONING = 0x10000 | |||
RTM_OLDADD = 0x9 | |||
RTM_OLDDEL = 0xa | |||
SIOCADDRT = 0x8040720a | |||
SIOCALIFADDR = 0x8118691b | |||
SIOCDELRT = 0x8040720b | |||
SIOCDLIFADDR = 0x8118691d | |||
SIOCGLIFADDR = 0xc118691c | |||
SIOCGLIFPHYADDR = 0xc118694b | |||
SIOCSLIFPHYADDR = 0x8118694a | |||
) |
@@ -1,226 +0,0 @@ | |||
// Copyright 2017 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. | |||
package unix | |||
const ( | |||
IFT_1822 = 0x2 | |||
IFT_A12MPPSWITCH = 0x82 | |||
IFT_AAL2 = 0xbb | |||
IFT_AAL5 = 0x31 | |||
IFT_ADSL = 0x5e | |||
IFT_AFLANE8023 = 0x3b | |||
IFT_AFLANE8025 = 0x3c | |||
IFT_ARAP = 0x58 | |||
IFT_ARCNET = 0x23 | |||
IFT_ARCNETPLUS = 0x24 | |||
IFT_ASYNC = 0x54 | |||
IFT_ATM = 0x25 | |||
IFT_ATMDXI = 0x69 | |||
IFT_ATMFUNI = 0x6a | |||
IFT_ATMIMA = 0x6b | |||
IFT_ATMLOGICAL = 0x50 | |||
IFT_ATMRADIO = 0xbd | |||
IFT_ATMSUBINTERFACE = 0x86 | |||
IFT_ATMVCIENDPT = 0xc2 | |||
IFT_ATMVIRTUAL = 0x95 | |||
IFT_BGPPOLICYACCOUNTING = 0xa2 | |||
IFT_BSC = 0x53 | |||
IFT_CCTEMUL = 0x3d | |||
IFT_CEPT = 0x13 | |||
IFT_CES = 0x85 | |||
IFT_CHANNEL = 0x46 | |||
IFT_CNR = 0x55 | |||
IFT_COFFEE = 0x84 | |||
IFT_COMPOSITELINK = 0x9b | |||
IFT_DCN = 0x8d | |||
IFT_DIGITALPOWERLINE = 0x8a | |||
IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba | |||
IFT_DLSW = 0x4a | |||
IFT_DOCSCABLEDOWNSTREAM = 0x80 | |||
IFT_DOCSCABLEMACLAYER = 0x7f | |||
IFT_DOCSCABLEUPSTREAM = 0x81 | |||
IFT_DS0 = 0x51 | |||
IFT_DS0BUNDLE = 0x52 | |||
IFT_DS1FDL = 0xaa | |||
IFT_DS3 = 0x1e | |||
IFT_DTM = 0x8c | |||
IFT_DVBASILN = 0xac | |||
IFT_DVBASIOUT = 0xad | |||
IFT_DVBRCCDOWNSTREAM = 0x93 | |||
IFT_DVBRCCMACLAYER = 0x92 | |||
IFT_DVBRCCUPSTREAM = 0x94 | |||
IFT_ENC = 0xf4 | |||
IFT_EON = 0x19 | |||
IFT_EPLRS = 0x57 | |||
IFT_ESCON = 0x49 | |||
IFT_ETHER = 0x6 | |||
IFT_FAST = 0x7d | |||
IFT_FASTETHER = 0x3e | |||
IFT_FASTETHERFX = 0x45 | |||
IFT_FDDI = 0xf | |||
IFT_FIBRECHANNEL = 0x38 | |||
IFT_FRAMERELAYINTERCONNECT = 0x3a | |||
IFT_FRAMERELAYMPI = 0x5c | |||
IFT_FRDLCIENDPT = 0xc1 | |||
IFT_FRELAY = 0x20 | |||
IFT_FRELAYDCE = 0x2c | |||
IFT_FRF16MFRBUNDLE = 0xa3 | |||
IFT_FRFORWARD = 0x9e | |||
IFT_G703AT2MB = 0x43 | |||
IFT_G703AT64K = 0x42 | |||
IFT_GIF = 0xf0 | |||
IFT_GIGABITETHERNET = 0x75 | |||
IFT_GR303IDT = 0xb2 | |||
IFT_GR303RDT = 0xb1 | |||
IFT_H323GATEKEEPER = 0xa4 | |||
IFT_H323PROXY = 0xa5 | |||
IFT_HDH1822 = 0x3 | |||
IFT_HDLC = 0x76 | |||
IFT_HDSL2 = 0xa8 | |||
IFT_HIPERLAN2 = 0xb7 | |||
IFT_HIPPI = 0x2f | |||
IFT_HIPPIINTERFACE = 0x39 | |||
IFT_HOSTPAD = 0x5a | |||
IFT_HSSI = 0x2e | |||
IFT_HY = 0xe | |||
IFT_IBM370PARCHAN = 0x48 | |||
IFT_IDSL = 0x9a | |||
IFT_IEEE80211 = 0x47 | |||
IFT_IEEE80212 = 0x37 | |||
IFT_IEEE8023ADLAG = 0xa1 | |||
IFT_IFGSN = 0x91 | |||
IFT_IMT = 0xbe | |||
IFT_INTERLEAVE = 0x7c | |||
IFT_IP = 0x7e | |||
IFT_IPFORWARD = 0x8e | |||
IFT_IPOVERATM = 0x72 | |||
IFT_IPOVERCDLC = 0x6d | |||
IFT_IPOVERCLAW = 0x6e | |||
IFT_IPSWITCH = 0x4e | |||
IFT_ISDN = 0x3f | |||
IFT_ISDNBASIC = 0x14 | |||
IFT_ISDNPRIMARY = 0x15 | |||
IFT_ISDNS = 0x4b | |||
IFT_ISDNU = 0x4c | |||
IFT_ISO88022LLC = 0x29 | |||
IFT_ISO88023 = 0x7 | |||
IFT_ISO88024 = 0x8 | |||
IFT_ISO88025 = 0x9 | |||
IFT_ISO88025CRFPINT = 0x62 | |||
IFT_ISO88025DTR = 0x56 | |||
IFT_ISO88025FIBER = 0x73 | |||
IFT_ISO88026 = 0xa | |||
IFT_ISUP = 0xb3 | |||
IFT_L3IPXVLAN = 0x89 | |||
IFT_LAPB = 0x10 | |||
IFT_LAPD = 0x4d | |||
IFT_LAPF = 0x77 | |||
IFT_LOCALTALK = 0x2a | |||
IFT_LOOP = 0x18 | |||
IFT_MEDIAMAILOVERIP = 0x8b | |||
IFT_MFSIGLINK = 0xa7 | |||
IFT_MIOX25 = 0x26 | |||
IFT_MODEM = 0x30 | |||
IFT_MPC = 0x71 | |||
IFT_MPLS = 0xa6 | |||
IFT_MPLSTUNNEL = 0x96 | |||
IFT_MSDSL = 0x8f | |||
IFT_MVL = 0xbf | |||
IFT_MYRINET = 0x63 | |||
IFT_NFAS = 0xaf | |||
IFT_NSIP = 0x1b | |||
IFT_OPTICALCHANNEL = 0xc3 | |||
IFT_OPTICALTRANSPORT = 0xc4 | |||
IFT_OTHER = 0x1 | |||
IFT_P10 = 0xc | |||
IFT_P80 = 0xd | |||
IFT_PARA = 0x22 | |||
IFT_PFLOG = 0xf6 | |||
IFT_PFSYNC = 0xf7 | |||
IFT_PLC = 0xae | |||
IFT_POS = 0xab | |||
IFT_PPPMULTILINKBUNDLE = 0x6c | |||
IFT_PROPBWAP2MP = 0xb8 | |||
IFT_PROPCNLS = 0x59 | |||
IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5 | |||
IFT_PROPDOCSWIRELESSMACLAYER = 0xb4 | |||
IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6 | |||
IFT_PROPMUX = 0x36 | |||
IFT_PROPWIRELESSP2P = 0x9d | |||
IFT_PTPSERIAL = 0x16 | |||
IFT_PVC = 0xf1 | |||
IFT_QLLC = 0x44 | |||
IFT_RADIOMAC = 0xbc | |||
IFT_RADSL = 0x5f | |||
IFT_REACHDSL = 0xc0 | |||
IFT_RFC1483 = 0x9f | |||
IFT_RS232 = 0x21 | |||
IFT_RSRB = 0x4f | |||
IFT_SDLC = 0x11 | |||
IFT_SDSL = 0x60 | |||
IFT_SHDSL = 0xa9 | |||
IFT_SIP = 0x1f | |||
IFT_SLIP = 0x1c | |||
IFT_SMDSDXI = 0x2b | |||
IFT_SMDSICIP = 0x34 | |||
IFT_SONET = 0x27 | |||
IFT_SONETOVERHEADCHANNEL = 0xb9 | |||
IFT_SONETPATH = 0x32 | |||
IFT_SONETVT = 0x33 | |||
IFT_SRP = 0x97 | |||
IFT_SS7SIGLINK = 0x9c | |||
IFT_STACKTOSTACK = 0x6f | |||
IFT_STARLAN = 0xb | |||
IFT_STF = 0xd7 | |||
IFT_T1 = 0x12 | |||
IFT_TDLC = 0x74 | |||
IFT_TERMPAD = 0x5b | |||
IFT_TR008 = 0xb0 | |||
IFT_TRANSPHDLC = 0x7b | |||
IFT_TUNNEL = 0x83 | |||
IFT_ULTRA = 0x1d | |||
IFT_USB = 0xa0 | |||
IFT_V11 = 0x40 | |||
IFT_V35 = 0x2d | |||
IFT_V36 = 0x41 | |||
IFT_V37 = 0x78 | |||
IFT_VDSL = 0x61 | |||
IFT_VIRTUALIPADDRESS = 0x70 | |||
IFT_VOICEEM = 0x64 | |||
IFT_VOICEENCAP = 0x67 | |||
IFT_VOICEFXO = 0x65 | |||
IFT_VOICEFXS = 0x66 | |||
IFT_VOICEOVERATM = 0x98 | |||
IFT_VOICEOVERFRAMERELAY = 0x99 | |||
IFT_VOICEOVERIP = 0x68 | |||
IFT_X213 = 0x5d | |||
IFT_X25 = 0x5 | |||
IFT_X25DDN = 0x4 | |||
IFT_X25HUNTGROUP = 0x7a | |||
IFT_X25MLP = 0x79 | |||
IFT_X25PLE = 0x28 | |||
IFT_XETHER = 0x1a | |||
// missing constants on FreeBSD-11.1-RELEASE, copied from old values in ztypes_freebsd_arm.go | |||
IFF_SMART = 0x20 | |||
IFT_FAITH = 0xf2 | |||
IFT_IPXIP = 0xf9 | |||
IPPROTO_MAXID = 0x34 | |||
IPV6_FAITH = 0x1d | |||
IP_FAITH = 0x16 | |||
MAP_NORESERVE = 0x40 | |||
MAP_RENAME = 0x20 | |||
NET_RT_MAXID = 0x6 | |||
RTF_PRCLONING = 0x10000 | |||
RTM_OLDADD = 0x9 | |||
RTM_OLDDEL = 0xa | |||
SIOCADDRT = 0x8030720a | |||
SIOCALIFADDR = 0x8118691b | |||
SIOCDELRT = 0x8030720b | |||
SIOCDLIFADDR = 0x8118691d | |||
SIOCGLIFADDR = 0xc118691c | |||
SIOCGLIFPHYADDR = 0xc118694b | |||
SIOCSLIFPHYADDR = 0x8118694a | |||
) |
@@ -2,19 +2,19 @@ | |||
// 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 netbsd openbsd | |||
//go:build dragonfly || freebsd || linux || netbsd || openbsd | |||
// +build dragonfly freebsd linux netbsd openbsd | |||
package unix | |||
import "unsafe" | |||
// fcntl64Syscall is usually SYS_FCNTL, but is overridden on 32-bit Linux | |||
// systems by flock_linux_32bit.go to be SYS_FCNTL64. | |||
// systems by fcntl_linux_32bit.go to be SYS_FCNTL64. | |||
var fcntl64Syscall uintptr = SYS_FCNTL | |||
// FcntlInt performs a fcntl syscall on fd with the provided command and argument. | |||
func FcntlInt(fd uintptr, cmd, arg int) (int, error) { | |||
valptr, _, errno := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(arg)) | |||
func fcntl(fd int, cmd, arg int) (int, error) { | |||
valptr, _, errno := Syscall(fcntl64Syscall, uintptr(fd), uintptr(cmd), uintptr(arg)) | |||
var err error | |||
if errno != 0 { | |||
err = errno | |||
@@ -22,6 +22,11 @@ func FcntlInt(fd uintptr, cmd, arg int) (int, error) { | |||
return int(valptr), err | |||
} | |||
// FcntlInt performs a fcntl syscall on fd with the provided command and argument. | |||
func FcntlInt(fd uintptr, cmd, arg int) (int, error) { | |||
return fcntl(int(fd), cmd, arg) | |||
} | |||
// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command. | |||
func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error { | |||
_, _, errno := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(unsafe.Pointer(lk))) | |||
@@ -0,0 +1,24 @@ | |||
// Copyright 2019 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. | |||
package unix | |||
import "unsafe" | |||
// FcntlInt performs a fcntl syscall on fd with the provided command and argument. | |||
func FcntlInt(fd uintptr, cmd, arg int) (int, error) { | |||
return fcntl(int(fd), cmd, arg) | |||
} | |||
// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command. | |||
func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error { | |||
_, err := fcntl(int(fd), cmd, int(uintptr(unsafe.Pointer(lk)))) | |||
return err | |||
} | |||
// FcntlFstore performs a fcntl syscall for the F_PREALLOCATE command. | |||
func FcntlFstore(fd uintptr, cmd int, fstore *Fstore_t) error { | |||
_, err := fcntl(int(fd), cmd, int(uintptr(unsafe.Pointer(fstore)))) | |||
return err | |||
} |
@@ -1,9 +1,10 @@ | |||
// +build linux,386 linux,arm linux,mips linux,mipsle | |||
// Copyright 2014 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. | |||
//go:build (linux && 386) || (linux && arm) || (linux && mips) || (linux && mipsle) || (linux && ppc) | |||
// +build linux,386 linux,arm linux,mips linux,mipsle linux,ppc | |||
package unix | |||
func init() { | |||
@@ -0,0 +1,30 @@ | |||
// Copyright 2019 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. | |||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos | |||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos | |||
package unix | |||
// Set adds fd to the set fds. | |||
func (fds *FdSet) Set(fd int) { | |||
fds.Bits[fd/NFDBITS] |= (1 << (uintptr(fd) % NFDBITS)) | |||
} | |||
// Clear removes fd from the set fds. | |||
func (fds *FdSet) Clear(fd int) { | |||
fds.Bits[fd/NFDBITS] &^= (1 << (uintptr(fd) % NFDBITS)) | |||
} | |||
// IsSet returns whether fd is in the set fds. | |||
func (fds *FdSet) IsSet(fd int) bool { | |||
return fds.Bits[fd/NFDBITS]&(1<<(uintptr(fd)%NFDBITS)) != 0 | |||
} | |||
// Zero clears the set fds. | |||
func (fds *FdSet) Zero() { | |||
for i := range fds.Bits { | |||
fds.Bits[i] = 0 | |||
} | |||
} |
@@ -0,0 +1,164 @@ | |||
// Copyright 2020 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. | |||
//go:build zos && s390x | |||
// +build zos,s390x | |||
package unix | |||
import ( | |||
"unsafe" | |||
) | |||
// This file simulates fstatfs on z/OS using fstatvfs and w_getmntent. | |||
func Fstatfs(fd int, stat *Statfs_t) (err error) { | |||
var stat_v Statvfs_t | |||
err = Fstatvfs(fd, &stat_v) | |||
if err == nil { | |||
// populate stat | |||
stat.Type = 0 | |||
stat.Bsize = stat_v.Bsize | |||
stat.Blocks = stat_v.Blocks | |||
stat.Bfree = stat_v.Bfree | |||
stat.Bavail = stat_v.Bavail | |||
stat.Files = stat_v.Files | |||
stat.Ffree = stat_v.Ffree | |||
stat.Fsid = stat_v.Fsid | |||
stat.Namelen = stat_v.Namemax | |||
stat.Frsize = stat_v.Frsize | |||
stat.Flags = stat_v.Flag | |||
for passn := 0; passn < 5; passn++ { | |||
switch passn { | |||
case 0: | |||
err = tryGetmntent64(stat) | |||
break | |||
case 1: | |||
err = tryGetmntent128(stat) | |||
break | |||
case 2: | |||
err = tryGetmntent256(stat) | |||
break | |||
case 3: | |||
err = tryGetmntent512(stat) | |||
break | |||
case 4: | |||
err = tryGetmntent1024(stat) | |||
break | |||
default: | |||
break | |||
} | |||
//proceed to return if: err is nil (found), err is nonnil but not ERANGE (another error occurred) | |||
if err == nil || err != nil && err != ERANGE { | |||
break | |||
} | |||
} | |||
} | |||
return err | |||
} | |||
func tryGetmntent64(stat *Statfs_t) (err error) { | |||
var mnt_ent_buffer struct { | |||
header W_Mnth | |||
filesys_info [64]W_Mntent | |||
} | |||
var buffer_size int = int(unsafe.Sizeof(mnt_ent_buffer)) | |||
fs_count, err := W_Getmntent((*byte)(unsafe.Pointer(&mnt_ent_buffer)), buffer_size) | |||
if err != nil { | |||
return err | |||
} | |||
err = ERANGE //return ERANGE if no match is found in this batch | |||
for i := 0; i < fs_count; i++ { | |||
if stat.Fsid == uint64(mnt_ent_buffer.filesys_info[i].Dev) { | |||
stat.Type = uint32(mnt_ent_buffer.filesys_info[i].Fstname[0]) | |||
err = nil | |||
break | |||
} | |||
} | |||
return err | |||
} | |||
func tryGetmntent128(stat *Statfs_t) (err error) { | |||
var mnt_ent_buffer struct { | |||
header W_Mnth | |||
filesys_info [128]W_Mntent | |||
} | |||
var buffer_size int = int(unsafe.Sizeof(mnt_ent_buffer)) | |||
fs_count, err := W_Getmntent((*byte)(unsafe.Pointer(&mnt_ent_buffer)), buffer_size) | |||
if err != nil { | |||
return err | |||
} | |||
err = ERANGE //return ERANGE if no match is found in this batch | |||
for i := 0; i < fs_count; i++ { | |||
if stat.Fsid == uint64(mnt_ent_buffer.filesys_info[i].Dev) { | |||
stat.Type = uint32(mnt_ent_buffer.filesys_info[i].Fstname[0]) | |||
err = nil | |||
break | |||
} | |||
} | |||
return err | |||
} | |||
func tryGetmntent256(stat *Statfs_t) (err error) { | |||
var mnt_ent_buffer struct { | |||
header W_Mnth | |||
filesys_info [256]W_Mntent | |||
} | |||
var buffer_size int = int(unsafe.Sizeof(mnt_ent_buffer)) | |||
fs_count, err := W_Getmntent((*byte)(unsafe.Pointer(&mnt_ent_buffer)), buffer_size) | |||
if err != nil { | |||
return err | |||
} | |||
err = ERANGE //return ERANGE if no match is found in this batch | |||
for i := 0; i < fs_count; i++ { | |||
if stat.Fsid == uint64(mnt_ent_buffer.filesys_info[i].Dev) { | |||
stat.Type = uint32(mnt_ent_buffer.filesys_info[i].Fstname[0]) | |||
err = nil | |||
break | |||
} | |||
} | |||
return err | |||
} | |||
func tryGetmntent512(stat *Statfs_t) (err error) { | |||
var mnt_ent_buffer struct { | |||
header W_Mnth | |||
filesys_info [512]W_Mntent | |||
} | |||
var buffer_size int = int(unsafe.Sizeof(mnt_ent_buffer)) | |||
fs_count, err := W_Getmntent((*byte)(unsafe.Pointer(&mnt_ent_buffer)), buffer_size) | |||
if err != nil { | |||
return err | |||
} | |||
err = ERANGE //return ERANGE if no match is found in this batch | |||
for i := 0; i < fs_count; i++ { | |||
if stat.Fsid == uint64(mnt_ent_buffer.filesys_info[i].Dev) { | |||
stat.Type = uint32(mnt_ent_buffer.filesys_info[i].Fstname[0]) | |||
err = nil | |||
break | |||
} | |||
} | |||
return err | |||
} | |||
func tryGetmntent1024(stat *Statfs_t) (err error) { | |||
var mnt_ent_buffer struct { | |||
header W_Mnth | |||
filesys_info [1024]W_Mntent | |||
} | |||
var buffer_size int = int(unsafe.Sizeof(mnt_ent_buffer)) | |||
fs_count, err := W_Getmntent((*byte)(unsafe.Pointer(&mnt_ent_buffer)), buffer_size) | |||
if err != nil { | |||
return err | |||
} | |||
err = ERANGE //return ERANGE if no match is found in this batch | |||
for i := 0; i < fs_count; i++ { | |||
if stat.Fsid == uint64(mnt_ent_buffer.filesys_info[i].Dev) { | |||
stat.Type = uint32(mnt_ent_buffer.filesys_info[i].Fstname[0]) | |||
err = nil | |||
break | |||
} | |||
} | |||
return err | |||
} |
@@ -2,8 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build gccgo | |||
// +build !aix | |||
//go:build gccgo && !aix && !hurd | |||
// +build gccgo,!aix,!hurd | |||
package unix | |||
@@ -12,10 +12,8 @@ import "syscall" | |||
// We can't use the gc-syntax .s files for gccgo. On the plus side | |||
// much of the functionality can be written directly in Go. | |||
//extern gccgoRealSyscallNoError | |||
func realSyscallNoError(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r uintptr) | |||
//extern gccgoRealSyscall | |||
func realSyscall(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r, errno uintptr) | |||
func SyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr) { | |||
@@ -2,8 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build gccgo | |||
// +build !aix | |||
//go:build gccgo && !aix && !hurd | |||
// +build gccgo,!aix,!hurd | |||
#include <errno.h> | |||
#include <stdint.h> | |||
@@ -21,6 +21,9 @@ struct ret { | |||
uintptr_t err; | |||
}; | |||
struct ret gccgoRealSyscall(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9) | |||
__asm__(GOSYM_PREFIX GOPKGPATH ".realSyscall"); | |||
struct ret | |||
gccgoRealSyscall(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9) | |||
{ | |||
@@ -32,6 +35,9 @@ gccgoRealSyscall(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintp | |||
return r; | |||
} | |||
uintptr_t gccgoRealSyscallNoError(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9) | |||
__asm__(GOSYM_PREFIX GOPKGPATH ".realSyscallNoError"); | |||
uintptr_t | |||
gccgoRealSyscallNoError(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9) | |||
{ | |||
@@ -2,6 +2,7 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
//go:build gccgo && linux && amd64 | |||
// +build gccgo,linux,amd64 | |||
package unix | |||
@@ -0,0 +1,142 @@ | |||
// Copyright 2021 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. | |||
//go:build linux | |||
// +build linux | |||
package unix | |||
import ( | |||
"unsafe" | |||
) | |||
// Helpers for dealing with ifreq since it contains a union and thus requires a | |||
// lot of unsafe.Pointer casts to use properly. | |||
// An Ifreq is a type-safe wrapper around the raw ifreq struct. An Ifreq | |||
// contains an interface name and a union of arbitrary data which can be | |||
// accessed using the Ifreq's methods. To create an Ifreq, use the NewIfreq | |||
// function. | |||
// | |||
// Use the Name method to access the stored interface name. The union data | |||
// fields can be get and set using the following methods: | |||
// - Uint16/SetUint16: flags | |||
// - Uint32/SetUint32: ifindex, metric, mtu | |||
type Ifreq struct{ raw ifreq } | |||
// NewIfreq creates an Ifreq with the input network interface name after | |||
// validating the name does not exceed IFNAMSIZ-1 (trailing NULL required) | |||
// bytes. | |||
func NewIfreq(name string) (*Ifreq, error) { | |||
// Leave room for terminating NULL byte. | |||
if len(name) >= IFNAMSIZ { | |||
return nil, EINVAL | |||
} | |||
var ifr ifreq | |||
copy(ifr.Ifrn[:], name) | |||
return &Ifreq{raw: ifr}, nil | |||
} | |||
// TODO(mdlayher): get/set methods for hardware address sockaddr, char array, etc. | |||
// Name returns the interface name associated with the Ifreq. | |||
func (ifr *Ifreq) Name() string { | |||
return ByteSliceToString(ifr.raw.Ifrn[:]) | |||
} | |||
// According to netdevice(7), only AF_INET addresses are returned for numerous | |||
// sockaddr ioctls. For convenience, we expose these as Inet4Addr since the Port | |||
// field and other data is always empty. | |||
// Inet4Addr returns the Ifreq union data from an embedded sockaddr as a C | |||
// in_addr/Go []byte (4-byte IPv4 address) value. If the sockaddr family is not | |||
// AF_INET, an error is returned. | |||
func (ifr *Ifreq) Inet4Addr() ([]byte, error) { | |||
raw := *(*RawSockaddrInet4)(unsafe.Pointer(&ifr.raw.Ifru[:SizeofSockaddrInet4][0])) | |||
if raw.Family != AF_INET { | |||
// Cannot safely interpret raw.Addr bytes as an IPv4 address. | |||
return nil, EINVAL | |||
} | |||
return raw.Addr[:], nil | |||
} | |||
// SetInet4Addr sets a C in_addr/Go []byte (4-byte IPv4 address) value in an | |||
// embedded sockaddr within the Ifreq's union data. v must be 4 bytes in length | |||
// or an error will be returned. | |||
func (ifr *Ifreq) SetInet4Addr(v []byte) error { | |||
if len(v) != 4 { | |||
return EINVAL | |||
} | |||
var addr [4]byte | |||
copy(addr[:], v) | |||
ifr.clear() | |||
*(*RawSockaddrInet4)( | |||
unsafe.Pointer(&ifr.raw.Ifru[:SizeofSockaddrInet4][0]), | |||
) = RawSockaddrInet4{ | |||
// Always set IP family as ioctls would require it anyway. | |||
Family: AF_INET, | |||
Addr: addr, | |||
} | |||
return nil | |||
} | |||
// Uint16 returns the Ifreq union data as a C short/Go uint16 value. | |||
func (ifr *Ifreq) Uint16() uint16 { | |||
return *(*uint16)(unsafe.Pointer(&ifr.raw.Ifru[:2][0])) | |||
} | |||
// SetUint16 sets a C short/Go uint16 value as the Ifreq's union data. | |||
func (ifr *Ifreq) SetUint16(v uint16) { | |||
ifr.clear() | |||
*(*uint16)(unsafe.Pointer(&ifr.raw.Ifru[:2][0])) = v | |||
} | |||
// Uint32 returns the Ifreq union data as a C int/Go uint32 value. | |||
func (ifr *Ifreq) Uint32() uint32 { | |||
return *(*uint32)(unsafe.Pointer(&ifr.raw.Ifru[:4][0])) | |||
} | |||
// SetUint32 sets a C int/Go uint32 value as the Ifreq's union data. | |||
func (ifr *Ifreq) SetUint32(v uint32) { | |||
ifr.clear() | |||
*(*uint32)(unsafe.Pointer(&ifr.raw.Ifru[:4][0])) = v | |||
} | |||
// clear zeroes the ifreq's union field to prevent trailing garbage data from | |||
// being sent to the kernel if an ifreq is reused. | |||
func (ifr *Ifreq) clear() { | |||
for i := range ifr.raw.Ifru { | |||
ifr.raw.Ifru[i] = 0 | |||
} | |||
} | |||
// TODO(mdlayher): export as IfreqData? For now we can provide helpers such as | |||
// IoctlGetEthtoolDrvinfo which use these APIs under the hood. | |||
// An ifreqData is an Ifreq which carries pointer data. To produce an ifreqData, | |||
// use the Ifreq.withData method. | |||
type ifreqData struct { | |||
name [IFNAMSIZ]byte | |||
// A type separate from ifreq is required in order to comply with the | |||
// unsafe.Pointer rules since the "pointer-ness" of data would not be | |||
// preserved if it were cast into the byte array of a raw ifreq. | |||
data unsafe.Pointer | |||
// Pad to the same size as ifreq. | |||
_ [len(ifreq{}.Ifru) - SizeofPtr]byte | |||
} | |||
// withData produces an ifreqData with the pointer p set for ioctls which require | |||
// arbitrary pointer data. | |||
func (ifr Ifreq) withData(p unsafe.Pointer) ifreqData { | |||
return ifreqData{ | |||
name: ifr.raw.Ifrn, | |||
data: p, | |||
} | |||
} |
@@ -2,11 +2,33 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris | |||
//go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris | |||
// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris | |||
package unix | |||
import "runtime" | |||
import ( | |||
"runtime" | |||
"unsafe" | |||
) | |||
// ioctl itself should not be exposed directly, but additional get/set | |||
// functions for specific types are permissible. | |||
// IoctlSetInt performs an ioctl operation which sets an integer value | |||
// on fd, using the specified request number. | |||
func IoctlSetInt(fd int, req uint, value int) error { | |||
return ioctl(fd, req, uintptr(value)) | |||
} | |||
// IoctlSetPointerInt performs an ioctl operation which sets an | |||
// integer value on fd, using the specified request number. The ioctl | |||
// argument is called with a pointer to the integer value, rather than | |||
// passing the integer value directly. | |||
func IoctlSetPointerInt(fd int, req uint, value int) error { | |||
v := int32(value) | |||
return ioctl(fd, req, uintptr(unsafe.Pointer(&v))) | |||
} | |||
// IoctlSetWinsize performs an ioctl on fd with a *Winsize argument. | |||
// | |||
@@ -14,7 +36,7 @@ import "runtime" | |||
func IoctlSetWinsize(fd int, req uint, value *Winsize) error { | |||
// TODO: if we get the chance, remove the req parameter and | |||
// hardcode TIOCSWINSZ. | |||
err := ioctlSetWinsize(fd, req, value) | |||
err := ioctl(fd, req, uintptr(unsafe.Pointer(value))) | |||
runtime.KeepAlive(value) | |||
return err | |||
} | |||
@@ -24,7 +46,30 @@ func IoctlSetWinsize(fd int, req uint, value *Winsize) error { | |||
// The req value will usually be TCSETA or TIOCSETA. | |||
func IoctlSetTermios(fd int, req uint, value *Termios) error { | |||
// TODO: if we get the chance, remove the req parameter. | |||
err := ioctlSetTermios(fd, req, value) | |||
err := ioctl(fd, req, uintptr(unsafe.Pointer(value))) | |||
runtime.KeepAlive(value) | |||
return err | |||
} | |||
// IoctlGetInt performs an ioctl operation which gets an integer value | |||
// from fd, using the specified request number. | |||
// | |||
// A few ioctl requests use the return value as an output parameter; | |||
// for those, IoctlRetInt should be used instead of this function. | |||
func IoctlGetInt(fd int, req uint) (int, error) { | |||
var value int | |||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) | |||
return value, err | |||
} | |||
func IoctlGetWinsize(fd int, req uint) (*Winsize, error) { | |||
var value Winsize | |||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) | |||
return &value, err | |||
} | |||
func IoctlGetTermios(fd int, req uint) (*Termios, error) { | |||
var value Termios | |||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) | |||
return &value, err | |||
} |
@@ -0,0 +1,233 @@ | |||
// Copyright 2021 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. | |||
package unix | |||
import "unsafe" | |||
// IoctlRetInt performs an ioctl operation specified by req on a device | |||
// associated with opened file descriptor fd, and returns a non-negative | |||
// integer that is returned by the ioctl syscall. | |||
func IoctlRetInt(fd int, req uint) (int, error) { | |||
ret, _, err := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), 0) | |||
if err != 0 { | |||
return 0, err | |||
} | |||
return int(ret), nil | |||
} | |||
func IoctlGetUint32(fd int, req uint) (uint32, error) { | |||
var value uint32 | |||
err := ioctlPtr(fd, req, unsafe.Pointer(&value)) | |||
return value, err | |||
} | |||
func IoctlGetRTCTime(fd int) (*RTCTime, error) { | |||
var value RTCTime | |||
err := ioctlPtr(fd, RTC_RD_TIME, unsafe.Pointer(&value)) | |||
return &value, err | |||
} | |||
func IoctlSetRTCTime(fd int, value *RTCTime) error { | |||
return ioctlPtr(fd, RTC_SET_TIME, unsafe.Pointer(value)) | |||
} | |||
func IoctlGetRTCWkAlrm(fd int) (*RTCWkAlrm, error) { | |||
var value RTCWkAlrm | |||
err := ioctlPtr(fd, RTC_WKALM_RD, unsafe.Pointer(&value)) | |||
return &value, err | |||
} | |||
func IoctlSetRTCWkAlrm(fd int, value *RTCWkAlrm) error { | |||
return ioctlPtr(fd, RTC_WKALM_SET, unsafe.Pointer(value)) | |||
} | |||
// IoctlGetEthtoolDrvinfo fetches ethtool driver information for the network | |||
// device specified by ifname. | |||
func IoctlGetEthtoolDrvinfo(fd int, ifname string) (*EthtoolDrvinfo, error) { | |||
ifr, err := NewIfreq(ifname) | |||
if err != nil { | |||
return nil, err | |||
} | |||
value := EthtoolDrvinfo{Cmd: ETHTOOL_GDRVINFO} | |||
ifrd := ifr.withData(unsafe.Pointer(&value)) | |||
err = ioctlIfreqData(fd, SIOCETHTOOL, &ifrd) | |||
return &value, err | |||
} | |||
// IoctlGetWatchdogInfo fetches information about a watchdog device from the | |||
// Linux watchdog API. For more information, see: | |||
// https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html. | |||
func IoctlGetWatchdogInfo(fd int) (*WatchdogInfo, error) { | |||
var value WatchdogInfo | |||
err := ioctlPtr(fd, WDIOC_GETSUPPORT, unsafe.Pointer(&value)) | |||
return &value, err | |||
} | |||
// IoctlWatchdogKeepalive issues a keepalive ioctl to a watchdog device. For | |||
// more information, see: | |||
// https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html. | |||
func IoctlWatchdogKeepalive(fd int) error { | |||
// arg is ignored and not a pointer, so ioctl is fine instead of ioctlPtr. | |||
return ioctl(fd, WDIOC_KEEPALIVE, 0) | |||
} | |||
// IoctlFileCloneRange performs an FICLONERANGE ioctl operation to clone the | |||
// range of data conveyed in value to the file associated with the file | |||
// descriptor destFd. See the ioctl_ficlonerange(2) man page for details. | |||
func IoctlFileCloneRange(destFd int, value *FileCloneRange) error { | |||
return ioctlPtr(destFd, FICLONERANGE, unsafe.Pointer(value)) | |||
} | |||
// IoctlFileClone performs an FICLONE ioctl operation to clone the entire file | |||
// associated with the file description srcFd to the file associated with the | |||
// file descriptor destFd. See the ioctl_ficlone(2) man page for details. | |||
func IoctlFileClone(destFd, srcFd int) error { | |||
return ioctl(destFd, FICLONE, uintptr(srcFd)) | |||
} | |||
type FileDedupeRange struct { | |||
Src_offset uint64 | |||
Src_length uint64 | |||
Reserved1 uint16 | |||
Reserved2 uint32 | |||
Info []FileDedupeRangeInfo | |||
} | |||
type FileDedupeRangeInfo struct { | |||
Dest_fd int64 | |||
Dest_offset uint64 | |||
Bytes_deduped uint64 | |||
Status int32 | |||
Reserved uint32 | |||
} | |||
// IoctlFileDedupeRange performs an FIDEDUPERANGE ioctl operation to share the | |||
// range of data conveyed in value from the file associated with the file | |||
// descriptor srcFd to the value.Info destinations. See the | |||
// ioctl_fideduperange(2) man page for details. | |||
func IoctlFileDedupeRange(srcFd int, value *FileDedupeRange) error { | |||
buf := make([]byte, SizeofRawFileDedupeRange+ | |||
len(value.Info)*SizeofRawFileDedupeRangeInfo) | |||
rawrange := (*RawFileDedupeRange)(unsafe.Pointer(&buf[0])) | |||
rawrange.Src_offset = value.Src_offset | |||
rawrange.Src_length = value.Src_length | |||
rawrange.Dest_count = uint16(len(value.Info)) | |||
rawrange.Reserved1 = value.Reserved1 | |||
rawrange.Reserved2 = value.Reserved2 | |||
for i := range value.Info { | |||
rawinfo := (*RawFileDedupeRangeInfo)(unsafe.Pointer( | |||
uintptr(unsafe.Pointer(&buf[0])) + uintptr(SizeofRawFileDedupeRange) + | |||
uintptr(i*SizeofRawFileDedupeRangeInfo))) | |||
rawinfo.Dest_fd = value.Info[i].Dest_fd | |||
rawinfo.Dest_offset = value.Info[i].Dest_offset | |||
rawinfo.Bytes_deduped = value.Info[i].Bytes_deduped | |||
rawinfo.Status = value.Info[i].Status | |||
rawinfo.Reserved = value.Info[i].Reserved | |||
} | |||
err := ioctlPtr(srcFd, FIDEDUPERANGE, unsafe.Pointer(&buf[0])) | |||
// Output | |||
for i := range value.Info { | |||
rawinfo := (*RawFileDedupeRangeInfo)(unsafe.Pointer( | |||
uintptr(unsafe.Pointer(&buf[0])) + uintptr(SizeofRawFileDedupeRange) + | |||
uintptr(i*SizeofRawFileDedupeRangeInfo))) | |||
value.Info[i].Dest_fd = rawinfo.Dest_fd | |||
value.Info[i].Dest_offset = rawinfo.Dest_offset | |||
value.Info[i].Bytes_deduped = rawinfo.Bytes_deduped | |||
value.Info[i].Status = rawinfo.Status | |||
value.Info[i].Reserved = rawinfo.Reserved | |||
} | |||
return err | |||
} | |||
func IoctlHIDGetDesc(fd int, value *HIDRawReportDescriptor) error { | |||
return ioctlPtr(fd, HIDIOCGRDESC, unsafe.Pointer(value)) | |||
} | |||
func IoctlHIDGetRawInfo(fd int) (*HIDRawDevInfo, error) { | |||
var value HIDRawDevInfo | |||
err := ioctlPtr(fd, HIDIOCGRAWINFO, unsafe.Pointer(&value)) | |||
return &value, err | |||
} | |||
func IoctlHIDGetRawName(fd int) (string, error) { | |||
var value [_HIDIOCGRAWNAME_LEN]byte | |||
err := ioctlPtr(fd, _HIDIOCGRAWNAME, unsafe.Pointer(&value[0])) | |||
return ByteSliceToString(value[:]), err | |||
} | |||
func IoctlHIDGetRawPhys(fd int) (string, error) { | |||
var value [_HIDIOCGRAWPHYS_LEN]byte | |||
err := ioctlPtr(fd, _HIDIOCGRAWPHYS, unsafe.Pointer(&value[0])) | |||
return ByteSliceToString(value[:]), err | |||
} | |||
func IoctlHIDGetRawUniq(fd int) (string, error) { | |||
var value [_HIDIOCGRAWUNIQ_LEN]byte | |||
err := ioctlPtr(fd, _HIDIOCGRAWUNIQ, unsafe.Pointer(&value[0])) | |||
return ByteSliceToString(value[:]), err | |||
} | |||
// IoctlIfreq performs an ioctl using an Ifreq structure for input and/or | |||
// output. See the netdevice(7) man page for details. | |||
func IoctlIfreq(fd int, req uint, value *Ifreq) error { | |||
// It is possible we will add more fields to *Ifreq itself later to prevent | |||
// misuse, so pass the raw *ifreq directly. | |||
return ioctlPtr(fd, req, unsafe.Pointer(&value.raw)) | |||
} | |||
// TODO(mdlayher): export if and when IfreqData is exported. | |||
// ioctlIfreqData performs an ioctl using an ifreqData structure for input | |||
// and/or output. See the netdevice(7) man page for details. | |||
func ioctlIfreqData(fd int, req uint, value *ifreqData) error { | |||
// The memory layout of IfreqData (type-safe) and ifreq (not type-safe) are | |||
// identical so pass *IfreqData directly. | |||
return ioctlPtr(fd, req, unsafe.Pointer(value)) | |||
} | |||
// IoctlKCMClone attaches a new file descriptor to a multiplexor by cloning an | |||
// existing KCM socket, returning a structure containing the file descriptor of | |||
// the new socket. | |||
func IoctlKCMClone(fd int) (*KCMClone, error) { | |||
var info KCMClone | |||
if err := ioctlPtr(fd, SIOCKCMCLONE, unsafe.Pointer(&info)); err != nil { | |||
return nil, err | |||
} | |||
return &info, nil | |||
} | |||
// IoctlKCMAttach attaches a TCP socket and associated BPF program file | |||
// descriptor to a multiplexor. | |||
func IoctlKCMAttach(fd int, info KCMAttach) error { | |||
return ioctlPtr(fd, SIOCKCMATTACH, unsafe.Pointer(&info)) | |||
} | |||
// IoctlKCMUnattach unattaches a TCP socket file descriptor from a multiplexor. | |||
func IoctlKCMUnattach(fd int, info KCMUnattach) error { | |||
return ioctlPtr(fd, SIOCKCMUNATTACH, unsafe.Pointer(&info)) | |||
} | |||
// IoctlLoopGetStatus64 gets the status of the loop device associated with the | |||
// file descriptor fd using the LOOP_GET_STATUS64 operation. | |||
func IoctlLoopGetStatus64(fd int) (*LoopInfo64, error) { | |||
var value LoopInfo64 | |||
if err := ioctlPtr(fd, LOOP_GET_STATUS64, unsafe.Pointer(&value)); err != nil { | |||
return nil, err | |||
} | |||
return &value, nil | |||
} | |||
// IoctlLoopSetStatus64 sets the status of the loop device associated with the | |||
// file descriptor fd using the LOOP_SET_STATUS64 operation. | |||
func IoctlLoopSetStatus64(fd int, value *LoopInfo64) error { | |||
return ioctlPtr(fd, LOOP_SET_STATUS64, unsafe.Pointer(value)) | |||
} |
@@ -0,0 +1,74 @@ | |||
// Copyright 2020 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. | |||
//go:build zos && s390x | |||
// +build zos,s390x | |||
package unix | |||
import ( | |||
"runtime" | |||
"unsafe" | |||
) | |||
// ioctl itself should not be exposed directly, but additional get/set | |||
// functions for specific types are permissible. | |||
// IoctlSetInt performs an ioctl operation which sets an integer value | |||
// on fd, using the specified request number. | |||
func IoctlSetInt(fd int, req uint, value int) error { | |||
return ioctl(fd, req, uintptr(value)) | |||
} | |||
// IoctlSetWinsize performs an ioctl on fd with a *Winsize argument. | |||
// | |||
// To change fd's window size, the req argument should be TIOCSWINSZ. | |||
func IoctlSetWinsize(fd int, req uint, value *Winsize) error { | |||
// TODO: if we get the chance, remove the req parameter and | |||
// hardcode TIOCSWINSZ. | |||
err := ioctl(fd, req, uintptr(unsafe.Pointer(value))) | |||
runtime.KeepAlive(value) | |||
return err | |||
} | |||
// IoctlSetTermios performs an ioctl on fd with a *Termios. | |||
// | |||
// The req value is expected to be TCSETS, TCSETSW, or TCSETSF | |||
func IoctlSetTermios(fd int, req uint, value *Termios) error { | |||
if (req != TCSETS) && (req != TCSETSW) && (req != TCSETSF) { | |||
return ENOSYS | |||
} | |||
err := Tcsetattr(fd, int(req), value) | |||
runtime.KeepAlive(value) | |||
return err | |||
} | |||
// IoctlGetInt performs an ioctl operation which gets an integer value | |||
// from fd, using the specified request number. | |||
// | |||
// A few ioctl requests use the return value as an output parameter; | |||
// for those, IoctlRetInt should be used instead of this function. | |||
func IoctlGetInt(fd int, req uint) (int, error) { | |||
var value int | |||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) | |||
return value, err | |||
} | |||
func IoctlGetWinsize(fd int, req uint) (*Winsize, error) { | |||
var value Winsize | |||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) | |||
return &value, err | |||
} | |||
// IoctlGetTermios performs an ioctl on fd with a *Termios. | |||
// | |||
// The req value is expected to be TCGETS | |||
func IoctlGetTermios(fd int, req uint) (*Termios, error) { | |||
var value Termios | |||
if req != TCGETS { | |||
return &value, ENOSYS | |||
} | |||
err := Tcgetattr(fd, &value) | |||
return &value, err | |||
} |
@@ -17,6 +17,7 @@ mksysctl="" | |||
zsysctl="zsysctl_$GOOSARCH.go" | |||
mksysnum= | |||
mktypes= | |||
mkasm= | |||
run="sh" | |||
cmd="" | |||
@@ -45,11 +46,11 @@ case "$#" in | |||
exit 2 | |||
esac | |||
if [[ "$GOOS" = "linux" ]] && [[ "$GOARCH" != "sparc64" ]]; then | |||
# Use then new build system | |||
if [[ "$GOOS" = "linux" ]]; then | |||
# Use the Docker-based build system | |||
# Files generated through docker (use $cmd so you can Ctl-C the build or run) | |||
$cmd docker build --tag generate:$GOOS $GOOS | |||
$cmd docker run --interactive --tty --volume $(dirname "$(readlink -f "$0")"):/build generate:$GOOS | |||
$cmd docker run --interactive --tty --volume $(cd -- "$(dirname -- "$0")/.." && /bin/pwd):/build generate:$GOOS | |||
exit | |||
fi | |||
@@ -61,116 +62,156 @@ _* | *_ | _) | |||
;; | |||
aix_ppc) | |||
mkerrors="$mkerrors -maix32" | |||
mksyscall="./mksyscall_aix_ppc.pl -aix" | |||
mksyscall="go run mksyscall_aix_ppc.go -aix" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
;; | |||
aix_ppc64) | |||
mkerrors="$mkerrors -maix64" | |||
mksyscall="./mksyscall_aix_ppc64.pl -aix" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
;; | |||
darwin_386) | |||
mkerrors="$mkerrors -m32" | |||
mksyscall="go run mksyscall.go -l32" | |||
mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk macosx)/usr/include/sys/syscall.h" | |||
mksyscall="go run mksyscall_aix_ppc64.go -aix" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
;; | |||
darwin_amd64) | |||
mkerrors="$mkerrors -m64" | |||
mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk macosx)/usr/include/sys/syscall.h" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
;; | |||
darwin_arm) | |||
mkerrors="$mkerrors" | |||
mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk iphoneos)/usr/include/sys/syscall.h" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
mkasm="go run mkasm.go" | |||
;; | |||
darwin_arm64) | |||
mkerrors="$mkerrors -m64" | |||
mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk iphoneos)/usr/include/sys/syscall.h" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
mkasm="go run mkasm.go" | |||
;; | |||
dragonfly_amd64) | |||
mkerrors="$mkerrors -m64" | |||
mksyscall="go run mksyscall.go -dragonfly" | |||
mksysnum="curl -s 'http://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/kern/syscalls.master' | ./mksysnum_dragonfly.pl" | |||
mksysnum="go run mksysnum.go 'https://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/kern/syscalls.master'" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
;; | |||
freebsd_386) | |||
mkerrors="$mkerrors -m32" | |||
mksyscall="go run mksyscall.go -l32" | |||
mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl" | |||
mksysnum="go run mksysnum.go 'https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12'" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
;; | |||
freebsd_amd64) | |||
mkerrors="$mkerrors -m64" | |||
mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl" | |||
mksysnum="go run mksysnum.go 'https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12'" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
;; | |||
freebsd_arm) | |||
mkerrors="$mkerrors" | |||
mksyscall="go run mksyscall.go -l32 -arm" | |||
mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl" | |||
mksysnum="go run mksysnum.go 'https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12'" | |||
# Let the type of C char be signed for making the bare syscall | |||
# API consistent across platforms. | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" | |||
;; | |||
linux_sparc64) | |||
GOOSARCH_in=syscall_linux_sparc64.go | |||
unistd_h=/usr/include/sparc64-linux-gnu/asm/unistd.h | |||
freebsd_arm64) | |||
mkerrors="$mkerrors -m64" | |||
mksysnum="./mksysnum_linux.pl $unistd_h" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
mksysnum="go run mksysnum.go 'https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12'" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" | |||
;; | |||
freebsd_riscv64) | |||
mkerrors="$mkerrors -m64" | |||
mksysnum="go run mksysnum.go 'https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12'" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" | |||
;; | |||
netbsd_386) | |||
mkerrors="$mkerrors -m32" | |||
mksyscall="go run mksyscall.go -l32 -netbsd" | |||
mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl" | |||
mksysnum="go run mksysnum.go 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master'" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
;; | |||
netbsd_amd64) | |||
mkerrors="$mkerrors -m64" | |||
mksyscall="go run mksyscall.go -netbsd" | |||
mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl" | |||
mksysnum="go run mksysnum.go 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master'" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
;; | |||
netbsd_arm) | |||
mkerrors="$mkerrors" | |||
mksyscall="go run mksyscall.go -l32 -netbsd -arm" | |||
mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl" | |||
mksysnum="go run mksysnum.go 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master'" | |||
# Let the type of C char be signed for making the bare syscall | |||
# API consistent across platforms. | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" | |||
;; | |||
netbsd_arm64) | |||
mkerrors="$mkerrors -m64" | |||
mksyscall="go run mksyscall.go -netbsd" | |||
mksysnum="go run mksysnum.go 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master'" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
;; | |||
openbsd_386) | |||
mkasm="go run mkasm.go" | |||
mkerrors="$mkerrors -m32" | |||
mksyscall="go run mksyscall.go -l32 -openbsd" | |||
mksysctl="./mksysctl_openbsd.pl" | |||
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl" | |||
mksyscall="go run mksyscall.go -l32 -openbsd -libc" | |||
mksysctl="go run mksysctl_openbsd.go" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
;; | |||
openbsd_amd64) | |||
mkasm="go run mkasm.go" | |||
mkerrors="$mkerrors -m64" | |||
mksyscall="go run mksyscall.go -openbsd" | |||
mksysctl="./mksysctl_openbsd.pl" | |||
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl" | |||
mksyscall="go run mksyscall.go -openbsd -libc" | |||
mksysctl="go run mksysctl_openbsd.go" | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
;; | |||
openbsd_arm) | |||
mkasm="go run mkasm.go" | |||
mkerrors="$mkerrors" | |||
mksyscall="go run mksyscall.go -l32 -openbsd -arm" | |||
mksysctl="./mksysctl_openbsd.pl" | |||
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl" | |||
mksyscall="go run mksyscall.go -l32 -openbsd -arm -libc" | |||
mksysctl="go run mksysctl_openbsd.go" | |||
# Let the type of C char be signed for making the bare syscall | |||
# API consistent across platforms. | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" | |||
;; | |||
openbsd_arm64) | |||
mkasm="go run mkasm.go" | |||
mkerrors="$mkerrors -m64" | |||
mksyscall="go run mksyscall.go -openbsd -libc" | |||
mksysctl="go run mksysctl_openbsd.go" | |||
# Let the type of C char be signed for making the bare syscall | |||
# API consistent across platforms. | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" | |||
;; | |||
openbsd_mips64) | |||
mkasm="go run mkasm.go" | |||
mkerrors="$mkerrors -m64" | |||
mksyscall="go run mksyscall.go -openbsd -libc" | |||
mksysctl="go run mksysctl_openbsd.go" | |||
# Let the type of C char be signed for making the bare syscall | |||
# API consistent across platforms. | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" | |||
;; | |||
openbsd_ppc64) | |||
mkasm="go run mkasm.go" | |||
mkerrors="$mkerrors -m64" | |||
mksyscall="go run mksyscall.go -openbsd -libc" | |||
mksysctl="go run mksysctl_openbsd.go" | |||
# Let the type of C char be signed for making the bare syscall | |||
# API consistent across platforms. | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" | |||
;; | |||
openbsd_riscv64) | |||
mkasm="go run mkasm.go" | |||
mkerrors="$mkerrors -m64" | |||
mksyscall="go run mksyscall.go -openbsd -libc" | |||
mksysctl="go run mksysctl_openbsd.go" | |||
# Let the type of C char be signed for making the bare syscall | |||
# API consistent across platforms. | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" | |||
;; | |||
solaris_amd64) | |||
mksyscall="./mksyscall_solaris.pl" | |||
mksyscall="go run mksyscall_solaris.go" | |||
mkerrors="$mkerrors -m64" | |||
mksysnum= | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
;; | |||
illumos_amd64) | |||
mksyscall="go run mksyscall_solaris.go" | |||
mkerrors= | |||
mksysnum= | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs" | |||
;; | |||
*) | |||
echo 'unrecognized $GOOS_$GOARCH: ' "$GOOSARCH" 1>&2 | |||
exit 1 | |||
@@ -191,6 +232,11 @@ esac | |||
if [ "$GOOSARCH" == "aix_ppc64" ]; then | |||
# aix/ppc64 script generates files instead of writing to stdin. | |||
echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in && gofmt -w zsyscall_$GOOSARCH.go && gofmt -w zsyscall_"$GOOSARCH"_gccgo.go && gofmt -w zsyscall_"$GOOSARCH"_gc.go " ; | |||
elif [ "$GOOS" == "illumos" ]; then | |||
# illumos code generation requires a --illumos switch | |||
echo "$mksyscall -illumos -tags illumos,$GOARCH syscall_illumos.go |gofmt > zsyscall_illumos_$GOARCH.go"; | |||
# illumos implies solaris, so solaris code generation is also required | |||
echo "$mksyscall -tags solaris,$GOARCH syscall_solaris.go syscall_solaris_$GOARCH.go |gofmt >zsyscall_solaris_$GOARCH.go"; | |||
else | |||
echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go"; | |||
fi | |||
@@ -198,7 +244,6 @@ esac | |||
esac | |||
if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi | |||
if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi | |||
if [ -n "$mktypes" ]; then | |||
echo "$mktypes types_$GOOS.go | go run mkpost.go > ztypes_$GOOSARCH.go"; | |||
fi | |||
if [ -n "$mktypes" ]; then echo "$mktypes types_$GOOS.go | go run mkpost.go > ztypes_$GOOSARCH.go"; fi | |||
if [ -n "$mkasm" ]; then echo "$mkasm $GOOS $GOARCH"; fi | |||
) | $run |
@@ -17,12 +17,10 @@ if test -z "$GOARCH" -o -z "$GOOS"; then | |||
fi | |||
# Check that we are using the new build system if we should | |||
if [[ "$GOOS" = "linux" ]] && [[ "$GOARCH" != "sparc64" ]]; then | |||
if [[ "$GOLANG_SYS_BUILD" != "docker" ]]; then | |||
echo 1>&2 "In the new build system, mkerrors should not be called directly." | |||
echo 1>&2 "See README.md" | |||
exit 1 | |||
fi | |||
if [[ "$GOOS" = "linux" ]] && [[ "$GOLANG_SYS_BUILD" != "docker" ]]; then | |||
echo 1>&2 "In the Docker based build system, mkerrors should not be called directly." | |||
echo 1>&2 "See README.md" | |||
exit 1 | |||
fi | |||
if [[ "$GOOS" = "aix" ]]; then | |||
@@ -46,6 +44,7 @@ includes_AIX=' | |||
#include <sys/stropts.h> | |||
#include <sys/mman.h> | |||
#include <sys/poll.h> | |||
#include <sys/select.h> | |||
#include <sys/termio.h> | |||
#include <termios.h> | |||
#include <fcntl.h> | |||
@@ -55,21 +54,28 @@ includes_AIX=' | |||
includes_Darwin=' | |||
#define _DARWIN_C_SOURCE | |||
#define KERNEL | |||
#define KERNEL 1 | |||
#define _DARWIN_USE_64_BIT_INODE | |||
#define __APPLE_USE_RFC_3542 | |||
#include <stdint.h> | |||
#include <sys/attr.h> | |||
#include <sys/clonefile.h> | |||
#include <sys/kern_control.h> | |||
#include <sys/types.h> | |||
#include <sys/event.h> | |||
#include <sys/ptrace.h> | |||
#include <sys/select.h> | |||
#include <sys/socket.h> | |||
#include <sys/un.h> | |||
#include <sys/sockio.h> | |||
#include <sys/sys_domain.h> | |||
#include <sys/sysctl.h> | |||
#include <sys/mman.h> | |||
#include <sys/mount.h> | |||
#include <sys/utsname.h> | |||
#include <sys/wait.h> | |||
#include <sys/xattr.h> | |||
#include <sys/vsock.h> | |||
#include <net/bpf.h> | |||
#include <net/if.h> | |||
#include <net/if_types.h> | |||
@@ -77,11 +83,15 @@ includes_Darwin=' | |||
#include <netinet/in.h> | |||
#include <netinet/ip.h> | |||
#include <termios.h> | |||
// for backwards compatibility because moved TIOCREMOTE to Kernel.framework after MacOSX12.0.sdk. | |||
#define TIOCREMOTE 0x80047469 | |||
' | |||
includes_DragonFly=' | |||
#include <sys/types.h> | |||
#include <sys/event.h> | |||
#include <sys/select.h> | |||
#include <sys/socket.h> | |||
#include <sys/sockio.h> | |||
#include <sys/stat.h> | |||
@@ -92,6 +102,7 @@ includes_DragonFly=' | |||
#include <sys/ioctl.h> | |||
#include <net/bpf.h> | |||
#include <net/if.h> | |||
#include <net/if_clone.h> | |||
#include <net/if_types.h> | |||
#include <net/route.h> | |||
#include <netinet/in.h> | |||
@@ -104,8 +115,12 @@ includes_FreeBSD=' | |||
#include <sys/capsicum.h> | |||
#include <sys/param.h> | |||
#include <sys/types.h> | |||
#include <sys/disk.h> | |||
#include <sys/event.h> | |||
#include <sys/sched.h> | |||
#include <sys/select.h> | |||
#include <sys/socket.h> | |||
#include <sys/un.h> | |||
#include <sys/sockio.h> | |||
#include <sys/stat.h> | |||
#include <sys/sysctl.h> | |||
@@ -113,6 +128,7 @@ includes_FreeBSD=' | |||
#include <sys/mount.h> | |||
#include <sys/wait.h> | |||
#include <sys/ioctl.h> | |||
#include <sys/ptrace.h> | |||
#include <net/bpf.h> | |||
#include <net/if.h> | |||
#include <net/if_types.h> | |||
@@ -181,49 +197,89 @@ struct ltchars { | |||
#include <sys/stat.h> | |||
#include <sys/types.h> | |||
#include <sys/time.h> | |||
#include <sys/select.h> | |||
#include <sys/signalfd.h> | |||
#include <sys/socket.h> | |||
#include <sys/timerfd.h> | |||
#include <sys/uio.h> | |||
#include <sys/xattr.h> | |||
#include <linux/audit.h> | |||
#include <linux/bpf.h> | |||
#include <linux/can.h> | |||
#include <linux/can/error.h> | |||
#include <linux/can/netlink.h> | |||
#include <linux/can/raw.h> | |||
#include <linux/capability.h> | |||
#include <linux/cryptouser.h> | |||
#include <linux/devlink.h> | |||
#include <linux/dm-ioctl.h> | |||
#include <linux/errqueue.h> | |||
#include <linux/ethtool_netlink.h> | |||
#include <linux/falloc.h> | |||
#include <linux/fanotify.h> | |||
#include <linux/fib_rules.h> | |||
#include <linux/filter.h> | |||
#include <linux/fs.h> | |||
#include <linux/fscrypt.h> | |||
#include <linux/fsverity.h> | |||
#include <linux/genetlink.h> | |||
#include <linux/hdreg.h> | |||
#include <linux/hidraw.h> | |||
#include <linux/if.h> | |||
#include <linux/if_addr.h> | |||
#include <linux/if_alg.h> | |||
#include <linux/if_arp.h> | |||
#include <linux/if_ether.h> | |||
#include <linux/if_ppp.h> | |||
#include <linux/if_tun.h> | |||
#include <linux/if_packet.h> | |||
#include <linux/if_addr.h> | |||
#include <linux/falloc.h> | |||
#include <linux/filter.h> | |||
#include <linux/fs.h> | |||
#include <linux/if_xdp.h> | |||
#include <linux/input.h> | |||
#include <linux/kcm.h> | |||
#include <linux/kexec.h> | |||
#include <linux/keyctl.h> | |||
#include <linux/landlock.h> | |||
#include <linux/loop.h> | |||
#include <linux/lwtunnel.h> | |||
#include <linux/magic.h> | |||
#include <linux/memfd.h> | |||
#include <linux/module.h> | |||
#include <linux/mount.h> | |||
#include <linux/netfilter/nfnetlink.h> | |||
#include <linux/netlink.h> | |||
#include <linux/net_namespace.h> | |||
#include <linux/nfc.h> | |||
#include <linux/nsfs.h> | |||
#include <linux/perf_event.h> | |||
#include <linux/pps.h> | |||
#include <linux/ptrace.h> | |||
#include <linux/random.h> | |||
#include <linux/reboot.h> | |||
#include <linux/rtc.h> | |||
#include <linux/rtnetlink.h> | |||
#include <linux/ptrace.h> | |||
#include <linux/sched.h> | |||
#include <linux/seccomp.h> | |||
#include <linux/sockios.h> | |||
#include <linux/wait.h> | |||
#include <linux/icmpv6.h> | |||
#include <linux/serial.h> | |||
#include <linux/can.h> | |||
#include <linux/vm_sockets.h> | |||
#include <linux/sockios.h> | |||
#include <linux/taskstats.h> | |||
#include <linux/genetlink.h> | |||
#include <linux/tipc.h> | |||
#include <linux/vm_sockets.h> | |||
#include <linux/wait.h> | |||
#include <linux/watchdog.h> | |||
#include <linux/hdreg.h> | |||
#include <linux/rtc.h> | |||
#include <linux/if_xdp.h> | |||
#include <linux/wireguard.h> | |||
#include <mtd/ubi-user.h> | |||
#include <mtd/mtd-user.h> | |||
#include <net/route.h> | |||
#if defined(__sparc__) | |||
// On sparc{,64}, the kernel defines struct termios2 itself which clashes with the | |||
// definition in glibc. As only the error constants are needed here, include the | |||
// generic termibits.h (which is included by termbits.h on sparc). | |||
#include <asm-generic/termbits.h> | |||
#else | |||
#include <asm/termbits.h> | |||
#endif | |||
#ifndef MSG_FASTOPEN | |||
#define MSG_FASTOPEN 0x20000000 | |||
@@ -241,6 +297,10 @@ struct ltchars { | |||
#define SOL_NETLINK 270 | |||
#endif | |||
#ifndef SOL_SMC | |||
#define SOL_SMC 286 | |||
#endif | |||
#ifdef SOL_BLUETOOTH | |||
// SPARC includes this in /usr/include/sparc64-linux-gnu/bits/socket.h | |||
// but it is already in bluetooth_linux.go | |||
@@ -252,15 +312,26 @@ struct ltchars { | |||
#define FS_KEY_DESC_PREFIX_SIZE 8 | |||
#define FS_MAX_KEY_SIZE 64 | |||
// XDP socket constants do not appear to be picked up otherwise. | |||
// Copied from samples/bpf/xdpsock_user.c. | |||
#ifndef SOL_XDP | |||
#define SOL_XDP 283 | |||
#endif | |||
// The code generator produces -0x1 for (~0), but an unsigned value is necessary | |||
// for the tipc_subscr timeout __u32 field. | |||
#undef TIPC_WAIT_FOREVER | |||
#define TIPC_WAIT_FOREVER 0xffffffff | |||
// Copied from linux/l2tp.h | |||
// Including linux/l2tp.h here causes conflicts between linux/in.h | |||
// and netinet/in.h included via net/route.h above. | |||
#define IPPROTO_L2TP 115 | |||
// Copied from linux/hid.h. | |||
// Keep in sync with the size of the referenced fields. | |||
#define _HIDIOCGRAWNAME_LEN 128 // sizeof_field(struct hid_device, name) | |||
#define _HIDIOCGRAWPHYS_LEN 64 // sizeof_field(struct hid_device, phys) | |||
#define _HIDIOCGRAWUNIQ_LEN 64 // sizeof_field(struct hid_device, uniq) | |||
#define _HIDIOCGRAWNAME HIDIOCGRAWNAME(_HIDIOCGRAWNAME_LEN) | |||
#define _HIDIOCGRAWPHYS HIDIOCGRAWPHYS(_HIDIOCGRAWPHYS_LEN) | |||
#define _HIDIOCGRAWUNIQ HIDIOCGRAWUNIQ(_HIDIOCGRAWUNIQ_LEN) | |||
#ifndef AF_XDP | |||
#define AF_XDP 44 | |||
#endif | |||
' | |||
includes_NetBSD=' | |||
@@ -270,6 +341,8 @@ includes_NetBSD=' | |||
#include <sys/extattr.h> | |||
#include <sys/mman.h> | |||
#include <sys/mount.h> | |||
#include <sys/sched.h> | |||
#include <sys/select.h> | |||
#include <sys/socket.h> | |||
#include <sys/sockio.h> | |||
#include <sys/sysctl.h> | |||
@@ -296,6 +369,8 @@ includes_OpenBSD=' | |||
#include <sys/event.h> | |||
#include <sys/mman.h> | |||
#include <sys/mount.h> | |||
#include <sys/select.h> | |||
#include <sys/sched.h> | |||
#include <sys/socket.h> | |||
#include <sys/sockio.h> | |||
#include <sys/stat.h> | |||
@@ -332,9 +407,11 @@ includes_OpenBSD=' | |||
includes_SunOS=' | |||
#include <limits.h> | |||
#include <sys/types.h> | |||
#include <sys/select.h> | |||
#include <sys/socket.h> | |||
#include <sys/sockio.h> | |||
#include <sys/stat.h> | |||
#include <sys/stream.h> | |||
#include <sys/mman.h> | |||
#include <sys/wait.h> | |||
#include <sys/ioctl.h> | |||
@@ -344,10 +421,11 @@ includes_SunOS=' | |||
#include <net/if_arp.h> | |||
#include <net/if_types.h> | |||
#include <net/route.h> | |||
#include <netinet/icmp6.h> | |||
#include <netinet/in.h> | |||
#include <termios.h> | |||
#include <netinet/ip.h> | |||
#include <netinet/ip_mroute.h> | |||
#include <termios.h> | |||
' | |||
@@ -402,6 +480,7 @@ ccflags="$@" | |||
$2 !~ /^EPROC_/ && | |||
$2 !~ /^EQUIV_/ && | |||
$2 !~ /^EXPR_/ && | |||
$2 !~ /^EVIOC/ && | |||
$2 ~ /^E[A-Z0-9_]+$/ || | |||
$2 ~ /^B[0-9_]+$/ || | |||
$2 ~ /^(OLD|NEW)DEV$/ || | |||
@@ -424,6 +503,7 @@ ccflags="$@" | |||
$2 == "XCASE" || | |||
$2 == "ALTWERASE" || | |||
$2 == "NOKERNINFO" || | |||
$2 == "NFDBITS" || | |||
$2 ~ /^PAR/ || | |||
$2 ~ /^SIG[^_]/ || | |||
$2 ~ /^O[CNPFPL][A-Z]+[^_][A-Z]+$/ || | |||
@@ -432,11 +512,18 @@ ccflags="$@" | |||
$2 ~ /^O?XTABS$/ || | |||
$2 ~ /^TC[IO](ON|OFF)$/ || | |||
$2 ~ /^IN_/ || | |||
$2 ~ /^KCM/ || | |||
$2 ~ /^LANDLOCK_/ || | |||
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ || | |||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ || | |||
$2 ~ /^LO_(KEY|NAME)_SIZE$/ || | |||
$2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ || | |||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT)_/ || | |||
$2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ || | |||
$2 ~ /^NFC_.*_(MAX)?SIZE$/ || | |||
$2 ~ /^RAW_PAYLOAD_/ || | |||
$2 ~ /^TP_STATUS_/ || | |||
$2 ~ /^FALLOC_/ || | |||
$2 == "ICMPV6_FILTER" || | |||
$2 ~ /^ICMPV?6?_(FILTER|SEC)/ || | |||
$2 == "SOMAXCONN" || | |||
$2 == "NAME_MAX" || | |||
$2 == "IFNAMSIZ" || | |||
@@ -445,42 +532,56 @@ ccflags="$@" | |||
$2 ~ /^HW_MACHINE$/ || | |||
$2 ~ /^SYSCTL_VERS/ || | |||
$2 !~ "MNT_BITS" && | |||
$2 ~ /^(MS|MNT|UMOUNT)_/ || | |||
$2 ~ /^(MS|MNT|MOUNT|UMOUNT)_/ || | |||
$2 ~ /^NS_GET_/ || | |||
$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ || | |||
$2 ~ /^(O|F|E?FD|NAME|S|PTRACE|PT)_/ || | |||
$2 ~ /^(O|F|[ES]?FD|NAME|S|PTRACE|PT|PIOD|TFD)_/ || | |||
$2 ~ /^KEXEC_/ || | |||
$2 ~ /^LINUX_REBOOT_CMD_/ || | |||
$2 ~ /^LINUX_REBOOT_MAGIC[12]$/ || | |||
$2 ~ /^MODULE_INIT_/ || | |||
$2 !~ "NLA_TYPE_MASK" && | |||
$2 !~ /^RTC_VL_(ACCURACY|BACKUP|DATA)/ && | |||
$2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTC|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P|NETNSA)_/ || | |||
$2 ~ /^FIORDCHK$/ || | |||
$2 ~ /^SIOC/ || | |||
$2 ~ /^TIOC/ || | |||
$2 ~ /^TCGET/ || | |||
$2 ~ /^TCSET/ || | |||
$2 ~ /^TC(FLSH|SBRKP?|XONC)$/ || | |||
$2 !~ "RTF_BITS" && | |||
$2 ~ /^(IFF|IFT|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ || | |||
$2 ~ /^(IFF|IFT|NET_RT|RTM(GRP)?|RTF|RTV|RTA|RTAX)_/ || | |||
$2 ~ /^BIOC/ || | |||
$2 ~ /^DIOC/ || | |||
$2 ~ /^RUSAGE_(SELF|CHILDREN|THREAD)/ || | |||
$2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|LOCKS|MEMLOCK|MSGQUEUE|NICE|NOFILE|NPROC|RSS|RTPRIO|RTTIME|SIGPENDING|STACK)|RLIM_INFINITY/ || | |||
$2 ~ /^PRIO_(PROCESS|PGRP|USER)/ || | |||
$2 ~ /^CLONE_[A-Z_]+/ || | |||
$2 !~ /^(BPF_TIMEVAL)$/ && | |||
$2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+)$/ && | |||
$2 ~ /^(BPF|DLT)_/ || | |||
$2 ~ /^CLOCK_/ || | |||
$2 ~ /^AUDIT_/ || | |||
$2 ~ /^(CLOCK|TIMER)_/ || | |||
$2 ~ /^CAN_/ || | |||
$2 ~ /^CAP_/ || | |||
$2 ~ /^CP_/ || | |||
$2 ~ /^CPUSTATES$/ || | |||
$2 ~ /^CTLIOCGINFO$/ || | |||
$2 ~ /^ALG_/ || | |||
$2 ~ /^FS_(POLICY_FLAGS|KEY_DESC|ENCRYPTION_MODE|[A-Z0-9_]+_KEY_SIZE|IOC_(GET|SET)_ENCRYPTION)/ || | |||
$2 ~ /^FI(CLONE|DEDUPERANGE)/ || | |||
$2 ~ /^FS_(POLICY_FLAGS|KEY_DESC|ENCRYPTION_MODE|[A-Z0-9_]+_KEY_SIZE)/ || | |||
$2 ~ /^FS_IOC_.*(ENCRYPTION|VERITY|[GS]ETFLAGS)/ || | |||
$2 ~ /^FS_VERITY_/ || | |||
$2 ~ /^FSCRYPT_/ || | |||
$2 ~ /^DM_/ || | |||
$2 ~ /^GRND_/ || | |||
$2 ~ /^RND/ || | |||
$2 ~ /^KEY_(SPEC|REQKEY_DEFL)_/ || | |||
$2 ~ /^KEYCTL_/ || | |||
$2 ~ /^PERF_EVENT_IOC_/ || | |||
$2 ~ /^PERF_/ || | |||
$2 ~ /^SECCOMP_MODE_/ || | |||
$2 ~ /^SEEK_/ || | |||
$2 ~ /^SPLICE_/ || | |||
$2 ~ /^SYNC_FILE_RANGE_/ || | |||
$2 !~ /^AUDIT_RECORD_MAGIC/ && | |||
$2 !~ /IOC_MAGIC/ && | |||
$2 ~ /^[A-Z][A-Z0-9_]+_MAGIC2?$/ || | |||
$2 ~ /^(VM|VMADDR)_/ || | |||
@@ -495,13 +596,31 @@ ccflags="$@" | |||
$2 ~ /^XATTR_(CREATE|REPLACE|NO(DEFAULT|FOLLOW|SECURITY)|SHOWCOMPRESSION)/ || | |||
$2 ~ /^ATTR_(BIT_MAP_COUNT|(CMN|VOL|FILE)_)/ || | |||
$2 ~ /^FSOPT_/ || | |||
$2 ~ /^WDIOC_/ || | |||
$2 ~ /^WDIO[CFS]_/ || | |||
$2 ~ /^NFN/ || | |||
$2 ~ /^XDP_/ || | |||
$2 ~ /^RWF_/ || | |||
$2 ~ /^(HDIO|WIN|SMART)_/ || | |||
$2 ~ /^CRYPTO_/ || | |||
$2 ~ /^TIPC_/ || | |||
$2 !~ "DEVLINK_RELOAD_LIMITS_VALID_MASK" && | |||
$2 ~ /^DEVLINK_/ || | |||
$2 ~ /^ETHTOOL_/ || | |||
$2 ~ /^LWTUNNEL_IP/ || | |||
$2 ~ /^ITIMER_/ || | |||
$2 !~ "WMESGLEN" && | |||
$2 ~ /^W[A-Z0-9]+$/ || | |||
$2 ~ /^P_/ || | |||
$2 ~/^PPPIOC/ || | |||
$2 ~ /^FAN_|FANOTIFY_/ || | |||
$2 == "HID_MAX_DESCRIPTOR_SIZE" || | |||
$2 ~ /^_?HIDIOC/ || | |||
$2 ~ /^BUS_(USB|HIL|BLUETOOTH|VIRTUAL)$/ || | |||
$2 ~ /^MTD/ || | |||
$2 ~ /^OTP/ || | |||
$2 ~ /^MEM/ || | |||
$2 ~ /^WG/ || | |||
$2 ~ /^FIB_RULE_/ || | |||
$2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE)/ {printf("\t%s = C.%s\n", $2, $2)} | |||
$2 ~ /^__WCOREFLAG$/ {next} | |||
$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)} | |||
@@ -523,7 +642,7 @@ errors=$( | |||
signals=$( | |||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags | | |||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print $2 }' | | |||
egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT|SIGMAX64)' | | |||
grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT\|SIGMAX64' | | |||
sort | |||
) | |||
@@ -533,12 +652,13 @@ echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags | | |||
sort >_error.grep | |||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags | | |||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print "^\t" $2 "[ \t]*=" }' | | |||
egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT|SIGMAX64)' | | |||
grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT\|SIGMAX64' | | |||
sort >_signal.grep | |||
echo '// mkerrors.sh' "$@" | |||
echo '// Code generated by the command above; see README.md. DO NOT EDIT.' | |||
echo | |||
echo "//go:build ${GOARCH} && ${GOOS}" | |||
echo "// +build ${GOARCH},${GOOS}" | |||
echo | |||
go tool cgo -godefs -- "$@" _const.go >_error.out | |||
@@ -1,384 +0,0 @@ | |||
#!/usr/bin/env perl | |||
# Copyright 2018 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. | |||
# This program reads a file containing function prototypes | |||
# (like syscall_aix.go) and generates system call bodies. | |||
# The prototypes are marked by lines beginning with "//sys" | |||
# and read like func declarations if //sys is replaced by func, but: | |||
# * The parameter lists must give a name for each argument. | |||
# This includes return parameters. | |||
# * The parameter lists must give a type for each argument: | |||
# the (x, y, z int) shorthand is not allowed. | |||
# * If the return parameter is an error number, it must be named err. | |||
# * If go func name needs to be different than its libc name, | |||
# * or the function is not in libc, name could be specified | |||
# * at the end, after "=" sign, like | |||
# //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt | |||
use strict; | |||
my $cmdline = "mksyscall_aix_ppc.pl " . join(' ', @ARGV); | |||
my $errors = 0; | |||
my $_32bit = ""; | |||
my $tags = ""; # build tags | |||
my $aix = 0; | |||
my $solaris = 0; | |||
binmode STDOUT; | |||
if($ARGV[0] eq "-b32") { | |||
$_32bit = "big-endian"; | |||
shift; | |||
} elsif($ARGV[0] eq "-l32") { | |||
$_32bit = "little-endian"; | |||
shift; | |||
} | |||
if($ARGV[0] eq "-aix") { | |||
$aix = 1; | |||
shift; | |||
} | |||
if($ARGV[0] eq "-tags") { | |||
shift; | |||
$tags = $ARGV[0]; | |||
shift; | |||
} | |||
if($ARGV[0] =~ /^-/) { | |||
print STDERR "usage: mksyscall_aix.pl [-b32 | -l32] [-tags x,y] [file ...]\n"; | |||
exit 1; | |||
} | |||
sub parseparamlist($) { | |||
my ($list) = @_; | |||
$list =~ s/^\s*//; | |||
$list =~ s/\s*$//; | |||
if($list eq "") { | |||
return (); | |||
} | |||
return split(/\s*,\s*/, $list); | |||
} | |||
sub parseparam($) { | |||
my ($p) = @_; | |||
if($p !~ /^(\S*) (\S*)$/) { | |||
print STDERR "$ARGV:$.: malformed parameter: $p\n"; | |||
$errors = 1; | |||
return ("xx", "int"); | |||
} | |||
return ($1, $2); | |||
} | |||
my $package = ""; | |||
my $text = ""; | |||
my $c_extern = "/*\n#include <stdint.h>\n#include <stddef.h>\n"; | |||
my @vars = (); | |||
while(<>) { | |||
chomp; | |||
s/\s+/ /g; | |||
s/^\s+//; | |||
s/\s+$//; | |||
$package = $1 if !$package && /^package (\S+)$/; | |||
my $nonblock = /^\/\/sysnb /; | |||
next if !/^\/\/sys / && !$nonblock; | |||
# Line must be of the form | |||
# func Open(path string, mode int, perm int) (fd int, err error) | |||
# Split into name, in params, out params. | |||
if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$/) { | |||
print STDERR "$ARGV:$.: malformed //sys declaration\n"; | |||
$errors = 1; | |||
next; | |||
} | |||
my ($nb, $func, $in, $out, $modname, $sysname) = ($1, $2, $3, $4, $5, $6); | |||
# Split argument lists on comma. | |||
my @in = parseparamlist($in); | |||
my @out = parseparamlist($out); | |||
$in = join(', ', @in); | |||
$out = join(', ', @out); | |||
# Try in vain to keep people from editing this file. | |||
# The theory is that they jump into the middle of the file | |||
# without reading the header. | |||
$text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"; | |||
# Check if value return, err return available | |||
my $errvar = ""; | |||
my $retvar = ""; | |||
my $rettype = ""; | |||
foreach my $p (@out) { | |||
my ($name, $type) = parseparam($p); | |||
if($type eq "error") { | |||
$errvar = $name; | |||
} else { | |||
$retvar = $name; | |||
$rettype = $type; | |||
} | |||
} | |||
# System call name. | |||
#if($func ne "fcntl") { | |||
if($sysname eq "") { | |||
$sysname = "$func"; | |||
} | |||
$sysname =~ s/([a-z])([A-Z])/${1}_$2/g; | |||
$sysname =~ y/A-Z/a-z/; # All libc functions are lowercase. | |||
my $C_rettype = ""; | |||
if($rettype eq "unsafe.Pointer") { | |||
$C_rettype = "uintptr_t"; | |||
} elsif($rettype eq "uintptr") { | |||
$C_rettype = "uintptr_t"; | |||
} elsif($rettype =~ /^_/) { | |||
$C_rettype = "uintptr_t"; | |||
} elsif($rettype eq "int") { | |||
$C_rettype = "int"; | |||
} elsif($rettype eq "int32") { | |||
$C_rettype = "int"; | |||
} elsif($rettype eq "int64") { | |||
$C_rettype = "long long"; | |||
} elsif($rettype eq "uint32") { | |||
$C_rettype = "unsigned int"; | |||
} elsif($rettype eq "uint64") { | |||
$C_rettype = "unsigned long long"; | |||
} else { | |||
$C_rettype = "int"; | |||
} | |||
if($sysname eq "exit") { | |||
$C_rettype = "void"; | |||
} | |||
# Change types to c | |||
my @c_in = (); | |||
foreach my $p (@in) { | |||
my ($name, $type) = parseparam($p); | |||
if($type =~ /^\*/) { | |||
push @c_in, "uintptr_t"; | |||
} elsif($type eq "string") { | |||
push @c_in, "uintptr_t"; | |||
} elsif($type =~ /^\[\](.*)/) { | |||
push @c_in, "uintptr_t", "size_t"; | |||
} elsif($type eq "unsafe.Pointer") { | |||
push @c_in, "uintptr_t"; | |||
} elsif($type eq "uintptr") { | |||
push @c_in, "uintptr_t"; | |||
} elsif($type =~ /^_/) { | |||
push @c_in, "uintptr_t"; | |||
} elsif($type eq "int") { | |||
push @c_in, "int"; | |||
} elsif($type eq "int32") { | |||
push @c_in, "int"; | |||
} elsif($type eq "int64") { | |||
push @c_in, "long long"; | |||
} elsif($type eq "uint32") { | |||
push @c_in, "unsigned int"; | |||
} elsif($type eq "uint64") { | |||
push @c_in, "unsigned long long"; | |||
} else { | |||
push @c_in, "int"; | |||
} | |||
} | |||
if ($func ne "fcntl" && $func ne "FcntlInt" && $func ne "readlen" && $func ne "writelen") { | |||
# Imports of system calls from libc | |||
$c_extern .= "$C_rettype $sysname"; | |||
my $c_in = join(', ', @c_in); | |||
$c_extern .= "($c_in);\n"; | |||
} | |||
# So file name. | |||
if($aix) { | |||
if($modname eq "") { | |||
$modname = "libc.a/shr_64.o"; | |||
} else { | |||
print STDERR "$func: only syscall using libc are available\n"; | |||
$errors = 1; | |||
next; | |||
} | |||
} | |||
my $strconvfunc = "C.CString"; | |||
my $strconvtype = "*byte"; | |||
# Go function header. | |||
if($out ne "") { | |||
$out = " ($out)"; | |||
} | |||
if($text ne "") { | |||
$text .= "\n" | |||
} | |||
$text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out ; | |||
# Prepare arguments to call. | |||
my @args = (); | |||
my $n = 0; | |||
my $arg_n = 0; | |||
foreach my $p (@in) { | |||
my ($name, $type) = parseparam($p); | |||
if($type =~ /^\*/) { | |||
push @args, "C.uintptr_t(uintptr(unsafe.Pointer($name)))"; | |||
} elsif($type eq "string" && $errvar ne "") { | |||
$text .= "\t_p$n := uintptr(unsafe.Pointer($strconvfunc($name)))\n"; | |||
push @args, "C.uintptr_t(_p$n)"; | |||
$n++; | |||
} elsif($type eq "string") { | |||
print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n"; | |||
$text .= "\t_p$n := uintptr(unsafe.Pointer($strconvfunc($name)))\n"; | |||
push @args, "C.uintptr_t(_p$n)"; | |||
$n++; | |||
} elsif($type =~ /^\[\](.*)/) { | |||
# Convert slice into pointer, length. | |||
# Have to be careful not to take address of &a[0] if len == 0: | |||
# pass nil in that case. | |||
$text .= "\tvar _p$n *$1\n"; | |||
$text .= "\tif len($name) > 0 {\n\t\t_p$n = \&$name\[0]\n\t}\n"; | |||
push @args, "C.uintptr_t(uintptr(unsafe.Pointer(_p$n)))"; | |||
$n++; | |||
$text .= "\tvar _p$n int\n"; | |||
$text .= "\t_p$n = len($name)\n"; | |||
push @args, "C.size_t(_p$n)"; | |||
$n++; | |||
} elsif($type eq "int64" && $_32bit ne "") { | |||
if($_32bit eq "big-endian") { | |||
push @args, "uintptr($name >> 32)", "uintptr($name)"; | |||
} else { | |||
push @args, "uintptr($name)", "uintptr($name >> 32)"; | |||
} | |||
$n++; | |||
} elsif($type eq "bool") { | |||
$text .= "\tvar _p$n uint32\n"; | |||
$text .= "\tif $name {\n\t\t_p$n = 1\n\t} else {\n\t\t_p$n = 0\n\t}\n"; | |||
push @args, "_p$n"; | |||
$n++; | |||
} elsif($type =~ /^_/) { | |||
push @args, "C.uintptr_t(uintptr($name))"; | |||
} elsif($type eq "unsafe.Pointer") { | |||
push @args, "C.uintptr_t(uintptr($name))"; | |||
} elsif($type eq "int") { | |||
if (($arg_n == 2) && (($func eq "readlen") || ($func eq "writelen"))) { | |||
push @args, "C.size_t($name)"; | |||
} elsif ($arg_n == 0 && $func eq "fcntl") { | |||
push @args, "C.uintptr_t($name)"; | |||
} elsif (($arg_n == 2) && (($func eq "fcntl") || ($func eq "FcntlInt"))) { | |||
push @args, "C.uintptr_t($name)"; | |||
} else { | |||
push @args, "C.int($name)"; | |||
} | |||
} elsif($type eq "int32") { | |||
push @args, "C.int($name)"; | |||
} elsif($type eq "int64") { | |||
push @args, "C.longlong($name)"; | |||
} elsif($type eq "uint32") { | |||
push @args, "C.uint($name)"; | |||
} elsif($type eq "uint64") { | |||
push @args, "C.ulonglong($name)"; | |||
} elsif($type eq "uintptr") { | |||
push @args, "C.uintptr_t($name)"; | |||
} else { | |||
push @args, "C.int($name)"; | |||
} | |||
$arg_n++; | |||
} | |||
my $nargs = @args; | |||
# Determine which form to use; pad args with zeros. | |||
if ($nonblock) { | |||
} | |||
my $args = join(', ', @args); | |||
my $call = ""; | |||
if ($sysname eq "exit") { | |||
if ($errvar ne "") { | |||
$call .= "er :="; | |||
} else { | |||
$call .= ""; | |||
} | |||
} elsif ($errvar ne "") { | |||
$call .= "r0,er :="; | |||
} elsif ($retvar ne "") { | |||
$call .= "r0,_ :="; | |||
} else { | |||
$call .= "" | |||
} | |||
$call .= "C.$sysname($args)"; | |||
# Assign return values. | |||
my $body = ""; | |||
my $failexpr = ""; | |||
for(my $i=0; $i<@out; $i++) { | |||
my $p = $out[$i]; | |||
my ($name, $type) = parseparam($p); | |||
my $reg = ""; | |||
if($name eq "err") { | |||
$reg = "e1"; | |||
} else { | |||
$reg = "r0"; | |||
} | |||
if($reg ne "e1" ) { | |||
$body .= "\t$name = $type($reg)\n"; | |||
} | |||
} | |||
# verify return | |||
if ($sysname ne "exit" && $errvar ne "") { | |||
if ($C_rettype =~ /^uintptr/) { | |||
$body .= "\tif \(uintptr\(r0\) ==\^uintptr\(0\) && er != nil\) {\n"; | |||
$body .= "\t\t$errvar = er\n"; | |||
$body .= "\t}\n"; | |||
} else { | |||
$body .= "\tif \(r0 ==-1 && er != nil\) {\n"; | |||
$body .= "\t\t$errvar = er\n"; | |||
$body .= "\t}\n"; | |||
} | |||
} elsif ($errvar ne "") { | |||
$body .= "\tif \(er != nil\) {\n"; | |||
$body .= "\t\t$errvar = er\n"; | |||
$body .= "\t}\n"; | |||
} | |||
$text .= "\t$call\n"; | |||
$text .= $body; | |||
$text .= "\treturn\n"; | |||
$text .= "}\n"; | |||
} | |||
if($errors) { | |||
exit 1; | |||
} | |||
print <<EOF; | |||
// $cmdline | |||
// Code generated by the command above; see README.md. DO NOT EDIT. | |||
// +build $tags | |||
package $package | |||
$c_extern | |||
*/ | |||
import "C" | |||
import ( | |||
"unsafe" | |||
) | |||
EOF | |||
print "import \"golang.org/x/sys/unix\"\n" if $package ne "unix"; | |||
chomp($_=<<EOF); | |||
$text | |||
EOF | |||
print $_; | |||
exit 0; |
@@ -1,579 +0,0 @@ | |||
#!/usr/bin/env perl | |||
# Copyright 2018 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. | |||
# This program reads a file containing function prototypes | |||
# (like syscall_aix.go) and generates system call bodies. | |||
# The prototypes are marked by lines beginning with "//sys" | |||
# and read like func declarations if //sys is replaced by func, but: | |||
# * The parameter lists must give a name for each argument. | |||
# This includes return parameters. | |||
# * The parameter lists must give a type for each argument: | |||
# the (x, y, z int) shorthand is not allowed. | |||
# * If the return parameter is an error number, it must be named err. | |||
# * If go func name needs to be different than its libc name, | |||
# * or the function is not in libc, name could be specified | |||
# * at the end, after "=" sign, like | |||
# //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt | |||
# This program will generate three files and handle both gc and gccgo implementation: | |||
# - zsyscall_aix_ppc64.go: the common part of each implementation (error handler, pointer creation) | |||
# - zsyscall_aix_ppc64_gc.go: gc part with //go_cgo_import_dynamic and a call to syscall6 | |||
# - zsyscall_aix_ppc64_gccgo.go: gccgo part with C function and conversion to C type. | |||
# The generated code looks like this | |||
# | |||
# zsyscall_aix_ppc64.go | |||
# func asyscall(...) (n int, err error) { | |||
# // Pointer Creation | |||
# r1, e1 := callasyscall(...) | |||
# // Type Conversion | |||
# // Error Handler | |||
# return | |||
# } | |||
# | |||
# zsyscall_aix_ppc64_gc.go | |||
# //go:cgo_import_dynamic libc_asyscall asyscall "libc.a/shr_64.o" | |||
# //go:linkname libc_asyscall libc_asyscall | |||
# var asyscall syscallFunc | |||
# | |||
# func callasyscall(...) (r1 uintptr, e1 Errno) { | |||
# r1, _, e1 = syscall6(uintptr(unsafe.Pointer(&libc_asyscall)), "nb_args", ... ) | |||
# return | |||
# } | |||
# | |||
# zsyscall_aix_ppc64_ggcgo.go | |||
# /* | |||
# int asyscall(...) | |||
# | |||
# */ | |||
# import "C" | |||
# | |||
# func callasyscall(...) (r1 uintptr, e1 Errno) { | |||
# r1 = uintptr(C.asyscall(...)) | |||
# e1 = syscall.GetErrno() | |||
# return | |||
# } | |||
use strict; | |||
my $cmdline = "mksyscall_aix_ppc64.pl " . join(' ', @ARGV); | |||
my $errors = 0; | |||
my $_32bit = ""; | |||
my $tags = ""; # build tags | |||
my $aix = 0; | |||
my $solaris = 0; | |||
binmode STDOUT; | |||
if($ARGV[0] eq "-b32") { | |||
$_32bit = "big-endian"; | |||
shift; | |||
} elsif($ARGV[0] eq "-l32") { | |||
$_32bit = "little-endian"; | |||
shift; | |||
} | |||
if($ARGV[0] eq "-aix") { | |||
$aix = 1; | |||
shift; | |||
} | |||
if($ARGV[0] eq "-tags") { | |||
shift; | |||
$tags = $ARGV[0]; | |||
shift; | |||
} | |||
if($ARGV[0] =~ /^-/) { | |||
print STDERR "usage: mksyscall_aix.pl [-b32 | -l32] [-tags x,y] [file ...]\n"; | |||
exit 1; | |||
} | |||
sub parseparamlist($) { | |||
my ($list) = @_; | |||
$list =~ s/^\s*//; | |||
$list =~ s/\s*$//; | |||
if($list eq "") { | |||
return (); | |||
} | |||
return split(/\s*,\s*/, $list); | |||
} | |||
sub parseparam($) { | |||
my ($p) = @_; | |||
if($p !~ /^(\S*) (\S*)$/) { | |||
print STDERR "$ARGV:$.: malformed parameter: $p\n"; | |||
$errors = 1; | |||
return ("xx", "int"); | |||
} | |||
return ($1, $2); | |||
} | |||
my $package = ""; | |||
# GCCGO | |||
my $textgccgo = ""; | |||
my $c_extern = "/*\n#include <stdint.h>\n"; | |||
# GC | |||
my $textgc = ""; | |||
my $dynimports = ""; | |||
my $linknames = ""; | |||
my @vars = (); | |||
# COMMUN | |||
my $textcommon = ""; | |||
while(<>) { | |||
chomp; | |||
s/\s+/ /g; | |||
s/^\s+//; | |||
s/\s+$//; | |||
$package = $1 if !$package && /^package (\S+)$/; | |||
my $nonblock = /^\/\/sysnb /; | |||
next if !/^\/\/sys / && !$nonblock; | |||
# Line must be of the form | |||
# func Open(path string, mode int, perm int) (fd int, err error) | |||
# Split into name, in params, out params. | |||
if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$/) { | |||
print STDERR "$ARGV:$.: malformed //sys declaration\n"; | |||
$errors = 1; | |||
next; | |||
} | |||
my ($nb, $func, $in, $out, $modname, $sysname) = ($1, $2, $3, $4, $5, $6); | |||
# Split argument lists on comma. | |||
my @in = parseparamlist($in); | |||
my @out = parseparamlist($out); | |||
$in = join(', ', @in); | |||
$out = join(', ', @out); | |||
if($sysname eq "") { | |||
$sysname = "$func"; | |||
} | |||
my $onlyCommon = 0; | |||
if ($func eq "readlen" || $func eq "writelen" || $func eq "FcntlInt" || $func eq "FcntlFlock") { | |||
# This function call another syscall which is already implemented. | |||
# Therefore, the gc and gccgo part must not be generated. | |||
$onlyCommon = 1 | |||
} | |||
# Try in vain to keep people from editing this file. | |||
# The theory is that they jump into the middle of the file | |||
# without reading the header. | |||
$textcommon .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"; | |||
if (!$onlyCommon) { | |||
$textgccgo .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"; | |||
$textgc .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"; | |||
} | |||
# Check if value return, err return available | |||
my $errvar = ""; | |||
my $retvar = ""; | |||
my $rettype = ""; | |||
foreach my $p (@out) { | |||
my ($name, $type) = parseparam($p); | |||
if($type eq "error") { | |||
$errvar = $name; | |||
} else { | |||
$retvar = $name; | |||
$rettype = $type; | |||
} | |||
} | |||
$sysname =~ s/([a-z])([A-Z])/${1}_$2/g; | |||
$sysname =~ y/A-Z/a-z/; # All libc functions are lowercase. | |||
# GCCGO Prototype return type | |||
my $C_rettype = ""; | |||
if($rettype eq "unsafe.Pointer") { | |||
$C_rettype = "uintptr_t"; | |||
} elsif($rettype eq "uintptr") { | |||
$C_rettype = "uintptr_t"; | |||
} elsif($rettype =~ /^_/) { | |||
$C_rettype = "uintptr_t"; | |||
} elsif($rettype eq "int") { | |||
$C_rettype = "int"; | |||
} elsif($rettype eq "int32") { | |||
$C_rettype = "int"; | |||
} elsif($rettype eq "int64") { | |||
$C_rettype = "long long"; | |||
} elsif($rettype eq "uint32") { | |||
$C_rettype = "unsigned int"; | |||
} elsif($rettype eq "uint64") { | |||
$C_rettype = "unsigned long long"; | |||
} else { | |||
$C_rettype = "int"; | |||
} | |||
if($sysname eq "exit") { | |||
$C_rettype = "void"; | |||
} | |||
# GCCGO Prototype arguments type | |||
my @c_in = (); | |||
foreach my $i (0 .. $#in) { | |||
my ($name, $type) = parseparam($in[$i]); | |||
if($type =~ /^\*/) { | |||
push @c_in, "uintptr_t"; | |||
} elsif($type eq "string") { | |||
push @c_in, "uintptr_t"; | |||
} elsif($type =~ /^\[\](.*)/) { | |||
push @c_in, "uintptr_t", "size_t"; | |||
} elsif($type eq "unsafe.Pointer") { | |||
push @c_in, "uintptr_t"; | |||
} elsif($type eq "uintptr") { | |||
push @c_in, "uintptr_t"; | |||
} elsif($type =~ /^_/) { | |||
push @c_in, "uintptr_t"; | |||
} elsif($type eq "int") { | |||
if (($i == 0 || $i == 2) && $func eq "fcntl"){ | |||
# These fcntl arguments needs to be uintptr to be able to call FcntlInt and FcntlFlock | |||
push @c_in, "uintptr_t"; | |||
} else { | |||
push @c_in, "int"; | |||
} | |||
} elsif($type eq "int32") { | |||
push @c_in, "int"; | |||
} elsif($type eq "int64") { | |||
push @c_in, "long long"; | |||
} elsif($type eq "uint32") { | |||
push @c_in, "unsigned int"; | |||
} elsif($type eq "uint64") { | |||
push @c_in, "unsigned long long"; | |||
} else { | |||
push @c_in, "int"; | |||
} | |||
} | |||
if (!$onlyCommon){ | |||
# GCCGO Prototype Generation | |||
# Imports of system calls from libc | |||
$c_extern .= "$C_rettype $sysname"; | |||
my $c_in = join(', ', @c_in); | |||
$c_extern .= "($c_in);\n"; | |||
} | |||
# GC Library name | |||
if($modname eq "") { | |||
$modname = "libc.a/shr_64.o"; | |||
} else { | |||
print STDERR "$func: only syscall using libc are available\n"; | |||
$errors = 1; | |||
next; | |||
} | |||
my $sysvarname = "libc_${sysname}"; | |||
if (!$onlyCommon){ | |||
# GC Runtime import of function to allow cross-platform builds. | |||
$dynimports .= "//go:cgo_import_dynamic ${sysvarname} ${sysname} \"$modname\"\n"; | |||
# GC Link symbol to proc address variable. | |||
$linknames .= "//go:linkname ${sysvarname} ${sysvarname}\n"; | |||
# GC Library proc address variable. | |||
push @vars, $sysvarname; | |||
} | |||
my $strconvfunc ="BytePtrFromString"; | |||
my $strconvtype = "*byte"; | |||
# Go function header. | |||
if($out ne "") { | |||
$out = " ($out)"; | |||
} | |||
if($textcommon ne "") { | |||
$textcommon .= "\n" | |||
} | |||
$textcommon .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out ; | |||
# Prepare arguments to call. | |||
my @argscommun = (); # Arguments in the commun part | |||
my @argscall = (); # Arguments for call prototype | |||
my @argsgc = (); # Arguments for gc call (with syscall6) | |||
my @argsgccgo = (); # Arguments for gccgo call (with C.name_of_syscall) | |||
my $n = 0; | |||
my $arg_n = 0; | |||
foreach my $p (@in) { | |||
my ($name, $type) = parseparam($p); | |||
if($type =~ /^\*/) { | |||
push @argscommun, "uintptr(unsafe.Pointer($name))"; | |||
push @argscall, "$name uintptr"; | |||
push @argsgc, "$name"; | |||
push @argsgccgo, "C.uintptr_t($name)"; | |||
} elsif($type eq "string" && $errvar ne "") { | |||
$textcommon .= "\tvar _p$n $strconvtype\n"; | |||
$textcommon .= "\t_p$n, $errvar = $strconvfunc($name)\n"; | |||
$textcommon .= "\tif $errvar != nil {\n\t\treturn\n\t}\n"; | |||
push @argscommun, "uintptr(unsafe.Pointer(_p$n))"; | |||
push @argscall, "_p$n uintptr "; | |||
push @argsgc, "_p$n"; | |||
push @argsgccgo, "C.uintptr_t(_p$n)"; | |||
$n++; | |||
} elsif($type eq "string") { | |||
print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n"; | |||
$textcommon .= "\tvar _p$n $strconvtype\n"; | |||
$textcommon .= "\t_p$n, $errvar = $strconvfunc($name)\n"; | |||
$textcommon .= "\tif $errvar != nil {\n\t\treturn\n\t}\n"; | |||
push @argscommun, "uintptr(unsafe.Pointer(_p$n))"; | |||
push @argscall, "_p$n uintptr"; | |||
push @argsgc, "_p$n"; | |||
push @argsgccgo, "C.uintptr_t(_p$n)"; | |||
$n++; | |||
} elsif($type =~ /^\[\](.*)/) { | |||
# Convert slice into pointer, length. | |||
# Have to be careful not to take address of &a[0] if len == 0: | |||
# pass nil in that case. | |||
$textcommon .= "\tvar _p$n *$1\n"; | |||
$textcommon .= "\tif len($name) > 0 {\n\t\t_p$n = \&$name\[0]\n\t}\n"; | |||
push @argscommun, "uintptr(unsafe.Pointer(_p$n))", "len($name)"; | |||
push @argscall, "_p$n uintptr", "_lenp$n int"; | |||
push @argsgc, "_p$n", "uintptr(_lenp$n)"; | |||
push @argsgccgo, "C.uintptr_t(_p$n)", "C.size_t(_lenp$n)"; | |||
$n++; | |||
} elsif($type eq "int64" && $_32bit ne "") { | |||
print STDERR "$ARGV:$.: $func uses int64 with 32 bits mode. Case not yet implemented\n"; | |||
# if($_32bit eq "big-endian") { | |||
# push @args, "uintptr($name >> 32)", "uintptr($name)"; | |||
# } else { | |||
# push @args, "uintptr($name)", "uintptr($name >> 32)"; | |||
# } | |||
# $n++; | |||
} elsif($type eq "bool") { | |||
print STDERR "$ARGV:$.: $func uses bool. Case not yet implemented\n"; | |||
# $text .= "\tvar _p$n uint32\n"; | |||
# $text .= "\tif $name {\n\t\t_p$n = 1\n\t} else {\n\t\t_p$n = 0\n\t}\n"; | |||
# push @args, "_p$n"; | |||
# $n++; | |||
} elsif($type =~ /^_/ ||$type eq "unsafe.Pointer") { | |||
push @argscommun, "uintptr($name)"; | |||
push @argscall, "$name uintptr"; | |||
push @argsgc, "$name"; | |||
push @argsgccgo, "C.uintptr_t($name)"; | |||
} elsif($type eq "int") { | |||
if (($arg_n == 0 || $arg_n == 2) && ($func eq "fcntl" || $func eq "FcntlInt" || $func eq "FcntlFlock")) { | |||
# These fcntl arguments need to be uintptr to be able to call FcntlInt and FcntlFlock | |||
push @argscommun, "uintptr($name)"; | |||
push @argscall, "$name uintptr"; | |||
push @argsgc, "$name"; | |||
push @argsgccgo, "C.uintptr_t($name)"; | |||
} else { | |||
push @argscommun, "$name"; | |||
push @argscall, "$name int"; | |||
push @argsgc, "uintptr($name)"; | |||
push @argsgccgo, "C.int($name)"; | |||
} | |||
} elsif($type eq "int32") { | |||
push @argscommun, "$name"; | |||
push @argscall, "$name int32"; | |||
push @argsgc, "uintptr($name)"; | |||
push @argsgccgo, "C.int($name)"; | |||
} elsif($type eq "int64") { | |||
push @argscommun, "$name"; | |||
push @argscall, "$name int64"; | |||
push @argsgc, "uintptr($name)"; | |||
push @argsgccgo, "C.longlong($name)"; | |||
} elsif($type eq "uint32") { | |||
push @argscommun, "$name"; | |||
push @argscall, "$name uint32"; | |||
push @argsgc, "uintptr($name)"; | |||
push @argsgccgo, "C.uint($name)"; | |||
} elsif($type eq "uint64") { | |||
push @argscommun, "$name"; | |||
push @argscall, "$name uint64"; | |||
push @argsgc, "uintptr($name)"; | |||
push @argsgccgo, "C.ulonglong($name)"; | |||
} elsif($type eq "uintptr") { | |||
push @argscommun, "$name"; | |||
push @argscall, "$name uintptr"; | |||
push @argsgc, "$name"; | |||
push @argsgccgo, "C.uintptr_t($name)"; | |||
} else { | |||
push @argscommun, "int($name)"; | |||
push @argscall, "$name int"; | |||
push @argsgc, "uintptr($name)"; | |||
push @argsgccgo, "C.int($name)"; | |||
} | |||
$arg_n++; | |||
} | |||
my $nargs = @argsgc; | |||
# COMMUN function generation | |||
my $argscommun = join(', ', @argscommun); | |||
my $callcommun = "call$sysname($argscommun)"; | |||
my @ret = ("_", "_"); | |||
my $body = ""; | |||
my $do_errno = 0; | |||
for(my $i=0; $i<@out; $i++) { | |||
my $p = $out[$i]; | |||
my ($name, $type) = parseparam($p); | |||
my $reg = ""; | |||
if($name eq "err") { | |||
$reg = "e1"; | |||
$ret[1] = $reg; | |||
$do_errno = 1; | |||
} else { | |||
$reg = "r0"; | |||
$ret[0] = $reg; | |||
} | |||
if($type eq "bool") { | |||
$reg = "$reg != 0"; | |||
} | |||
if($reg ne "e1") { | |||
$body .= "\t$name = $type($reg)\n"; | |||
} | |||
} | |||
if ($ret[0] eq "_" && $ret[1] eq "_") { | |||
$textcommon .= "\t$callcommun\n"; | |||
} else { | |||
$textcommon .= "\t$ret[0], $ret[1] := $callcommun\n"; | |||
} | |||
$textcommon .= $body; | |||
if ($do_errno) { | |||
$textcommon .= "\tif e1 != 0 {\n"; | |||
$textcommon .= "\t\terr = errnoErr(e1)\n"; | |||
$textcommon .= "\t}\n"; | |||
} | |||
$textcommon .= "\treturn\n"; | |||
$textcommon .= "}\n"; | |||
if ($onlyCommon){ | |||
next | |||
} | |||
# CALL Prototype | |||
my $callProto = sprintf "func call%s(%s) (r1 uintptr, e1 Errno) {\n", $sysname, join(', ', @argscall); | |||
# GC function generation | |||
my $asm = "syscall6"; | |||
if ($nonblock) { | |||
$asm = "rawSyscall6"; | |||
} | |||
if(@argsgc <= 6) { | |||
while(@argsgc < 6) { | |||
push @argsgc, "0"; | |||
} | |||
} else { | |||
print STDERR "$ARGV:$.: too many arguments to system call\n"; | |||
} | |||
my $argsgc = join(', ', @argsgc); | |||
my $callgc = "$asm(uintptr(unsafe.Pointer(&$sysvarname)), $nargs, $argsgc)"; | |||
$textgc .= $callProto; | |||
$textgc .= "\tr1, _, e1 = $callgc\n"; | |||
$textgc .= "\treturn\n}\n"; | |||
# GCCGO function generation | |||
my $argsgccgo = join(', ', @argsgccgo); | |||
my $callgccgo = "C.$sysname($argsgccgo)"; | |||
$textgccgo .= $callProto; | |||
$textgccgo .= "\tr1 = uintptr($callgccgo)\n"; | |||
$textgccgo .= "\te1 = syscall.GetErrno()\n"; | |||
$textgccgo .= "\treturn\n}\n"; | |||
} | |||
if($errors) { | |||
exit 1; | |||
} | |||
# Print zsyscall_aix_ppc64.go | |||
open(my $fcommun, '>', 'zsyscall_aix_ppc64.go'); | |||
my $tofcommun = <<EOF; | |||
// $cmdline | |||
// Code generated by the command above; see README.md. DO NOT EDIT. | |||
// +build $tags | |||
package $package | |||
import ( | |||
"unsafe" | |||
) | |||
EOF | |||
$tofcommun .= "import \"golang.org/x/sys/unix\"\n" if $package ne "unix"; | |||
$tofcommun .=<<EOF; | |||
$textcommon | |||
EOF | |||
print $fcommun $tofcommun; | |||
# Print zsyscall_aix_ppc64_gc.go | |||
open(my $fgc, '>', 'zsyscall_aix_ppc64_gc.go'); | |||
my $tofgc = <<EOF; | |||
// $cmdline | |||
// Code generated by the command above; see README.md. DO NOT EDIT. | |||
// +build $tags | |||
// +build !gccgo | |||
package $package | |||
import ( | |||
"unsafe" | |||
) | |||
EOF | |||
$tofgc .= "import \"golang.org/x/sys/unix\"\n" if $package ne "unix"; | |||
my $vardecls = "\t" . join(",\n\t", @vars); | |||
$vardecls .= " syscallFunc"; | |||
$tofgc .=<<EOF; | |||
$dynimports | |||
$linknames | |||
type syscallFunc uintptr | |||
var ( | |||
$vardecls | |||
) | |||
// Implemented in runtime/syscall_aix.go. | |||
func rawSyscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) | |||
func syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) | |||
$textgc | |||
EOF | |||
print $fgc $tofgc; | |||
# Print zsyscall_aix_ppc64_gc.go | |||
open(my $fgccgo, '>', 'zsyscall_aix_ppc64_gccgo.go'); | |||
my $tofgccgo = <<EOF; | |||
// $cmdline | |||
// Code generated by the command above; see README.md. DO NOT EDIT. | |||
// +build $tags | |||
// +build gccgo | |||
package $package | |||
$c_extern | |||
*/ | |||
import "C" | |||
import ( | |||
"syscall" | |||
) | |||
EOF | |||
$tofgccgo .= "import \"golang.org/x/sys/unix\"\n" if $package ne "unix"; | |||
$tofgccgo .=<<EOF; | |||
$textgccgo | |||
EOF | |||
print $fgccgo $tofgccgo; | |||
exit 0; |
@@ -1,294 +0,0 @@ | |||
#!/usr/bin/env perl | |||
# 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. | |||
# This program reads a file containing function prototypes | |||
# (like syscall_solaris.go) and generates system call bodies. | |||
# The prototypes are marked by lines beginning with "//sys" | |||
# and read like func declarations if //sys is replaced by func, but: | |||
# * The parameter lists must give a name for each argument. | |||
# This includes return parameters. | |||
# * The parameter lists must give a type for each argument: | |||
# the (x, y, z int) shorthand is not allowed. | |||
# * If the return parameter is an error number, it must be named err. | |||
# * If go func name needs to be different than its libc name, | |||
# * or the function is not in libc, name could be specified | |||
# * at the end, after "=" sign, like | |||
# //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt | |||
use strict; | |||
my $cmdline = "mksyscall_solaris.pl " . join(' ', @ARGV); | |||
my $errors = 0; | |||
my $_32bit = ""; | |||
my $tags = ""; # build tags | |||
binmode STDOUT; | |||
if($ARGV[0] eq "-b32") { | |||
$_32bit = "big-endian"; | |||
shift; | |||
} elsif($ARGV[0] eq "-l32") { | |||
$_32bit = "little-endian"; | |||
shift; | |||
} | |||
if($ARGV[0] eq "-tags") { | |||
shift; | |||
$tags = $ARGV[0]; | |||
shift; | |||
} | |||
if($ARGV[0] =~ /^-/) { | |||
print STDERR "usage: mksyscall_solaris.pl [-b32 | -l32] [-tags x,y] [file ...]\n"; | |||
exit 1; | |||
} | |||
sub parseparamlist($) { | |||
my ($list) = @_; | |||
$list =~ s/^\s*//; | |||
$list =~ s/\s*$//; | |||
if($list eq "") { | |||
return (); | |||
} | |||
return split(/\s*,\s*/, $list); | |||
} | |||
sub parseparam($) { | |||
my ($p) = @_; | |||
if($p !~ /^(\S*) (\S*)$/) { | |||
print STDERR "$ARGV:$.: malformed parameter: $p\n"; | |||
$errors = 1; | |||
return ("xx", "int"); | |||
} | |||
return ($1, $2); | |||
} | |||
my $package = ""; | |||
my $text = ""; | |||
my $dynimports = ""; | |||
my $linknames = ""; | |||
my @vars = (); | |||
while(<>) { | |||
chomp; | |||
s/\s+/ /g; | |||
s/^\s+//; | |||
s/\s+$//; | |||
$package = $1 if !$package && /^package (\S+)$/; | |||
my $nonblock = /^\/\/sysnb /; | |||
next if !/^\/\/sys / && !$nonblock; | |||
# Line must be of the form | |||
# func Open(path string, mode int, perm int) (fd int, err error) | |||
# Split into name, in params, out params. | |||
if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$/) { | |||
print STDERR "$ARGV:$.: malformed //sys declaration\n"; | |||
$errors = 1; | |||
next; | |||
} | |||
my ($nb, $func, $in, $out, $modname, $sysname) = ($1, $2, $3, $4, $5, $6); | |||
# Split argument lists on comma. | |||
my @in = parseparamlist($in); | |||
my @out = parseparamlist($out); | |||
# Try in vain to keep people from editing this file. | |||
# The theory is that they jump into the middle of the file | |||
# without reading the header. | |||
$text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"; | |||
# So file name. | |||
if($modname eq "") { | |||
$modname = "libc"; | |||
} | |||
# System call name. | |||
if($sysname eq "") { | |||
$sysname = "$func"; | |||
} | |||
# System call pointer variable name. | |||
my $sysvarname = "proc$sysname"; | |||
my $strconvfunc = "BytePtrFromString"; | |||
my $strconvtype = "*byte"; | |||
$sysname =~ y/A-Z/a-z/; # All libc functions are lowercase. | |||
# Runtime import of function to allow cross-platform builds. | |||
$dynimports .= "//go:cgo_import_dynamic libc_${sysname} ${sysname} \"$modname.so\"\n"; | |||
# Link symbol to proc address variable. | |||
$linknames .= "//go:linkname ${sysvarname} libc_${sysname}\n"; | |||
# Library proc address variable. | |||
push @vars, $sysvarname; | |||
# Go function header. | |||
$out = join(', ', @out); | |||
if($out ne "") { | |||
$out = " ($out)"; | |||
} | |||
if($text ne "") { | |||
$text .= "\n" | |||
} | |||
$text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out; | |||
# Check if err return available | |||
my $errvar = ""; | |||
foreach my $p (@out) { | |||
my ($name, $type) = parseparam($p); | |||
if($type eq "error") { | |||
$errvar = $name; | |||
last; | |||
} | |||
} | |||
# Prepare arguments to Syscall. | |||
my @args = (); | |||
my $n = 0; | |||
foreach my $p (@in) { | |||
my ($name, $type) = parseparam($p); | |||
if($type =~ /^\*/) { | |||
push @args, "uintptr(unsafe.Pointer($name))"; | |||
} elsif($type eq "string" && $errvar ne "") { | |||
$text .= "\tvar _p$n $strconvtype\n"; | |||
$text .= "\t_p$n, $errvar = $strconvfunc($name)\n"; | |||
$text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n"; | |||
push @args, "uintptr(unsafe.Pointer(_p$n))"; | |||
$n++; | |||
} elsif($type eq "string") { | |||
print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n"; | |||
$text .= "\tvar _p$n $strconvtype\n"; | |||
$text .= "\t_p$n, _ = $strconvfunc($name)\n"; | |||
push @args, "uintptr(unsafe.Pointer(_p$n))"; | |||
$n++; | |||
} elsif($type =~ /^\[\](.*)/) { | |||
# Convert slice into pointer, length. | |||
# Have to be careful not to take address of &a[0] if len == 0: | |||
# pass nil in that case. | |||
$text .= "\tvar _p$n *$1\n"; | |||
$text .= "\tif len($name) > 0 {\n\t\t_p$n = \&$name\[0]\n\t}\n"; | |||
push @args, "uintptr(unsafe.Pointer(_p$n))", "uintptr(len($name))"; | |||
$n++; | |||
} elsif($type eq "int64" && $_32bit ne "") { | |||
if($_32bit eq "big-endian") { | |||
push @args, "uintptr($name >> 32)", "uintptr($name)"; | |||
} else { | |||
push @args, "uintptr($name)", "uintptr($name >> 32)"; | |||
} | |||
} elsif($type eq "bool") { | |||
$text .= "\tvar _p$n uint32\n"; | |||
$text .= "\tif $name {\n\t\t_p$n = 1\n\t} else {\n\t\t_p$n = 0\n\t}\n"; | |||
push @args, "uintptr(_p$n)"; | |||
$n++; | |||
} else { | |||
push @args, "uintptr($name)"; | |||
} | |||
} | |||
my $nargs = @args; | |||
# Determine which form to use; pad args with zeros. | |||
my $asm = "sysvicall6"; | |||
if ($nonblock) { | |||
$asm = "rawSysvicall6"; | |||
} | |||
if(@args <= 6) { | |||
while(@args < 6) { | |||
push @args, "0"; | |||
} | |||
} else { | |||
print STDERR "$ARGV:$.: too many arguments to system call\n"; | |||
} | |||
# Actual call. | |||
my $args = join(', ', @args); | |||
my $call = "$asm(uintptr(unsafe.Pointer(&$sysvarname)), $nargs, $args)"; | |||
# Assign return values. | |||
my $body = ""; | |||
my $failexpr = ""; | |||
my @ret = ("_", "_", "_"); | |||
my @pout= (); | |||
my $do_errno = 0; | |||
for(my $i=0; $i<@out; $i++) { | |||
my $p = $out[$i]; | |||
my ($name, $type) = parseparam($p); | |||
my $reg = ""; | |||
if($name eq "err") { | |||
$reg = "e1"; | |||
$ret[2] = $reg; | |||
$do_errno = 1; | |||
} else { | |||
$reg = sprintf("r%d", $i); | |||
$ret[$i] = $reg; | |||
} | |||
if($type eq "bool") { | |||
$reg = "$reg != 0"; | |||
} | |||
if($type eq "int64" && $_32bit ne "") { | |||
# 64-bit number in r1:r0 or r0:r1. | |||
if($i+2 > @out) { | |||
print STDERR "$ARGV:$.: not enough registers for int64 return\n"; | |||
} | |||
if($_32bit eq "big-endian") { | |||
$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i, $i+1); | |||
} else { | |||
$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i+1, $i); | |||
} | |||
$ret[$i] = sprintf("r%d", $i); | |||
$ret[$i+1] = sprintf("r%d", $i+1); | |||
} | |||
if($reg ne "e1") { | |||
$body .= "\t$name = $type($reg)\n"; | |||
} | |||
} | |||
if ($ret[0] eq "_" && $ret[1] eq "_" && $ret[2] eq "_") { | |||
$text .= "\t$call\n"; | |||
} else { | |||
$text .= "\t$ret[0], $ret[1], $ret[2] := $call\n"; | |||
} | |||
$text .= $body; | |||
if ($do_errno) { | |||
$text .= "\tif e1 != 0 {\n"; | |||
$text .= "\t\terr = e1\n"; | |||
$text .= "\t}\n"; | |||
} | |||
$text .= "\treturn\n"; | |||
$text .= "}\n"; | |||
} | |||
if($errors) { | |||
exit 1; | |||
} | |||
print <<EOF; | |||
// $cmdline | |||
// Code generated by the command above; see README.md. DO NOT EDIT. | |||
// +build $tags | |||
package $package | |||
import ( | |||
"syscall" | |||
"unsafe" | |||
) | |||
EOF | |||
print "import \"golang.org/x/sys/unix\"\n" if $package ne "unix"; | |||
my $vardecls = "\t" . join(",\n\t", @vars); | |||
$vardecls .= " syscallFunc"; | |||
chomp($_=<<EOF); | |||
$dynimports | |||
$linknames | |||
var ( | |||
$vardecls | |||
) | |||
$text | |||
EOF | |||
print $_; | |||
exit 0; |
@@ -1,265 +0,0 @@ | |||
#!/usr/bin/env perl | |||
# Copyright 2011 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. | |||
# | |||
# Parse the header files for OpenBSD and generate a Go usable sysctl MIB. | |||
# | |||
# Build a MIB with each entry being an array containing the level, type and | |||
# a hash that will contain additional entries if the current entry is a node. | |||
# We then walk this MIB and create a flattened sysctl name to OID hash. | |||
# | |||
use strict; | |||
if($ENV{'GOARCH'} eq "" || $ENV{'GOOS'} eq "") { | |||
print STDERR "GOARCH or GOOS not defined in environment\n"; | |||
exit 1; | |||
} | |||
my $debug = 0; | |||
my %ctls = (); | |||
my @headers = qw ( | |||
sys/sysctl.h | |||
sys/socket.h | |||
sys/tty.h | |||
sys/malloc.h | |||
sys/mount.h | |||
sys/namei.h | |||
sys/sem.h | |||
sys/shm.h | |||
sys/vmmeter.h | |||
uvm/uvmexp.h | |||
uvm/uvm_param.h | |||
uvm/uvm_swap_encrypt.h | |||
ddb/db_var.h | |||
net/if.h | |||
net/if_pfsync.h | |||
net/pipex.h | |||
netinet/in.h | |||
netinet/icmp_var.h | |||
netinet/igmp_var.h | |||
netinet/ip_ah.h | |||
netinet/ip_carp.h | |||
netinet/ip_divert.h | |||
netinet/ip_esp.h | |||
netinet/ip_ether.h | |||
netinet/ip_gre.h | |||
netinet/ip_ipcomp.h | |||
netinet/ip_ipip.h | |||
netinet/pim_var.h | |||
netinet/tcp_var.h | |||
netinet/udp_var.h | |||
netinet6/in6.h | |||
netinet6/ip6_divert.h | |||
netinet6/pim6_var.h | |||
netinet/icmp6.h | |||
netmpls/mpls.h | |||
); | |||
my @ctls = qw ( | |||
kern | |||
vm | |||
fs | |||
net | |||
#debug # Special handling required | |||
hw | |||
#machdep # Arch specific | |||
user | |||
ddb | |||
#vfs # Special handling required | |||
fs.posix | |||
kern.forkstat | |||
kern.intrcnt | |||
kern.malloc | |||
kern.nchstats | |||
kern.seminfo | |||
kern.shminfo | |||
kern.timecounter | |||
kern.tty | |||
kern.watchdog | |||
net.bpf | |||
net.ifq | |||
net.inet | |||
net.inet.ah | |||
net.inet.carp | |||
net.inet.divert | |||
net.inet.esp | |||
net.inet.etherip | |||
net.inet.gre | |||
net.inet.icmp | |||
net.inet.igmp | |||
net.inet.ip | |||
net.inet.ip.ifq | |||
net.inet.ipcomp | |||
net.inet.ipip | |||
net.inet.mobileip | |||
net.inet.pfsync | |||
net.inet.pim | |||
net.inet.tcp | |||
net.inet.udp | |||
net.inet6 | |||
net.inet6.divert | |||
net.inet6.ip6 | |||
net.inet6.icmp6 | |||
net.inet6.pim6 | |||
net.inet6.tcp6 | |||
net.inet6.udp6 | |||
net.mpls | |||
net.mpls.ifq | |||
net.key | |||
net.pflow | |||
net.pfsync | |||
net.pipex | |||
net.rt | |||
vm.swapencrypt | |||
#vfsgenctl # Special handling required | |||
); | |||
# Node name "fixups" | |||
my %ctl_map = ( | |||
"ipproto" => "net.inet", | |||
"net.inet.ipproto" => "net.inet", | |||
"net.inet6.ipv6proto" => "net.inet6", | |||
"net.inet6.ipv6" => "net.inet6.ip6", | |||
"net.inet.icmpv6" => "net.inet6.icmp6", | |||
"net.inet6.divert6" => "net.inet6.divert", | |||
"net.inet6.tcp6" => "net.inet.tcp", | |||
"net.inet6.udp6" => "net.inet.udp", | |||
"mpls" => "net.mpls", | |||
"swpenc" => "vm.swapencrypt" | |||
); | |||
# Node mappings | |||
my %node_map = ( | |||
"net.inet.ip.ifq" => "net.ifq", | |||
"net.inet.pfsync" => "net.pfsync", | |||
"net.mpls.ifq" => "net.ifq" | |||
); | |||
my $ctlname; | |||
my %mib = (); | |||
my %sysctl = (); | |||
my $node; | |||
sub debug() { | |||
print STDERR "$_[0]\n" if $debug; | |||
} | |||
# Walk the MIB and build a sysctl name to OID mapping. | |||
sub build_sysctl() { | |||
my ($node, $name, $oid) = @_; | |||
my %node = %{$node}; | |||
my @oid = @{$oid}; | |||
foreach my $key (sort keys %node) { | |||
my @node = @{$node{$key}}; | |||
my $nodename = $name.($name ne '' ? '.' : '').$key; | |||
my @nodeoid = (@oid, $node[0]); | |||
if ($node[1] eq 'CTLTYPE_NODE') { | |||
if (exists $node_map{$nodename}) { | |||
$node = \%mib; | |||
$ctlname = $node_map{$nodename}; | |||
foreach my $part (split /\./, $ctlname) { | |||
$node = \%{@{$$node{$part}}[2]}; | |||
} | |||
} else { | |||
$node = $node[2]; | |||
} | |||
&build_sysctl($node, $nodename, \@nodeoid); | |||
} elsif ($node[1] ne '') { | |||
$sysctl{$nodename} = \@nodeoid; | |||
} | |||
} | |||
} | |||
foreach my $ctl (@ctls) { | |||
$ctls{$ctl} = $ctl; | |||
} | |||
# Build MIB | |||
foreach my $header (@headers) { | |||
&debug("Processing $header..."); | |||
open HEADER, "/usr/include/$header" || | |||
print STDERR "Failed to open $header\n"; | |||
while (<HEADER>) { | |||
if ($_ =~ /^#define\s+(CTL_NAMES)\s+{/ || | |||
$_ =~ /^#define\s+(CTL_(.*)_NAMES)\s+{/ || | |||
$_ =~ /^#define\s+((.*)CTL_NAMES)\s+{/) { | |||
if ($1 eq 'CTL_NAMES') { | |||
# Top level. | |||
$node = \%mib; | |||
} else { | |||
# Node. | |||
my $nodename = lc($2); | |||
if ($header =~ /^netinet\//) { | |||
$ctlname = "net.inet.$nodename"; | |||
} elsif ($header =~ /^netinet6\//) { | |||
$ctlname = "net.inet6.$nodename"; | |||
} elsif ($header =~ /^net\//) { | |||
$ctlname = "net.$nodename"; | |||
} else { | |||
$ctlname = "$nodename"; | |||
$ctlname =~ s/^(fs|net|kern)_/$1\./; | |||
} | |||
if (exists $ctl_map{$ctlname}) { | |||
$ctlname = $ctl_map{$ctlname}; | |||
} | |||
if (not exists $ctls{$ctlname}) { | |||
&debug("Ignoring $ctlname..."); | |||
next; | |||
} | |||
# Walk down from the top of the MIB. | |||
$node = \%mib; | |||
foreach my $part (split /\./, $ctlname) { | |||
if (not exists $$node{$part}) { | |||
&debug("Missing node $part"); | |||
$$node{$part} = [ 0, '', {} ]; | |||
} | |||
$node = \%{@{$$node{$part}}[2]}; | |||
} | |||
} | |||
# Populate current node with entries. | |||
my $i = -1; | |||
while (defined($_) && $_ !~ /^}/) { | |||
$_ = <HEADER>; | |||
$i++ if $_ =~ /{.*}/; | |||
next if $_ !~ /{\s+"(\w+)",\s+(CTLTYPE_[A-Z]+)\s+}/; | |||
$$node{$1} = [ $i, $2, {} ]; | |||
} | |||
} | |||
} | |||
close HEADER; | |||
} | |||
&build_sysctl(\%mib, "", []); | |||
print <<EOF; | |||
// mksysctl_openbsd.pl | |||
// Code generated by the command above; DO NOT EDIT. | |||
// +build $ENV{'GOARCH'},$ENV{'GOOS'} | |||
package unix; | |||
type mibentry struct { | |||
ctlname string | |||
ctloid []_C_int | |||
} | |||
var sysctlMib = []mibentry { | |||
EOF | |||
foreach my $name (sort keys %sysctl) { | |||
my @oid = @{$sysctl{$name}}; | |||
print "\t{ \"$name\", []_C_int{ ", join(', ', @oid), " } }, \n"; | |||
} | |||
print <<EOF; | |||
} | |||
EOF |