go/src/lib/os/os_file.go
Rob Pike d94c5aba12 Fix Readdirnames to behave properly if reading in little pieces. Requires storing some
state in the FD.

This is Darwin only.  Next CL will make Readdir use Readdirnames to generate its files
and move Readdir into portable code, as well as fix Readdirnames for Linux.

R=rsc
DELTA=116  (79 added, 12 deleted, 25 changed)
OCL=24756
CL=24768
2009-02-10 11:27:45 -08:00

163 lines
3.5 KiB
Go

// 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 "syscall"
import os "os"
// Auxiliary information if the FD describes a directory
type DirInfo struct { // TODO(r): 6g bug means this can't be private
buf []byte; // buffer for directory I/O
nbuf int64; // length of buf; return value from Getdirentries
bufp int64; // location of next record in buf.
}
// FDs are wrappers for file descriptors
type FD struct {
fd int64;
name string;
dirinfo *DirInfo; // nil unless directory being read
}
func (fd *FD) Fd() int64 {
return fd.fd
}
func (fd *FD) Name() string {
return fd.name
}
func NewFD(fd int64, name string) *FD {
if fd < 0 {
return nil
}
return &FD{fd, name, nil}
}
var (
Stdin = NewFD(0, "/dev/stdin");
Stdout = NewFD(1, "/dev/stdout");
Stderr = NewFD(2, "/dev/stderr");
)
const (
O_RDONLY = syscall.O_RDONLY;
O_WRONLY = syscall.O_WRONLY;
O_RDWR = syscall.O_RDWR;
O_APPEND = syscall.O_APPEND;
O_ASYNC = syscall.O_ASYNC;
O_CREAT = syscall.O_CREAT;
O_NOCTTY = syscall.O_NOCTTY;
O_NONBLOCK = syscall.O_NONBLOCK;
O_NDELAY = O_NONBLOCK;
O_SYNC = syscall.O_SYNC;
O_TRUNC = syscall.O_TRUNC;
)
func Open(name string, mode int, flags int) (fd *FD, err *Error) {
r, e := syscall.Open(name, int64(mode), int64(flags));
return NewFD(r, name), ErrnoToError(e)
}
func (fd *FD) Close() *Error {
if fd == nil {
return EINVAL
}
r, e := syscall.Close(fd.fd);
fd.fd = -1; // so it can't be closed again
return ErrnoToError(e)
}
func (fd *FD) Read(b []byte) (ret int, err *Error) {
if fd == nil {
return 0, EINVAL
}
var r, e int64;
if len(b) > 0 { // because we access b[0]
r, e = syscall.Read(fd.fd, &b[0], int64(len(b)));
if r < 0 {
r = 0
}
}
return int(r), ErrnoToError(e)
}
func (fd *FD) Write(b []byte) (ret int, err *Error) {
if fd == nil {
return 0, EINVAL
}
var r, e int64;
if len(b) > 0 { // because we access b[0]
r, e = syscall.Write(fd.fd, &b[0], int64(len(b)));
if r < 0 {
r = 0
}
}
return int(r), ErrnoToError(e)
}
func (fd *FD) Seek(offset int64, whence int) (ret int64, err *Error) {
r, e := syscall.Seek(fd.fd, offset, int64(whence));
if e != 0 {
return -1, ErrnoToError(e)
}
if fd.dirinfo != nil && r != 0 {
return -1, ErrnoToError(syscall.EISDIR)
}
return r, nil
}
func (fd *FD) WriteString(s string) (ret int, err *Error) {
if fd == nil {
return 0, EINVAL
}
r, e := syscall.Write(fd.fd, syscall.StringBytePtr(s), int64(len(s)));
if r < 0 {
r = 0
}
return int(r), ErrnoToError(e)
}
func Pipe() (fd1 *FD, fd2 *FD, err *Error) {
var p [2]int64;
r, e := syscall.Pipe(&p);
if e != 0 {
return nil, nil, ErrnoToError(e)
}
return NewFD(p[0], "|0"), NewFD(p[1], "|1"), nil
}
func Mkdir(name string, perm int) *Error {
r, e := syscall.Mkdir(name, int64(perm));
return ErrnoToError(e)
}
func Stat(name string) (dir *Dir, err *Error) {
stat := new(syscall.Stat_t);
r, e := syscall.Stat(name, stat);
if e != 0 {
return nil, ErrnoToError(e)
}
return dirFromStat(name, new(Dir), stat), nil
}
func Fstat(fd *FD) (dir *Dir, err *Error) {
stat := new(syscall.Stat_t);
r, e := syscall.Fstat(fd.fd, stat);
if e != 0 {
return nil, ErrnoToError(e)
}
return dirFromStat(fd.name, new(Dir), stat), nil
}
func Lstat(name string) (dir *Dir, err *Error) {
stat := new(syscall.Stat_t);
r, e := syscall.Lstat(name, stat);
if e != 0 {
return nil, ErrnoToError(e)
}
return dirFromStat(name, new(Dir), stat), nil
}