/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/_readdir_pyx.pyx

  • Committer: Andrew Bennetts
  • Date: 2008-10-27 06:14:45 UTC
  • mfrom: (3793 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3795.
  • Revision ID: andrew.bennetts@canonical.com-20081027061445-eqt9lz6uw1mbvq4g
Merge from bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
import os
21
21
import sys
22
22
 
23
 
 
24
 
# the opaque C library DIR type.
 
23
#python2.4 support
 
24
cdef extern from "python-compat.h":
 
25
    pass
 
26
 
 
27
 
25
28
cdef extern from 'errno.h':
26
29
    int ENOENT
27
30
    int ENOTDIR
28
31
    int EAGAIN
 
32
    int EINTR
 
33
    char *strerror(int errno)
 
34
    # not necessarily a real variable, but this should be close enough
29
35
    int errno
30
 
    char *strerror(int errno)
 
36
 
 
37
cdef extern from 'unistd.h':
 
38
    int chdir(char *path)
 
39
    char *getcwd(char *, int size)
 
40
 
 
41
cdef extern from 'stdlib.h':
 
42
    void *malloc(int)
 
43
    void free(void *)
 
44
 
31
45
 
32
46
cdef extern from 'sys/types.h':
33
47
    ctypedef long ssize_t
34
48
    ctypedef unsigned long size_t
 
49
    ctypedef long time_t
 
50
    ctypedef unsigned long ino_t
 
51
    ctypedef unsigned long long off_t
 
52
 
 
53
 
 
54
cdef extern from 'sys/stat.h':
 
55
    cdef struct stat:
 
56
        int st_mode
 
57
        off_t st_size
 
58
        int st_dev
 
59
        ino_t st_ino
 
60
        int st_mtime
 
61
        int st_ctime
 
62
    int lstat(char *path, stat *buf)
 
63
    int S_ISDIR(int mode)
 
64
    int S_ISCHR(int mode)
 
65
    int S_ISBLK(int mode)
 
66
    int S_ISREG(int mode)
 
67
    int S_ISFIFO(int mode)
 
68
    int S_ISLNK(int mode)
 
69
    int S_ISSOCK(int mode)
 
70
 
 
71
 
 
72
cdef extern from 'Python.h':
 
73
    char * PyString_AS_STRING(object)
 
74
    ctypedef int Py_ssize_t # Required for older pyrex versions
 
75
    ctypedef struct PyObject:
 
76
        pass
 
77
    Py_ssize_t PyString_Size(object s)
 
78
    object PyList_GetItem(object lst, Py_ssize_t index)
 
79
    void *PyList_GetItem_object_void "PyList_GET_ITEM" (object lst, int index)
 
80
    int PyList_Append(object lst, object item) except -1
 
81
    void *PyTuple_GetItem_void_void "PyTuple_GET_ITEM" (void* tpl, int index)
 
82
    int PyTuple_SetItem(void *, Py_ssize_t pos, object item) except -1
 
83
    int PyTuple_SetItem_obj "PyTuple_SetItem" (void *, Py_ssize_t pos, PyObject * item) except -1
 
84
    void Py_INCREF(object o)
 
85
    void Py_DECREF(object o)
 
86
    void PyString_Concat(PyObject **string, object newpart)
 
87
 
35
88
 
36
89
cdef extern from 'dirent.h':
37
 
    int DT_UNKNOWN
38
 
    int DT_REG
39
 
    int DT_DIR
40
 
    int DT_FIFO
41
 
    int DT_SOCK
42
 
    int DT_CHR
43
 
    int DT_BLK
44
90
    ctypedef struct dirent:
45
91
        char d_name[256]
46
 
        # this will fail to compile if d_type is not defined.
47
 
        # if this module fails to compile, use the .py version.
48
 
        unsigned char d_type
49
 
        int d_ino
 
92
        ino_t d_ino
 
93
    # the opaque C library DIR type.
50
94
    ctypedef struct DIR
51
95
    # should be DIR *, pyrex barfs.
52
96
    DIR * opendir(char * name)
61
105
_symlink = 'symlink'
62
106
_socket = 'socket'
63
107
_unknown = 'unknown'
64
 
 
65
 
dot = ord('.')
 
108
_missing = 'missing'
66
109
 
67
110
# add a typedef struct dirent dirent to workaround pyrex
68
111
cdef extern from 'readdir.h':
69
112
    pass
70
113
 
71
 
def read_dir(path):
 
114
 
 
115
cdef class _Stat:
 
116
    """Represent a 'stat' result."""
 
117
 
 
118
    cdef stat _st
 
119
 
 
120
    property st_dev:
 
121
        def __get__(self):
 
122
            return self._st.st_dev
 
123
 
 
124
    property st_ino:
 
125
        def __get__(self):
 
126
            return self._st.st_ino
 
127
 
 
128
    property st_mode:
 
129
        def __get__(self):
 
130
            return self._st.st_mode
 
131
 
 
132
    property st_ctime:
 
133
        def __get__(self):
 
134
            return self._st.st_ctime
 
135
 
 
136
    property st_mtime:
 
137
        def __get__(self):
 
138
            return self._st.st_mtime
 
139
 
 
140
    property st_size:
 
141
        def __get__(self):
 
142
            return self._st.st_size
 
143
 
 
144
    def __repr__(self):
 
145
        """Repr is the same as a Stat object.
 
146
 
 
147
        (mode, ino, dev, nlink, uid, gid, size, None(atime), mtime, ctime)
 
148
        """
 
149
        return repr((self.st_mode, 0, 0, 0, 0, 0, self.st_size, None,
 
150
                     self._mtime, self._ctime))
 
151
 
 
152
 
 
153
from bzrlib import osutils
 
154
 
 
155
 
 
156
cdef class UTF8DirReader:
 
157
    """A dir reader for utf8 file systems."""
 
158
 
 
159
    cdef readonly object _safe_utf8
 
160
    cdef _directory, _chardev, _block, _file, _fifo, _symlink
 
161
    cdef _socket, _unknown
 
162
 
 
163
    def __init__(self):
 
164
        self._safe_utf8 = osutils.safe_utf8
 
165
        self._directory = _directory
 
166
        self._chardev = _chardev
 
167
        self._block = _block
 
168
        self._file = _file
 
169
        self._fifo = _fifo
 
170
        self._symlink = _symlink
 
171
        self._socket = _socket
 
172
        self._unknown = _unknown
 
173
 
 
174
    def kind_from_mode(self, int mode):
 
175
        """Get the kind of a path from a mode status."""
 
176
        return self._kind_from_mode(mode)
 
177
 
 
178
    cdef _kind_from_mode(self, int mode):
 
179
        # Files and directories are the most common - check them first.
 
180
        if S_ISREG(mode):
 
181
            return self._file
 
182
        if S_ISDIR(mode):
 
183
            return self._directory
 
184
        if S_ISCHR(mode):
 
185
            return self._chardev
 
186
        if S_ISBLK(mode):
 
187
            return self._block
 
188
        if S_ISLNK(mode):
 
189
            return self._symlink
 
190
        if S_ISFIFO(mode):
 
191
            return self._fifo
 
192
        if S_ISSOCK(mode):
 
193
            return self._socket
 
194
        return self._unknown
 
195
 
 
196
    def top_prefix_to_starting_dir(self, top, prefix=""):
 
197
        """See DirReader.top_prefix_to_starting_dir."""
 
198
        return (self._safe_utf8(prefix), None, None, None,
 
199
            self._safe_utf8(top))
 
200
 
 
201
    def read_dir(self, prefix, top):
 
202
        """Read a single directory from a utf8 file system.
 
203
 
 
204
        All paths in and out are utf8.
 
205
 
 
206
        This sub-function is called when we know the filesystem is already in utf8
 
207
        encoding. So we don't need to transcode filenames.
 
208
 
 
209
        See DirReader.read_dir for details.
 
210
        """
 
211
        #cdef char *_prefix = prefix
 
212
        #cdef char *_top = top
 
213
        # Use C accelerated directory listing.
 
214
        cdef object newval
 
215
        cdef int index
 
216
        cdef int length
 
217
        cdef void * atuple
 
218
        cdef object name
 
219
        cdef PyObject * new_val_obj
 
220
 
 
221
        if PyString_Size(prefix):
 
222
            relprefix = prefix + '/'
 
223
        else:
 
224
            relprefix = ''
 
225
        top_slash = top + '/'
 
226
 
 
227
        # read_dir supplies in should-stat order.
 
228
        # for _, name in sorted(_listdir(top)):
 
229
        result = _read_dir(top)
 
230
        length = len(result)
 
231
        # result.sort()
 
232
        for index from 0 <= index < length:
 
233
            atuple = PyList_GetItem_object_void(result, index)
 
234
            name = <object>PyTuple_GetItem_void_void(atuple, 1)
 
235
            # We have a tuple with (inode, name, None, statvalue, None)
 
236
            # Now edit it:
 
237
            # inode -> path_from_top
 
238
            # direct concat - faster than operator +.
 
239
            new_val_obj = <PyObject *>relprefix
 
240
            Py_INCREF(relprefix)
 
241
            PyString_Concat(&new_val_obj, name)
 
242
            if NULL == new_val_obj:
 
243
                # PyString_Concat will have setup an exception, but how to get
 
244
                # at it?
 
245
                raise Exception("failed to strcat")
 
246
            PyTuple_SetItem_obj(atuple, 0, new_val_obj)
 
247
            # 1st None -> kind
 
248
            newval = self._kind_from_mode(
 
249
                (<_Stat>PyTuple_GetItem_void_void(atuple, 3)).st_mode)
 
250
            Py_INCREF(newval)
 
251
            PyTuple_SetItem(atuple, 2, newval)
 
252
            # 2nd None -> abspath # for all - the caller may need to stat files
 
253
            # etc.
 
254
            # direct concat - faster than operator +.
 
255
            new_val_obj = <PyObject *>top_slash
 
256
            Py_INCREF(top_slash)
 
257
            PyString_Concat(&new_val_obj, name)
 
258
            if NULL == new_val_obj:
 
259
                # PyString_Concat will have setup an exception, but how to get
 
260
                # at it?
 
261
                raise Exception("failed to strcat")
 
262
            PyTuple_SetItem_obj(atuple, 4, new_val_obj)
 
263
        return result
 
264
 
 
265
 
 
266
cdef _read_dir(path):
72
267
    """Like os.listdir, this reads the contents of a directory.
73
268
 
74
269
    :param path: the directory to list.
75
 
    :return: a list of (sort_key, basename) tuples.
 
270
    :return: a list of single-owner (the list) tuples ready for editing into
 
271
        the result tuples walkdirs needs to yield. They contain (inode, name,
 
272
        None, statvalue, None).
76
273
    """
77
274
    cdef DIR *the_dir
78
275
    # currently this needs a fixup - the C code says 'dirent' but should say
80
277
    cdef dirent * entry
81
278
    cdef dirent sentinel
82
279
    cdef char *name
83
 
    the_dir = opendir(path)
 
280
    cdef int stat_result
 
281
    cdef _Stat statvalue
 
282
    cdef char *cwd
 
283
    global errno
 
284
 
 
285
    cwd = getcwd(NULL, 0)
 
286
    if -1 == chdir(path):
 
287
        raise OSError(errno, strerror(errno))
 
288
    the_dir = opendir(".")
84
289
    if NULL == the_dir:
85
290
        raise OSError(errno, strerror(errno))
86
291
    result = []
87
292
    try:
88
293
        entry = &sentinel
89
294
        while entry != NULL:
90
 
            entry = readdir(the_dir)
 
295
            # Unlike most libc functions, readdir needs errno set to 0
 
296
            # beforehand so that eof can be distinguished from errors.  See
 
297
            # <https://bugs.launchpad.net/bzr/+bug/279381>
 
298
            while True:
 
299
                errno = 0;
 
300
                entry = readdir(the_dir)
 
301
                if entry == NULL and (errno == EAGAIN or errno == EINTR):
 
302
                    # try again
 
303
                    continue
 
304
                else:
 
305
                    break
91
306
            if entry == NULL:
92
 
                if errno == EAGAIN:
93
 
                    # try again
94
 
                    continue
95
 
                elif errno != ENOTDIR and errno != ENOENT and errno != 0:
 
307
                if errno == ENOTDIR or errno == 0:
96
308
                    # We see ENOTDIR at the end of a normal directory.
97
309
                    # As ENOTDIR for read_dir(file) is triggered on opendir,
98
310
                    # we consider ENOTDIR to be 'no error'.
99
 
                    # ENOENT is listed as 'invalid position in the dir stream' for
100
 
                    # readdir. We swallow this for now and just keep reading.
 
311
                    continue
 
312
                else:
101
313
                    raise OSError(errno, strerror(errno))
102
 
                else:
103
 
                    # done
104
 
                    continue
105
314
            name = entry.d_name
106
 
            if not (name[0] == dot and (
 
315
            if not (name[0] == c"." and (
107
316
                (name[1] == 0) or 
108
 
                (name[1] == dot and name[2] == 0))
 
317
                (name[1] == c"." and name[2] == 0))
109
318
                ):
110
 
                result.append((entry.d_ino, entry.d_name))
 
319
                statvalue = _Stat()
 
320
                stat_result = lstat(entry.d_name, &statvalue._st)
 
321
                if stat_result != 0:
 
322
                    if errno != ENOENT:
 
323
                        raise OSError(errno, strerror(errno))
 
324
                    else:
 
325
                        kind = _missing
 
326
                        statvalue = None
 
327
                # We append a 5-tuple that can be modified in-place by the C
 
328
                # api:
 
329
                # inode to sort on (to replace with top_path)
 
330
                # name (to keep)
 
331
                # kind (None, to set)
 
332
                # statvalue (to keep)
 
333
                # abspath (None, to set)
 
334
                PyList_Append(result, (entry.d_ino, entry.d_name, None,
 
335
                    statvalue, None))
111
336
    finally:
 
337
        if -1 == chdir(cwd):
 
338
            free(cwd)
 
339
            raise OSError(errno, strerror(errno))
 
340
        free(cwd)
112
341
        if -1 == closedir(the_dir):
113
342
            raise OSError(errno, strerror(errno))
114
343
    return result