~bzr-pqm/bzr/bzr.dev

5609.29.5 by John Arbash Meinel
Fix bug #740932. Transform should update the sha cache.
1
# Copyright (C) 2008-2011 Canonical Ltd
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
16
17
"""Helper functions for Walkdirs on win32."""
18
19
3737.1.3 by John Arbash Meinel
Move more compatibility code into python-compat.h
20
cdef extern from "python-compat.h":
3557.2.8 by Alexander Belchenko
We don't need all the extra cdef statments in a cdef extern block
21
    struct _HANDLE:
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
22
        pass
23
    ctypedef _HANDLE *HANDLE
3504.4.8 by John Arbash Meinel
Some code cleanups.
24
    ctypedef unsigned long DWORD
3504.4.10 by John Arbash Meinel
Move the helpers to be standalone, rather than members
25
    ctypedef long long __int64
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
26
    ctypedef unsigned short WCHAR
3557.2.8 by Alexander Belchenko
We don't need all the extra cdef statments in a cdef extern block
27
    struct _FILETIME:
3504.4.6 by John Arbash Meinel
Start exposing the times on the stat, this now seems to be a complete walkdirs implementation.
28
        DWORD dwHighDateTime
29
        DWORD dwLowDateTime
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
30
    ctypedef _FILETIME FILETIME
31
3557.2.8 by Alexander Belchenko
We don't need all the extra cdef statments in a cdef extern block
32
    struct _WIN32_FIND_DATAW:
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
33
        DWORD dwFileAttributes
34
        FILETIME ftCreationTime
35
        FILETIME ftLastAccessTime
36
        FILETIME ftLastWriteTime
37
        DWORD nFileSizeHigh
38
        DWORD nFileSizeLow
39
        # Some reserved stuff here
40
        WCHAR cFileName[260] # MAX_PATH
41
        WCHAR cAlternateFilename[14]
42
43
    # We have to use the typedef trick, otherwise pyrex uses:
44
    #  struct WIN32_FIND_DATAW
45
    # which fails due to 'incomplete type'
46
    ctypedef _WIN32_FIND_DATAW WIN32_FIND_DATAW
47
3557.2.8 by Alexander Belchenko
We don't need all the extra cdef statments in a cdef extern block
48
    HANDLE INVALID_HANDLE_VALUE
49
    HANDLE FindFirstFileW(WCHAR *path, WIN32_FIND_DATAW *data)
50
    int FindNextFileW(HANDLE search, WIN32_FIND_DATAW *data)
51
    int FindClose(HANDLE search)
52
53
    DWORD FILE_ATTRIBUTE_READONLY
54
    DWORD FILE_ATTRIBUTE_DIRECTORY
55
    int ERROR_NO_MORE_FILES
56
57
    int GetLastError()
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
58
3504.4.4 by John Arbash Meinel
We have walkdirs basically working, only without timestamps
59
    # Wide character functions
60
    DWORD wcslen(WCHAR *)
61
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
62
63
cdef extern from "Python.h":
64
    WCHAR *PyUnicode_AS_UNICODE(object)
65
    Py_ssize_t PyUnicode_GET_SIZE(object)
3504.4.4 by John Arbash Meinel
We have walkdirs basically working, only without timestamps
66
    object PyUnicode_FromUnicode(WCHAR *, Py_ssize_t)
3504.4.8 by John Arbash Meinel
Some code cleanups.
67
    int PyList_Append(object, object) except -1
68
    object PyUnicode_AsUTF8String(object)
69
70
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
71
import operator
5609.29.5 by John Arbash Meinel
Fix bug #740932. Transform should update the sha cache.
72
import os
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
73
import stat
74
5609.29.5 by John Arbash Meinel
Fix bug #740932. Transform should update the sha cache.
75
from bzrlib import _readdir_py
76
77
cdef object osutils
78
osutils = None
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
79
80
3504.4.9 by John Arbash Meinel
Switch to using a cdef object with readonly attributes.
81
cdef class _Win32Stat:
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
82
    """Represent a 'stat' result generated from WIN32_FIND_DATA"""
83
3504.4.10 by John Arbash Meinel
Move the helpers to be standalone, rather than members
84
    cdef readonly int st_mode
85
    cdef readonly double st_ctime
86
    cdef readonly double st_mtime
87
    cdef readonly double st_atime
