/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: Robert Collins
  • Date: 2005-10-16 22:31:25 UTC
  • mto: This revision was merged to the branch mainline in revision 1458.
  • Revision ID: robertc@lifelesslap.robertcollins.net-20051016223125-26d4401cb94b7b82
Branch.relpath has been moved to WorkingTree.relpath.

WorkingTree no no longer takes an inventory, rather it takes an optional branch
parameter, and if None is given will open the branch at basedir implicitly.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2008, 2009, 2010 Canonical Ltd
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
"""Wrapper for readdir which returns files ordered by inode."""
18
 
 
19
 
 
20
 
import os
21
 
import sys
22
 
 
23
 
#python2.4 support
24
 
cdef extern from "python-compat.h":
25
 
    pass
26
 
 
27
 
 
28
 
cdef extern from 'errno.h':
29
 
    int ENOENT
30
 
    int ENOTDIR
31
 
    int EAGAIN
32
 
    int EINTR
33
 
    char *strerror(int errno)
34
 
    # not necessarily a real variable, but this should be close enough
35
 
    int errno
36
 
 
37
 
cdef extern from 'unistd.h':
38
 
    int chdir(char *path)
39
 
    int close(int fd)
40
 
    int fchdir(int fd)
41
 
    char *getcwd(char *, int size)
42
 
 
43
 
cdef extern from 'stdlib.h':
44
 
    void *malloc(int)
45
 
    void free(void *)
46
 
 
47
 
 
48
 
cdef extern from 'sys/types.h':
49
 
    ctypedef long ssize_t
50
 
    ctypedef unsigned long size_t
51
 
    ctypedef long time_t
52
 
    ctypedef unsigned long ino_t
53
 
    ctypedef unsigned long long off_t
54
 
    ctypedef int mode_t
55
 
 
56
 
 
57
 
cdef extern from 'sys/stat.h':
58
 
    cdef struct stat:
59
 
        int st_mode
60
 
        off_t st_size
61
 
        int st_dev
62
 
        ino_t st_ino
63
 
        int st_mtime
64
 
        int st_ctime
65
 
    int lstat(char *path, stat *buf)
66
 
    int S_ISDIR(int mode)
67
 
    int S_ISCHR(int mode)
68
 
    int S_ISBLK(int mode)
69
 
    int S_ISREG(int mode)
70
 
    int S_ISFIFO(int mode)
71
 
    int S_ISLNK(int mode)
72
 
    int S_ISSOCK(int mode)
73
 
 
74
 
 
75
 
cdef extern from 'fcntl.h':
76
 
    int O_RDONLY
77
 
    int open(char *pathname, int flags, mode_t mode)
78
 
 
79
 
 
80
 
cdef extern from 'Python.h':
81
 
    int PyErr_CheckSignals() except -1
82
 
    char * PyString_AS_STRING(object)
83
 
    ctypedef int Py_ssize_t # Required for older pyrex versions
84
 
    ctypedef struct PyObject:
85
 
        pass
86
 
    Py_ssize_t PyString_Size(object s)
87
 
    object PyList_GetItem(object lst, Py_ssize_t index)
88
 
    void *PyList_GetItem_object_void "PyList_GET_ITEM" (object lst, int index)
89
 
    int PyList_Append(object lst, object item) except -1
90
 
    void *PyTuple_GetItem_void_void "PyTuple_GET_ITEM" (void* tpl, int index)
91
 
    int PyTuple_SetItem(void *, Py_ssize_t pos, object item) except -1
92
 
    int PyTuple_SetItem_obj "PyTuple_SetItem" (void *, Py_ssize_t pos, PyObject * item) except -1
93
 
    void Py_INCREF(object o)
94
 
    void Py_DECREF(object o)
95
 
    void PyString_Concat(PyObject **string, object newpart)
96
 
 
97
 
 
98
 
cdef extern from 'dirent.h':
99
 
    ctypedef struct dirent:
100
 
        char d_name[256]
