aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/exp
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2012-01-13 05:11:45 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2012-01-13 05:11:45 +0000
commitdf4aa89a5e7acb315655f193e7f549e8d32367e2 (patch)
treeeb5eccc07097c5fcf940967f33ab84a7d47c96fe /libgo/go/exp
parentf83fa0bf8f411697ec908cfa86ee6faf4cd9c476 (diff)
downloadgcc-df4aa89a5e7acb315655f193e7f549e8d32367e2.zip
gcc-df4aa89a5e7acb315655f193e7f549e8d32367e2.tar.gz
gcc-df4aa89a5e7acb315655f193e7f549e8d32367e2.tar.bz2
libgo: Update to weekly.2011-12-22.
From-SVN: r183150
Diffstat (limited to 'libgo/go/exp')
-rw-r--r--libgo/go/exp/inotify/inotify_linux_test.go5
-rw-r--r--libgo/go/exp/sql/sql.go41
-rw-r--r--libgo/go/exp/sql/sql_test.go25
-rw-r--r--libgo/go/exp/ssh/client_auth.go4
-rw-r--r--libgo/go/exp/ssh/client_auth_test.go10
-rw-r--r--libgo/go/exp/ssh/client_func_test.go2
-rw-r--r--libgo/go/exp/ssh/server.go12
-rw-r--r--libgo/go/exp/ssh/session.go59
-rw-r--r--libgo/go/exp/ssh/session_test.go2
-rw-r--r--libgo/go/exp/ssh/tcpip.go1
-rw-r--r--libgo/go/exp/terminal/terminal.go206
-rw-r--r--libgo/go/exp/terminal/terminal_test.go2
-rw-r--r--libgo/go/exp/terminal/util.go17
-rw-r--r--libgo/go/exp/winfsnotify/winfsnotify.go2
-rw-r--r--libgo/go/exp/winfsnotify/winfsnotify_test.go2
-rw-r--r--libgo/go/exp/wingui/gui.go2
-rw-r--r--libgo/go/exp/wingui/winapi.go2
-rw-r--r--libgo/go/exp/wingui/zwinapi.go1
18 files changed, 304 insertions, 91 deletions
diff --git a/libgo/go/exp/inotify/inotify_linux_test.go b/libgo/go/exp/inotify/inotify_linux_test.go
index 92384b6..d035ec1 100644
--- a/libgo/go/exp/inotify/inotify_linux_test.go
+++ b/libgo/go/exp/inotify/inotify_linux_test.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build linux
+
package inotify
import (
@@ -17,6 +19,9 @@ func TestInotifyEvents(t *testing.T) {
t.Fatalf("NewWatcher() failed: %s", err)
}
+ t.Logf("NEEDS TO BE CONVERTED TO NEW GO TOOL") // TODO
+ return
+
// Add a watch for "_test"
err = watcher.Watch("_test")
if err != nil {
diff --git a/libgo/go/exp/sql/sql.go b/libgo/go/exp/sql/sql.go
index 948b911..937982c 100644
--- a/libgo/go/exp/sql/sql.go
+++ b/libgo/go/exp/sql/sql.go
@@ -22,10 +22,10 @@ var drivers = make(map[string]driver.Driver)
// it panics.
func Register(name string, driver driver.Driver) {
if driver == nil {
- panic("db: Register driver is nil")
+ panic("sql: Register driver is nil")
}
if _, dup := drivers[name]; dup {
- panic("db: Register called twice for driver " + name)
+ panic("sql: Register called twice for driver " + name)
}
drivers[name] = driver
}
@@ -80,7 +80,7 @@ type ScannerInto interface {
// ErrNoRows is returned by Scan when QueryRow doesn't return a
// row. In such a case, QueryRow returns a placeholder *Row value that
// defers this error until a Scan.
-var ErrNoRows = errors.New("db: no rows in result set")
+var ErrNoRows = errors.New("sql: no rows in result set")
// DB is a database handle. It's safe for concurrent use by multiple
// goroutines.
@@ -102,7 +102,7 @@ type DB struct {
func Open(driverName, dataSourceName string) (*DB, error) {
driver, ok := drivers[driverName]
if !ok {
- return nil, fmt.Errorf("db: unknown driver %q (forgotten import?)", driverName)
+ return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
}
return &DB{driver: driver, dsn: dataSourceName}, nil
}
@@ -514,7 +514,7 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
// placeholders, so we won't sanity check input here and instead let the
// driver deal with errors.
if want := si.NumInput(); want != -1 && len(args) != want {
- return nil, fmt.Errorf("db: expected %d arguments, got %d", want, len(args))
+ return nil, fmt.Errorf("sql: expected %d arguments, got %d", want, len(args))
}
// Convert args to subset types.
@@ -522,10 +522,10 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
for n, arg := range args {
args[n], err = cc.ColumnConverter(n).ConvertValue(arg)
if err != nil {
- return nil, fmt.Errorf("db: converting Exec argument #%d's type: %v", n, err)
+ return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
}
if !driver.IsParameterSubsetType(args[n]) {
- return nil, fmt.Errorf("db: driver ColumnConverter error converted %T to unsupported type %T",
+ return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T",
arg, args[n])
}
}
@@ -533,7 +533,7 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
for n, arg := range args {
args[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
if err != nil {
- return nil, fmt.Errorf("db: converting Exec argument #%d's type: %v", n, err)
+ return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
}
}
}
@@ -555,7 +555,7 @@ func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(), si driver.Stmt, e
s.mu.Lock()
if s.closed {
s.mu.Unlock()
- err = errors.New("db: statement is closed")
+ err = errors.New("sql: statement is closed")
return
}
@@ -617,7 +617,7 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
// placeholders, so we won't sanity check input here and instead let the
// driver deal with errors.
if want := si.NumInput(); want != -1 && len(args) != want {
- return nil, fmt.Errorf("db: statement expects %d inputs; got %d", si.NumInput(), len(args))
+ return nil, fmt.Errorf("sql: statement expects %d inputs; got %d", si.NumInput(), len(args))
}
sargs, err := subsetTypeArgs(args)
if err != nil {
@@ -737,27 +737,40 @@ func (rs *Rows) Err() error {
return rs.lasterr
}
+// Columns returns the column names.
+// Columns returns an error if the rows are closed, or if the rows
+// are from QueryRow and there was a deferred error.
+func (rs *Rows) Columns() ([]string, error) {
+ if rs.closed {
+ return nil, errors.New("sql: Rows are closed")
+ }
+ if rs.rowsi == nil {
+ return nil, errors.New("sql: no Rows available")
+ }
+ return rs.rowsi.Columns(), nil
+}
+
// Scan copies the columns in the current row into the values pointed
// at by dest. If dest contains pointers to []byte, the slices should
// not be modified and should only be considered valid until the next
// call to Next or Scan.
func (rs *Rows) Scan(dest ...interface{}) error {
if rs.closed {
- return errors.New("db: Rows closed")
+ return errors.New("sql: Rows closed")
}
if rs.lasterr != nil {
return rs.lasterr
}
if rs.lastcols == nil {
- return errors.New("db: Scan called without calling Next")
+ return errors.New("sql: Scan called without calling Next")
}
if len(dest) != len(rs.lastcols) {
- return fmt.Errorf("db: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest))
+ return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest))
}
for i, sv := range rs.lastcols {
err := convertAssign(dest[i], sv)
if err != nil {
- return fmt.Errorf("db: Scan error on column index %d: %v", i, err)
+ return fmt.Errorf("sql: Scan error on column index %d: %v", i, err)
}
}
return nil
diff --git a/libgo/go/exp/sql/sql_test.go b/libgo/go/exp/sql/sql_test.go
index f8ccf76..5307a23 100644
--- a/libgo/go/exp/sql/sql_test.go
+++ b/libgo/go/exp/sql/sql_test.go
@@ -75,6 +75,23 @@ func TestQuery(t *testing.T) {
}
}
+func TestRowsColumns(t *testing.T) {
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+ rows, err := db.Query("SELECT|people|age,name|")
+ if err != nil {
+ t.Fatalf("Query: %v", err)
+ }
+ cols, err := rows.Columns()
+ if err != nil {
+ t.Fatalf("Columns: %v", err)
+ }
+ want := []string{"age", "name"}
+ if !reflect.DeepEqual(cols, want) {
+ t.Errorf("got %#v; want %#v", cols, want)
+ }
+}
+
func TestQueryRow(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
@@ -187,12 +204,12 @@ func TestExec(t *testing.T) {
{[]interface{}{7, 9}, ""},
// Invalid conversions:
- {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "db: converting Exec argument #1's type: sql/driver: value 4294967295 overflows int32"},
- {[]interface{}{"Brad", "strconv fail"}, "db: converting Exec argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"},
+ {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "sql: converting Exec argument #1's type: sql/driver: value 4294967295 overflows int32"},
+ {[]interface{}{"Brad", "strconv fail"}, "sql: converting Exec argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"},
// Wrong number of args:
- {[]interface{}{}, "db: expected 2 arguments, got 0"},
- {[]interface{}{1, 2, 3}, "db: expected 2 arguments, got 3"},
+ {[]interface{}{}, "sql: expected 2 arguments, got 0"},
+ {[]interface{}{1, 2, 3}, "sql: expected 2 arguments, got 3"},
}
for n, et := range execTests {
_, err := stmt.Exec(et.args...)
diff --git a/libgo/go/exp/ssh/client_auth.go b/libgo/go/exp/ssh/client_auth.go
index 1a38235..3a7e9fb 100644
--- a/libgo/go/exp/ssh/client_auth.go
+++ b/libgo/go/exp/ssh/client_auth.go
@@ -283,8 +283,8 @@ func (p *publickeyAuth) method() string {
return "publickey"
}
-// ClientAuthPublickey returns a ClientAuth using public key authentication.
-func ClientAuthPublickey(impl ClientKeyring) ClientAuth {
+// ClientAuthKeyring returns a ClientAuth using public key authentication.
+func ClientAuthKeyring(impl ClientKeyring) ClientAuth {
return &publickeyAuth{impl}
}
diff --git a/libgo/go/exp/ssh/client_auth_test.go b/libgo/go/exp/ssh/client_auth_test.go
index 2b89e97..c41a93b 100644
--- a/libgo/go/exp/ssh/client_auth_test.go
+++ b/libgo/go/exp/ssh/client_auth_test.go
@@ -122,7 +122,7 @@ var (
PasswordCallback: func(user, pass string) bool {
return user == "testuser" && pass == string(clientPassword)
},
- PubKeyCallback: func(user, algo string, pubkey []byte) bool {
+ PublicKeyCallback: func(user, algo string, pubkey []byte) bool {
key := clientKeychain.keys[0].(*rsa.PrivateKey).PublicKey
expected := []byte(serializePublickey(key))
algoname := algoName(key)
@@ -179,7 +179,7 @@ func TestClientAuthPublickey(t *testing.T) {
config := &ClientConfig{
User: "testuser",
Auth: []ClientAuth{
- ClientAuthPublickey(clientKeychain),
+ ClientAuthKeyring(clientKeychain),
},
}
c, err := Dial("tcp", newMockAuthServer(t), config)
@@ -210,7 +210,7 @@ func TestClientAuthWrongPassword(t *testing.T) {
User: "testuser",
Auth: []ClientAuth{
ClientAuthPassword(wrongPw),
- ClientAuthPublickey(clientKeychain),
+ ClientAuthKeyring(clientKeychain),
},
}
@@ -228,7 +228,7 @@ func TestClientAuthInvalidPublickey(t *testing.T) {
config := &ClientConfig{
User: "testuser",
Auth: []ClientAuth{
- ClientAuthPublickey(kc),
+ ClientAuthKeyring(kc),
},
}
@@ -246,7 +246,7 @@ func TestClientAuthRSAandDSA(t *testing.T) {
config := &ClientConfig{
User: "testuser",
Auth: []ClientAuth{
- ClientAuthPublickey(kc),
+ ClientAuthKeyring(kc),
},
}
c, err := Dial("tcp", newMockAuthServer(t), config)
diff --git a/libgo/go/exp/ssh/client_func_test.go b/libgo/go/exp/ssh/client_func_test.go
index 24e3a63..b4bdba9 100644
--- a/libgo/go/exp/ssh/client_func_test.go
+++ b/libgo/go/exp/ssh/client_func_test.go
@@ -50,7 +50,7 @@ func TestFuncPublickeyAuth(t *testing.T) {
config := &ClientConfig{
User: *sshuser,
Auth: []ClientAuth{
- ClientAuthPublickey(kc),
+ ClientAuthKeyring(kc),
},
}
conn, err := Dial("tcp", "localhost:22", config)
diff --git a/libgo/go/exp/ssh/server.go b/libgo/go/exp/ssh/server.go
index 1eee9a4..31011c6 100644
--- a/libgo/go/exp/ssh/server.go
+++ b/libgo/go/exp/ssh/server.go
@@ -36,10 +36,10 @@ type ServerConfig struct {
// several goroutines.
PasswordCallback func(user, password string) bool
- // PubKeyCallback, if non-nil, is called when a client attempts public
+ // PublicKeyCallback, if non-nil, is called when a client attempts public
// key authentication. It must return true iff the given public key is
// valid for the given user.
- PubKeyCallback func(user, algo string, pubkey []byte) bool
+ PublicKeyCallback func(user, algo string, pubkey []byte) bool
// Cryptographic-related configuration.
Crypto CryptoConfig
@@ -359,7 +359,7 @@ func isAcceptableAlgo(algo string) bool {
// testPubKey returns true if the given public key is acceptable for the user.
func (s *ServerConn) testPubKey(user, algo string, pubKey []byte) bool {
- if s.config.PubKeyCallback == nil || !isAcceptableAlgo(algo) {
+ if s.config.PublicKeyCallback == nil || !isAcceptableAlgo(algo) {
return false
}
@@ -369,7 +369,7 @@ func (s *ServerConn) testPubKey(user, algo string, pubKey []byte) bool {
}
}
- result := s.config.PubKeyCallback(user, algo, pubKey)
+ result := s.config.PublicKeyCallback(user, algo, pubKey)
if len(s.cachedPubKeys) < maxCachedPubKeys {
c := cachedPubKey{
user: user,
@@ -425,7 +425,7 @@ userAuthLoop:
break userAuthLoop
}
case "publickey":
- if s.config.PubKeyCallback == nil {
+ if s.config.PublicKeyCallback == nil {
break
}
payload := userAuthReq.Payload
@@ -499,7 +499,7 @@ userAuthLoop:
if s.config.PasswordCallback != nil {
failureMsg.Methods = append(failureMsg.Methods, "password")
}
- if s.config.PubKeyCallback != nil {
+ if s.config.PublicKeyCallback != nil {
failureMsg.Methods = append(failureMsg.Methods, "publickey")
}
diff --git a/libgo/go/exp/ssh/session.go b/libgo/go/exp/ssh/session.go
index bf9a88e..807dd87 100644
--- a/libgo/go/exp/ssh/session.go
+++ b/libgo/go/exp/ssh/session.go
@@ -68,10 +68,12 @@ type Session struct {
*clientChan // the channel backing this session
- started bool // true once Start, Run or Shell is invoked.
- closeAfterWait []io.Closer
- copyFuncs []func() error
- errch chan error // one send per copyFunc
+ started bool // true once Start, Run or Shell is invoked.
+ copyFuncs []func() error
+ errch chan error // one send per copyFunc
+
+ // true if pipe method is active
+ stdinpipe, stdoutpipe, stderrpipe bool
}
// RFC 4254 Section 6.4.
@@ -237,11 +239,9 @@ func (s *Session) waitForResponse() error {
func (s *Session) start() error {
s.started = true
- type F func(*Session) error
+ type F func(*Session)
for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} {
- if err := setupFd(s); err != nil {
- return err
- }
+ setupFd(s)
}
s.errch = make(chan error, len(s.copyFuncs))
@@ -274,9 +274,6 @@ func (s *Session) Wait() error {
copyError = err
}
}
- for _, fd := range s.closeAfterWait {
- fd.Close()
- }
if waitErr != nil {
return waitErr
}
@@ -341,7 +338,10 @@ func (s *Session) wait() error {
return &ExitError{wm}
}
-func (s *Session) stdin() error {
+func (s *Session) stdin() {
+ if s.stdinpipe {
+ return
+ }
if s.Stdin == nil {
s.Stdin = new(bytes.Buffer)
}
@@ -352,10 +352,12 @@ func (s *Session) stdin() error {
}
return err
})
- return nil
}
-func (s *Session) stdout() error {
+func (s *Session) stdout() {
+ if s.stdoutpipe {
+ return
+ }
if s.Stdout == nil {
s.Stdout = ioutil.Discard
}
@@ -363,10 +365,12 @@ func (s *Session) stdout() error {
_, err := io.Copy(s.Stdout, s.clientChan.stdout)
return err
})
- return nil
}
-func (s *Session) stderr() error {
+func (s *Session) stderr() {
+ if s.stderrpipe {
+ return
+ }
if s.Stderr == nil {
s.Stderr = ioutil.Discard
}
@@ -374,7 +378,6 @@ func (s *Session) stderr() error {
_, err := io.Copy(s.Stderr, s.clientChan.stderr)
return err
})
- return nil
}
// StdinPipe returns a pipe that will be connected to the
@@ -386,10 +389,8 @@ func (s *Session) StdinPipe() (io.WriteCloser, error) {
if s.started {
return nil, errors.New("ssh: StdinPipe after process started")
}
- pr, pw := io.Pipe()
- s.Stdin = pr
- s.closeAfterWait = append(s.closeAfterWait, pr)
- return pw, nil
+ s.stdinpipe = true
+ return s.clientChan.stdin, nil
}
// StdoutPipe returns a pipe that will be connected to the
@@ -398,17 +399,15 @@ func (s *Session) StdinPipe() (io.WriteCloser, error) {
// stdout and stderr streams. If the StdoutPipe reader is
// not serviced fast enought it may eventually cause the
// remote command to block.
-func (s *Session) StdoutPipe() (io.ReadCloser, error) {
+func (s *Session) StdoutPipe() (io.Reader, error) {
if s.Stdout != nil {
return nil, errors.New("ssh: Stdout already set")
}
if s.started {
return nil, errors.New("ssh: StdoutPipe after process started")
}
- pr, pw := io.Pipe()
- s.Stdout = pw
- s.closeAfterWait = append(s.closeAfterWait, pw)
- return pr, nil
+ s.stdoutpipe = true
+ return s.clientChan.stdout, nil
}
// StderrPipe returns a pipe that will be connected to the
@@ -417,17 +416,15 @@ func (s *Session) StdoutPipe() (io.ReadCloser, error) {
// stdout and stderr streams. If the StderrPipe reader is
// not serviced fast enought it may eventually cause the
// remote command to block.
-func (s *Session) StderrPipe() (io.ReadCloser, error) {
+func (s *Session) StderrPipe() (io.Reader, error) {
if s.Stderr != nil {
return nil, errors.New("ssh: Stderr already set")
}
if s.started {
return nil, errors.New("ssh: StderrPipe after process started")
}
- pr, pw := io.Pipe()
- s.Stderr = pw
- s.closeAfterWait = append(s.closeAfterWait, pw)
- return pr, nil
+ s.stderrpipe = true
+ return s.clientChan.stderr, nil
}
// TODO(dfc) add Output and CombinedOutput helpers
diff --git a/libgo/go/exp/ssh/session_test.go b/libgo/go/exp/ssh/session_test.go
index a28ead0..2882620 100644
--- a/libgo/go/exp/ssh/session_test.go
+++ b/libgo/go/exp/ssh/session_test.go
@@ -20,7 +20,7 @@ func dial(handler serverType, t *testing.T) *ClientConn {
serverConfig.PasswordCallback = func(user, pass string) bool {
return user == "testuser" && pass == string(pw)
}
- serverConfig.PubKeyCallback = nil
+ serverConfig.PublicKeyCallback = nil
l, err := Listen("tcp", "127.0.0.1:0", serverConfig)
if err != nil {
diff --git a/libgo/go/exp/ssh/tcpip.go b/libgo/go/exp/ssh/tcpip.go
index a85044a..bee41ee 100644
--- a/libgo/go/exp/ssh/tcpip.go
+++ b/libgo/go/exp/ssh/tcpip.go
@@ -10,6 +10,7 @@ import (
"io"
"net"
)
+
// Dial initiates a connection to the addr from the remote host.
// addr is resolved using net.ResolveTCPAddr before connection.
// This could allow an observer to observe the DNS name of the
diff --git a/libgo/go/exp/terminal/terminal.go b/libgo/go/exp/terminal/terminal.go
index 18d76cd..809e88c 100644
--- a/libgo/go/exp/terminal/terminal.go
+++ b/libgo/go/exp/terminal/terminal.go
@@ -2,13 +2,56 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build linux
+
package terminal
-import "io"
+import (
+ "io"
+ "sync"
+)
+
+// 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'},
+}
// 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.
+ // If it returns a nil newLine, the key press is processed normally.
+ // Otherwise it returns a replacement line and the new cursor position.
+ AutoCompleteCallback func(line []byte, pos, key int) (newLine []byte, newPos int)
+
+ // 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 string
@@ -16,6 +59,8 @@ type Terminal struct {
line []byte
// pos is the logical position of the cursor in line
pos int
+ // echo is true if local echo is enabled
+ echo 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
@@ -40,10 +85,12 @@ type Terminal struct {
// "> ").
func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
return &Terminal{
+ Escape: &vt100EscapeCodes,
c: c,
prompt: prompt,
termWidth: 80,
termHeight: 24,
+ echo: true,
}
}
@@ -109,18 +156,11 @@ func bytesToKey(b []byte) (int, []byte) {
// queue appends data to the end of t.outBuf
func (t *Terminal) queue(data []byte) {
- if len(t.outBuf)+len(data) > cap(t.outBuf) {
- newOutBuf := make([]byte, len(t.outBuf), 2*(len(t.outBuf)+len(data)))
- copy(newOutBuf, t.outBuf)
- t.outBuf = newOutBuf
- }
-
- oldLen := len(t.outBuf)
- t.outBuf = t.outBuf[:len(t.outBuf)+len(data)]
- copy(t.outBuf[oldLen:], data)
+ t.outBuf = append(t.outBuf, data...)
}
var eraseUnderCursor = []byte{' ', keyEscape, '[', 'D'}
+var space = []byte{' '}
func isPrintable(key int) bool {
return key >= 32 && key < 127
@@ -129,6 +169,10 @@ func isPrintable(key int) bool {
// 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 := len(t.prompt) + pos
y := x / t.termWidth
x = x % t.termWidth
@@ -153,6 +197,12 @@ func (t *Terminal) moveCursorToPos(pos int) {
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([]byte, 3*(up+down+left+right))
m := movement
for i := 0; i < up; i++ {
@@ -180,11 +230,14 @@ func (t *Terminal) moveCursorToPos(pos int) {
m = m[3:]
}
- t.cursorX = x
- t.cursorY = y
t.queue(movement)
}
+func (t *Terminal) clearLineToRight() {
+ op := []byte{keyEscape, '[', 'K'}
+ t.queue(op)
+}
+
const maxLineLength = 4096
// handleKey processes the given key and, optionally, returns a line of text
@@ -196,12 +249,15 @@ func (t *Terminal) handleKey(key int) (line string, ok bool) {
return
}
t.pos--
+ t.moveCursorToPos(t.pos)
copy(t.line[t.pos:], t.line[1+t.pos:])
t.line = t.line[:len(t.line)-1]
- t.writeLine(t.line[t.pos:])
- t.moveCursorToPos(t.pos)
+ if t.echo {
+ t.writeLine(t.line[t.pos:])
+ }
t.queue(eraseUnderCursor)
+ t.moveCursorToPos(t.pos)
case keyAltLeft:
// move left by a word.
if t.pos == 0 {
@@ -260,6 +316,25 @@ func (t *Terminal) handleKey(key int) (line string, ok bool) {
t.cursorY = 0
t.maxLine = 0
default:
+ if t.AutoCompleteCallback != nil {
+ t.lock.Unlock()
+ newLine, newPos := t.AutoCompleteCallback(t.line, t.pos, key)
+ t.lock.Lock()
+
+ if newLine != nil {
+ 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
+ return
+ }
+ }
if !isPrintable(key) {
return
}
@@ -274,7 +349,9 @@ func (t *Terminal) handleKey(key int) (line string, ok bool) {
t.line = t.line[:len(t.line)+1]
copy(t.line[t.pos+1:], t.line[t.pos:])
t.line[t.pos] = byte(key)
- t.writeLine(t.line[t.pos:])
+ if t.echo {
+ t.writeLine(t.line[t.pos:])
+ }
t.pos++
t.moveCursorToPos(t.pos)
}
@@ -283,15 +360,6 @@ func (t *Terminal) handleKey(key int) (line string, ok bool) {
func (t *Terminal) writeLine(line []byte) {
for len(line) != 0 {
- if t.cursorX == t.termWidth {
- t.queue([]byte("\r\n"))
- t.cursorX = 0
- t.cursorY++
- if t.cursorY > t.maxLine {
- t.maxLine = t.cursorY
- }
- }
-
remainingOnLine := t.termWidth - t.cursorX
todo := len(line)
if todo > remainingOnLine {
@@ -300,16 +368,95 @@ func (t *Terminal) writeLine(line []byte) {
t.queue(line[:todo])
t.cursorX += todo
line = line[todo:]
+
+ if t.cursorX == t.termWidth {
+ t.cursorX = 0
+ t.cursorY++
+ if t.cursorY > t.maxLine {
+ t.maxLine = t.cursorY
+ }
+ }
}
}
func (t *Terminal) Write(buf []byte) (n int, err error) {
- return t.c.Write(buf)
+ 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 t.c.Write(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 = t.c.Write(buf); err != nil {
+ return
+ }
+
+ t.queue([]byte(t.prompt))
+ chars := len(t.prompt)
+ if t.echo {
+ t.queue(t.line)
+ chars += len(t.line)
+ }
+ t.cursorX = chars % t.termWidth
+ t.cursorY = chars / t.termWidth
+ 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 = 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) {
- if t.cursorX == 0 {
+ 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([]byte(t.prompt))
t.c.Write(t.outBuf)
t.outBuf = t.outBuf[:0]
@@ -320,7 +467,11 @@ func (t *Terminal) ReadLine() (line string, err error) {
// 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
}
@@ -358,5 +509,8 @@ func (t *Terminal) ReadLine() (line string, err error) {
}
func (t *Terminal) SetSize(width, height int) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
t.termWidth, t.termHeight = width, height
}
diff --git a/libgo/go/exp/terminal/terminal_test.go b/libgo/go/exp/terminal/terminal_test.go
index a219721..75628f6 100644
--- a/libgo/go/exp/terminal/terminal_test.go
+++ b/libgo/go/exp/terminal/terminal_test.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build linux
+
package terminal
import (
diff --git a/libgo/go/exp/terminal/util.go b/libgo/go/exp/terminal/util.go
index 0303567..a5bbfca 100644
--- a/libgo/go/exp/terminal/util.go
+++ b/libgo/go/exp/terminal/util.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build linux
+
// Package terminal provides support functions for dealing with terminals, as
// commonly found on UNIX systems.
//
@@ -9,7 +11,7 @@
//
// oldState, err := terminal.MakeRaw(0)
// if err != nil {
-// panic(err.String())
+// panic(err)
// }
// defer terminal.Restore(0, oldState)
package terminal
@@ -17,6 +19,7 @@ package terminal
import (
"io"
"syscall"
+ "unsafe"
)
// State contains the state of a terminal.
@@ -57,6 +60,18 @@ func Restore(fd int, state *State) error {
return err
}
+func ioctl(int, int, unsafe.Pointer) int __asm__("ioctl")
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+ var dimensions [4]uint16
+
+ if ioctl(fd, syscall.TIOCGWINSZ, unsafe.Pointer(&dimensions)) < 0 {
+ return -1, -1, syscall.GetErrno()
+ }
+ return int(dimensions[1]), int(dimensions[0]), 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.
diff --git a/libgo/go/exp/winfsnotify/winfsnotify.go b/libgo/go/exp/winfsnotify/winfsnotify.go
index d47ffd1..a6e3a6a 100644
--- a/libgo/go/exp/winfsnotify/winfsnotify.go
+++ b/libgo/go/exp/winfsnotify/winfsnotify.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build windows
+
// Package winfsnotify allows the user to receive
// file system event notifications on Windows.
package winfsnotify
diff --git a/libgo/go/exp/winfsnotify/winfsnotify_test.go b/libgo/go/exp/winfsnotify/winfsnotify_test.go
index b9c43d9..59ac162 100644
--- a/libgo/go/exp/winfsnotify/winfsnotify_test.go
+++ b/libgo/go/exp/winfsnotify/winfsnotify_test.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build windows
+
package winfsnotify
import (
diff --git a/libgo/go/exp/wingui/gui.go b/libgo/go/exp/wingui/gui.go
index d58421b..3b79873 100644
--- a/libgo/go/exp/wingui/gui.go
+++ b/libgo/go/exp/wingui/gui.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build windows
+
package main
import (
diff --git a/libgo/go/exp/wingui/winapi.go b/libgo/go/exp/wingui/winapi.go
index 24f3dd4..f876088 100644
--- a/libgo/go/exp/wingui/winapi.go
+++ b/libgo/go/exp/wingui/winapi.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build windows
+
package main
import (
diff --git a/libgo/go/exp/wingui/zwinapi.go b/libgo/go/exp/wingui/zwinapi.go
index b062ca3..5666c6d 100644
--- a/libgo/go/exp/wingui/zwinapi.go
+++ b/libgo/go/exp/wingui/zwinapi.go
@@ -1,3 +1,4 @@
+// +build windows
// mksyscall_windows.pl winapi.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT