/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: 2006-07-18 18:57:54 UTC
  • mto: This revision was merged to the branch mainline in revision 1868.
  • Revision ID: john@arbash-meinel.com-20060718185754-4007745748e28db9
Commit timestamp restricted to 1ms precision.

The old code would restrict to 1s resolution if the timestamp was
supplied, while it preserved full resolution if the timestamp was
auto generated. Now both paths preserve only 1ms resolution.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 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
 
"""Helper functions for Walkdirs on win32."""
18
 
 
19
 
 
20
 
cdef extern from "python-compat.h":
21
 
    struct _HANDLE:
22
 
        pass
23
 
    ctypedef _HANDLE *HANDLE
24
 
    ctypedef unsigned long DWORD
25
 
    ctypedef long long __int64
26
 
    ctypedef unsigned short WCHAR
27
 
    struct _FILETIME:
28
 
        DWORD dwHighDateTime
29
 
        DWORD dwLowDateTime
30
 
    ctypedef _FILETIME FILETIME
31
 
 
32
 
    struct _WIN32_FIND_DATAW:
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
 
 
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()
58
 
 
59
 
    # Wide character functions
60
 
    DWORD wcslen(WCHAR *)
61
 
 
62
 
 
63
 
cdef extern from "Python.h":
64
 
    WCHAR *PyUnicode_AS_UNICODE(object)
65
 
    Py_ssize_t PyUnicode_GET_SIZE(object)
66
 
    object PyUnicode_FromUnicode(WCHAR *, Py_ssize_t)
67
 
    int PyList_Append(object, object) except -1
68
 
    object PyUnicode_AsUTF8String(object)
69
 
 
70
 
 
71
 
import operator
72
 
import stat
73
 
 
74
 
from bzrlib import osutils, _readdir_py
75
 
 
76
 
 
77
 
cdef class _Win32Stat:
78
 
    """Represent a 'stat' result generated from WIN32_FIND_DATA"""
79
 
 
80
 
    cdef readonly int st_mode
81
 
    cdef readonly double st_ctime
82
 
    cdef readonly double st_mtime
83
 
    cdef readonly double st_atime
84
 
    # We can't just declare this as 'readonly' because python2.4 doesn't define
85
 
    # T_LONGLONG as a structure member. So instead we just use a property that
86
 
    # will convert it correctly anyway.
87
 
    cdef __int64 _st_size
88
 
 
89
 
    property st_size:
90
 
        def __get__(self):
91
 
            return self._st_size
92
 
 
93
 
    # os.stat always returns 0, so we hard code it here
94
 
    cdef readonly int st_dev
95
 
    cdef readonly int st_ino
96
 
 
97
 
    def __repr__(self):
98
 
        """Repr is the same as a Stat object.
99
 
 
100
 
        (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)
101
 
        """
102
 
        return repr((self.st_mode, 0, 0, 0, 0, 0, self.st_size, self.st_atime,
103
 
                     self.st_mtime, self.st_ctime))
104
 
 
105
 
 
106
 
cdef object _get_name(WIN32_FIND_DATAW *data):
107
 
    """Extract the Unicode name for this file/dir."""
108
 
    return PyUnicode_FromUnicode(data.cFileName,
109
 
                                 wcslen(data.cFileName))
110
 
 
111
 
 
112
 
cdef int _get_mode_bits(WIN32_FIND_DATAW *data): # cannot_raise
113
 
    cdef int mode_bits
114
 
 
115
 
    mode_bits = 0100666 # writeable file, the most common
116
 
    if data.dwFileAttributes & FILE_ATTRIBUTE_READONLY == FILE_ATTRIBUTE_READONLY:
117
 
        mode_bits = mode_bits ^ 0222 # remove the write bits
118
 
    if data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY:
119
 
        # Remove the FILE bit, set the DIR bit, and set the EXEC bits
120
 
        mode_bits = mode_bits ^ 0140111
121
 
    return mode_bits
122
 
 
123
 
 
124
 
cdef __int64 _get_size(WIN32_FIND_DATAW *data): # cannot_raise
125
 
    # Pyrex casts a DWORD into a PyLong anyway, so it is safe to do << 32
126
 
    # on a DWORD
127
 
    return ((<__int64>data.nFileSizeHigh) << 32) + data.nFileSizeLow
128
 
 
129
 
 
130
 
cdef double _ftime_to_timestamp(FILETIME *ft): # cannot_raise
131
 
    """Convert from a FILETIME struct into a floating point timestamp.
132
 
 
133
 
    The fields of a FILETIME structure are the hi and lo part
134
 
    of a 64-bit value expressed in 100 nanosecond units.
135
 
    1e7 is one second in such units; 1e-7 the inverse.
136
 
    429.4967296 is 2**32 / 1e7 or 2**32 * 1e-7.
137
 
    It also uses the epoch 1601-01-01 rather than 1970-01-01
138
 
    (taken from posixmodule.c)
139
 
    """
140
 
    cdef __int64 val
141
 
    # NB: This gives slightly different results versus casting to a 64-bit
142
 
    #     integer and doing integer math before casting into a floating
