1
# Copyright (C) 2007 Canonical Ltd
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.
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.
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
17
"""Helper functions for Walkdirs on win32."""
20
cdef extern from "_walkdirs_win32.h":
27
ctypedef _HANDLE *HANDLE
28
ctypedef unsigned int DWORD
29
ctypedef unsigned short WCHAR
30
cdef struct _FILETIME:
32
ctypedef _FILETIME FILETIME
34
cdef struct _WIN32_FIND_DATAW:
35
DWORD dwFileAttributes
36
FILETIME ftCreationTime
37
FILETIME ftLastAccessTime
38
FILETIME ftLastWriteTime
41
# Some reserved stuff here
42
WCHAR cFileName[260] # MAX_PATH
43
WCHAR cAlternateFilename[14]
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
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)
55
cdef DWORD FILE_ATTRIBUTE_DIRECTORY
57
cdef int GetLastError()
60
cdef extern from "Python.h":
61
WCHAR *PyUnicode_AS_UNICODE(object)
62
Py_ssize_t PyUnicode_GET_SIZE(object)
69
from bzrlib import osutils
72
class _Win32Stat(object):
73
"""Represent a 'stat' result generated from WIN32_FIND_DATA"""
75
__slots__ = ['st_mode', 'st_ctime', 'st_mtime', 'st_atime',
78
# os.stat always returns 0, so we hard code it here
83
"""Create a new Stat object, based on the WIN32_FIND_DATA tuple"""
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
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
102
self.st_mode = mode_bits
105
"""Repr is the same as a Stat object.
107
(mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)
109
return repr((self.st_mode, 0, 0, 0, 0, 0, self.st_size, self.st_atime,
110
self.st_mtime, self.st_ctime))
114
cdef class Win32Finder:
115
"""A class which encapsulates the search of files in a given directory"""
120
cdef object _utf8_encode
121
cdef object _directory
125
cdef object _last_dirblock
127
def __init__(self, top, prefix=""):
129
self._prefix = prefix
131
self._utf8_encode = codecs.getencoder('utf8')
132
self._directory = osutils._directory_kind
133
self._file = osutils._formats[stat.S_IFREG]
135
self._pending = [(osutils.safe_utf8(prefix), None, None, None,
136
osutils.safe_unicode(top))]
137
self._last_dirblock = None
142
def _get_files_in(self, directory):
143
cdef WIN32_FIND_DATAW search_data
144
cdef HANDLE hFindFile
149
top_star = directory + '*'
152
append = dirblock.append
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
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)
172
result = FindNextFileW(hFindFile, &search_data)
174
result = FindClose(hFindFile)
176
last_err = GetLastError()
180
# for record in FindFilesIterator(top_star):
182
# if name in ('.', '..'):
185
# statvalue = osutils._Win32Stat(record)
186
# name_utf8 = _utf8_encode(name)[0]
187
# abspath = top_slash + name
188
# if DIRECTORY & attrib == DIRECTORY:
192
# append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
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)
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
209
# Maybe we could use unicode(x) during __init__?
211
relprefix = relroot + '/'
214
top_slash = top + '/'
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
222
def _walkdirs_utf8_win32_find_file(top, prefix=""):
223
"""Implement a version of walkdirs_utf8 for win32.
225
This uses the find files api to both list the files and to stat them.
227
cdef WIN32_FIND_DATAW find_data
229
_utf8_encode = codecs.getencoder('utf8')
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]
237
# Possible attributes:
238
DIRECTORY = FILE_ATTRIBUTE_DIRECTORY
240
pending = [(osutils.safe_utf8(prefix), None, None, None,
241
osutils.safe_unicode(top))]
243
relroot, _, _, _, top = pending.pop()
245
relprefix = relroot + '/'
248
top_slash = top + '/'
249
top_star = top_slash + '*'
252
append = dirblock.append
253
for record in FindFilesIterator(top_star):
255
if name in ('.', '..'):
258
statvalue = osutils._Win32Stat(record)
259
name_utf8 = _utf8_encode(name)[0]
260
abspath = top_slash + name
261
if DIRECTORY & attrib == DIRECTORY:
265
append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
266
dirblock.sort(key=operator.itemgetter(1))
267
yield (relroot, top), dirblock
269
# push the user specified dirs from dirblock
270
for d in reversed(dirblock):
271
if d[2] == _directory: