/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/_walkdirs_win32.pyx

  • Committer: John Arbash Meinel
  • Date: 2008-07-16 22:06:22 UTC
  • mto: This revision was merged to the branch mainline in revision 3557.
  • Revision ID: john@arbash-meinel.com-20080716220622-m6zsz00j08co7l5g
Start working on an extension specifically for win32,
This will wrap the win32 FindFile code into nice objects for higher levels.
Mostly, I'm cribbing the code from other places like posixmodule.c
I'm doing it in Pyrex, though, so I get a couple conveniences, like
not having to watch all of my allocations, etc.
I might *want* to do that for performance before all is said and done,
but for now, get it working.

ATM, it just iterates, but doesn't actually work out any data.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2007 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Helper functions for Walkdirs on win32."""
 
18
 
 
19
 
 
20
cdef extern from "_walkdirs_win32.h":
 
21
    cdef struct FINDFILE:
 
22
        int low
 
23
        int high
 
24
 
 
25
    cdef struct _HANDLE:
 
26
        pass
 
27
    ctypedef _HANDLE *HANDLE
 
28
    ctypedef unsigned int DWORD
 
29
    ctypedef unsigned short WCHAR
 
30
    cdef struct _FILETIME:
 
31
        pass
 
32
    ctypedef _FILETIME FILETIME
 
33
 
 
34
    cdef struct _WIN32_FIND_DATAW:
 
35
        DWORD dwFileAttributes
 
36
        FILETIME ftCreationTime
 
37
        FILETIME ftLastAccessTime
 
38
        FILETIME ftLastWriteTime
 
39
        DWORD nFileSizeHigh
 
40
        DWORD nFileSizeLow
 
41
        # Some reserved stuff here
 
42
        WCHAR cFileName[260] # MAX_PATH
 
43
        WCHAR cAlternateFilename[14]
 
44
 
 
45
    # We have to use the typedef trick, otherwise pyrex uses:
 
46
    #  struct WIN32_FIND_DATAW
 
47
    # which fails due to 'incomplete type'
 
48
    ctypedef _WIN32_FIND_DATAW WIN32_FIND_DATAW
 
49
 
 
50
    cdef HANDLE INVALID_HANDLE_VALUE
 
51
    cdef HANDLE FindFirstFileW(WCHAR *path, WIN32_FIND_DATAW *data)
 
52
    cdef int FindNextFileW(HANDLE search, WIN32_FIND_DATAW *data)
 
53
    cdef int FindClose(HANDLE search)
 
54
 
 
55
    cdef DWORD FILE_ATTRIBUTE_DIRECTORY
 
56
 
 
57
    cdef int GetLastError()
 
58
 
 
59
 
 
60
cdef extern from "Python.h":
 
61
    WCHAR *PyUnicode_AS_UNICODE(object)
 
62
    Py_ssize_t PyUnicode_GET_SIZE(object)
 
63
 
 
64
 
 
65
import codecs
 
66
import operator
 
67
import stat
 
68
 
 
69
from bzrlib import osutils
 
70
 
 
71
 
 
72
class _Win32Stat(object):
 
73
    """Represent a 'stat' result generated from WIN32_FIND_DATA"""
 
74
 
 
75
    __slots__ = ['st_mode', 'st_ctime', 'st_mtime', 'st_atime',
 
76
                 'st_size']
 
77
 
 
78
    # os.stat always returns 0, so we hard code it here
 
79
    st_dev = 0
 
80
    st_ino = 0
 
81
 
 
82
    def __init__(self):
 
83
        """Create a new Stat object, based on the WIN32_FIND_DATA tuple"""
 
84
        pass
 
85
 
 
86
    def set_stuff(self):
 
87
        (attrib, ctime, atime, wtime, size_high, size_low,
 
88
         res0, res1, name, alt_name) = win32_find_data_record
 
89
        self.st_ctime = int(ctime)
 
90
        self.st_mtime = int(wtime)
 
