##// END OF EJS Templates
cffi: split modules from pure...
Yuya Nishihara -
r32512:0e8b0b9a default
parent child Browse files
Show More
@@ -0,0 +1,10
1 # base85.py: pure python base85 codec
2 #
3 # Copyright (C) 2009 Brendan Cully <brendan@kublai.com>
4 #
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
7
8 from __future__ import absolute_import
9
10 from ..pure.base85 import *
@@ -0,0 +1,10
1 # diffhelpers.py - pure Python implementation of diffhelpers.c
2 #
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
4 #
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
7
8 from __future__ import absolute_import
9
10 from ..pure.diffhelpers import *
@@ -0,0 +1,10
1 # parsers.py - Python implementation of parsers.c
2 #
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
4 #
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
7
8 from __future__ import absolute_import
9
10 from ..pure.parsers import *
@@ -1,162 +1,78
1 # bdiff.py - Python implementation of bdiff.c
1 # bdiff.py - CFFI implementation of bdiff.c
2 2 #
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2016 Maciej Fijalkowski <fijall@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 import difflib
11 import re
12 10 import struct
13 11
14 from .. import policy
15 policynocffi = policy.policynocffi
16 modulepolicy = policy.policy
17
18 def splitnewlines(text):
19 '''like str.splitlines, but only split on newlines.'''
20 lines = [l + '\n' for l in text.split('\n')]
21 if lines:
22 if lines[-1] == '\n':
23 lines.pop()
24 else:
25 lines[-1] = lines[-1][:-1]
26 return lines
27
28 def _normalizeblocks(a, b, blocks):
29 prev = None
30 r = []
31 for curr in blocks:
32 if prev is None:
33 prev = curr
34 continue
35 shift = 0
36
37 a1, b1, l1 = prev
38 a1end = a1 + l1
39 b1end = b1 + l1
40
41 a2, b2, l2 = curr
42 a2end = a2 + l2
43 b2end = b2 + l2
44 if a1end == a2:
45 while (a1end + shift < a2end and
46 a[a1end + shift] == b[b1end + shift]):
47 shift += 1
48 elif b1end == b2:
49 while (b1end + shift < b2end and
50 a[a1end + shift] == b[b1end + shift]):
51 shift += 1
52 r.append((a1, b1, l1 + shift))
53 prev = a2 + shift, b2 + shift, l2 - shift
54 r.append(prev)
55 return r
12 from ..pure.bdiff import *
13 from . import _bdiff
56 14
57 def bdiff(a, b):
58 a = bytes(a).splitlines(True)
59 b = bytes(b).splitlines(True)
60
61 if not a:
62 s = "".join(b)
63 return s and (struct.pack(">lll", 0, 0, len(s)) + s)
64
65 bin = []
66 p = [0]
67 for i in a: p.append(p[-1] + len(i))
68
69 d = difflib.SequenceMatcher(None, a, b).get_matching_blocks()
70 d = _normalizeblocks(a, b, d)
71 la = 0
72 lb = 0
73 for am, bm, size in d:
74 s = "".join(b[lb:bm])
75 if am > la or s:
76 bin.append(struct.pack(">lll", p[la], p[am], len(s)) + s)
77 la = am + size
78 lb = bm + size
15 ffi = _bdiff.ffi
16 lib = _bdiff.lib
79 17
80 return "".join(bin)
81
82 def blocks(a, b):
83 an = splitnewlines(a)
84 bn = splitnewlines(b)
85 d = difflib.SequenceMatcher(None, an, bn).get_matching_blocks()
86 d = _normalizeblocks(an, bn, d)
87 return [(i, i + n, j, j + n) for (i, j, n) in d]
88
89 def fixws(text, allws):
90 if allws:
91 text = re.sub('[ \t\r]+', '', text)
92 else:
93 text = re.sub('[ \t\r]+', ' ', text)
94 text = text.replace(' \n', '\n')
95 return text
96
97 if modulepolicy not in policynocffi:
98 try:
99 from ..cffi._bdiff import ffi, lib
100 except ImportError:
101 if modulepolicy == 'cffi': # strict cffi import
102 raise
103 else:
18 if True:
19 if True:
104 20 def blocks(sa, sb):
105 21 a = ffi.new("struct bdiff_line**")
106 22 b = ffi.new("struct bdiff_line**")
107 23 ac = ffi.new("char[]", str(sa))
108 24 bc = ffi.new("char[]", str(sb))
109 25 l = ffi.new("struct bdiff_hunk*")
110 26 try:
111 27 an = lib.bdiff_splitlines(ac, len(sa), a)
112 28 bn = lib.bdiff_splitlines(bc, len(sb), b)
113 29 if not a[0] or not b[0]:
114 30 raise MemoryError
115 31 count = lib.bdiff_diff(a[0], an, b[0], bn, l)
116 32 if count < 0:
117 33 raise MemoryError
118 34 rl = [None] * count
119 35 h = l.next
120 36 i = 0
121 37 while h:
122 38 rl[i] = (h.a1, h.a2, h.b1, h.b2)
123 39 h = h.next
124 40 i += 1
125 41 finally:
126 42 lib.free(a[0])
127 43 lib.free(b[0])
128 44 lib.bdiff_freehunks(l.next)
129 45 return rl
130 46
131 47 def bdiff(sa, sb):
132 48 a = ffi.new("struct bdiff_line**")
133 49 b = ffi.new("struct bdiff_line**")
134 50 ac = ffi.new("char[]", str(sa))
135 51 bc = ffi.new("char[]", str(sb))
136 52 l = ffi.new("struct bdiff_hunk*")
137 53 try:
138 54 an = lib.bdiff_splitlines(ac, len(sa), a)
139 55 bn = lib.bdiff_splitlines(bc, len(sb), b)
140 56 if not a[0] or not b[0]:
141 57 raise MemoryError
142 58 count = lib.bdiff_diff(a[0], an, b[0], bn, l)
143 59 if count < 0:
144 60 raise MemoryError
145 61 rl = []
146 62 h = l.next
147 63 la = lb = 0
148 64 while h:
149 65 if h.a1 != la or h.b1 != lb:
150 66 lgt = (b[0] + h.b1).l - (b[0] + lb).l
151 67 rl.append(struct.pack(">lll", (a[0] + la).l - a[0].l,
152 68 (a[0] + h.a1).l - a[0].l, lgt))
153 69 rl.append(str(ffi.buffer((b[0] + lb).l, lgt)))
154 70 la = h.a2
155 71 lb = h.b2
156 72 h = h.next
157 73
158 74 finally:
159 75 lib.free(a[0])
160 76 lib.free(b[0])
161 77 lib.bdiff_freehunks(l.next)
162 78 return "".join(rl)
@@ -1,169 +1,50
1 # mpatch.py - Python implementation of mpatch.c
1 # mpatch.py - CFFI implementation of mpatch.c
2 2 #
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2016 Maciej Fijalkowski <fijall@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 import struct
11
12 from .. import policy, pycompat
13 stringio = pycompat.stringio
14 modulepolicy = policy.policy
15 policynocffi = policy.policynocffi
16
17 class mpatchError(Exception):
18 """error raised when a delta cannot be decoded
19 """
20
21 # This attempts to apply a series of patches in time proportional to
22 # the total size of the patches, rather than patches * len(text). This
23 # means rather than shuffling strings around, we shuffle around
24 # pointers to fragments with fragment lists.
25 #
26 # When the fragment lists get too long, we collapse them. To do this
27 # efficiently, we do all our operations inside a buffer created by
28 # mmap and simply use memmove. This avoids creating a bunch of large
29 # temporary string buffers.
30
31 def _pull(dst, src, l): # pull l bytes from src
32 while l:
33 f = src.pop()
34 if f[0] > l: # do we need to split?
35 src.append((f[0] - l, f[1] + l))
36 dst.append((l, f[1]))
37 return
38 dst.append(f)
39 l -= f[0]
40
41 def _move(m, dest, src, count):
42 """move count bytes from src to dest
43
44 The file pointer is left at the end of dest.
45 """
46 m.seek(src)
47 buf = m.read(count)
48 m.seek(dest)
49 m.write(buf)
50
51 def _collect(m, buf, list):
52 start = buf
53 for l, p in reversed(list):
54 _move(m, buf, p, l)
55 buf += l
56 return (buf - start, start)
57
58 def patches(a, bins):
59 if not bins:
60 return a
61
62 plens = [len(x) for x in bins]
63 pl = sum(plens)
64 bl = len(a) + pl
65 tl = bl + bl + pl # enough for the patches and two working texts
66 b1, b2 = 0, bl
67
68 if not tl:
69 return a
70
71 m = stringio()
10 from ..pure.mpatch import *
11 from ..pure.mpatch import mpatchError # silence pyflakes
12 from . import _mpatch
72 13
73 # load our original text
74 m.write(a)
75 frags = [(len(a), b1)]
76
77 # copy all the patches into our segment so we can memmove from them
78 pos = b2 + bl
79 m.seek(pos)
80 for p in bins: m.write(p)
81
82 for plen in plens:
83 # if our list gets too long, execute it
84 if len(frags) > 128:
85 b2, b1 = b1, b2
86 frags = [_collect(m, b1, frags)]
87
88 new = []
89 end = pos + plen
90 last = 0
91 while pos < end:
92 m.seek(pos)
93 try:
94 p1, p2, l = struct.unpack(">lll", m.read(12))
95 except struct.error:
96 raise mpatchError("patch cannot be decoded")
97 _pull(new, frags, p1 - last) # what didn't change
98 _pull([], frags, p2 - p1) # what got deleted
99 new.append((l, pos + 12)) # what got added
100 pos += l + 12
101 last = p2
102 frags.extend(reversed(new)) # what was left at the end
103
104 t = _collect(m, b2, frags)
14 ffi = _mpatch.ffi
15 lib = _mpatch.lib
105 16
106 m.seek(t[1])
107 return m.read(t[0])
108
109 def patchedsize(orig, delta):
110 outlen, last, bin = 0, 0, 0
111 binend = len(delta)
112 data = 12
113
114 while data <= binend:
115 decode = delta[bin:bin + 12]
116 start, end, length = struct.unpack(">lll", decode)
117 if start > end:
118 break
119 bin = data + length
120 data = bin + 12
121 outlen += start - last
122 last = end
123 outlen += length
124
125 if bin != binend:
126 raise mpatchError("patch cannot be decoded")
127
128 outlen += orig - last
129 return outlen
130
131 if modulepolicy not in policynocffi:
132 try:
133 from ..cffi._mpatch import ffi, lib
134 except ImportError:
135 if modulepolicy == 'cffi': # strict cffi import
136 raise
137 else:
17 if True:
18 if True:
138 19 @ffi.def_extern()
139 20 def cffi_get_next_item(arg, pos):
140 21 all, bins = ffi.from_handle(arg)
141 22 container = ffi.new("struct mpatch_flist*[1]")
142 23 to_pass = ffi.new("char[]", str(bins[pos]))
143 24 all.append(to_pass)
144 25 r = lib.mpatch_decode(to_pass, len(to_pass) - 1, container)
145 26 if r < 0:
146 27 return ffi.NULL
147 28 return container[0]
148 29
149 30 def patches(text, bins):
150 31 lgt = len(bins)
151 32 all = []
152 33 if not lgt:
153 34 return text
154 35 arg = (all, bins)
155 36 patch = lib.mpatch_fold(ffi.new_handle(arg),
156 37 lib.cffi_get_next_item, 0, lgt)
157 38 if not patch:
158 39 raise mpatchError("cannot decode chunk")
159 40 outlen = lib.mpatch_calcsize(len(text), patch)
160 41 if outlen < 0:
161 42 lib.mpatch_lfree(patch)
162 43 raise mpatchError("inconsistency detected")
163 44 buf = ffi.new("char[]", outlen)
164 45 if lib.mpatch_apply(buf, text, len(text), patch) < 0:
165 46 lib.mpatch_lfree(patch)
166 47 raise mpatchError("error applying patches")
167 48 res = ffi.buffer(buf, outlen)[:]
168 49 lib.mpatch_lfree(patch)
169 50 return res
@@ -1,365 +1,102
1 # osutil.py - pure Python version of osutil.c
1 # osutil.py - CFFI version of osutil.c
2 2 #
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2016 Maciej Fijalkowski <fijall@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 import ctypes
11 import ctypes.util
12 10 import os
13 import socket
14 11 import stat as statmod
15 12
13 from ..pure.osutil import *
14
16 15 from .. import (
17 policy,
18 16 pycompat,
19 17 )
20 18
21 modulepolicy = policy.policy
22 policynocffi = policy.policynocffi
23
24 def _mode_to_kind(mode):
25 if statmod.S_ISREG(mode):
26 return statmod.S_IFREG
27 if statmod.S_ISDIR(mode):
28 return statmod.S_IFDIR
29 if statmod.S_ISLNK(mode):
30 return statmod.S_IFLNK
31 if statmod.S_ISBLK(mode):
32 return statmod.S_IFBLK
33 if statmod.S_ISCHR(mode):
34 return statmod.S_IFCHR
35 if statmod.S_ISFIFO(mode):
36 return statmod.S_IFIFO
37 if statmod.S_ISSOCK(mode):
38 return statmod.S_IFSOCK
39 return mode
40
41 def listdirpure(path, stat=False, skip=None):
42 '''listdir(path, stat=False) -> list_of_tuples
43
44 Return a sorted list containing information about the entries
45 in the directory.
46
47 If stat is True, each element is a 3-tuple:
48
49 (name, type, stat object)
19 if pycompat.sysplatform == 'darwin':
20 from . import _osutil
50 21
51 Otherwise, each element is a 2-tuple:
22 ffi = _osutil.ffi
23 lib = _osutil.lib
52 24
53 (name, type)
54 '''
55 result = []
56 prefix = path
57 if not prefix.endswith(pycompat.ossep):
58 prefix += pycompat.ossep
59 names = os.listdir(path)
60 names.sort()
61 for fn in names:
62 st = os.lstat(prefix + fn)
63 if fn == skip and statmod.S_ISDIR(st.st_mode):
64 return []
65 if stat:
66 result.append((fn, _mode_to_kind(st.st_mode), st))
67 else:
68 result.append((fn, _mode_to_kind(st.st_mode)))
69 return result
70
71 ffi = None
72 if modulepolicy not in policynocffi and pycompat.sysplatform == 'darwin':
73 try:
74 from ..cffi._osutil import ffi, lib
75 except ImportError:
76 if modulepolicy == 'cffi': # strict cffi import
77 raise
78
79 if pycompat.sysplatform == 'darwin' and ffi is not None:
80 25 listdir_batch_size = 4096
81 26 # tweakable number, only affects performance, which chunks
82 27 # of bytes do we get back from getattrlistbulk
83 28
84 29 attrkinds = [None] * 20 # we need the max no for enum VXXX, 20 is plenty
85 30
86 31 attrkinds[lib.VREG] = statmod.S_IFREG
87 32 attrkinds[lib.VDIR] = statmod.S_IFDIR
88 33 attrkinds[lib.VLNK] = statmod.S_IFLNK
89 34 attrkinds[lib.VBLK] = statmod.S_IFBLK
90 35 attrkinds[lib.VCHR] = statmod.S_IFCHR
91 36 attrkinds[lib.VFIFO] = statmod.S_IFIFO
92 37 attrkinds[lib.VSOCK] = statmod.S_IFSOCK
93 38
94 39 class stat_res(object):
95 40 def __init__(self, st_mode, st_mtime, st_size):
96 41 self.st_mode = st_mode
97 42 self.st_mtime = st_mtime
98 43 self.st_size = st_size
99 44
100 45 tv_sec_ofs = ffi.offsetof("struct timespec", "tv_sec")
101 46 buf = ffi.new("char[]", listdir_batch_size)
102 47
103 48 def listdirinternal(dfd, req, stat, skip):
104 49 ret = []
105 50 while True:
106 51 r = lib.getattrlistbulk(dfd, req, buf, listdir_batch_size, 0)
107 52 if r == 0:
108 53 break
109 54 if r == -1:
110 55 raise OSError(ffi.errno, os.strerror(ffi.errno))
111 56 cur = ffi.cast("val_attrs_t*", buf)
112 57 for i in range(r):
113 58 lgt = cur.length
114 59 assert lgt == ffi.cast('uint32_t*', cur)[0]
115 60 ofs = cur.name_info.attr_dataoffset
116 61 str_lgt = cur.name_info.attr_length
117 62 base_ofs = ffi.offsetof('val_attrs_t', 'name_info')
118 63 name = str(ffi.buffer(ffi.cast("char*", cur) + base_ofs + ofs,
119 64 str_lgt - 1))
120 65 tp = attrkinds[cur.obj_type]
121 66 if name == "." or name == "..":
122 67 continue
123 68 if skip == name and tp == statmod.S_ISDIR:
124 69 return []
125 70 if stat:
126 71 mtime = cur.mtime.tv_sec
127 72 mode = (cur.accessmask & ~lib.S_IFMT)| tp
128 73 ret.append((name, tp, stat_res(st_mode=mode, st_mtime=mtime,
129 74 st_size=cur.datalength)))
130 75 else:
131 76 ret.append((name, tp))
132 77 cur = ffi.cast("val_attrs_t*", int(ffi.cast("intptr_t", cur))
133 78 + lgt)
134 79 return ret
135 80
136 81 def listdir(path, stat=False, skip=None):
137 82 req = ffi.new("struct attrlist*")
138 83 req.bitmapcount = lib.ATTR_BIT_MAP_COUNT
139 84 req.commonattr = (lib.ATTR_CMN_RETURNED_ATTRS |
140 85 lib.ATTR_CMN_NAME |
141 86 lib.ATTR_CMN_OBJTYPE |
142 87 lib.ATTR_CMN_ACCESSMASK |
143 88 lib.ATTR_CMN_MODTIME)
144 89 req.fileattr = lib.ATTR_FILE_DATALENGTH
145 90 dfd = lib.open(path, lib.O_RDONLY, 0)
146 91 if dfd == -1:
147 92 raise OSError(ffi.errno, os.strerror(ffi.errno))
148 93
149 94 try:
150 95 ret = listdirinternal(dfd, req, stat, skip)
151 96 finally:
152 97 try:
153 98 lib.close(dfd)
154 99 except BaseException:
155 100 pass # we ignore all the errors from closing, not
156 101 # much we can do about that
157 102 return ret
158 else:
159 listdir = listdirpure
160
161 if pycompat.osname != 'nt':
162 posixfile = open
163
164 _SCM_RIGHTS = 0x01
165 _socklen_t = ctypes.c_uint
166
167 if pycompat.sysplatform.startswith('linux'):
168 # socket.h says "the type should be socklen_t but the definition of
169 # the kernel is incompatible with this."
170 _cmsg_len_t = ctypes.c_size_t
171 _msg_controllen_t = ctypes.c_size_t
172 _msg_iovlen_t = ctypes.c_size_t
173 else:
174 _cmsg_len_t = _socklen_t
175 _msg_controllen_t = _socklen_t
176 _msg_iovlen_t = ctypes.c_int
177
178 class _iovec(ctypes.Structure):
179 _fields_ = [
180 (u'iov_base', ctypes.c_void_p),
181 (u'iov_len', ctypes.c_size_t),
182 ]
183
184 class _msghdr(ctypes.Structure):
185 _fields_ = [
186 (u'msg_name', ctypes.c_void_p),
187 (u'msg_namelen', _socklen_t),
188 (u'msg_iov', ctypes.POINTER(_iovec)),
189 (u'msg_iovlen', _msg_iovlen_t),
190 (u'msg_control', ctypes.c_void_p),
191 (u'msg_controllen', _msg_controllen_t),
192 (u'msg_flags', ctypes.c_int),
193 ]
194
195 class _cmsghdr(ctypes.Structure):
196 _fields_ = [
197 (u'cmsg_len', _cmsg_len_t),
198 (u'cmsg_level', ctypes.c_int),
199 (u'cmsg_type', ctypes.c_int),
200 (u'cmsg_data', ctypes.c_ubyte * 0),
201 ]
202
203 _libc = ctypes.CDLL(ctypes.util.find_library(u'c'), use_errno=True)
204 _recvmsg = getattr(_libc, 'recvmsg', None)
205 if _recvmsg:
206 _recvmsg.restype = getattr(ctypes, 'c_ssize_t', ctypes.c_long)
207 _recvmsg.argtypes = (ctypes.c_int, ctypes.POINTER(_msghdr),
208 ctypes.c_int)
209 else:
210 # recvmsg isn't always provided by libc; such systems are unsupported
211 def _recvmsg(sockfd, msg, flags):
212 raise NotImplementedError('unsupported platform')
213
214 def _CMSG_FIRSTHDR(msgh):
215 if msgh.msg_controllen < ctypes.sizeof(_cmsghdr):
216 return
217 cmsgptr = ctypes.cast(msgh.msg_control, ctypes.POINTER(_cmsghdr))
218 return cmsgptr.contents
219
220 # The pure version is less portable than the native version because the
221 # handling of socket ancillary data heavily depends on C preprocessor.
222 # Also, some length fields are wrongly typed in Linux kernel.
223 def recvfds(sockfd):
224 """receive list of file descriptors via socket"""
225 dummy = (ctypes.c_ubyte * 1)()
226 iov = _iovec(ctypes.cast(dummy, ctypes.c_void_p), ctypes.sizeof(dummy))
227 cbuf = ctypes.create_string_buffer(256)
228 msgh = _msghdr(None, 0,
229 ctypes.pointer(iov), 1,
230 ctypes.cast(cbuf, ctypes.c_void_p), ctypes.sizeof(cbuf),
231 0)
232 r = _recvmsg(sockfd, ctypes.byref(msgh), 0)
233 if r < 0:
234 e = ctypes.get_errno()
235 raise OSError(e, os.strerror(e))
236 # assumes that the first cmsg has fds because it isn't easy to write
237 # portable CMSG_NXTHDR() with ctypes.
238 cmsg = _CMSG_FIRSTHDR(msgh)
239 if not cmsg:
240 return []
241 if (cmsg.cmsg_level != socket.SOL_SOCKET or
242 cmsg.cmsg_type != _SCM_RIGHTS):
243 return []
244 rfds = ctypes.cast(cmsg.cmsg_data, ctypes.POINTER(ctypes.c_int))
245 rfdscount = ((cmsg.cmsg_len - _cmsghdr.cmsg_data.offset) /
246 ctypes.sizeof(ctypes.c_int))
247 return [rfds[i] for i in xrange(rfdscount)]
248
249 else:
250 import msvcrt
251
252 _kernel32 = ctypes.windll.kernel32
253
254 _DWORD = ctypes.c_ulong
255 _LPCSTR = _LPSTR = ctypes.c_char_p
256 _HANDLE = ctypes.c_void_p
257
258 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
259
260 # CreateFile
261 _FILE_SHARE_READ = 0x00000001
262 _FILE_SHARE_WRITE = 0x00000002
263 _FILE_SHARE_DELETE = 0x00000004
264
265 _CREATE_ALWAYS = 2
266 _OPEN_EXISTING = 3
267 _OPEN_ALWAYS = 4
268
269 _GENERIC_READ = 0x80000000
270 _GENERIC_WRITE = 0x40000000
271
272 _FILE_ATTRIBUTE_NORMAL = 0x80
273
274 # open_osfhandle flags
275 _O_RDONLY = 0x0000
276 _O_RDWR = 0x0002
277 _O_APPEND = 0x0008
278
279 _O_TEXT = 0x4000
280 _O_BINARY = 0x8000
281
282 # types of parameters of C functions used (required by pypy)
283
284 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
285 _DWORD, _DWORD, _HANDLE]
286 _kernel32.CreateFileA.restype = _HANDLE
287
288 def _raiseioerror(name):
289 err = ctypes.WinError()
290 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
291
292 class posixfile(object):
293 '''a file object aiming for POSIX-like semantics
294
295 CPython's open() returns a file that was opened *without* setting the
296 _FILE_SHARE_DELETE flag, which causes rename and unlink to abort.
297 This even happens if any hardlinked copy of the file is in open state.
298 We set _FILE_SHARE_DELETE here, so files opened with posixfile can be
299 renamed and deleted while they are held open.
300 Note that if a file opened with posixfile is unlinked, the file
301 remains but cannot be opened again or be recreated under the same name,
302 until all reading processes have closed the file.'''
303
304 def __init__(self, name, mode='r', bufsize=-1):
305 if 'b' in mode:
306 flags = _O_BINARY
307 else:
308 flags = _O_TEXT
309
310 m0 = mode[0]
311 if m0 == 'r' and '+' not in mode:
312 flags |= _O_RDONLY
313 access = _GENERIC_READ
314 else:
315 # work around http://support.microsoft.com/kb/899149 and
316 # set _O_RDWR for 'w' and 'a', even if mode has no '+'
317 flags |= _O_RDWR
318 access = _GENERIC_READ | _GENERIC_WRITE
319
320 if m0 == 'r':
321 creation = _OPEN_EXISTING
322 elif m0 == 'w':
323 creation = _CREATE_ALWAYS
324 elif m0 == 'a':
325 creation = _OPEN_ALWAYS
326 flags |= _O_APPEND
327 else:
328 raise ValueError("invalid mode: %s" % mode)
329
330 fh = _kernel32.CreateFileA(name, access,
331 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
332 None, creation, _FILE_ATTRIBUTE_NORMAL, None)
333 if fh == _INVALID_HANDLE_VALUE:
334 _raiseioerror(name)
335
336 fd = msvcrt.open_osfhandle(fh, flags)
337 if fd == -1:
338 _kernel32.CloseHandle(fh)
339 _raiseioerror(name)
340
341 f = os.fdopen(fd, pycompat.sysstr(mode), bufsize)
342 # unfortunately, f.name is '<fdopen>' at this point -- so we store
343 # the name on this wrapper. We cannot just assign to f.name,
344 # because that attribute is read-only.
345 object.__setattr__(self, r'name', name)
346 object.__setattr__(self, r'_file', f)
347
348 def __iter__(self):
349 return self._file
350
351 def __getattr__(self, name):
352 return getattr(self._file, name)
353
354 def __setattr__(self, name, value):
355 '''mimics the read-only attributes of Python file objects
356 by raising 'TypeError: readonly attribute' if someone tries:
357 f = posixfile('foo.txt')
358 f.name = 'bla' '''
359 return self._file.__setattr__(name, value)
360
361 def __enter__(self):
362 return self._file.__enter__()
363
364 def __exit__(self, exc_type, exc_value, exc_tb):
365 return self._file.__exit__(exc_type, exc_value, exc_tb)
@@ -1,106 +1,106
1 1 # policy.py - module policy logic for Mercurial.
2 2 #
3 3 # Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import os
11 11 import sys
12 12
13 13 # Rules for how modules can be loaded. Values are:
14 14 #
15 15 # c - require C extensions
16 16 # allow - allow pure Python implementation when C loading fails
17 17 # cffi - required cffi versions (implemented within pure module)
18 18 # cffi-allow - allow pure Python implementation if cffi version is missing
19 19 # py - only load pure Python modules
20 20 #
21 21 # By default, fall back to the pure modules so the in-place build can
22 22 # run without recompiling the C extensions. This will be overridden by
23 23 # __modulepolicy__ generated by setup.py.
24 24 policy = b'allow'
25 25 policynoc = (b'cffi', b'cffi-allow', b'py')
26 26 policynocffi = (b'c', b'py')
27 27 _packageprefs = {
28 28 # policy: (versioned package, pure package)
29 29 b'c': (r'cext', None),
30 30 b'allow': (r'cext', r'pure'),
31 b'cffi': (None, r'pure'), # TODO: (r'cffi', None)
32 b'cffi-allow': (None, r'pure'), # TODO: (r'cffi', r'pure')
31 b'cffi': (r'cffi', None),
32 b'cffi-allow': (r'cffi', r'pure'),
33 33 b'py': (None, r'pure'),
34 34 }
35 35
36 36 try:
37 37 from . import __modulepolicy__
38 38 policy = __modulepolicy__.modulepolicy
39 39 except ImportError:
40 40 pass
41 41
42 42 # PyPy doesn't load C extensions.
43 43 #
44 44 # The canonical way to do this is to test platform.python_implementation().
45 45 # But we don't import platform and don't bloat for it here.
46 46 if r'__pypy__' in sys.builtin_module_names:
47 47 policy = b'cffi'
48 48
49 49 # Our C extensions aren't yet compatible with Python 3. So use pure Python
50 50 # on Python 3 for now.
51 51 if sys.version_info[0] >= 3:
52 52 policy = b'py'
53 53
54 54 # Environment variable can always force settings.
55 55 if sys.version_info[0] >= 3:
56 56 if r'HGMODULEPOLICY' in os.environ:
57 57 policy = os.environ[r'HGMODULEPOLICY'].encode(r'utf-8')
58 58 else:
59 59 policy = os.environ.get(r'HGMODULEPOLICY', policy)
60 60
61 61 def _importfrom(pkgname, modname):
62 62 # from .<pkgname> import <modname> (where . is looked through this module)
63 63 fakelocals = {}
64 64 pkg = __import__(pkgname, globals(), fakelocals, [modname], level=1)
65 65 try:
66 66 fakelocals[modname] = mod = getattr(pkg, modname)
67 67 except AttributeError:
68 68 raise ImportError(r'cannot import name %s' % modname)
69 69 # force import; fakelocals[modname] may be replaced with the real module
70 70 getattr(mod, r'__doc__', None)
71 71 return fakelocals[modname]
72 72
73 73 # keep in sync with "version" in C modules
74 74 _cextversions = {
75 75 (r'cext', r'base85'): 1,
76 76 (r'cext', r'bdiff'): 1,
77 77 (r'cext', r'diffhelpers'): 1,
78 78 (r'cext', r'mpatch'): 1,
79 79 (r'cext', r'osutil'): 1,
80 80 (r'cext', r'parsers'): 1,
81 81 }
82 82
83 83 def _checkmod(pkgname, modname, mod):
84 84 expected = _cextversions.get((pkgname, modname))
85 85 actual = getattr(mod, r'version', None)
86 86 if actual != expected:
87 87 raise ImportError(r'cannot import module %s.%s '
88 88 r'(expected version: %d, actual: %r)'
89 89 % (pkgname, modname, expected, actual))
90 90
91 91 def importmod(modname):
92 92 """Import module according to policy and check API version"""
93 93 try:
94 94 verpkg, purepkg = _packageprefs[policy]
95 95 except KeyError:
96 96 raise ImportError(r'invalid HGMODULEPOLICY %r' % policy)
97 97 assert verpkg or purepkg
98 98 if verpkg:
99 99 try:
100 100 mod = _importfrom(verpkg, modname)
101 101 _checkmod(verpkg, modname, mod)
102 102 return mod
103 103 except ImportError:
104 104 if not purepkg:
105 105 raise
106 106 return _importfrom(purepkg, modname)
@@ -1,162 +1,91
1 1 # bdiff.py - Python implementation of bdiff.c
2 2 #
3 3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import difflib
11 11 import re
12 12 import struct
13 13
14 from .. import policy
15 policynocffi = policy.policynocffi
16 modulepolicy = policy.policy
17
18 14 def splitnewlines(text):
19 15 '''like str.splitlines, but only split on newlines.'''
20 16 lines = [l + '\n' for l in text.split('\n')]
21 17 if lines:
22 18 if lines[-1] == '\n':
23 19 lines.pop()
24 20 else:
25 21 lines[-1] = lines[-1][:-1]
26 22 return lines
27 23
28 24 def _normalizeblocks(a, b, blocks):
29 25 prev = None
30 26 r = []
31 27 for curr in blocks:
32 28 if prev is None:
33 29 prev = curr
34 30 continue
35 31 shift = 0
36 32
37 33 a1, b1, l1 = prev
38 34 a1end = a1 + l1
39 35 b1end = b1 + l1
40 36
41 37 a2, b2, l2 = curr
42 38 a2end = a2 + l2
43 39 b2end = b2 + l2
44 40 if a1end == a2:
45 41 while (a1end + shift < a2end and
46 42 a[a1end + shift] == b[b1end + shift]):
47 43 shift += 1
48 44 elif b1end == b2:
49 45 while (b1end + shift < b2end and
50 46 a[a1end + shift] == b[b1end + shift]):
51 47 shift += 1
52 48 r.append((a1, b1, l1 + shift))
53 49 prev = a2 + shift, b2 + shift, l2 - shift
54 50 r.append(prev)
55 51 return r
56 52
57 53 def bdiff(a, b):
58 54 a = bytes(a).splitlines(True)
59 55 b = bytes(b).splitlines(True)
60 56
61 57 if not a:
62 58 s = "".join(b)
63 59 return s and (struct.pack(">lll", 0, 0, len(s)) + s)
64 60
65 61 bin = []
66 62 p = [0]
67 63 for i in a: p.append(p[-1] + len(i))
68 64
69 65 d = difflib.SequenceMatcher(None, a, b).get_matching_blocks()
70 66 d = _normalizeblocks(a, b, d)
71 67 la = 0
72 68 lb = 0
73 69 for am, bm, size in d:
74 70 s = "".join(b[lb:bm])
75 71 if am > la or s:
76 72 bin.append(struct.pack(">lll", p[la], p[am], len(s)) + s)
77 73 la = am + size
78 74 lb = bm + size
79 75
80 76 return "".join(bin)
81 77
82 78 def blocks(a, b):
83 79 an = splitnewlines(a)
84 80 bn = splitnewlines(b)
85 81 d = difflib.SequenceMatcher(None, an, bn).get_matching_blocks()
86 82 d = _normalizeblocks(an, bn, d)
87 83 return [(i, i + n, j, j + n) for (i, j, n) in d]
88 84
89 85 def fixws(text, allws):
90 86 if allws:
91 87 text = re.sub('[ \t\r]+', '', text)
92 88 else:
93 89 text = re.sub('[ \t\r]+', ' ', text)
94 90 text = text.replace(' \n', '\n')
95 91 return text
96
97 if modulepolicy not in policynocffi:
98 try:
99 from ..cffi._bdiff import ffi, lib
100 except ImportError:
101 if modulepolicy == 'cffi': # strict cffi import
102 raise
103 else:
104 def blocks(sa, sb):
105 a = ffi.new("struct bdiff_line**")
106 b = ffi.new("struct bdiff_line**")
107 ac = ffi.new("char[]", str(sa))
108 bc = ffi.new("char[]", str(sb))
109 l = ffi.new("struct bdiff_hunk*")
110 try:
111 an = lib.bdiff_splitlines(ac, len(sa), a)
112 bn = lib.bdiff_splitlines(bc, len(sb), b)
113 if not a[0] or not b[0]:
114 raise MemoryError
115 count = lib.bdiff_diff(a[0], an, b[0], bn, l)
116 if count < 0:
117 raise MemoryError
118 rl = [None] * count
119 h = l.next
120 i = 0
121 while h:
122 rl[i] = (h.a1, h.a2, h.b1, h.b2)
123 h = h.next
124 i += 1
125 finally:
126 lib.free(a[0])
127 lib.free(b[0])
128 lib.bdiff_freehunks(l.next)
129 return rl
130
131 def bdiff(sa, sb):
132 a = ffi.new("struct bdiff_line**")
133 b = ffi.new("struct bdiff_line**")
134 ac = ffi.new("char[]", str(sa))
135 bc = ffi.new("char[]", str(sb))
136 l = ffi.new("struct bdiff_hunk*")
137 try:
138 an = lib.bdiff_splitlines(ac, len(sa), a)
139 bn = lib.bdiff_splitlines(bc, len(sb), b)
140 if not a[0] or not b[0]:
141 raise MemoryError
142 count = lib.bdiff_diff(a[0], an, b[0], bn, l)
143 if count < 0:
144 raise MemoryError
145 rl = []
146 h = l.next
147 la = lb = 0
148 while h:
149 if h.a1 != la or h.b1 != lb:
150 lgt = (b[0] + h.b1).l - (b[0] + lb).l
151 rl.append(struct.pack(">lll", (a[0] + la).l - a[0].l,
152 (a[0] + h.a1).l - a[0].l, lgt))
153 rl.append(str(ffi.buffer((b[0] + lb).l, lgt)))
154 la = h.a2
155 lb = h.b2
156 h = h.next
157
158 finally:
159 lib.free(a[0])
160 lib.free(b[0])
161 lib.bdiff_freehunks(l.next)
162 return "".join(rl)
@@ -1,169 +1,127
1 1 # mpatch.py - Python implementation of mpatch.c
2 2 #
3 3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import struct
11 11
12 from .. import policy, pycompat
12 from .. import pycompat
13 13 stringio = pycompat.stringio
14 modulepolicy = policy.policy
15 policynocffi = policy.policynocffi
16 14
17 15 class mpatchError(Exception):
18 16 """error raised when a delta cannot be decoded
19 17 """
20 18
21 19 # This attempts to apply a series of patches in time proportional to
22 20 # the total size of the patches, rather than patches * len(text). This
23 21 # means rather than shuffling strings around, we shuffle around
24 22 # pointers to fragments with fragment lists.
25 23 #
26 24 # When the fragment lists get too long, we collapse them. To do this
27 25 # efficiently, we do all our operations inside a buffer created by
28 26 # mmap and simply use memmove. This avoids creating a bunch of large
29 27 # temporary string buffers.
30 28
31 29 def _pull(dst, src, l): # pull l bytes from src
32 30 while l:
33 31 f = src.pop()
34 32 if f[0] > l: # do we need to split?
35 33 src.append((f[0] - l, f[1] + l))
36 34 dst.append((l, f[1]))
37 35 return
38 36 dst.append(f)
39 37 l -= f[0]
40 38
41 39 def _move(m, dest, src, count):
42 40 """move count bytes from src to dest
43 41
44 42 The file pointer is left at the end of dest.
45 43 """
46 44 m.seek(src)
47 45 buf = m.read(count)
48 46 m.seek(dest)
49 47 m.write(buf)
50 48
51 49 def _collect(m, buf, list):
52 50 start = buf
53 51 for l, p in reversed(list):
54 52 _move(m, buf, p, l)
55 53 buf += l
56 54 return (buf - start, start)
57 55
58 56 def patches(a, bins):
59 57 if not bins:
60 58 return a
61 59
62 60 plens = [len(x) for x in bins]
63 61 pl = sum(plens)
64 62 bl = len(a) + pl
65 63 tl = bl + bl + pl # enough for the patches and two working texts
66 64 b1, b2 = 0, bl
67 65
68 66 if not tl:
69 67 return a
70 68
71 69 m = stringio()
72 70
73 71 # load our original text
74 72 m.write(a)
75 73 frags = [(len(a), b1)]
76 74
77 75 # copy all the patches into our segment so we can memmove from them
78 76 pos = b2 + bl
79 77 m.seek(pos)
80 78 for p in bins: m.write(p)
81 79
82 80 for plen in plens:
83 81 # if our list gets too long, execute it
84 82 if len(frags) > 128:
85 83 b2, b1 = b1, b2
86 84 frags = [_collect(m, b1, frags)]
87 85
88 86 new = []
89 87 end = pos + plen
90 88 last = 0
91 89 while pos < end:
92 90 m.seek(pos)
93 91 try:
94 92 p1, p2, l = struct.unpack(">lll", m.read(12))
95 93 except struct.error:
96 94 raise mpatchError("patch cannot be decoded")
97 95 _pull(new, frags, p1 - last) # what didn't change
98 96 _pull([], frags, p2 - p1) # what got deleted
99 97 new.append((l, pos + 12)) # what got added
100 98 pos += l + 12
101 99 last = p2
102 100 frags.extend(reversed(new)) # what was left at the end
103 101
104 102 t = _collect(m, b2, frags)
105 103
106 104 m.seek(t[1])
107 105 return m.read(t[0])
108 106
109 107 def patchedsize(orig, delta):
110 108 outlen, last, bin = 0, 0, 0
111 109 binend = len(delta)
112 110 data = 12
113 111
114 112 while data <= binend:
115 113 decode = delta[bin:bin + 12]
116 114 start, end, length = struct.unpack(">lll", decode)
117 115 if start > end:
118 116 break
119 117 bin = data + length
120 118 data = bin + 12
121 119 outlen += start - last
122 120 last = end
123 121 outlen += length
124 122
125 123 if bin != binend:
126 124 raise mpatchError("patch cannot be decoded")
127 125
128 126 outlen += orig - last
129 127 return outlen
130
131 if modulepolicy not in policynocffi:
132 try:
133 from ..cffi._mpatch import ffi, lib
134 except ImportError:
135 if modulepolicy == 'cffi': # strict cffi import
136 raise
137 else:
138 @ffi.def_extern()
139 def cffi_get_next_item(arg, pos):
140 all, bins = ffi.from_handle(arg)
141 container = ffi.new("struct mpatch_flist*[1]")
142 to_pass = ffi.new("char[]", str(bins[pos]))
143 all.append(to_pass)
144 r = lib.mpatch_decode(to_pass, len(to_pass) - 1, container)
145 if r < 0:
146 return ffi.NULL
147 return container[0]
148
149 def patches(text, bins):
150 lgt = len(bins)
151 all = []
152 if not lgt:
153 return text
154 arg = (all, bins)
155 patch = lib.mpatch_fold(ffi.new_handle(arg),
156 lib.cffi_get_next_item, 0, lgt)
157 if not patch:
158 raise mpatchError("cannot decode chunk")
159 outlen = lib.mpatch_calcsize(len(text), patch)
160 if outlen < 0:
161 lib.mpatch_lfree(patch)
162 raise mpatchError("inconsistency detected")
163 buf = ffi.new("char[]", outlen)
164 if lib.mpatch_apply(buf, text, len(text), patch) < 0:
165 lib.mpatch_lfree(patch)
166 raise mpatchError("error applying patches")
167 res = ffi.buffer(buf, outlen)[:]
168 lib.mpatch_lfree(patch)
169 return res
@@ -1,365 +1,271
1 1 # osutil.py - pure Python version of osutil.c
2 2 #
3 3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import ctypes
11 11 import ctypes.util
12 12 import os
13 13 import socket
14 14 import stat as statmod
15 15
16 16 from .. import (
17 policy,
18 17 pycompat,
19 18 )
20 19
21 modulepolicy = policy.policy
22 policynocffi = policy.policynocffi
23
24 20 def _mode_to_kind(mode):
25 21 if statmod.S_ISREG(mode):
26 22 return statmod.S_IFREG
27 23 if statmod.S_ISDIR(mode):
28 24 return statmod.S_IFDIR
29 25 if statmod.S_ISLNK(mode):
30 26 return statmod.S_IFLNK
31 27 if statmod.S_ISBLK(mode):
32 28 return statmod.S_IFBLK
33 29 if statmod.S_ISCHR(mode):
34 30 return statmod.S_IFCHR
35 31 if statmod.S_ISFIFO(mode):
36 32 return statmod.S_IFIFO
37 33 if statmod.S_ISSOCK(mode):
38 34 return statmod.S_IFSOCK
39 35 return mode
40 36
41 def listdirpure(path, stat=False, skip=None):
37 def listdir(path, stat=False, skip=None):
42 38 '''listdir(path, stat=False) -> list_of_tuples
43 39
44 40 Return a sorted list containing information about the entries
45 41 in the directory.
46 42
47 43 If stat is True, each element is a 3-tuple:
48 44
49 45 (name, type, stat object)
50 46
51 47 Otherwise, each element is a 2-tuple:
52 48
53 49 (name, type)
54 50 '''
55 51 result = []
56 52 prefix = path
57 53 if not prefix.endswith(pycompat.ossep):
58 54 prefix += pycompat.ossep
59 55 names = os.listdir(path)
60 56 names.sort()
61 57 for fn in names:
62 58 st = os.lstat(prefix + fn)
63 59 if fn == skip and statmod.S_ISDIR(st.st_mode):
64 60 return []
65 61 if stat:
66 62 result.append((fn, _mode_to_kind(st.st_mode), st))
67 63 else:
68 64 result.append((fn, _mode_to_kind(st.st_mode)))
69 65 return result
70 66
71 ffi = None
72 if modulepolicy not in policynocffi and pycompat.sysplatform == 'darwin':
73 try:
74 from ..cffi._osutil import ffi, lib
75 except ImportError:
76 if modulepolicy == 'cffi': # strict cffi import
77 raise
78
79 if pycompat.sysplatform == 'darwin' and ffi is not None:
80 listdir_batch_size = 4096
81 # tweakable number, only affects performance, which chunks
82 # of bytes do we get back from getattrlistbulk
83
84 attrkinds = [None] * 20 # we need the max no for enum VXXX, 20 is plenty
85
86 attrkinds[lib.VREG] = statmod.S_IFREG
87 attrkinds[lib.VDIR] = statmod.S_IFDIR
88 attrkinds[lib.VLNK] = statmod.S_IFLNK
89 attrkinds[lib.VBLK] = statmod.S_IFBLK
90 attrkinds[lib.VCHR] = statmod.S_IFCHR
91 attrkinds[lib.VFIFO] = statmod.S_IFIFO
92 attrkinds[lib.VSOCK] = statmod.S_IFSOCK
93
94 class stat_res(object):
95 def __init__(self, st_mode, st_mtime, st_size):
96 self.st_mode = st_mode
97 self.st_mtime = st_mtime
98 self.st_size = st_size
99
100 tv_sec_ofs = ffi.offsetof("struct timespec", "tv_sec")
101 buf = ffi.new("char[]", listdir_batch_size)
102
103 def listdirinternal(dfd, req, stat, skip):
104 ret = []
105 while True:
106 r = lib.getattrlistbulk(dfd, req, buf, listdir_batch_size, 0)
107 if r == 0:
108 break
109 if r == -1:
110 raise OSError(ffi.errno, os.strerror(ffi.errno))
111 cur = ffi.cast("val_attrs_t*", buf)
112 for i in range(r):
113 lgt = cur.length
114 assert lgt == ffi.cast('uint32_t*', cur)[0]
115 ofs = cur.name_info.attr_dataoffset
116 str_lgt = cur.name_info.attr_length
117 base_ofs = ffi.offsetof('val_attrs_t', 'name_info')
118 name = str(ffi.buffer(ffi.cast("char*", cur) + base_ofs + ofs,
119 str_lgt - 1))
120 tp = attrkinds[cur.obj_type]
121 if name == "." or name == "..":
122 continue
123 if skip == name and tp == statmod.S_ISDIR:
124 return []
125 if stat:
126 mtime = cur.mtime.tv_sec
127 mode = (cur.accessmask & ~lib.S_IFMT)| tp
128 ret.append((name, tp, stat_res(st_mode=mode, st_mtime=mtime,
129 st_size=cur.datalength)))
130 else:
131 ret.append((name, tp))
132 cur = ffi.cast("val_attrs_t*", int(ffi.cast("intptr_t", cur))
133 + lgt)
134 return ret
135
136 def listdir(path, stat=False, skip=None):
137 req = ffi.new("struct attrlist*")
138 req.bitmapcount = lib.ATTR_BIT_MAP_COUNT
139 req.commonattr = (lib.ATTR_CMN_RETURNED_ATTRS |
140 lib.ATTR_CMN_NAME |
141 lib.ATTR_CMN_OBJTYPE |
142 lib.ATTR_CMN_ACCESSMASK |
143 lib.ATTR_CMN_MODTIME)
144 req.fileattr = lib.ATTR_FILE_DATALENGTH
145 dfd = lib.open(path, lib.O_RDONLY, 0)
146 if dfd == -1:
147 raise OSError(ffi.errno, os.strerror(ffi.errno))
148
149 try:
150 ret = listdirinternal(dfd, req, stat, skip)
151 finally:
152 try:
153 lib.close(dfd)
154 except BaseException:
155 pass # we ignore all the errors from closing, not
156 # much we can do about that
157 return ret
158 else:
159 listdir = listdirpure
160
161 67 if pycompat.osname != 'nt':
162 68 posixfile = open
163 69
164 70 _SCM_RIGHTS = 0x01
165 71 _socklen_t = ctypes.c_uint
166 72
167 73 if pycompat.sysplatform.startswith('linux'):
168 74 # socket.h says "the type should be socklen_t but the definition of
169 75 # the kernel is incompatible with this."
170 76 _cmsg_len_t = ctypes.c_size_t
171 77 _msg_controllen_t = ctypes.c_size_t
172 78 _msg_iovlen_t = ctypes.c_size_t
173 79 else:
174 80 _cmsg_len_t = _socklen_t
175 81 _msg_controllen_t = _socklen_t
176 82 _msg_iovlen_t = ctypes.c_int
177 83
178 84 class _iovec(ctypes.Structure):
179 85 _fields_ = [
180 86 (u'iov_base', ctypes.c_void_p),
181 87 (u'iov_len', ctypes.c_size_t),
182 88 ]
183 89
184 90 class _msghdr(ctypes.Structure):
185 91 _fields_ = [
186 92 (u'msg_name', ctypes.c_void_p),
187 93 (u'msg_namelen', _socklen_t),
188 94 (u'msg_iov', ctypes.POINTER(_iovec)),
189 95 (u'msg_iovlen', _msg_iovlen_t),
190 96 (u'msg_control', ctypes.c_void_p),
191 97 (u'msg_controllen', _msg_controllen_t),
192 98 (u'msg_flags', ctypes.c_int),
193 99 ]
194 100
195 101 class _cmsghdr(ctypes.Structure):
196 102 _fields_ = [
197 103 (u'cmsg_len', _cmsg_len_t),
198 104 (u'cmsg_level', ctypes.c_int),
199 105 (u'cmsg_type', ctypes.c_int),
200 106 (u'cmsg_data', ctypes.c_ubyte * 0),
201 107 ]
202 108
203 109 _libc = ctypes.CDLL(ctypes.util.find_library(u'c'), use_errno=True)
204 110 _recvmsg = getattr(_libc, 'recvmsg', None)
205 111 if _recvmsg:
206 112 _recvmsg.restype = getattr(ctypes, 'c_ssize_t', ctypes.c_long)
207 113 _recvmsg.argtypes = (ctypes.c_int, ctypes.POINTER(_msghdr),
208 114 ctypes.c_int)
209 115 else:
210 116 # recvmsg isn't always provided by libc; such systems are unsupported
211 117 def _recvmsg(sockfd, msg, flags):
212 118 raise NotImplementedError('unsupported platform')
213 119
214 120 def _CMSG_FIRSTHDR(msgh):
215 121 if msgh.msg_controllen < ctypes.sizeof(_cmsghdr):
216 122 return
217 123 cmsgptr = ctypes.cast(msgh.msg_control, ctypes.POINTER(_cmsghdr))
218 124 return cmsgptr.contents
219 125
220 126 # The pure version is less portable than the native version because the
221 127 # handling of socket ancillary data heavily depends on C preprocessor.
222 128 # Also, some length fields are wrongly typed in Linux kernel.
223 129 def recvfds(sockfd):
224 130 """receive list of file descriptors via socket"""
225 131 dummy = (ctypes.c_ubyte * 1)()
226 132 iov = _iovec(ctypes.cast(dummy, ctypes.c_void_p), ctypes.sizeof(dummy))
227 133 cbuf = ctypes.create_string_buffer(256)
228 134 msgh = _msghdr(None, 0,
229 135 ctypes.pointer(iov), 1,
230 136 ctypes.cast(cbuf, ctypes.c_void_p), ctypes.sizeof(cbuf),
231 137 0)
232 138 r = _recvmsg(sockfd, ctypes.byref(msgh), 0)
233 139 if r < 0:
234 140 e = ctypes.get_errno()
235 141 raise OSError(e, os.strerror(e))
236 142 # assumes that the first cmsg has fds because it isn't easy to write
237 143 # portable CMSG_NXTHDR() with ctypes.
238 144 cmsg = _CMSG_FIRSTHDR(msgh)
239 145 if not cmsg:
240 146 return []
241 147 if (cmsg.cmsg_level != socket.SOL_SOCKET or
242 148 cmsg.cmsg_type != _SCM_RIGHTS):
243 149 return []
244 150 rfds = ctypes.cast(cmsg.cmsg_data, ctypes.POINTER(ctypes.c_int))
245 151 rfdscount = ((cmsg.cmsg_len - _cmsghdr.cmsg_data.offset) /
246 152 ctypes.sizeof(ctypes.c_int))
247 153 return [rfds[i] for i in xrange(rfdscount)]
248 154
249 155 else:
250 156 import msvcrt
251 157
252 158 _kernel32 = ctypes.windll.kernel32
253 159
254 160 _DWORD = ctypes.c_ulong
255 161 _LPCSTR = _LPSTR = ctypes.c_char_p
256 162 _HANDLE = ctypes.c_void_p
257 163
258 164 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
259 165
260 166 # CreateFile
261 167 _FILE_SHARE_READ = 0x00000001
262 168 _FILE_SHARE_WRITE = 0x00000002
263 169 _FILE_SHARE_DELETE = 0x00000004
264 170
265 171 _CREATE_ALWAYS = 2
266 172 _OPEN_EXISTING = 3
267 173 _OPEN_ALWAYS = 4
268 174
269 175 _GENERIC_READ = 0x80000000
270 176 _GENERIC_WRITE = 0x40000000
271 177
272 178 _FILE_ATTRIBUTE_NORMAL = 0x80
273 179
274 180 # open_osfhandle flags
275 181 _O_RDONLY = 0x0000
276 182 _O_RDWR = 0x0002
277 183 _O_APPEND = 0x0008
278 184
279 185 _O_TEXT = 0x4000
280 186 _O_BINARY = 0x8000
281 187
282 188 # types of parameters of C functions used (required by pypy)
283 189
284 190 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
285 191 _DWORD, _DWORD, _HANDLE]
286 192 _kernel32.CreateFileA.restype = _HANDLE
287 193
288 194 def _raiseioerror(name):
289 195 err = ctypes.WinError()
290 196 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
291 197
292 198 class posixfile(object):
293 199 '''a file object aiming for POSIX-like semantics
294 200
295 201 CPython's open() returns a file that was opened *without* setting the
296 202 _FILE_SHARE_DELETE flag, which causes rename and unlink to abort.
297 203 This even happens if any hardlinked copy of the file is in open state.
298 204 We set _FILE_SHARE_DELETE here, so files opened with posixfile can be
299 205 renamed and deleted while they are held open.
300 206 Note that if a file opened with posixfile is unlinked, the file
301 207 remains but cannot be opened again or be recreated under the same name,
302 208 until all reading processes have closed the file.'''
303 209
304 210 def __init__(self, name, mode='r', bufsize=-1):
305 211 if 'b' in mode:
306 212 flags = _O_BINARY
307 213 else:
308 214 flags = _O_TEXT
309 215
310 216 m0 = mode[0]
311 217 if m0 == 'r' and '+' not in mode:
312 218 flags |= _O_RDONLY
313 219 access = _GENERIC_READ
314 220 else:
315 221 # work around http://support.microsoft.com/kb/899149 and
316 222 # set _O_RDWR for 'w' and 'a', even if mode has no '+'
317 223 flags |= _O_RDWR
318 224 access = _GENERIC_READ | _GENERIC_WRITE
319 225
320 226 if m0 == 'r':
321 227 creation = _OPEN_EXISTING
322 228 elif m0 == 'w':
323 229 creation = _CREATE_ALWAYS
324 230 elif m0 == 'a':
325 231 creation = _OPEN_ALWAYS
326 232 flags |= _O_APPEND
327 233 else:
328 234 raise ValueError("invalid mode: %s" % mode)
329 235
330 236 fh = _kernel32.CreateFileA(name, access,
331 237 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
332 238 None, creation, _FILE_ATTRIBUTE_NORMAL, None)
333 239 if fh == _INVALID_HANDLE_VALUE:
334 240 _raiseioerror(name)
335 241
336 242 fd = msvcrt.open_osfhandle(fh, flags)
337 243 if fd == -1:
338 244 _kernel32.CloseHandle(fh)
339 245 _raiseioerror(name)
340 246
341 247 f = os.fdopen(fd, pycompat.sysstr(mode), bufsize)
342 248 # unfortunately, f.name is '<fdopen>' at this point -- so we store
343 249 # the name on this wrapper. We cannot just assign to f.name,
344 250 # because that attribute is read-only.
345 251 object.__setattr__(self, r'name', name)
346 252 object.__setattr__(self, r'_file', f)
347 253
348 254 def __iter__(self):
349 255 return self._file
350 256
351 257 def __getattr__(self, name):
352 258 return getattr(self._file, name)
353 259
354 260 def __setattr__(self, name, value):
355 261 '''mimics the read-only attributes of Python file objects
356 262 by raising 'TypeError: readonly attribute' if someone tries:
357 263 f = posixfile('foo.txt')
358 264 f.name = 'bla' '''
359 265 return self._file.__setattr__(name, value)
360 266
361 267 def __enter__(self):
362 268 return self._file.__enter__()
363 269
364 270 def __exit__(self, exc_type, exc_value, exc_tb):
365 271 return self._file.__exit__(exc_type, exc_value, exc_tb)
General Comments 0
You need to be logged in to leave comments. Login now