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