dir_gccgo.go 2.27 KB
Newer Older
1 2 3 4 5 6 7 8
// 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 (
	"io"
9
	"runtime"
10 11 12 13 14 15 16 17 18
	"sync/atomic"
	"syscall"
	"unsafe"
)

// FIXME: pathconf returns long, not int.
//extern pathconf
func libc_pathconf(*byte, int) int

19 20 21
//extern fdopendir
func libc_fdopendir(int32) *syscall.DIR

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
func clen(n []byte) int {
	for i := 0; i < len(n); i++ {
		if n[i] == 0 {
			return i
		}
	}
	return len(n)
}

var nameMax int32

func (file *File) readdirnames(n int) (names []string, err error) {
	if file.dirinfo == nil {
		p, err := syscall.BytePtrFromString(file.name)
		if err != nil {
			return nil, err
		}

		elen := int(atomic.LoadInt32(&nameMax))
		if elen == 0 {
			syscall.Entersyscall()
			plen := libc_pathconf(p, syscall.PC_NAME_MAX)
			syscall.Exitsyscall()
			if plen < 1024 {
				plen = 1024
			}
			var dummy syscall.Dirent
			elen = int(unsafe.Offsetof(dummy.Name)) + plen + 1
			atomic.StoreInt32(&nameMax, int32(elen))
		}

		syscall.Entersyscall()
54
		r := libc_fdopendir(int32(file.pfd.Sysfd))
55 56 57
		errno := syscall.GetErrno()
		syscall.Exitsyscall()
		if r == nil {
58
			return nil, &PathError{"fdopendir", file.name, errno}
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
		}

		file.dirinfo = new(dirInfo)
		file.dirinfo.buf = make([]byte, elen)
		file.dirinfo.dir = r
	}

	entryDirent := (*syscall.Dirent)(unsafe.Pointer(&file.dirinfo.buf[0]))

	size := n
	if size <= 0 {
		size = 100
		n = -1
	}

	names = make([]string, 0, size) // Empty with room to grow.

	for n != 0 {
		var dirent *syscall.Dirent
		pr := &dirent
		syscall.Entersyscall()
		i := libc_readdir_r(file.dirinfo.dir, entryDirent, pr)
		syscall.Exitsyscall()
82 83 84 85 86
		// On AIX when readdir_r hits EOF it sets dirent to nil and returns 9.
		//  https://www.ibm.com/support/knowledgecenter/ssw_aix_71/com.ibm.aix.basetrf2/readdir_r.htm
		if runtime.GOOS == "aix" && i == 9 && dirent == nil {
			break
		}
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
		if i != 0 {
			return names, NewSyscallError("readdir_r", i)
		}
		if dirent == nil {
			break // EOF
		}
		bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
		var name = string(bytes[0:clen(bytes[:])])
		if name == "." || name == ".." { // Useless names
			continue
		}
		names = append(names, name)
		n--
	}
	if n >= 0 && len(names) == 0 {
		return names, io.EOF
	}
	return names, nil
}