91
        self.st_atime = int(atime)
 
92
        self.st_size = (size_high * 1<<32) + size_low
 
93
 
 
94
        mode_bits = 0100666 # writeable file, the most common
 
95
        if (win32file.FILE_ATTRIBUTE_READONLY & attrib ==
 
96
            win32file.FILE_ATTRIBUTE_READONLY):
 
97
            mode_bits ^= 0222 # remove writable bits
 
98
        if (win32file.FILE_ATTRIBUTE_DIRECTORY & attrib ==
 
99
            win32file.FILE_ATTRIBUTE_DIRECTORY):
 
100
            # Remove the FILE bit, set the DIR bit, and set the EXEC bits
 
101
            mode_bits ^= 0140111
 
102
        self.st_mode = mode_bits
 
103
 
 
104
    def __repr__(self):
 
105
        """Repr is the same as a Stat object.
 
106
 
 
107
        (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)
 
108
        """
 
109
        return repr((self.st_mode, 0, 0, 0, 0, 0, self.st_size, self.st_atime,
 
110
                     self.st_mtime, self.st_ctime))
 
111
 
 
112
 
 
113
 
 
114
cdef class Win32Finder:
 
115
    """A class which encapsulates the search of files in a given directory"""
 
116
 
 
117
    cdef object _top
 
118
    cdef object _prefix
 
119
 
 
120
    cdef object _utf8_encode
 
121
    cdef object _directory
 
122
    cdef object _file
 
123
 
 
124
    cdef object _pending
 
125
    cdef object _last_dirblock
 
126
 
 
127
    def __init__(self, top, prefix=""):
 
128
        self._top = top
 
129
        self._prefix = prefix
 
130
 
 
131
        self._utf8_encode = codecs.getencoder('utf8')
 
132
        self._directory = osutils._directory_kind
 
133
        self._file = osutils._formats[stat.S_IFREG]
 
134
 
 
135
        self._pending = [(osutils.safe_utf8(prefix), None, None, None,
 
136
                          osutils.safe_unicode(top))]
 
137
        self._last_dirblock = None
 
138
 
 
139
    def __iter__(self):
 
140
        return self
 
141
 
 
142
    def _get_files_in(self, directory):
 
143
        cdef WIN32_FIND_DATAW search_data
 
144
        cdef HANDLE hFindFile
 
145
        cdef int last_err
 
146
        cdef WCHAR *query
 
147
        cdef int result
 
148
 
 
149
        top_star = directory + '*'
 
150
 
 
151
        dirblock = []
 
152
        append = dirblock.append
 
153
 
 
154
        query = PyUnicode_AS_UNICODE(top_star)
 
155
        hFindFile = FindFirstFileW(query, &search_data)
 
156
        if hFindFile == INVALID_HANDLE_VALUE:
 
157
            # Raise an exception? This path doesn't seem to exist
 
158
            last_err = GetLastError()
 
159
            # Could be last_err == ERROR_FILE_NOT_FOUND
 
160
            return []
 
161
 
 
162
        try:
 
163
            result = 1
 
164
            while result:
 
165
                # Skip '.' and '..'
 
166
                if (search_data.cFileName[0] == c'.'
 
167
                    and (search_data.cFileName[1] == c'\0'
 
168
                         or (search_data.cFileName[1] == c'.'
 
169
                             and search_data.cFileName[2] == c'\0'))):
 
170
                    result = FindNextFileW(hFindFile, &search_data)
 
171
                    continue
 
172
                result = FindNextFileW(hFindFile, &search_data)
 
173
        finally:
 
174
            result = FindClose(hFindFile)
 
175
            if result == 0:
 
176
                last_err = GetLastError()
 
177
                pass
 
178
        return dirblock
 
179
 
 
180
        # for record in FindFilesIterator(top_star):
 
181
        #     name = record[-2]
 
182
        #     if name in ('.', '..'):
 