101
 
        ino_t d_ino
102
 
    # the opaque C library DIR type.
103
 
    ctypedef struct DIR
104
 
    # should be DIR *, pyrex barfs.
105
 
    DIR * opendir(char * name)
106
 
    int closedir(DIR * dir)
107
 
    dirent *readdir(DIR *dir)
108
 
 
109
 
_directory = 'directory'
110
 
_chardev = 'chardev'
111
 
_block = 'block'
112
 
_file = 'file'
113
 
_fifo = 'fifo'
114
 
_symlink = 'symlink'
115
 
_socket = 'socket'
116
 
_unknown = 'unknown'
117
 
_missing = 'missing'
118
 
 
119
 
# add a typedef struct dirent dirent to workaround pyrex
120
 
cdef extern from 'readdir.h':
121
 
    pass
122
 
 
123
 
 
124
 
cdef class _Stat:
125
 
    """Represent a 'stat' result."""
126
 
 
127
 
    cdef stat _st
128
 
 
129
 
    property st_dev:
130
 
        def __get__(self):
131
 
            return self._st.st_dev
132
 
 
133
 
    property st_ino:
134
 
        def __get__(self):
135
 
            return self._st.st_ino
136
 
 
137
 
    property st_mode:
138
 
        def __get__(self):
139
 
            return self._st.st_mode
140
 
 
141
 
    property st_ctime:
142
 
        def __get__(self):
143
 
            return self._st.st_ctime
144
 
 
145
 
    property st_mtime:
146
 
        def __get__(self):
147
 
            return self._st.st_mtime
148
 
 
149
 
    property st_size:
150
 
        def __get__(self):
151
 
            return self._st.st_size
152
 
 
153
 
    def __repr__(self):
154
 
        """Repr is the same as a Stat object.
155
 
 
156
 
        (mode, ino, dev, nlink, uid, gid, size, None(atime), mtime, ctime)
157
 
        """
158
 
        return repr((self.st_mode, 0, 0, 0, 0, 0, self.st_size, None,
159
 
                     self.st_mtime, self.st_ctime))
160
 
 
161
 
 
162
 
from bzrlib import osutils
163
 
 
164
 
 
165
 
cdef class UTF8DirReader:
166
 
    """A dir reader for utf8 file systems."""
167
 
 
168
 
    cdef readonly object _safe_utf8
169
 
    cdef _directory, _chardev, _block, _file, _fifo, _symlink
170
 
    cdef _socket, _unknown
171
 
 
172
 
    def __init__(self):
173
 
        self._safe_utf8 = osutils.safe_utf8
174
 
        self._directory = _directory
175
 
        self._chardev = _chardev
176
 
        self._block = _block
177
 
        self._file = _file
178
 
        self._fifo = _fifo
179
 
        self._symlink = _symlink
180
 
        self._socket = _socket
181
 
        self._unknown = _unknown
182
 
 
183
 
    def kind_from_mode(self, int mode):
184
 
        """Get the kind of a path from a mode status."""
185
 
        return self._kind_from_mode(mode)
186
 
 
187
 
    cdef _kind_from_mode(self, int mode):
188
 
        # Files and directories are the most common - check them first.
189
 
        if S_ISREG(mode):
190
 
            return self._file
191
 
        if S_ISDIR(mode):
192
 
            return self._directory
193
 
        if S_ISCHR(mode):
194
 
            return self._chardev
195
 
        if S_ISBLK(mode):
196
 
            return self._block
197
 
        if S_ISLNK(mode):
198
 
            return self._symlink
199
 
        if S_ISFIFO(mode):
200
 
            return self._fifo
201
 
        if S_ISSOCK(mode):
202
 
            return self._socket
203
 
        return self._unknown
204
 
 
205
 
    def top_prefix_to_starting_dir(self, top, prefix=""):
206
 
        """See DirReader.top_prefix_to_starting_dir."""
