~bzr-pqm/bzr/bzr.dev

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# Copyright (C) 2006, 2008 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

"""Wrapper for readdir which returns files ordered by inode."""


import os
import sys


# the opaque C library DIR type.
cdef extern from 'errno.h':
    int ENOENT
    int ENOTDIR
    int EAGAIN
    int errno
    char *strerror(int errno)

cdef extern from 'sys/types.h':
    ctypedef long ssize_t
    ctypedef unsigned long size_t

cdef extern from 'dirent.h':
    int DT_UNKNOWN
    int DT_REG
    int DT_DIR
    int DT_FIFO
    int DT_SOCK
    int DT_CHR
    int DT_BLK
    ctypedef struct dirent:
        char d_name[256]
        # this will fail to compile if d_type is not defined.
        # if this module fails to compile, use the .py version.
        unsigned char d_type
        int d_ino
    ctypedef struct DIR
    # should be DIR *, pyrex barfs.
    DIR * opendir(char * name)
    int closedir(DIR * dir)
    dirent *readdir(DIR *dir)

_directory = 'directory'
_chardev = 'chardev'
_block = 'block'
_file = 'file'
_fifo = 'fifo'
_symlink = 'symlink'
_socket = 'socket'
_unknown = 'unknown'

dot = ord('.')

# add a typedef struct dirent dirent to workaround pyrex
cdef extern from 'readdir.h':
    pass

def read_dir(path):
    """Like os.listdir, this reads the contents of a directory.

    :param path: the directory to list.
    :return: a list of (sort_key, basename) tuples.
    """
    cdef DIR *the_dir
    # currently this needs a fixup - the C code says 'dirent' but should say
    # 'struct dirent'
    cdef dirent * entry
    cdef dirent sentinel
    cdef char *name
    the_dir = opendir(path)
    if NULL == the_dir:
        raise OSError(errno, strerror(errno))
    result = []
    try:
        entry = &sentinel
        while entry != NULL:
            entry = readdir(the_dir)
            if entry == NULL:
                if errno == EAGAIN:
                    # try again
                    continue
                elif errno != ENOTDIR and errno != ENOENT and errno != 0:
                    # We see ENOTDIR at the end of a normal directory.
                    # As ENOTDIR for read_dir(file) is triggered on opendir,
                    # we consider ENOTDIR to be 'no error'.
                    # ENOENT is listed as 'invalid position in the dir stream' for
                    # readdir. We swallow this for now and just keep reading.
                    raise OSError(errno, strerror(errno))
                else:
                    # done
                    continue
            name = entry.d_name
            if not (name[0] == dot and (
                (name[1] == 0) or 
                (name[1] == dot and name[2] == 0))
                ):
                result.append((entry.d_ino, entry.d_name))
    finally:
        if -1 == closedir(the_dir):
            raise OSError(errno, strerror(errno))
    return result


# vim: tw=79 ai expandtab sw=4 sts=4