diff options
Diffstat (limited to 'libgo/go/os')
40 files changed, 672 insertions, 249 deletions
diff --git a/libgo/go/os/dir.go b/libgo/go/os/dir.go index b3b5d3e37dc..5f383c12fda 100644 --- a/libgo/go/os/dir.go +++ b/libgo/go/os/dir.go @@ -27,8 +27,19 @@ func clen(n []byte) int { var elen int; -// Negative count means read until EOF. -func (file *File) Readdirnames(count int) (names []string, err Error) { +// Readdirnames reads and returns a slice of names from the directory f. +// +// If n > 0, Readdirnames returns at most n names. In this case, if +// Readdirnames returns an empty slice, it will return a non-nil error +// explaining why. At the end of a directory, the error is os.EOF. +// +// If n <= 0, Readdirnames returns all the names from the directory in +// a single slice. In this case, if Readdirnames succeeds (reads all +// the way to the end of the directory), it returns the slice and a +// nil os.Error. If it encounters an error before the end of the +// directory, Readdirnames returns the names read until that point and +// a non-nil error. +func (file *File) Readdirnames(n int) (names []string, err Error) { if elen == 0 { var dummy syscall.Dirent; elen = (unsafe.Offsetof(dummy.Name) + @@ -44,10 +55,12 @@ func (file *File) Readdirnames(count int) (names []string, err Error) { entry_dirent := unsafe.Pointer(&file.dirinfo.buf[0]).(*syscall.Dirent) - size := count + size := n if size < 0 { size = 100 + n = -1 } + names = make([]string, 0, size) // Empty with room to grow. dir := file.dirinfo.dir @@ -55,18 +68,24 @@ func (file *File) Readdirnames(count int) (names []string, err Error) { return names, NewSyscallError("opendir", syscall.GetErrno()) } - for count != 0 { + for n != 0 { var result *syscall.Dirent i := libc_readdir_r(dir, entry_dirent, &result) + if i != 0 { + return names, NewSyscallError("readdir_r", i) + } if result == nil { - break + break // EOF } var name = string(result.Name[0:clen(result.Name[0:])]) if name == "." || name == ".." { // Useless names continue } - count-- names = append(names, name) + n-- + } + if n >= 0 && len(names) == 0 { + return names, EOF } return names, nil } diff --git a/libgo/go/os/dir_plan9.go b/libgo/go/os/dir_plan9.go index d9514191d79..bbc2cb64726 100644 --- a/libgo/go/os/dir_plan9.go +++ b/libgo/go/os/dir_plan9.go @@ -9,35 +9,46 @@ import ( ) // Readdir reads the contents of the directory associated with file and -// returns an array of up to count FileInfo structures, in directory order. -// Subsequent calls on the same file will yield further FileInfos. -// A negative count means to read until EOF. -// Readdir returns the array and an Error, if any. -func (file *File) Readdir(count int) (fi []FileInfo, err Error) { +// returns an array of up to n FileInfo structures, as would be returned +// by Lstat, in directory order. Subsequent calls on the same file will yield +// further FileInfos. +// +// If n > 0, Readdir returns at most n FileInfo structures. In this case, if +// Readdirnames returns an empty slice, it will return a non-nil error +// explaining why. At the end of a directory, the error is os.EOF. +// +// If n <= 0, Readdir returns all the FileInfo from the directory in +// a single slice. In this case, if Readdir succeeds (reads all +// the way to the end of the directory), it returns the slice and a +// nil os.Error. If it encounters an error before the end of the +// directory, Readdir returns the FileInfo read until that point +// and a non-nil error. +func (file *File) Readdir(n int) (fi []FileInfo, err Error) { // If this file has no dirinfo, create one. if file.dirinfo == nil { file.dirinfo = new(dirInfo) } d := file.dirinfo - size := count - if size < 0 { + size := n + if size <= 0 { size = 100 + n = -1 } result := make([]FileInfo, 0, size) // Empty with room to grow. - for count != 0 { + for n != 0 { // Refill the buffer if necessary if d.bufp >= d.nbuf { d.bufp = 0 var e Error d.nbuf, e = file.Read(d.buf[:]) if e != nil && e != EOF { - return nil, &PathError{"readdir", file.name, e} + return result, &PathError{"readdir", file.name, e} } if e == EOF { break } if d.nbuf < syscall.STATFIXLEN { - return nil, &PathError{"readdir", file.name, Eshortstat} + return result, &PathError{"readdir", file.name, Eshortstat} } } @@ -45,39 +56,44 @@ func (file *File) Readdir(count int) (fi []FileInfo, err Error) { m, _ := gbit16(d.buf[d.bufp:]) m += 2 if m < syscall.STATFIXLEN { - return nil, &PathError{"readdir", file.name, Eshortstat} + return result, &PathError{"readdir", file.name, Eshortstat} } dir, e := UnmarshalDir(d.buf[d.bufp : d.bufp+int(m)]) if e != nil { - return nil, &PathError{"readdir", file.name, e} + return result, &PathError{"readdir", file.name, e} } var f FileInfo fileInfoFromStat(&f, dir) result = append(result, f) d.bufp += int(m) - count-- + n-- } - return result, nil -} -// Readdirnames returns an array of up to count file names residing in the -// directory associated with file. A negative count will return all of them. -// Readdir returns the array and an Error, if any. -func (file *File) Readdirnames(count int) (names []string, err Error) { - fi, e := file.Readdir(count) - - if e != nil { - return []string{}, e + if n >= 0 && len(result) == 0 { + return result, EOF } + return result, nil +} +// Readdirnames reads and returns a slice of names from the directory f. +// +// If n > 0, Readdirnames returns at most n names. In this case, if +// Readdirnames returns an empty slice, it will return a non-nil error +// explaining why. At the end of a directory, the error is os.EOF. +// +// If n <= 0, Readdirnames returns all the names from the directory in +// a single slice. In this case, if Readdirnames succeeds (reads all +// the way to the end of the directory), it returns the slice and a +// nil os.Error. If it encounters an error before the end of the +// directory, Readdirnames returns the names read until that point and +// a non-nil error. +func (file *File) Readdirnames(n int) (names []string, err Error) { + fi, err := file.Readdir(n) names = make([]string, len(fi)) - err = nil - for i := range fi { names[i] = fi[i].Name } - return } @@ -142,7 +158,7 @@ func pdir(b []byte, d *Dir) []byte { return b } -// UnmarshalDir reads a 9P Stat message from a 9P protocol message strored in b, +// UnmarshalDir reads a 9P Stat message from a 9P protocol message stored in b, // returning the corresponding Dir struct. func UnmarshalDir(b []byte) (d *Dir, err Error) { n := uint16(0) @@ -172,7 +188,7 @@ func UnmarshalDir(b []byte) (d *Dir, err Error) { return d, nil } -// gqid reads the qid part of a 9P Stat message from a 9P protocol message strored in b, +// gqid reads the qid part of a 9P Stat message from a 9P protocol message stored in b, // returning the corresponding Qid struct and the remaining slice of b. func gqid(b []byte) (Qid, []byte) { var q Qid @@ -190,25 +206,25 @@ func pqid(b []byte, q Qid) []byte { return b } -// gbit8 reads a byte-sized numeric value from a 9P protocol message strored in b, +// gbit8 reads a byte-sized numeric value from a 9P protocol message stored in b, // returning the value and the remaining slice of b. func gbit8(b []byte) (uint8, []byte) { return uint8(b[0]), b[1:] } -// gbit16 reads a 16-bit numeric value from a 9P protocol message strored in b, +// gbit16 reads a 16-bit numeric value from a 9P protocol message stored in b, // returning the value and 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 numeric value from a 9P protocol message strored in b, +// gbit32 reads a 32-bit numeric value from a 9P protocol message stored in b, // returning the value and 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 numeric value from a 9P protocol message strored in b, +// gbit64 reads a 64-bit numeric value from a 9P protocol message stored in b, // returning the value and the remaining slice of b. func gbit64(b []byte) (uint64, []byte) { lo, b := gbit32(b) @@ -216,7 +232,7 @@ func gbit64(b []byte) (uint64, []byte) { return uint64(hi)<<32 | uint64(lo), b } -// gstring reads a string from a 9P protocol message strored in b, +// gstring reads a string from a 9P protocol message stored in b, // returning the value as a Go string and the remaining slice of b. func gstring(b []byte) (string, []byte) { n, b := gbit16(b) diff --git a/libgo/go/os/dir_unix.go b/libgo/go/os/dir_unix.go index f5b82230d1b..7835ed52b55 100644 --- a/libgo/go/os/dir_unix.go +++ b/libgo/go/os/dir_unix.go @@ -12,30 +12,40 @@ const ( blockSize = 4096 ) -// Readdirnames reads the contents of the directory associated with file and -// returns an array of up to count names, in directory order. Subsequent -// calls on the same file will yield further names. -// A negative count means to read until EOF. -// Readdirnames returns the array and an Error, if any. -func (file *File) Readdirnames(count int) (names []string, err Error) { +// Readdirnames reads and returns a slice of names from the directory f. +// +// If n > 0, Readdirnames returns at most n names. In this case, if +// Readdirnames returns an empty slice, it will return a non-nil error +// explaining why. At the end of a directory, the error is os.EOF. +// +// If n <= 0, Readdirnames returns all the names from the directory in +// a single slice. In this case, if Readdirnames succeeds (reads all +// the way to the end of the directory), it returns the slice and a +// nil os.Error. If it encounters an error before the end of the +// directory, Readdirnames returns the names read until that point and +// a non-nil error. +func (f *File) Readdirnames(n int) (names []string, err Error) { // If this file has no dirinfo, create one. - if file.dirinfo == nil { - file.dirinfo = new(dirInfo) + if f.dirinfo == nil { + f.dirinfo = new(dirInfo) // The buffer must be at least a block long. - file.dirinfo.buf = make([]byte, blockSize) + f.dirinfo.buf = make([]byte, blockSize) } - d := file.dirinfo - size := count - if size < 0 { + d := f.dirinfo + + size := n + if size <= 0 { size = 100 + n = -1 } + names = make([]string, 0, size) // Empty with room to grow. - for count != 0 { + for n != 0 { // Refill the buffer if necessary if d.bufp >= d.nbuf { d.bufp = 0 var errno int - d.nbuf, errno = syscall.ReadDirent(file.fd, d.buf) + d.nbuf, errno = syscall.ReadDirent(f.fd, d.buf) if errno != 0 { return names, NewSyscallError("readdirent", errno) } @@ -46,9 +56,12 @@ func (file *File) Readdirnames(count int) (names []string, err Error) { // Drain the buffer var nb, nc int - nb, nc, names = syscall.ParseDirent(d.buf[d.bufp:d.nbuf], count, names) + nb, nc, names = syscall.ParseDirent(d.buf[d.bufp:d.nbuf], n, names) d.bufp += nb - count -= nc + n -= nc + } + if n >= 0 && len(names) == 0 { + return names, EOF } return names, nil } diff --git a/libgo/go/os/env_plan9.go b/libgo/go/os/env_plan9.go index 14df55ed0ed..1fed89f9279 100644 --- a/libgo/go/os/env_plan9.go +++ b/libgo/go/os/env_plan9.go @@ -23,13 +23,18 @@ func Getenverror(key string) (value string, err Error) { } defer f.Close() - var buf [4096]byte - n, e := f.Read(buf[:len(buf)-1]) + l, _ := f.Seek(0, 2) + f.Seek(0, 0) + buf := make([]byte, l) + n, e := f.Read(buf) if iserror(e) { return "", ENOENV } - buf[n] = 0 - return string(buf[0:n]), nil + + if n > 0 && buf[n-1] == 0 { + buf = buf[:n-1] + } + return string(buf), nil } // Getenv retrieves the value of the environment variable named by the key. @@ -52,7 +57,7 @@ func Setenv(key, value string) Error { } defer f.Close() - _, e = f.Write(syscall.StringByteSlice(value)) + _, e = f.Write([]byte(value)) return nil } diff --git a/libgo/go/os/env_unix.go b/libgo/go/os/env_unix.go index 8aa71e83a0c..9cc0b03d877 100644 --- a/libgo/go/os/env_unix.go +++ b/libgo/go/os/env_unix.go @@ -16,7 +16,6 @@ var ENOENV = NewError("no such environment variable") var env map[string]string var once sync.Once - func copyenv() { env = make(map[string]string) for _, s := range Envs { diff --git a/libgo/go/os/env_windows.go b/libgo/go/os/env_windows.go index a45d79be32a..e6ddc4065fe 100644 --- a/libgo/go/os/env_windows.go +++ b/libgo/go/os/env_windows.go @@ -119,7 +119,7 @@ func init() { if e != 0 { return } - defer syscall.LocalFree(uint32(uintptr(unsafe.Pointer(argv)))) + defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv)))) Args = make([]string, argc) for i, v := range (*argv)[:argc] { Args[i] = string(syscall.UTF16ToString((*v)[:])) diff --git a/libgo/go/os/error.go b/libgo/go/os/error.go index 2c4516ca7da..b4511dd2f4d 100644 --- a/libgo/go/os/error.go +++ b/libgo/go/os/error.go @@ -9,20 +9,17 @@ type Error interface { String() string } -// A helper type that can be embedded or wrapped to simplify satisfying -// Error. -type ErrorString string +// // errorString is a helper type used by NewError. +type errorString string -func (e ErrorString) String() string { return string(e) } -func (e ErrorString) Temporary() bool { return false } -func (e ErrorString) Timeout() bool { return false } +func (e errorString) String() string { return string(e) } // Note: If the name of the function NewError changes, // pkg/go/doc/doc.go should be adjusted since it hardwires // this name in a heuristic. -// NewError converts s to an ErrorString, which satisfies the Error interface. -func NewError(s string) Error { return ErrorString(s) } +// // NewError returns a new error with error.String() == s. +func NewError(s string) Error { return errorString(s) } // PathError records an error and the operation and file path that caused it. type PathError struct { diff --git a/libgo/go/os/error_plan9.go b/libgo/go/os/error_plan9.go index 3374775b8e7..cacfc150c43 100644 --- a/libgo/go/os/error_plan9.go +++ b/libgo/go/os/error_plan9.go @@ -45,6 +45,7 @@ var ( EEXIST = Eexist EIO = Eio EACCES = Eperm + EPERM = Eperm EISDIR = syscall.EISDIR ENAMETOOLONG = NewError("file name too long") diff --git a/libgo/go/os/error_posix.go b/libgo/go/os/error_posix.go index 0ee34e4b0ef..d43f1786d37 100644 --- a/libgo/go/os/error_posix.go +++ b/libgo/go/os/error_posix.go @@ -13,7 +13,7 @@ type Errno int64 func (e Errno) String() string { return syscall.Errstr(int(e)) } func (e Errno) Temporary() bool { - return e == Errno(syscall.EINTR) || e.Timeout() + return e == Errno(syscall.EINTR) || e == Errno(syscall.EMFILE) || e.Timeout() } func (e Errno) Timeout() bool { diff --git a/libgo/go/os/exec.go b/libgo/go/os/exec.go index f62caf9a069..33e223fd296 100644 --- a/libgo/go/os/exec.go +++ b/libgo/go/os/exec.go @@ -13,10 +13,11 @@ import ( type Process struct { Pid int handle int + done bool // process has been successfuly waited on } func newProcess(pid, handle int) *Process { - p := &Process{pid, handle} + p := &Process{Pid: pid, handle: handle} runtime.SetFinalizer(p, (*Process).Release) return p } @@ -37,6 +38,17 @@ type ProcAttr struct { // depending on the underlying operating system. A nil entry corresponds // to that file being closed when the process starts. Files []*File + + // Operating system-specific process creation attributes. + // Note that setting this field means that your program + // may not execute properly or even compile on some + // operating systems. + Sys *syscall.SysProcAttr +} + +// A Signal can represent any operating system signal. +type Signal interface { + String() string } // Getpid returns the process id of the caller. diff --git a/libgo/go/os/exec_plan9.go b/libgo/go/os/exec_plan9.go index 11874aba677..6f0722a222e 100644 --- a/libgo/go/os/exec_plan9.go +++ b/libgo/go/os/exec_plan9.go @@ -15,6 +15,7 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err E sysattr := &syscall.ProcAttr{ Dir: attr.Dir, Env: attr.Env, + Sys: attr.Sys, } // Create array of integer (system) fds. @@ -37,6 +38,38 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err E return newProcess(pid, h), nil } +// Plan9Note implements the Signal interface on Plan 9. +type Plan9Note string + +func (note Plan9Note) String() string { + return string(note) +} + +func (p *Process) Signal(sig Signal) Error { + if p.done { + return NewError("os: process already finished") + } + + f, e := OpenFile("/proc/"+itoa(p.Pid)+"/note", O_WRONLY, 0) + if iserror(e) { + return NewSyscallError("signal", e) + } + defer f.Close() + _, e = f.Write([]byte(sig.String())) + return e +} + +// Kill causes the Process to exit immediately. +func (p *Process) Kill() Error { + f, e := OpenFile("/proc/"+itoa(p.Pid)+"/ctl", O_WRONLY, 0) + if iserror(e) { + return NewSyscallError("kill", e) + } + defer f.Close() + _, e = f.Write([]byte("kill")) + return e +} + // Exec replaces the current process with an execution of the // named binary, with arguments argv and environment envv. // If successful, Exec never returns. If it fails, it returns an Error. @@ -51,7 +84,9 @@ func Exec(name string, argv []string, envv []string) Error { } // Waitmsg stores the information about an exited process as reported by Wait. -type Waitmsg syscall.Waitmsg +type Waitmsg struct { + syscall.Waitmsg +} // Wait waits for the Process to exit or stop, and then returns a // Waitmsg describing its status and an Error, if any. The options @@ -71,11 +106,12 @@ func (p *Process) Wait(options int) (w *Waitmsg, err Error) { } if waitmsg.Pid == p.Pid { + p.done = true break } } - return (*Waitmsg)(&waitmsg), nil + return &Waitmsg{waitmsg}, nil } // Wait waits for process pid to exit or stop, and then returns a @@ -109,6 +145,9 @@ func FindProcess(pid int) (p *Process, err Error) { return newProcess(pid, 0), nil } -func (w Waitmsg) String() string { +func (w *Waitmsg) String() string { + if w == nil { + return "<nil>" + } return "exit status: " + w.Msg } diff --git a/libgo/go/os/exec_posix.go b/libgo/go/os/exec_posix.go index 9102dc0a4cb..f37bfab589a 100644 --- a/libgo/go/os/exec_posix.go +++ b/libgo/go/os/exec_posix.go @@ -4,28 +4,38 @@ package os -import "syscall" +import ( + "runtime" + "syscall" +) + +type UnixSignal int32 + +func (sig UnixSignal) String() string { + s := runtime.Signame(int32(sig)) + if len(s) > 0 { + return s + } + return "UnixSignal" +} // StartProcess starts a new process with the program, arguments and attributes // specified by name, argv and attr. +// +// StartProcess is a low-level interface. The exec package provides +// higher-level interfaces. func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err Error) { sysattr := &syscall.ProcAttr{ Dir: attr.Dir, Env: attr.Env, + Sys: attr.Sys, } if sysattr.Env == nil { sysattr.Env = Environ() } - // Create array of integer (system) fds. - intfd := make([]int, len(attr.Files)) - for i, f := range attr.Files { - if f == nil { - intfd[i] = -1 - } else { - intfd[i] = f.Fd() - } + for _, f := range attr.Files { + sysattr.Files = append(sysattr.Files, f.Fd()) } - sysattr.Files = intfd pid, h, e := syscall.StartProcess(name, argv, sysattr) if iserror(e) { @@ -34,10 +44,17 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err E return newProcess(pid, h), nil } +// Kill causes the Process to exit immediately. +func (p *Process) Kill() Error { + return p.Signal(SIGKILL) +} + // Exec replaces the current process with an execution of the // named binary, with arguments argv and environment envv. // If successful, Exec never returns. If it fails, it returns an Error. -// StartProcess is almost always a better way to execute a program. +// +// To run a child process, see StartProcess (for a low-level interface) +// or the exec package (for higher-level interfaces). func Exec(name string, argv []string, envv []string) Error { if envv == nil { envv = Environ() @@ -104,7 +121,10 @@ func itod(i int) string { return string(b[bp:]) } -func (w Waitmsg) String() string { +func (w *Waitmsg) String() string { + if w == nil { + return "<nil>" + } // TODO(austin) Use signal names when possible? res := "" switch { diff --git a/libgo/go/os/exec_unix.go b/libgo/go/os/exec_unix.go index 8990d6a97ec..8a4b2e1b806 100644 --- a/libgo/go/os/exec_unix.go +++ b/libgo/go/os/exec_unix.go @@ -38,6 +38,9 @@ func (p *Process) Wait(options int) (w *Waitmsg, err Error) { if e != 0 { return nil, NewSyscallError("wait", e) } + if options&WSTOPPED == 0 { + p.done = true + } w = new(Waitmsg) w.Pid = pid1 w.WaitStatus = status @@ -45,6 +48,17 @@ func (p *Process) Wait(options int) (w *Waitmsg, err Error) { return w, nil } +// Signal sends a signal to the Process. +func (p *Process) Signal(sig Signal) Error { + if p.done { + return NewError("os: process already finished") + } + if e := syscall.Kill(p.Pid, int(sig.(UnixSignal))); e != 0 { + return Errno(e) + } + return nil +} + // Release releases any resources associated with the Process. func (p *Process) Release() Error { // NOOP for unix. diff --git a/libgo/go/os/exec_windows.go b/libgo/go/os/exec_windows.go index ae8ffeab2e9..65e94ac4acc 100644 --- a/libgo/go/os/exec_windows.go +++ b/libgo/go/os/exec_windows.go @@ -10,28 +10,42 @@ import ( ) func (p *Process) Wait(options int) (w *Waitmsg, err Error) { - s, e := syscall.WaitForSingleObject(int32(p.handle), syscall.INFINITE) + s, e := syscall.WaitForSingleObject(syscall.Handle(p.handle), syscall.INFINITE) switch s { case syscall.WAIT_OBJECT_0: break case syscall.WAIT_FAILED: return nil, NewSyscallError("WaitForSingleObject", e) default: - return nil, ErrorString("os: unexpected result from WaitForSingleObject") + return nil, NewError("os: unexpected result from WaitForSingleObject") } var ec uint32 - e = syscall.GetExitCodeProcess(uint32(p.handle), &ec) + e = syscall.GetExitCodeProcess(syscall.Handle(p.handle), &ec) if e != 0 { return nil, NewSyscallError("GetExitCodeProcess", e) } + p.done = true return &Waitmsg{p.Pid, syscall.WaitStatus{s, ec}, new(syscall.Rusage)}, nil } +// Signal sends a signal to the Process. +func (p *Process) Signal(sig Signal) Error { + if p.done { + return NewError("os: process already finished") + } + switch sig.(UnixSignal) { + case SIGKILL: + e := syscall.TerminateProcess(syscall.Handle(p.handle), 1) + return NewSyscallError("TerminateProcess", e) + } + return Errno(syscall.EWINDOWS) +} + func (p *Process) Release() Error { if p.handle == -1 { return EINVAL } - e := syscall.CloseHandle(int32(p.handle)) + e := syscall.CloseHandle(syscall.Handle(p.handle)) if e != 0 { return NewSyscallError("CloseHandle", e) } diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go index dff8fa862ce..4335d45e5a0 100644 --- a/libgo/go/os/file.go +++ b/libgo/go/os/file.go @@ -4,39 +4,17 @@ // Package os provides a platform-independent interface to operating system // functionality. The design is Unix-like. +// The os interface is intended to be uniform across all operating systems. +// Features not generally available appear in the system-specific package syscall. package os import ( - "runtime" - "sync" "syscall" ) -// File represents an open file descriptor. -type File struct { - fd int - name string - dirinfo *dirInfo // nil unless directory being read - nepipe int // number of consecutive EPIPE in Write - l sync.Mutex // used to implement windows pread/pwrite -} - -// Fd returns the integer Unix file descriptor referencing the open file. -func (file *File) Fd() int { return file.fd } - // Name returns the name of the file as presented to Open. func (file *File) Name() string { return file.name } -// NewFile returns a new File with the given file descriptor and name. -func NewFile(fd int, name string) *File { - if fd < 0 { - return nil - } - f := &File{fd: fd, name: name} - runtime.SetFinalizer(f, (*File).Close) - return f -} - // Stdin, Stdout, and Stderr are open Files pointing to the standard input, // standard output, and standard error file descriptors. var ( @@ -185,9 +163,7 @@ func (file *File) WriteString(s string) (ret int, err Error) { if file == nil { return 0, EINVAL } - b := syscall.StringByteSlice(s) - b = b[0 : len(b)-1] - return file.Write(b) + return file.Write([]byte(s)) } // Mkdir creates a new directory with the specified name and permission bits. diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go index 7b473f80221..1e94fb715b9 100644 --- a/libgo/go/os/file_plan9.go +++ b/libgo/go/os/file_plan9.go @@ -9,6 +9,31 @@ import ( "syscall" ) +// File represents an open file descriptor. +type File struct { + fd int + name string + dirinfo *dirInfo // nil unless directory being read +} + +// Fd returns the integer Unix file descriptor referencing the open file. +func (file *File) Fd() int { + if file == nil { + return -1 + } + return file.fd +} + +// NewFile returns a new File with the given file descriptor and name. +func NewFile(fd int, name string) *File { + if fd < 0 { + return nil + } + f := &File{fd: fd, name: name} + runtime.SetFinalizer(f, (*File).Close) + return f +} + // Auxiliary information if the File describes a directory type dirInfo struct { buf [syscall.STATMAX]byte // buffer for directory I/O @@ -19,7 +44,6 @@ type dirInfo struct { func epipecheck(file *File, e syscall.Error) { } - // DevNull is the name of the operating system's ``null device.'' // On Unix-like systems, it is "/dev/null"; on Windows, "NUL". const DevNull = "/dev/null" @@ -30,14 +54,43 @@ const DevNull = "/dev/null" // methods on the returned File can be used for I/O. // It returns the File and an Error, if any. func OpenFile(name string, flag int, perm uint32) (file *File, err Error) { - var fd int - var e syscall.Error + var ( + fd int + e syscall.Error + create bool + excl bool + trunc bool + append bool + ) - syscall.ForkLock.RLock() if flag&O_CREATE == O_CREATE { - fd, e = syscall.Create(name, flag & ^O_CREATE, perm) + flag = flag & ^O_CREATE + create = true + } + if flag&O_EXCL == O_EXCL { + excl = true + } + if flag&O_TRUNC == O_TRUNC { + trunc = true + } + // O_APPEND is emulated on Plan 9 + if flag&O_APPEND == O_APPEND { + flag = flag &^ O_APPEND + append = true + } + + syscall.ForkLock.RLock() + if (create && trunc) || excl { + fd, e = syscall.Create(name, flag, perm) } else { fd, e = syscall.Open(name, flag) + if e != nil && create { + var e1 syscall.Error + fd, e1 = syscall.Create(name, flag, perm) + if e1 == nil { + e = nil + } + } } syscall.ForkLock.RUnlock() @@ -45,6 +98,12 @@ func OpenFile(name string, flag int, perm uint32) (file *File, err Error) { return nil, &PathError{"open", name, e} } + if append { + if _, e = syscall.Seek(fd, 0, SEEK_END); e != nil { + return nil, &PathError{"seek", name, e} + } + } + return NewFile(fd, name), nil } @@ -69,8 +128,12 @@ func (file *File) Close() Error { // Stat returns the FileInfo structure describing file. // It returns the FileInfo and an error, if any. -func (file *File) Stat() (fi *FileInfo, err Error) { - return dirstat(file) +func (f *File) Stat() (fi *FileInfo, err Error) { + d, err := dirstat(f) + if iserror(err) { + return nil, err + } + return fileInfoFromStat(new(FileInfo), d), err } // Truncate changes the size of the file. @@ -90,10 +153,15 @@ func (f *File) Truncate(size int64) Error { // Chmod changes the mode of the file to mode. func (f *File) Chmod(mode uint32) Error { var d Dir - d.Null() + var mask = ^uint32(0777) - d.Mode = mode & 0777 + d.Null() + odir, e := dirstat(f) + if iserror(e) { + return &PathError{"chmod", f.name, e} + } + d.Mode = (odir.Mode & mask) | (mode &^ mask) if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) { return &PathError{"chmod", f.name, e} } @@ -188,26 +256,17 @@ func Rename(oldname, newname string) Error { // Chmod changes the mode of the named file to mode. func Chmod(name string, mode uint32) Error { var d Dir - d.Null() - - d.Mode = mode & 0777 + var mask = ^uint32(0777) - if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) { + d.Null() + odir, e := dirstat(name) + if iserror(e) { return &PathError{"chmod", name, e} } - return nil -} - -// ChownPlan9 changes the uid and gid strings of the named file. -func ChownPlan9(name, uid, gid string) Error { - var d Dir - d.Null() - - d.Uid = uid - d.Gid = gid + d.Mode = (odir.Mode & mask) | (mode &^ mask) if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) { - return &PathError{"chown_plan9", name, e} + return &PathError{"chmod", name, e} } return nil } @@ -244,7 +303,6 @@ func Pipe() (r *File, w *File, err Error) { return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil } - // not supported on Plan 9 // Link creates a hard link. diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go index f1191d61feb..0791a0dc04b 100644 --- a/libgo/go/os/file_posix.go +++ b/libgo/go/os/file_posix.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// The os package provides a platform-independent interface to operating -// system functionality. The design is Unix-like. package os import ( @@ -23,26 +21,6 @@ func epipecheck(file *File, e int) { } } - -// Pipe returns a connected pair of Files; reads from r return bytes written to w. -// It returns the files and an Error, if any. -func Pipe() (r *File, w *File, err Error) { - var p [2]int - - // See ../syscall/exec.go for description of lock. - syscall.ForkLock.RLock() - e := syscall.Pipe(p[0:]) - if iserror(e) { - syscall.ForkLock.RUnlock() - return nil, nil, NewSyscallError("pipe", e) - } - syscall.CloseOnExec(p[0]) - syscall.CloseOnExec(p[1]) - syscall.ForkLock.RUnlock() - - return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil -} - // Stat returns a FileInfo structure describing the named file and an error, if any. // If name names a valid symbolic link, the returned FileInfo describes // the file pointed at by the link and has fi.FollowedSymlink set to true. diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go index 4c69cc83802..8243701ad24 100644 --- a/libgo/go/os/file_unix.go +++ b/libgo/go/os/file_unix.go @@ -9,6 +9,32 @@ import ( "syscall" ) +// File represents an open file descriptor. +type File struct { + fd int + name string + dirinfo *dirInfo // nil unless directory being read + nepipe int // number of consecutive EPIPE in Write +} + +// Fd returns the integer Unix file descriptor referencing the open file. +func (file *File) Fd() int { + if file == nil { + return -1 + } + return file.fd +} + +// NewFile returns a new File with the given file descriptor and name. +func NewFile(fd int, name string) *File { + if fd < 0 { + return nil + } + f := &File{fd: fd, name: name} + runtime.SetFinalizer(f, (*File).Close) + return f +} + // Auxiliary information if the File describes a directory type dirInfo struct { buf []byte // buffer for directory I/O @@ -75,21 +101,27 @@ func (file *File) Stat() (fi *FileInfo, err Error) { } // Readdir reads the contents of the directory associated with file and -// returns an array of up to count FileInfo structures, as would be returned -// by Lstat, in directory order. Subsequent calls on the same file will yield +// returns an array of up to n FileInfo structures, as would be returned +// by Lstat, in directory order. Subsequent calls on the same file will yield // further FileInfos. -// A negative count means to read until EOF. -// Readdir returns the array and an Error, if any. -func (file *File) Readdir(count int) (fi []FileInfo, err Error) { +// +// If n > 0, Readdir returns at most n FileInfo structures. In this case, if +// Readdir returns an empty slice, it will return a non-nil error +// explaining why. At the end of a directory, the error is os.EOF. +// +// If n <= 0, Readdir returns all the FileInfo from the directory in +// a single slice. In this case, if Readdir succeeds (reads all +// the way to the end of the directory), it returns the slice and a +// nil os.Error. If it encounters an error before the end of the +// directory, Readdir returns the FileInfo read until that point +// and a non-nil error. +func (file *File) Readdir(n int) (fi []FileInfo, err Error) { dirname := file.name if dirname == "" { dirname = "." } dirname += "/" - names, err1 := file.Readdirnames(count) - if err1 != nil { - return nil, err1 - } + names, err := file.Readdirnames(n) fi = make([]FileInfo, len(names)) for i, filename := range names { fip, err := Lstat(dirname + filename) @@ -161,3 +193,22 @@ func basename(name string) string { return name } + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an Error, if any. +func Pipe() (r *File, w *File, err Error) { + var p [2]int + + // See ../syscall/exec.go for description of lock. + syscall.ForkLock.RLock() + e := syscall.Pipe(p[0:]) + if iserror(e) { + syscall.ForkLock.RUnlock() + return nil, nil, NewSyscallError("pipe", e) + } + syscall.CloseOnExec(p[0]) + syscall.CloseOnExec(p[1]) + syscall.ForkLock.RUnlock() + + return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil +} diff --git a/libgo/go/os/inotify/inotify_linux.go b/libgo/go/os/inotify/inotify_linux.go index 7c7b7698feb..99fa5162223 100644 --- a/libgo/go/os/inotify/inotify_linux.go +++ b/libgo/go/os/inotify/inotify_linux.go @@ -34,7 +34,6 @@ import ( "unsafe" ) - type Event struct { Mask uint32 // Mask of events Cookie uint32 // Unique cookie associating related events (for rename(2)) @@ -56,7 +55,6 @@ type Watcher struct { isClosed bool // Set to true when Close() is first called } - // NewWatcher creates and returns a new inotify instance using inotify_init(2) func NewWatcher() (*Watcher, os.Error) { fd, errno := syscall.InotifyInit() @@ -76,7 +74,6 @@ func NewWatcher() (*Watcher, os.Error) { return w, nil } - // Close closes an inotify watcher instance // It sends a message to the reader goroutine to quit and removes all watches // associated with the inotify instance @@ -119,13 +116,11 @@ func (w *Watcher) AddWatch(path string, flags uint32) os.Error { return nil } - // Watch adds path to the watched file set, watching all events. func (w *Watcher) Watch(path string) os.Error { return w.AddWatch(path, IN_ALL_EVENTS) } - // RemoveWatch removes path from the watched file set. func (w *Watcher) RemoveWatch(path string) os.Error { watch, ok := w.watches[path] @@ -140,7 +135,6 @@ func (w *Watcher) RemoveWatch(path string) os.Error { return nil } - // readEvents reads from the inotify file descriptor, converts the // received events into Event objects and sends them via the Event channel func (w *Watcher) readEvents() { @@ -208,7 +202,6 @@ func (w *Watcher) readEvents() { } } - // String formats the event e in the form // "filename: 0xEventMask = IN_ACCESS|IN_ATTRIB_|..." func (e *Event) String() string { diff --git a/libgo/go/os/inotify/inotify_linux_test.go b/libgo/go/os/inotify/inotify_linux_test.go index e29a46d6c2d..aa72604eb92 100644 --- a/libgo/go/os/inotify/inotify_linux_test.go +++ b/libgo/go/os/inotify/inotify_linux_test.go @@ -74,7 +74,6 @@ func TestInotifyEvents(t *testing.T) { } } - func TestInotifyClose(t *testing.T) { watcher, _ := NewWatcher() watcher.Close() diff --git a/libgo/go/os/signal/mkunix.sh b/libgo/go/os/mkunixsignals.sh index ec5c9d6808e..3487bc3bc3c 100644 --- a/libgo/go/os/signal/mkunix.sh +++ b/libgo/go/os/mkunixsignals.sh @@ -8,13 +8,13 @@ echo '// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT' echo cat <<EOH -package signal +package os import ( "syscall" ) -var _ = syscall.Syscall // in case there are zero signals +var _ = syscall.Open // in case there are zero signals const ( EOH diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go index 8df69626113..3f7d3063106 100644 --- a/libgo/go/os/os_test.go +++ b/libgo/go/os/os_test.go @@ -234,11 +234,14 @@ func smallReaddirnames(file *File, length int, t *testing.T) []string { count := 0 for { d, err := file.Readdirnames(1) + if err == EOF { + break + } if err != nil { - t.Fatalf("readdir %q failed: %v", file.Name(), err) + t.Fatalf("readdirnames %q failed: %v", file.Name(), err) } if len(d) == 0 { - break + t.Fatalf("readdirnames %q returned empty slice and no error", file.Name()) } names[count] = d[0] count++ @@ -281,9 +284,81 @@ func TestReaddirnamesOneAtATime(t *testing.T) { } } +func TestReaddirNValues(t *testing.T) { + if testing.Short() { + t.Logf("test.short; skipping") + return + } + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatalf("TempDir: %v", err) + } + defer RemoveAll(dir) + for i := 1; i <= 105; i++ { + f, err := Create(filepath.Join(dir, fmt.Sprintf("%d", i))) + if err != nil { + t.Fatalf("Create: %v", err) + } + f.Write([]byte(strings.Repeat("X", i))) + f.Close() + } + + var d *File + openDir := func() { + var err Error + d, err = Open(dir) + if err != nil { + t.Fatalf("Open directory: %v", err) + } + } + + readDirExpect := func(n, want int, wantErr Error) { + fi, err := d.Readdir(n) + if err != wantErr { + t.Fatalf("Readdir of %d got error %v, want %v", n, err, wantErr) + } + if g, e := len(fi), want; g != e { + t.Errorf("Readdir of %d got %d files, want %d", n, g, e) + } + } + + readDirNamesExpect := func(n, want int, wantErr Error) { + fi, err := d.Readdirnames(n) + if err != wantErr { + t.Fatalf("Readdirnames of %d got error %v, want %v", n, err, wantErr) + } + if g, e := len(fi), want; g != e { + t.Errorf("Readdirnames of %d got %d files, want %d", n, g, e) + } + } + + for _, fn := range []func(int, int, Error){readDirExpect, readDirNamesExpect} { + // Test the slurp case + openDir() + fn(0, 105, nil) + fn(0, 0, nil) + d.Close() + + // Slurp with -1 instead + openDir() + fn(-1, 105, nil) + fn(-2, 0, nil) + fn(0, 0, nil) + d.Close() + + // Test the bounded case + openDir() + fn(1, 1, nil) + fn(2, 2, nil) + fn(105, 102, nil) // and tests buffer >100 case + fn(3, 0, EOF) + d.Close() + } +} + func TestHardLink(t *testing.T) { - // Hardlinks are not supported under windows. - if syscall.OS == "windows" { + // Hardlinks are not supported under windows or Plan 9. + if syscall.OS == "windows" || syscall.OS == "plan9" { return } from, to := "hardlinktestfrom", "hardlinktestto" @@ -315,8 +390,8 @@ func TestHardLink(t *testing.T) { } func TestSymLink(t *testing.T) { - // Symlinks are not supported under windows. - if syscall.OS == "windows" { + // Symlinks are not supported under windows or Plan 9. + if syscall.OS == "windows" || syscall.OS == "plan9" { return } from, to := "symlinktestfrom", "symlinktestto" @@ -377,8 +452,8 @@ func TestSymLink(t *testing.T) { } func TestLongSymlink(t *testing.T) { - // Symlinks are not supported under windows. - if syscall.OS == "windows" { + // Symlinks are not supported under windows or Plan 9. + if syscall.OS == "windows" || syscall.OS == "plan9" { return } s := "0123456789abcdef" @@ -511,8 +586,9 @@ func checkUidGid(t *testing.T, path string, uid, gid int) { } func TestChown(t *testing.T) { - // Chown is not supported under windows. - if syscall.OS == "windows" { + // Chown is not supported under windows or Plan 9. + // Plan9 provides a native ChownPlan9 version instead. + if syscall.OS == "windows" || syscall.OS == "plan9" { return } // Use TempDir() to make sure we're on a local file system, @@ -631,7 +707,11 @@ func TestChtimes(t *testing.T) { t.Fatalf("second Stat %s: %s", f.Name(), err) } - if postStat.Atime_ns >= preStat.Atime_ns { + /* Plan 9: + Mtime is the time of the last change of content. Similarly, atime is set whenever the + contents are accessed; also, it is set whenever mtime is set. + */ + if postStat.Atime_ns >= preStat.Atime_ns && syscall.OS != "plan9" { t.Errorf("Atime_ns didn't go backwards; was=%d, after=%d", preStat.Atime_ns, postStat.Atime_ns) @@ -656,6 +736,10 @@ func TestChdirAndGetwd(t *testing.T) { // These are chosen carefully not to be symlinks on a Mac // (unlike, say, /var, /etc, and /tmp). dirs := []string{"/", "/usr/bin"} + // /usr/bin does not usually exist on Plan 9. + if syscall.OS == "plan9" { + dirs = []string{"/", "/usr"} + } for mode := 0; mode < 2; mode++ { for _, d := range dirs { if mode == 0 { @@ -781,7 +865,15 @@ func TestOpenError(t *testing.T) { t.Errorf("Open(%q, %d) returns error of %T type; want *os.PathError", tt.path, tt.mode, err) } if perr.Error != tt.error { - t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, perr.Error.String(), tt.error.String()) + if syscall.OS == "plan9" { + syscallErrStr := perr.Error.String() + expectedErrStr := strings.Replace(tt.error.String(), "file ", "", 1) + if !strings.HasSuffix(syscallErrStr, expectedErrStr) { + t.Errorf("Open(%q, %d) = _, %q; want suffix %q", tt.path, tt.mode, syscallErrStr, expectedErrStr) + } + } else { + t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, perr.Error.String(), tt.error.String()) + } } } } @@ -801,7 +893,14 @@ func run(t *testing.T, cmd []string) string { var b bytes.Buffer io.Copy(&b, r) - p.Wait(0) + _, err = p.Wait(0) + if err != nil { + t.Fatalf("run hostname Wait: %v", err) + } + err = p.Kill() + if err == nil { + t.Errorf("expected an error from Kill running 'hostname'") + } output := b.String() if n := len(output); n > 0 && output[n-1] == '\n' { output = output[0 : n-1] @@ -813,10 +912,10 @@ func run(t *testing.T, cmd []string) string { return output } - func TestHostname(t *testing.T) { // There is no other way to fetch hostname on windows, but via winapi. - if syscall.OS == "windows" { + // On Plan 9 it is can be taken from #c/sysname as Hostname() does. + if syscall.OS == "windows" || syscall.OS == "plan9" { return } // Check internal Hostname() against the output of /bin/hostname. @@ -913,7 +1012,15 @@ func TestAppend(t *testing.T) { } s = writeFile(t, f, O_CREATE|O_APPEND|O_RDWR, "new&append") if s != "new&append" { - t.Fatalf("writeFile: have %q want %q", s, "new&append") + t.Fatalf("writeFile: after append have %q want %q", s, "new&append") + } + s = writeFile(t, f, O_CREATE|O_RDWR, "old") + if s != "old&append" { + t.Fatalf("writeFile: after create have %q want %q", s, "old&append") + } + s = writeFile(t, f, O_CREATE|O_TRUNC|O_RDWR, "new") + if s != "new" { + t.Fatalf("writeFile: after truncate have %q want %q", s, "new") } } @@ -939,3 +1046,11 @@ func TestStatDirWithTrailingSlash(t *testing.T) { t.Fatal("stat failed:", err) } } + +func TestNilWaitmsgString(t *testing.T) { + var w *Waitmsg + s := w.String() + if s != "<nil>" { + t.Errorf("(*Waitmsg)(nil).String() = %q, want %q", s, "<nil>") + } +} diff --git a/libgo/go/os/path.go b/libgo/go/os/path.go index 0eb3ee50369..a8dfce30752 100644 --- a/libgo/go/os/path.go +++ b/libgo/go/os/path.go @@ -4,7 +4,6 @@ package os - // MkdirAll creates a directory named path, // along with any necessary parents, and returns nil, // or else returns an error. @@ -24,12 +23,12 @@ func MkdirAll(path string, perm uint32) Error { // Doesn't already exist; make sure parent does. i := len(path) - for i > 0 && path[i-1] == '/' { // Skip trailing slashes. + for i > 0 && IsPathSeparator(path[i-1]) { // Skip trailing path separator. i-- } j := i - for j > 0 && path[j-1] != '/' { // Scan backward over element. + for j > 0 && !IsPathSeparator(path[j-1]) { // Scan backward over element. j-- } @@ -90,11 +89,14 @@ func RemoveAll(path string) Error { for { names, err1 := fd.Readdirnames(100) for _, name := range names { - err1 := RemoveAll(path + "/" + name) + err1 := RemoveAll(path + string(PathSeparator) + name) if err == nil { err = err1 } } + if err1 == EOF { + break + } // If Readdirnames returned an error, use it. if err == nil { err = err1 diff --git a/libgo/go/os/path_plan9.go b/libgo/go/os/path_plan9.go new file mode 100644 index 00000000000..3121b7bc712 --- /dev/null +++ b/libgo/go/os/path_plan9.go @@ -0,0 +1,15 @@ +// 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 os + +const ( + PathSeparator = '/' // OS-specific path separator + PathListSeparator = 0 // OS-specific path list separator +) + +// IsPathSeparator returns true if c is a directory separator character. +func IsPathSeparator(c uint8) bool { + return PathSeparator == c +} diff --git a/libgo/go/os/path_test.go b/libgo/go/os/path_test.go index 483bb639535..31acbaa4356 100644 --- a/libgo/go/os/path_test.go +++ b/libgo/go/os/path_test.go @@ -6,6 +6,7 @@ package os_test import ( . "os" + "path/filepath" "testing" "runtime" "syscall" @@ -29,10 +30,11 @@ func TestMkdirAll(t *testing.T) { // Make file. fpath := path + "/file" - _, err = Create(fpath) + f, err := Create(fpath) if err != nil { t.Fatalf("create %q: %s", fpath, err) } + defer f.Close() // Can't make directory named after file. err = MkdirAll(fpath, 0777) @@ -43,8 +45,8 @@ func TestMkdirAll(t *testing.T) { if !ok { t.Fatalf("MkdirAll %q returned %T, not *PathError", fpath, err) } - if perr.Path != fpath { - t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", fpath, perr.Path, fpath) + if filepath.Clean(perr.Path) != filepath.Clean(fpath) { + t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", fpath, filepath.Clean(perr.Path), filepath.Clean(fpath)) } // Can't make subdirectory of file. @@ -57,8 +59,16 @@ func TestMkdirAll(t *testing.T) { if !ok { t.Fatalf("MkdirAll %q returned %T, not *PathError", ffpath, err) } - if perr.Path != fpath { - t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", ffpath, perr.Path, fpath) + if filepath.Clean(perr.Path) != filepath.Clean(fpath) { + t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", ffpath, filepath.Clean(perr.Path), filepath.Clean(fpath)) + } + + if syscall.OS == "windows" { + path := `_test\_TestMkdirAll_\dir\.\dir2\` + err := MkdirAll(path, 0777) + if err != nil { + t.Fatalf("MkdirAll %q: %s", path, err) + } } } @@ -156,8 +166,8 @@ func TestRemoveAll(t *testing.T) { } func TestMkdirAllWithSymlink(t *testing.T) { - if runtime.GOOS == "windows" { - t.Log("Skipping test: symlinks don't exist under Windows") + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { + t.Log("Skipping test: symlinks don't exist under Windows/Plan 9") return } @@ -181,7 +191,7 @@ func TestMkdirAllWithSymlink(t *testing.T) { } func TestMkdirAllAtSlash(t *testing.T) { - if runtime.GOOS == "windows" { + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return } RemoveAll("/_go_os_test") diff --git a/libgo/go/os/path_unix.go b/libgo/go/os/path_unix.go new file mode 100644 index 00000000000..0d327cddd3e --- /dev/null +++ b/libgo/go/os/path_unix.go @@ -0,0 +1,15 @@ +// 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 os + +const ( + PathSeparator = '/' // OS-specific path separator + PathListSeparator = ':' // OS-specific path list separator +) + +// IsPathSeparator returns true if c is a directory separator character. +func IsPathSeparator(c uint8) bool { + return PathSeparator == c +} diff --git a/libgo/go/os/path_windows.go b/libgo/go/os/path_windows.go new file mode 100644 index 00000000000..61f2ca59ff4 --- /dev/null +++ b/libgo/go/os/path_windows.go @@ -0,0 +1,16 @@ +// 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 os + +const ( + PathSeparator = '\\' // OS-specific path separator + PathListSeparator = ';' // OS-specific path list separator +) + +// IsPathSeparator returns true if c is a directory separator character. +func IsPathSeparator(c uint8) bool { + // NOTE: Windows accept / as path separator. + return c == '\\' || c == '/' +} diff --git a/libgo/go/os/proc.go b/libgo/go/os/proc.go index 481ef603371..dfe388f2502 100644 --- a/libgo/go/os/proc.go +++ b/libgo/go/os/proc.go @@ -11,7 +11,6 @@ import "syscall" var Args []string // provided by runtime var Envs []string // provided by runtime - // Getuid returns the numeric user id of the caller. func Getuid() int { return syscall.Getuid() } diff --git a/libgo/go/os/signal/signal.go b/libgo/go/os/signal/signal.go index 666c03e73c4..520f3f8a9ea 100644 --- a/libgo/go/os/signal/signal.go +++ b/libgo/go/os/signal/signal.go @@ -6,35 +6,20 @@ package signal import ( + "os" "runtime" - "strconv" ) -// A Signal can represent any operating system signal. -type Signal interface { - String() string -} - -type UnixSignal int32 - -func (sig UnixSignal) String() string { - s := runtime.Signame(int32(sig)) - if len(s) > 0 { - return s - } - return "Signal " + strconv.Itoa(int(sig)) -} - // Incoming is the global signal channel. // All signals received by the program will be delivered to this channel. -var Incoming <-chan Signal +var Incoming <-chan os.Signal -func process(ch chan<- Signal) { +func process(ch chan<- os.Signal) { for { var mask uint32 = runtime.Sigrecv() for sig := uint(0); sig < 32; sig++ { if mask&(1<<sig) != 0 { - ch <- UnixSignal(sig) + ch <- os.UnixSignal(sig) } } } @@ -42,7 +27,7 @@ func process(ch chan<- Signal) { func init() { runtime.Siginit() - ch := make(chan Signal) // Done here so Incoming can have type <-chan Signal + ch := make(chan os.Signal) // Done here so Incoming can have type <-chan Signal Incoming = ch go process(ch) } diff --git a/libgo/go/os/signal/signal_test.go b/libgo/go/os/signal/signal_test.go index f2679f14dc6..00eb29578f9 100644 --- a/libgo/go/os/signal/signal_test.go +++ b/libgo/go/os/signal/signal_test.go @@ -5,6 +5,7 @@ package signal import ( + "os" "syscall" "testing" ) @@ -13,7 +14,7 @@ func TestSignal(t *testing.T) { // Send this process a SIGHUP. syscall.Syscall(syscall.SYS_KILL, uintptr(syscall.Getpid()), syscall.SIGHUP, 0) - if sig := (<-Incoming).(UnixSignal); sig != SIGHUP { - t.Errorf("signal was %v, want %v", sig, SIGHUP) + if sig := (<-Incoming).(os.UnixSignal); sig != os.SIGHUP { + t.Errorf("signal was %v, want %v", sig, os.SIGHUP) } } diff --git a/libgo/go/os/stat_openbsd.go b/libgo/go/os/stat_openbsd.go new file mode 100644 index 00000000000..6d3a3813b09 --- /dev/null +++ b/libgo/go/os/stat_openbsd.go @@ -0,0 +1,32 @@ +// 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. + +package os + +import "syscall" + +func isSymlink(stat *syscall.Stat_t) bool { + return stat.Mode&syscall.S_IFMT == syscall.S_IFLNK +} + +func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *FileInfo { + fi.Dev = uint64(stat.Dev) + fi.Ino = uint64(stat.Ino) + fi.Nlink = uint64(stat.Nlink) + fi.Mode = uint32(stat.Mode) + fi.Uid = int(stat.Uid) + fi.Gid = int(stat.Gid) + fi.Rdev = uint64(stat.Rdev) + fi.Size = int64(stat.Size) + fi.Blksize = int64(stat.Blksize) + fi.Blocks = stat.Blocks + fi.Atime_ns = syscall.TimespecToNsec(stat.Atim) + fi.Mtime_ns = syscall.TimespecToNsec(stat.Mtim) + fi.Ctime_ns = syscall.TimespecToNsec(stat.Ctim) + fi.Name = basename(name) + if isSymlink(lstat) && !isSymlink(stat) { + fi.FollowedSymlink = true + } + return fi +} diff --git a/libgo/go/os/stat_plan9.go b/libgo/go/os/stat_plan9.go index e96749d33f3..173a23f8bde 100644 --- a/libgo/go/os/stat_plan9.go +++ b/libgo/go/os/stat_plan9.go @@ -26,7 +26,7 @@ func fileInfoFromStat(fi *FileInfo, d *Dir) *FileInfo { } // arg is an open *File or a path string. -func dirstat(arg interface{}) (fi *FileInfo, err Error) { +func dirstat(arg interface{}) (d *Dir, err Error) { var name string nd := syscall.STATFIXLEN + 16*4 @@ -62,23 +62,29 @@ func dirstat(arg interface{}) (fi *FileInfo, err Error) { if e != nil { return nil, &PathError{"stat", name, e} } - - return fileInfoFromStat(new(FileInfo), d), nil + return d, e } } return nil, &PathError{"stat", name, Ebadstat} } - // Stat returns a FileInfo structure describing the named file and an error, if any. func Stat(name string) (fi *FileInfo, err Error) { - return dirstat(name) + d, err := dirstat(name) + if iserror(err) { + return nil, err + } + return fileInfoFromStat(new(FileInfo), d), err } // Lstat returns the FileInfo structure describing the named file and an // error, if any. If the file is a symbolic link (though Plan 9 does not have symbolic links), // the returned FileInfo describes the symbolic link. Lstat makes no attempt to follow the link. func Lstat(name string) (fi *FileInfo, err Error) { - return dirstat(name) + d, err := dirstat(name) + if iserror(err) { + return nil, err + } + return fileInfoFromStat(new(FileInfo), d), err } diff --git a/libgo/go/os/str.go b/libgo/go/os/str.go new file mode 100644 index 00000000000..8dc9e4747df --- /dev/null +++ b/libgo/go/os/str.go @@ -0,0 +1,20 @@ +// 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. + +package os + +func itoa(val int) string { // do it here rather than with fmt to avoid dependency + if val < 0 { + return "-" + itoa(-val) + } + var buf [32]byte // big enough for int64 + i := len(buf) - 1 + for val >= 10 { + buf[i] = byte(val%10 + '0') + i-- + val /= 10 + } + buf[i] = byte(val + '0') + return string(buf[i:]) +} diff --git a/libgo/go/os/sys_linux.go b/libgo/go/os/sys_linux.go index 408d667c7cf..2accd6c1f0d 100644 --- a/libgo/go/os/sys_linux.go +++ b/libgo/go/os/sys_linux.go @@ -6,7 +6,6 @@ package os - // Hostname returns the host name reported by the kernel. func Hostname() (name string, err Error) { f, err := Open("/proc/sys/kernel/hostname") diff --git a/libgo/go/os/sys_plan9.go b/libgo/go/os/sys_plan9.go index f6af28b6116..c24cde05ec0 100644 --- a/libgo/go/os/sys_plan9.go +++ b/libgo/go/os/sys_plan9.go @@ -6,7 +6,6 @@ package os - func Hostname() (name string, err Error) { f, err := Open("#c/sysname") if err != nil { diff --git a/libgo/go/os/time.go b/libgo/go/os/time.go index 8e87a49e162..949574d19a0 100644 --- a/libgo/go/os/time.go +++ b/libgo/go/os/time.go @@ -6,7 +6,6 @@ package os import "syscall" - // Time returns the current time, in whole seconds and // fractional nanoseconds, plus an Error if any. The current // time is thus 1e9*sec+nsec, in nanoseconds. The zero of diff --git a/libgo/go/os/types.go b/libgo/go/os/types.go index 79f6e9d4976..df57b59a388 100644 --- a/libgo/go/os/types.go +++ b/libgo/go/os/types.go @@ -27,7 +27,7 @@ type FileInfo struct { Atime_ns int64 // access time; nanoseconds since epoch. Mtime_ns int64 // modified time; nanoseconds since epoch. Ctime_ns int64 // status change time; nanoseconds since epoch. - Name string // name of file as presented to Open. + Name string // base name of the file name provided in Open, Stat, etc. FollowedSymlink bool // followed a symlink to get this information } diff --git a/libgo/go/os/user/lookup_unix.go b/libgo/go/os/user/lookup_unix.go index 7060530154a..0f04012c02f 100644 --- a/libgo/go/os/user/lookup_unix.go +++ b/libgo/go/os/user/lookup_unix.go @@ -27,6 +27,10 @@ static int mygetpwuid_r(int uid, struct passwd *pwd, func libc_getpwnam_r(name *byte, pwd *syscall.Passwd, buf *byte, buflen syscall.Size_t, result **syscall.Passwd) int __asm__ ("getpwnam_r") func libc_getpwuid_r(uid syscall.Uid_t, pwd *syscall.Passwd, buf *byte, buflen syscall.Size_t, result **syscall.Passwd) int __asm__ ("getpwuid_r") +func init() { + implemented = true +} + // Lookup looks up a user by username. If the user cannot be found, // the returned error is of type UnknownUserError. func Lookup(username string) (*User, os.Error) { diff --git a/libgo/go/os/user/user.go b/libgo/go/os/user/user.go index dd009211d76..f71e11d8b21 100644 --- a/libgo/go/os/user/user.go +++ b/libgo/go/os/user/user.go @@ -9,6 +9,8 @@ import ( "strconv" ) +var implemented = false // set to true by lookup_unix.go's init + // User represents a user account. type User struct { Uid int // user id diff --git a/libgo/go/os/user/user_test.go b/libgo/go/os/user/user_test.go index 2c142bf1817..59f15e4c675 100644 --- a/libgo/go/os/user/user_test.go +++ b/libgo/go/os/user/user_test.go @@ -13,8 +13,8 @@ import ( ) func skip(t *testing.T) bool { - if runtime.GOARCH == "arm" { - t.Logf("user: cgo not implemented on arm; skipping tests") + if !implemented { + t.Logf("user: not implemented; skipping tests") return true } @@ -42,7 +42,7 @@ func TestLookup(t *testing.T) { } fi, err := os.Stat(u.HomeDir) if err != nil || !fi.IsDirectory() { - t.Errorf("expected a valid HomeDir; stat(%q): err=%v, IsDirectory=%v", err, fi.IsDirectory()) + t.Errorf("expected a valid HomeDir; stat(%q): err=%v, IsDirectory=%v", u.HomeDir, err, fi.IsDirectory()) } if u.Username == "" { t.Fatalf("didn't get a username") @@ -56,6 +56,6 @@ func TestLookup(t *testing.T) { if !reflect.DeepEqual(u, un) { t.Errorf("Lookup by userid vs. name didn't match\n"+ "LookupId(%d): %#v\n"+ - "Lookup(%q): %#v\n",uid, u, u.Username, un) + "Lookup(%q): %#v\n", uid, u, u.Username, un) } } |