207
 
        return (self._safe_utf8(prefix), None, None, None,
208
 
            self._safe_utf8(top))
209
 
 
210
 
    def read_dir(self, prefix, top):
211
 
        """Read a single directory from a utf8 file system.
212
 
 
213
 
        All paths in and out are utf8.
214
 
 
215
 
        This sub-function is called when we know the filesystem is already in utf8
216
 
        encoding. So we don't need to transcode filenames.
217
 
 
218
 
        See DirReader.read_dir for details.
219
 
        """
220
 
        #cdef char *_prefix = prefix
221
 
        #cdef char *_top = top
222
 
        # Use C accelerated directory listing.
223
 
        cdef object newval
224
 
        cdef int index
225
 
        cdef int length
226
 
        cdef void * atuple
227
 
        cdef object name
228
 
        cdef PyObject * new_val_obj
229
 
 
230
 
        if PyString_Size(prefix):
231
 
            relprefix = prefix + '/'
232
 
        else:
233
 
            relprefix = ''
234
 
        top_slash = top + '/'
235
 
 
236
 
        # read_dir supplies in should-stat order.
237
 
        # for _, name in sorted(_listdir(top)):
238
 
        result = _read_dir(top)
239
 
        length = len(result)
240
 
        # result.sort()
241
 
        for index from 0 <= index < length:
242
 
            atuple = PyList_GetItem_object_void(result, index)
243
 
            name = <object>PyTuple_GetItem_void_void(atuple, 1)
244
 
            # We have a tuple with (inode, name, None, statvalue, None)
245
 
            # Now edit it:
246
 
            # inode -> path_from_top
247
 
            # direct concat - faster than operator +.
248
 
            new_val_obj = <PyObject *>relprefix
249
 
            Py_INCREF(relprefix)
250
 
            PyString_Concat(&new_val_obj, name)
251
 
            if NULL == new_val_obj:
252
 
                # PyString_Concat will have setup an exception, but how to get
253
 
                # at it?
254
 
                raise Exception("failed to strcat")
255
 
            PyTuple_SetItem_obj(atuple, 0, new_val_obj)
256
 
            # 1st None -> kind
257
 
            newval = self._kind_from_mode(
258
 
                (<_Stat>PyTuple_GetItem_void_void(atuple, 3)).st_mode)
259
 
            Py_INCREF(newval)
260
 
            PyTuple_SetItem(atuple, 2, newval)
261
 
            # 2nd None -> abspath # for all - the caller may need to stat files
262
 
            # etc.
263
 
            # direct concat - faster than operator +.
264
 
            new_val_obj = <PyObject *>top_slash
265
 
            Py_INCREF(top_slash)
266
 
            PyString_Concat(&new_val_obj, name)
267
 
            if NULL == new_val_obj:
268
 
                # PyString_Concat will have setup an exception, but how to get
269
 
                # at it?
270
 
                raise Exception("failed to strcat")
271
 
            PyTuple_SetItem_obj(atuple, 4, new_val_obj)
272
 
        return result
273
 
 
274
 
 
275
 
cdef raise_os_error(int errnum, char *msg_prefix, path):
276
 
    if errnum == EINTR:
277
 
        PyErr_CheckSignals()
278
 
    raise OSError(errnum, msg_prefix + strerror(errnum), path)
279
 
 
280
 
 
281
 
cdef _read_dir(path):
282
 
    """Like os.listdir, this reads the contents of a directory.
283
 
 
284
 
    :param path: the directory to list.
285
 
    :return: a list of single-owner (the list) tuples ready for editing into
286
 
        the result tuples walkdirs needs to yield. They contain (inode, name,
287
 
        None, statvalue, None).
288
 
    """
289
 
    cdef DIR *the_dir
290
 
    # currently this needs a fixup - the C code says 'dirent' but should say
291
 
    # 'struct dirent'
292
 
    cdef dirent * entry
293
 
    cdef dirent sentinel
294
 
    cdef char *name
295
 
    cdef int stat_result