143
 
    #     point number. But the difference is in the sub millisecond range,
144
 
    #     which doesn't seem critical here.
145
 
    # secs between epochs: 11,644,473,600
146
 
    val = ((<__int64>ft.dwHighDateTime) << 32) + ft.dwLowDateTime
147
 
    return (val * 1.0e-7) - 11644473600.0
148
 
 
149
 
 
150
 
cdef int _should_skip(WIN32_FIND_DATAW *data): # cannot_raise
151
 
    """Is this '.' or '..' so we should skip it?"""
152
 
    if (data.cFileName[0] != c'.'):
153
 
        return 0
154
 
    if data.cFileName[1] == c'\0':
155
 
        return 1
156
 
    if data.cFileName[1] == c'.' and data.cFileName[2] == c'\0':
157
 
        return 1
158
 
    return 0
159
 
 
160
 
 
161
 
cdef class Win32ReadDir:
162
 
    """Read directories on win32."""
163
 
 
164
 
    cdef object _directory_kind
165
 
    cdef object _file_kind
166
 
 
167
 
    def __init__(self):
168
 
        self._directory_kind = _readdir_py._directory
169
 
        self._file_kind = _readdir_py._file
170
 
 
171
 
    def top_prefix_to_starting_dir(self, top, prefix=""):
172
 
        """See DirReader.top_prefix_to_starting_dir."""
173
 
        return (osutils.safe_utf8(prefix), None, None, None,
174
 
                osutils.safe_unicode(top))
175
 
 
176
 
    cdef object _get_kind(self, WIN32_FIND_DATAW *data):
177
 
        if data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY:
178
 
            return self._directory_kind
179
 
        return self._file_kind
180
 
 
181
 
    cdef _Win32Stat _get_stat_value(self, WIN32_FIND_DATAW *data):
182
 
        """Get the filename and the stat information."""
183
 
        cdef _Win32Stat statvalue
184
 
 
185
 
        statvalue = _Win32Stat()
186
 
        statvalue.st_mode = _get_mode_bits(data)
187
 
        statvalue.st_ctime = _ftime_to_timestamp(&data.ftCreationTime)
188
 
        statvalue.st_mtime = _ftime_to_timestamp(&data.ftLastWriteTime)
189
 
        statvalue.st_atime = _ftime_to_timestamp(&data.ftLastAccessTime)
190
 
        statvalue._st_size = _get_size(data)
191
 
        statvalue.st_ino = 0
192
 
        statvalue.st_dev = 0
193
 
        return statvalue
194
 
 
195
 
    def read_dir(self, prefix, top):
196
 
        """Win32 implementation of DirReader.read_dir.
197
 
 
198
 
        :seealso: DirReader.read_dir
199
 
        """
200
 
        cdef WIN32_FIND_DATAW search_data
201
 
        cdef HANDLE hFindFile
202
 
        cdef int last_err
203
 
        cdef WCHAR *query
204
 
        cdef int result
205
 
 
206
 
        if prefix:
207
 
            relprefix = prefix + '/'
208
 
        else:
209
 
            relprefix = ''
210
 
        top_slash = top + '/'
211
 
 
212
 
        top_star = top_slash + '*'
213
 
 
214
 
        dirblock = []
215
 
 
216
 
        query = PyUnicode_AS_UNICODE(top_star)
217
 
        hFindFile = FindFirstFileW(query, &search_data)
218
 
        if hFindFile == INVALID_HANDLE_VALUE:
219
 
            # Raise an exception? This path doesn't seem to exist
220
 
            raise WindowsError(GetLastError(), top_star)
221
 
 
222
 
        try:
223
 
            result = 1
224
 
            while result:
225
 
                # Skip '.' and '..'
226
 
                if _should_skip(&search_data):
227
 
                    result = FindNextFileW(hFindFile, &search_data)
228
 
                    continue
229
 
                name_unicode = _get_name(&search_data)
230
 
                name_utf8 = PyUnicode_AsUTF8String(name_unicode)
231
 
                PyList_Append(dirblock,
232
 
                    (relprefix + name_utf8, name_utf8,
233
 
                     self._get_kind(&search_data),
234
 
                     self._get_stat_value(&search_data),
235
 
                     top_slash + name_unicode))
236
 
 
237
 
                result = FindNextFileW(hFindFile, &search_data)
238
 
            # FindNextFileW sets GetLastError() == ERROR_NO_MORE_FILES when it
239
 
            # actually finishes. If we have anything else, then we have a
240
 
            # genuine problem
241
 
            last_err = GetLastError()
242
 
            if last_err != ERROR_NO_MORE_FILES:
243
 
                raise WindowsError(last_err)
244
 
        finally:
245
 
            result = FindClose(hFindFile)
246
 
            if result == 0:
247
 
                last_err = GetLastError()
248
 
                # TODO: We should probably raise an exception if FindClose
249
 
                #       returns an error, however, I don't want to supress an
250
 
                #       earlier Exception, so for now, I'm ignoring this
251
 
        dirblock.sort(key=operator.itemgetter(1))
252
 
        return dirblock