3788.1.2 by John Arbash Meinel
Switch from using cdef readonly __int64 to using a property.
88
    # We can't just declare this as 'readonly' because python2.4 doesn't define
89
    # T_LONGLONG as a structure member. So instead we just use a property that
90
    # will convert it correctly anyway.
91
    cdef __int64 _st_size
92
93
    property st_size:
94
        def __get__(self):
95
            return self._st_size
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
96
97
    # os.stat always returns 0, so we hard code it here
3504.4.10 by John Arbash Meinel
Move the helpers to be standalone, rather than members
98
    cdef readonly int st_dev
99
    cdef readonly int st_ino
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
100
101
    def __repr__(self):
102
        """Repr is the same as a Stat object.
103
104
        (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)
105
        """
106
        return repr((self.st_mode, 0, 0, 0, 0, 0, self.st_size, self.st_atime,
107
                     self.st_mtime, self.st_ctime))
108
109
3504.4.11 by John Arbash Meinel
A bit more reorganizing.
110
cdef object _get_name(WIN32_FIND_DATAW *data):
111
    """Extract the Unicode name for this file/dir."""
112
    return PyUnicode_FromUnicode(data.cFileName,
113
                                 wcslen(data.cFileName))
114
115
4634.117.10 by John Arbash Meinel
Change 'no except' to 'cannot_raise'
116
cdef int _get_mode_bits(WIN32_FIND_DATAW *data): # cannot_raise
3504.4.10 by John Arbash Meinel
Move the helpers to be standalone, rather than members
117
    cdef int mode_bits
118
119
    mode_bits = 0100666 # writeable file, the most common
120
    if data.dwFileAttributes & FILE_ATTRIBUTE_READONLY == FILE_ATTRIBUTE_READONLY:
3557.2.5 by John Arbash Meinel
Test that the empty-directory logic for all _walkdirs implementations is correct.
121
        mode_bits = mode_bits ^ 0222 # remove the write bits
3504.4.10 by John Arbash Meinel
Move the helpers to be standalone, rather than members
122
    if data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY:
123
        # Remove the FILE bit, set the DIR bit, and set the EXEC bits
3557.2.5 by John Arbash Meinel
Test that the empty-directory logic for all _walkdirs implementations is correct.
124
        mode_bits = mode_bits ^ 0140111
3504.4.10 by John Arbash Meinel
Move the helpers to be standalone, rather than members
125
    return mode_bits
126
127
4634.117.10 by John Arbash Meinel
Change 'no except' to 'cannot_raise'
128
cdef __int64 _get_size(WIN32_FIND_DATAW *data): # cannot_raise
3504.4.10 by John Arbash Meinel
Move the helpers to be standalone, rather than members
129
    # Pyrex casts a DWORD into a PyLong anyway, so it is safe to do << 32
130
    # on a DWORD
131
    return ((<__int64>data.nFileSizeHigh) << 32) + data.nFileSizeLow
132
133
4634.117.10 by John Arbash Meinel
Change 'no except' to 'cannot_raise'
134
cdef double _ftime_to_timestamp(FILETIME *ft): # cannot_raise
3504.4.10 by John Arbash Meinel
Move the helpers to be standalone, rather than members
135
    """Convert from a FILETIME struct into a floating point timestamp.
136
137
    The fields of a FILETIME structure are the hi and lo part
138
    of a 64-bit value expressed in 100 nanosecond units.
139
    1e7 is one second in such units; 1e-7 the inverse.
140
    429.4967296 is 2**32 / 1e7 or 2**32 * 1e-7.
141
    It also uses the epoch 1601-01-01 rather than 1970-01-01
142
    (taken from posixmodule.c)
143
    """
144
    cdef __int64 val
145
    # NB: This gives slightly different results versus casting to a 64-bit
146
    #     integer and doing integer math before casting into a floating
147
    #     point number. But the difference is in the sub millisecond range,
148
    #     which doesn't seem critical here.
149
    # secs between epochs: 11,644,473,600
150
    val = ((<__int64>ft.dwHighDateTime) << 32) + ft.dwLowDateTime
151
    return (val * 1.0e-7) - 11644473600.0
152
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
153
4634.117.10 by John Arbash Meinel
Change 'no except' to 'cannot_raise'
154
cdef int _should_skip(WIN32_FIND_DATAW *data): # cannot_raise
3504.4.11 by John Arbash Meinel
A bit more reorganizing.
155
    """Is this '.' or '..' so we should skip it?"""