296
 
    cdef _Stat statvalue
297
 
    global errno
298
 
    cdef int orig_dir_fd
299
 
 
300
 
    # Avoid chdir('') because it causes problems on Sun OS, and avoid this if
301
 
    # staying in .
302
 
    if path != "" and path != '.':
303
 
        # we change into the requested directory before reading, and back at the
304
 
        # end, because that turns out to make the stat calls measurably faster than
305
 
        # passing full paths every time.
306
 
        orig_dir_fd = open(".", O_RDONLY, 0)
307
 
        if orig_dir_fd == -1:
308
 
            raise_os_error(errno, "open: ", ".")
309
 
        if -1 == chdir(path):
310
 
            raise_os_error(errno, "chdir: ", path)
311
 
    else:
312
 
        orig_dir_fd = -1
313
 
 
314
 
    try:
315
 
        the_dir = opendir(".")
316
 
        if NULL == the_dir:
317
 
            raise_os_error(errno, "opendir: ", path)
318
 
        try:
319
 
            result = []
320
 
            entry = &sentinel
321
 
            while entry != NULL:
322
 
                # Unlike most libc functions, readdir needs errno set to 0
323
 
                # beforehand so that eof can be distinguished from errors.  See
324
 
                # <https://bugs.launchpad.net/bzr/+bug/279381>
325
 
                while True:
326
 
                    errno = 0
327
 
                    entry = readdir(the_dir)
328
 
                    if entry == NULL and (errno == EAGAIN or errno == EINTR):
329
 
                        if errno == EINTR:
330
 
                            PyErr_CheckSignals()
331
 
                        # try again
332
 
                        continue
333
 
                    else:
334
 
                        break
335
 
                if entry == NULL:
336
 
                    if errno == ENOTDIR or errno == 0:
337
 
                        # We see ENOTDIR at the end of a normal directory.
338
 
                        # As ENOTDIR for read_dir(file) is triggered on opendir,
339
 
                        # we consider ENOTDIR to be 'no error'.
340
 
                        continue
341
 
                    else:
342
 
                        raise_os_error(errno, "readdir: ", path)
343
 
                name = entry.d_name
344
 
                if not (name[0] == c"." and (
345
 
                    (name[1] == 0) or 
346
 
                    (name[1] == c"." and name[2] == 0))
347
 
                    ):
348
 
                    statvalue = _Stat()
349
 
                    stat_result = lstat(entry.d_name, &statvalue._st)
350
 
                    if stat_result != 0:
351
 
                        if errno != ENOENT:
352
 
                            raise_os_error(errno, "lstat: ",
353
 
                                path + "/" + entry.d_name)
354
 
                        else:
355
 
                            # the file seems to have disappeared after being
356
 
                            # seen by readdir - perhaps a transient temporary
357
 
                            # file.  there's no point returning it.
358
 
                            continue
359
 
                    # We append a 5-tuple that can be modified in-place by the C
360
 
                    # api:
361
 
                    # inode to sort on (to replace with top_path)
362
 
                    # name (to keep)
363
 
                    # kind (None, to set)
364
 
                    # statvalue (to keep)
365
 
                    # abspath (None, to set)
366
 
                    PyList_Append(result, (entry.d_ino, entry.d_name, None,
367
 
                        statvalue, None))
368
 
        finally:
369
 
            if -1 == closedir(the_dir):
370
 
                raise_os_error(errno, "closedir: ", path)
371
 
    finally:
372
 
        if -1 != orig_dir_fd:
373
 
            failed = False
374
 
            if -1 == fchdir(orig_dir_fd):
375
 
                # try to close the original directory anyhow
376
 
                failed = True
377
 
            if -1 == close(orig_dir_fd) or failed:
378
 
                raise_os_error(errno, "return to orig_dir: ", "")
379
 
 
380
 
    return result
381
 
 
382
 
 
383
 
# vim: tw=79 ai expandtab sw=4 sts=4