183
        #         continue
 
184
        #     attrib = record[0]
 
185
        #     statvalue = osutils._Win32Stat(record)
 
186
        #     name_utf8 = _utf8_encode(name)[0]
 
187
        #     abspath = top_slash + name
 
188
        #     if DIRECTORY & attrib == DIRECTORY:
 
189
        #         kind = _directory
 
190
        #     else:
 
191
        #         kind = _file
 
192
        #     append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
 
193
 
 
194
    def __next__(self):
 
195
        if self._last_dirblock is not None:
 
196
            # push the entries left in the dirblock onto the pending queue
 
197
            # we do this here, because we allow the user to modified the
 
198
            # queue before the next iteration
 
199
            for d in reversed(self._last_dirblock):
 
200
                if d[2] == _directory:
 
201
                    self._pending.append(d)
 
202
 
 
203
        if not self._pending:
 
204
            raise StopIteration()
 
205
        relroot, _, _, _, top = self._pending.pop()
 
206
        # NB: At the moment Pyrex doesn't support Unicode literals, which means
 
207
        # that all of these string literals are going to be upcasted to Unicode
 
208
        # at runtime... :(
 
209
        # Maybe we could use unicode(x) during __init__?
 
210
        if relroot:
 
211
            relprefix = relroot + '/'
 
212
        else:
 
213
            relprefix = ''
 
214
        top_slash = top + '/'
 
215
 
 
216
        dirblock = self._get_files_in(top_slash)
 
217
        dirblock.sort(key=operator.itemgetter(1))
 
218
        self._last_dirblock = dirblock
 
219
        return (relroot, top), dirblock
 
220
 
 
221
 
 
222
def _walkdirs_utf8_win32_find_file(top, prefix=""):
 
223
    """Implement a version of walkdirs_utf8 for win32.
 
224
 
 
225
    This uses the find files api to both list the files and to stat them.
 
226
    """
 
227
    cdef WIN32_FIND_DATAW find_data
 
228
 
 
229
    _utf8_encode = codecs.getencoder('utf8')
 
230
 
 
231
    # WIN32_FIND_DATA object looks like:
 
232
    # (FILE_ATTRIBUTES, createTime, accessTime, writeTime, nFileSizeHigh,
 
233
    #  nFileSizeLow, reserved0, reserved1, name, alternateFilename)
 
234
    _directory = osutils._directory_kind
 
235
    _file = osutils._formats[stat.S_IFREG]
 
236
 
 
237
    # Possible attributes:
 
238
    DIRECTORY = FILE_ATTRIBUTE_DIRECTORY
 
239
 
 
240
    pending = [(osutils.safe_utf8(prefix), None, None, None,
 
241
                osutils.safe_unicode(top))]
 
242
    while pending:
 
243
        relroot, _, _, _, top = pending.pop()
 
244
        if relroot:
 
245
            relprefix = relroot + '/'
 
246
        else:
 
247
            relprefix = ''
 
248
        top_slash = top + '/'
 
249
        top_star = top_slash + '*'
 
250
 
 
251
        dirblock = []
 
252
        append = dirblock.append
 
253
        for record in FindFilesIterator(top_star):
 
254
            name = record[-2]
 
255
            if name in ('.', '..'):
 
256
                continue
 
257
            attrib = record[0]
 
258
            statvalue = osutils._Win32Stat(record)
 
259
            name_utf8 = _utf8_encode(name)[0]
 
260
            abspath = top_slash + name
 
261
            if DIRECTORY & attrib == DIRECTORY:
 
262
                kind = _directory
 
263
            else:
 
264
                kind = _file
 
265
            append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
 
266
        dirblock.sort(key=operator.itemgetter(1))
 
267
        yield (relroot, top), dirblock
 
268
 
 
269
        # push the user specified dirs from dirblock
 
270
        for d in reversed(dirblock):
 
271
            if d[2] == _directory:
 
272
                pending.append(d)
 
273