156
    if (data.cFileName[0] != c'.'):
157
        return 0
158
    if data.cFileName[1] == c'\0':
159
        return 1
160
    if data.cFileName[1] == c'.' and data.cFileName[2] == c'\0':
161
        return 1
162
    return 0
163
164
3696.3.1 by Robert Collins
Refactor bzrlib.osutils._walkdirs_utf8 to aid API migration in future.
165
cdef class Win32ReadDir:
166
    """Read directories on win32."""
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
167
3504.4.4 by John Arbash Meinel
We have walkdirs basically working, only without timestamps
168
    cdef object _directory_kind
169
    cdef object _file_kind
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
170
3696.3.1 by Robert Collins
Refactor bzrlib.osutils._walkdirs_utf8 to aid API migration in future.
171
    def __init__(self):
3696.3.10 by Robert Collins
Review feedback.
172
        self._directory_kind = _readdir_py._directory
173
        self._file_kind = _readdir_py._file
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
174
3696.3.1 by Robert Collins
Refactor bzrlib.osutils._walkdirs_utf8 to aid API migration in future.
175
    def top_prefix_to_starting_dir(self, top, prefix=""):
176
        """See DirReader.top_prefix_to_starting_dir."""
5752.2.8 by John Arbash Meinel
Use global osutils, otherwise it creates a local var.
177
        global osutils
5609.29.5 by John Arbash Meinel
Fix bug #740932. Transform should update the sha cache.
178
        if osutils is None:
179
            from bzrlib import osutils
3696.3.2 by John Arbash Meinel
Fix up some of the path joining logic.
180
        return (osutils.safe_utf8(prefix), None, None, None,
181
                osutils.safe_unicode(top))
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
182
3504.4.12 by John Arbash Meinel
A couple small cleanups, make test_osutils more correct
183
    cdef object _get_kind(self, WIN32_FIND_DATAW *data):
184
        if data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY:
185
            return self._directory_kind
186
        return self._file_kind
187
3504.4.9 by John Arbash Meinel
Switch to using a cdef object with readonly attributes.
188
    cdef _Win32Stat _get_stat_value(self, WIN32_FIND_DATAW *data):
3504.4.4 by John Arbash Meinel
We have walkdirs basically working, only without timestamps
189
        """Get the filename and the stat information."""
3504.4.10 by John Arbash Meinel
Move the helpers to be standalone, rather than members
190
        cdef _Win32Stat statvalue
191
192
        statvalue = _Win32Stat()
193
        statvalue.st_mode = _get_mode_bits(data)
194
        statvalue.st_ctime = _ftime_to_timestamp(&data.ftCreationTime)
195
        statvalue.st_mtime = _ftime_to_timestamp(&data.ftLastWriteTime)
196
        statvalue.st_atime = _ftime_to_timestamp(&data.ftLastAccessTime)
3788.1.6 by John Arbash Meinel
Quick fix for win32 extension.
197
        statvalue._st_size = _get_size(data)
3504.4.10 by John Arbash Meinel
Move the helpers to be standalone, rather than members
198
        statvalue.st_ino = 0
199
        statvalue.st_dev = 0
200
        return statvalue
3504.4.4 by John Arbash Meinel
We have walkdirs basically working, only without timestamps
201
3696.3.1 by Robert Collins
Refactor bzrlib.osutils._walkdirs_utf8 to aid API migration in future.
202
    def read_dir(self, prefix, top):
203
        """Win32 implementation of DirReader.read_dir.
3557.2.2 by John Arbash Meinel
Include better documentation about what is going on. (suggested by Bialix)
204
3696.3.1 by Robert Collins
Refactor bzrlib.osutils._walkdirs_utf8 to aid API migration in future.
205
        :seealso: DirReader.read_dir
3557.2.2 by John Arbash Meinel
Include better documentation about what is going on. (suggested by Bialix)
206
        """
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
207
        cdef WIN32_FIND_DATAW search_data
208
        cdef HANDLE hFindFile
209
        cdef int last_err
210
        cdef WCHAR *query
211
        cdef int result
212
3696.3.1 by Robert Collins
Refactor bzrlib.osutils._walkdirs_utf8 to aid API migration in future.
213
        if prefix:
214
            relprefix = prefix + '/'
215
        else:
216
            relprefix = ''
217
        top_slash = top + '/'
218
3696.3.2 by John Arbash Meinel
Fix up some of the path joining logic.
219
        top_star = top_slash + '*'
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
220
221
        dirblock = []
222
223
        query = PyUnicode_AS_UNICODE(top_star)
224
        hFindFile = FindFirstFileW(query, &search_data)
225
        if hFindFile == INVALID_HANDLE_VALUE:
226
            # Raise an exception? This path doesn't seem to exist
3504.4.8 by John Arbash Meinel
Some code cleanups.
227
            raise WindowsError(GetLastError(), top_star)
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
228
229
        try:
230
            result = 1
231
            while result:
232
                # Skip '.' and '..'
3504.4.11 by John Arbash Meinel
A bit more reorganizing.
233
                if _should_skip(&search_data):
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
234
                    result = FindNextFileW(hFindFile, &search_data)
235
                    continue
3504.4.12 by John Arbash Meinel
A couple small cleanups, make test_osutils more correct
236
                name_unicode = _get_name(&search_data)
3504.4.8 by John Arbash Meinel
Some code cleanups.
237
                name_utf8 = PyUnicode_AsUTF8String(name_unicode)
3696.3.2 by John Arbash Meinel
Fix up some of the path joining logic.
238
                PyList_Append(dirblock,
239
                    (relprefix + name_utf8, name_utf8,
3504.4.8 by John Arbash Meinel
Some code cleanups.
240
                     self._get_kind(&search_data),
241
                     self._get_stat_value(&search_data),
3696.3.2 by John Arbash Meinel
Fix up some of the path joining logic.
242
                     top_slash + name_unicode))
3504.4.4 by John Arbash Meinel
We have walkdirs basically working, only without timestamps
243
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
244
                result = FindNextFileW(hFindFile, &search_data)
3504.4.8 by John Arbash Meinel
Some code cleanups.
245
            # FindNextFileW sets GetLastError() == ERROR_NO_MORE_FILES when it
246
            # actually finishes. If we have anything else, then we have a
247
            # genuine problem
248
            last_err = GetLastError()
249
            if last_err != ERROR_NO_MORE_FILES:
250
                raise WindowsError(last_err)
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
251
        finally:
252
            result = FindClose(hFindFile)
253
            if result == 0:
254
                last_err = GetLastError()
3557.2.2 by John Arbash Meinel
Include better documentation about what is going on. (suggested by Bialix)
255
                # TODO: We should probably raise an exception if FindClose
256
                #       returns an error, however, I don't want to supress an
257
                #       earlier Exception, so for now, I'm ignoring this
3696.3.1 by Robert Collins
Refactor bzrlib.osutils._walkdirs_utf8 to aid API migration in future.
258
        dirblock.sort(key=operator.itemgetter(1))
3504.4.3 by John Arbash Meinel
Start working on an extension specifically for win32,
259
        return dirblock
5609.29.5 by John Arbash Meinel
Fix bug #740932. Transform should update the sha cache.
260
261
262
def lstat(path):
263
    """Equivalent to os.lstat, except match Win32ReadDir._get_stat_value.
264
    """
265
    return wrap_stat(os.lstat(path))
266
267
268
def fstat(fd):
269
    """Like os.fstat, except match Win32ReadDir._get_stat_value
270
271
    :seealso: wrap_stat
272
    """
273
    return wrap_stat(os.fstat(fd))
274
275
276
def wrap_stat(st):
277
    """Return a _Win32Stat object, based on the given stat result.
278
279
    On Windows, os.fstat(open(fname).fileno()) != os.lstat(fname). This is
280
    generally because os.lstat and os.fstat differ in what they put into st_ino
281
    and st_dev. What gets set where seems to also be dependent on the python
282
    version. So we always set it to 0 to avoid worrying about it.
283
    """
284
    cdef _Win32Stat statvalue
285
    statvalue = _Win32Stat()
286
    statvalue.st_mode = st.st_mode
287
    statvalue.st_ctime = st.st_ctime
288
    statvalue.st_mtime = st.st_mtime
289
    statvalue.st_atime = st.st_atime
290
    statvalue._st_size = st.st_size
291
    statvalue.st_ino = 0
292
    statvalue.st_dev = 0
293
    return statvalue