##// END OF EJS Templates
Fix patch failure reporting exception
Matt Mackall -
r1391:9395c2f9 default
parent child Browse files
Show More
@@ -1,566 +1,566 b''
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8
8
9 This contains helper routines that are independent of the SCM core and hide
9 This contains helper routines that are independent of the SCM core and hide
10 platform-specific details from the core.
10 platform-specific details from the core.
11 """
11 """
12
12
13 import os, errno
13 import os, errno
14 from demandload import *
14 from demandload import *
15 demandload(globals(), "re cStringIO shutil popen2 tempfile threading time")
15 demandload(globals(), "re cStringIO shutil popen2 tempfile threading time")
16
16
17 def pipefilter(s, cmd):
17 def pipefilter(s, cmd):
18 '''filter string S through command CMD, returning its output'''
18 '''filter string S through command CMD, returning its output'''
19 (pout, pin) = popen2.popen2(cmd, -1, 'b')
19 (pout, pin) = popen2.popen2(cmd, -1, 'b')
20 def writer():
20 def writer():
21 pin.write(s)
21 pin.write(s)
22 pin.close()
22 pin.close()
23
23
24 # we should use select instead on UNIX, but this will work on most
24 # we should use select instead on UNIX, but this will work on most
25 # systems, including Windows
25 # systems, including Windows
26 w = threading.Thread(target=writer)
26 w = threading.Thread(target=writer)
27 w.start()
27 w.start()
28 f = pout.read()
28 f = pout.read()
29 pout.close()
29 pout.close()
30 w.join()
30 w.join()
31 return f
31 return f
32
32
33 def tempfilter(s, cmd):
33 def tempfilter(s, cmd):
34 '''filter string S through a pair of temporary files with CMD.
34 '''filter string S through a pair of temporary files with CMD.
35 CMD is used as a template to create the real command to be run,
35 CMD is used as a template to create the real command to be run,
36 with the strings INFILE and OUTFILE replaced by the real names of
36 with the strings INFILE and OUTFILE replaced by the real names of
37 the temporary files generated.'''
37 the temporary files generated.'''
38 inname, outname = None, None
38 inname, outname = None, None
39 try:
39 try:
40 infd, inname = tempfile.mkstemp(prefix='hgfin')
40 infd, inname = tempfile.mkstemp(prefix='hgfin')
41 fp = os.fdopen(infd, 'wb')
41 fp = os.fdopen(infd, 'wb')
42 fp.write(s)
42 fp.write(s)
43 fp.close()
43 fp.close()
44 outfd, outname = tempfile.mkstemp(prefix='hgfout')
44 outfd, outname = tempfile.mkstemp(prefix='hgfout')
45 os.close(outfd)
45 os.close(outfd)
46 cmd = cmd.replace('INFILE', inname)
46 cmd = cmd.replace('INFILE', inname)
47 cmd = cmd.replace('OUTFILE', outname)
47 cmd = cmd.replace('OUTFILE', outname)
48 code = os.system(cmd)
48 code = os.system(cmd)
49 if code: raise Abort("command '%s' failed: %s" %
49 if code: raise Abort("command '%s' failed: %s" %
50 (cmd, explain_exit(code)))
50 (cmd, explain_exit(code)))
51 return open(outname, 'rb').read()
51 return open(outname, 'rb').read()
52 finally:
52 finally:
53 try:
53 try:
54 if inname: os.unlink(inname)
54 if inname: os.unlink(inname)
55 except: pass
55 except: pass
56 try:
56 try:
57 if outname: os.unlink(outname)
57 if outname: os.unlink(outname)
58 except: pass
58 except: pass
59
59
60 filtertable = {
60 filtertable = {
61 'tempfile:': tempfilter,
61 'tempfile:': tempfilter,
62 'pipe:': pipefilter,
62 'pipe:': pipefilter,
63 }
63 }
64
64
65 def filter(s, cmd):
65 def filter(s, cmd):
66 "filter a string through a command that transforms its input to its output"
66 "filter a string through a command that transforms its input to its output"
67 for name, fn in filtertable.iteritems():
67 for name, fn in filtertable.iteritems():
68 if cmd.startswith(name):
68 if cmd.startswith(name):
69 return fn(s, cmd[len(name):].lstrip())
69 return fn(s, cmd[len(name):].lstrip())
70 return pipefilter(s, cmd)
70 return pipefilter(s, cmd)
71
71
72 def patch(strip, patchname, ui):
72 def patch(strip, patchname, ui):
73 """apply the patch <patchname> to the working directory.
73 """apply the patch <patchname> to the working directory.
74 a list of patched files is returned"""
74 a list of patched files is returned"""
75 fp = os.popen('patch -p%d < "%s"' % (strip, patchname))
75 fp = os.popen('patch -p%d < "%s"' % (strip, patchname))
76 files = {}
76 files = {}
77 for line in fp:
77 for line in fp:
78 line = line.rstrip()
78 line = line.rstrip()
79 ui.status("%s\n" % line)
79 ui.status("%s\n" % line)
80 if line.startswith('patching file '):
80 if line.startswith('patching file '):
81 pf = parse_patch_output(line)
81 pf = parse_patch_output(line)
82 files.setdefault(pf, 1)
82 files.setdefault(pf, 1)
83 code = fp.close()
83 code = fp.close()
84 if code:
84 if code:
85 raise Abort("patch command failed: %s" % explain_exit(code))
85 raise Abort("patch command failed: %s" % explain_exit(code)[0])
86 return files.keys()
86 return files.keys()
87
87
88 def binary(s):
88 def binary(s):
89 """return true if a string is binary data using diff's heuristic"""
89 """return true if a string is binary data using diff's heuristic"""
90 if s and '\0' in s[:4096]:
90 if s and '\0' in s[:4096]:
91 return True
91 return True
92 return False
92 return False
93
93
94 def unique(g):
94 def unique(g):
95 """return the uniq elements of iterable g"""
95 """return the uniq elements of iterable g"""
96 seen = {}
96 seen = {}
97 for f in g:
97 for f in g:
98 if f not in seen:
98 if f not in seen:
99 seen[f] = 1
99 seen[f] = 1
100 yield f
100 yield f
101
101
102 class Abort(Exception):
102 class Abort(Exception):
103 """Raised if a command needs to print an error and exit."""
103 """Raised if a command needs to print an error and exit."""
104
104
105 def always(fn): return True
105 def always(fn): return True
106 def never(fn): return False
106 def never(fn): return False
107
107
108 def globre(pat, head='^', tail='$'):
108 def globre(pat, head='^', tail='$'):
109 "convert a glob pattern into a regexp"
109 "convert a glob pattern into a regexp"
110 i, n = 0, len(pat)
110 i, n = 0, len(pat)
111 res = ''
111 res = ''
112 group = False
112 group = False
113 def peek(): return i < n and pat[i]
113 def peek(): return i < n and pat[i]
114 while i < n:
114 while i < n:
115 c = pat[i]
115 c = pat[i]
116 i = i+1
116 i = i+1
117 if c == '*':
117 if c == '*':
118 if peek() == '*':
118 if peek() == '*':
119 i += 1
119 i += 1
120 res += '.*'
120 res += '.*'
121 else:
121 else:
122 res += '[^/]*'
122 res += '[^/]*'
123 elif c == '?':
123 elif c == '?':
124 res += '.'
124 res += '.'
125 elif c == '[':
125 elif c == '[':
126 j = i
126 j = i
127 if j < n and pat[j] in '!]':
127 if j < n and pat[j] in '!]':
128 j += 1
128 j += 1
129 while j < n and pat[j] != ']':
129 while j < n and pat[j] != ']':
130 j += 1
130 j += 1
131 if j >= n:
131 if j >= n:
132 res += '\\['
132 res += '\\['
133 else:
133 else:
134 stuff = pat[i:j].replace('\\','\\\\')
134 stuff = pat[i:j].replace('\\','\\\\')
135 i = j + 1
135 i = j + 1
136 if stuff[0] == '!':
136 if stuff[0] == '!':
137 stuff = '^' + stuff[1:]
137 stuff = '^' + stuff[1:]
138 elif stuff[0] == '^':
138 elif stuff[0] == '^':
139 stuff = '\\' + stuff
139 stuff = '\\' + stuff
140 res = '%s[%s]' % (res, stuff)
140 res = '%s[%s]' % (res, stuff)
141 elif c == '{':
141 elif c == '{':
142 group = True
142 group = True
143 res += '(?:'
143 res += '(?:'
144 elif c == '}' and group:
144 elif c == '}' and group:
145 res += ')'
145 res += ')'
146 group = False
146 group = False
147 elif c == ',' and group:
147 elif c == ',' and group:
148 res += '|'
148 res += '|'
149 else:
149 else:
150 res += re.escape(c)
150 res += re.escape(c)
151 return head + res + tail
151 return head + res + tail
152
152
153 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
153 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
154
154
155 def pathto(n1, n2):
155 def pathto(n1, n2):
156 '''return the relative path from one place to another.
156 '''return the relative path from one place to another.
157 this returns a path in the form used by the local filesystem, not hg.'''
157 this returns a path in the form used by the local filesystem, not hg.'''
158 if not n1: return localpath(n2)
158 if not n1: return localpath(n2)
159 a, b = n1.split('/'), n2.split('/')
159 a, b = n1.split('/'), n2.split('/')
160 a.reverse(), b.reverse()
160 a.reverse(), b.reverse()
161 while a and b and a[-1] == b[-1]:
161 while a and b and a[-1] == b[-1]:
162 a.pop(), b.pop()
162 a.pop(), b.pop()
163 b.reverse()
163 b.reverse()
164 return os.sep.join((['..'] * len(a)) + b)
164 return os.sep.join((['..'] * len(a)) + b)
165
165
166 def canonpath(root, cwd, myname):
166 def canonpath(root, cwd, myname):
167 """return the canonical path of myname, given cwd and root"""
167 """return the canonical path of myname, given cwd and root"""
168 rootsep = root + os.sep
168 rootsep = root + os.sep
169 name = myname
169 name = myname
170 if not name.startswith(os.sep):
170 if not name.startswith(os.sep):
171 name = os.path.join(root, cwd, name)
171 name = os.path.join(root, cwd, name)
172 name = os.path.normpath(name)
172 name = os.path.normpath(name)
173 if name.startswith(rootsep):
173 if name.startswith(rootsep):
174 return pconvert(name[len(rootsep):])
174 return pconvert(name[len(rootsep):])
175 elif name == root:
175 elif name == root:
176 return ''
176 return ''
177 else:
177 else:
178 raise Abort('%s not under root' % myname)
178 raise Abort('%s not under root' % myname)
179
179
180 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head=''):
180 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head=''):
181 """build a function to match a set of file patterns
181 """build a function to match a set of file patterns
182
182
183 arguments:
183 arguments:
184 canonroot - the canonical root of the tree you're matching against
184 canonroot - the canonical root of the tree you're matching against
185 cwd - the current working directory, if relevant
185 cwd - the current working directory, if relevant
186 names - patterns to find
186 names - patterns to find
187 inc - patterns to include
187 inc - patterns to include
188 exc - patterns to exclude
188 exc - patterns to exclude
189 head - a regex to prepend to patterns to control whether a match is rooted
189 head - a regex to prepend to patterns to control whether a match is rooted
190
190
191 a pattern is one of:
191 a pattern is one of:
192 'glob:<rooted glob>'
192 'glob:<rooted glob>'
193 're:<rooted regexp>'
193 're:<rooted regexp>'
194 'path:<rooted path>'
194 'path:<rooted path>'
195 'relglob:<relative glob>'
195 'relglob:<relative glob>'
196 'relpath:<relative path>'
196 'relpath:<relative path>'
197 'relre:<relative regexp>'
197 'relre:<relative regexp>'
198 '<rooted path or regexp>'
198 '<rooted path or regexp>'
199
199
200 returns:
200 returns:
201 a 3-tuple containing
201 a 3-tuple containing
202 - list of explicit non-pattern names passed in
202 - list of explicit non-pattern names passed in
203 - a bool match(filename) function
203 - a bool match(filename) function
204 - a bool indicating if any patterns were passed in
204 - a bool indicating if any patterns were passed in
205
205
206 todo:
206 todo:
207 make head regex a rooted bool
207 make head regex a rooted bool
208 """
208 """
209
209
210 def patkind(name):
210 def patkind(name):
211 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
211 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
212 if name.startswith(prefix + ':'): return name.split(':', 1)
212 if name.startswith(prefix + ':'): return name.split(':', 1)
213 for c in name:
213 for c in name:
214 if c in _globchars: return 'glob', name
214 if c in _globchars: return 'glob', name
215 return 'relpath', name
215 return 'relpath', name
216
216
217 def regex(kind, name, tail):
217 def regex(kind, name, tail):
218 '''convert a pattern into a regular expression'''
218 '''convert a pattern into a regular expression'''
219 if kind == 're':
219 if kind == 're':
220 return name
220 return name
221 elif kind == 'path':
221 elif kind == 'path':
222 return '^' + re.escape(name) + '(?:/|$)'
222 return '^' + re.escape(name) + '(?:/|$)'
223 elif kind == 'relglob':
223 elif kind == 'relglob':
224 return head + globre(name, '(?:|.*/)', tail)
224 return head + globre(name, '(?:|.*/)', tail)
225 elif kind == 'relpath':
225 elif kind == 'relpath':
226 return head + re.escape(name) + tail
226 return head + re.escape(name) + tail
227 elif kind == 'relre':
227 elif kind == 'relre':
228 if name.startswith('^'):
228 if name.startswith('^'):
229 return name
229 return name
230 return '.*' + name
230 return '.*' + name
231 return head + globre(name, '', tail)
231 return head + globre(name, '', tail)
232
232
233 def matchfn(pats, tail):
233 def matchfn(pats, tail):
234 """build a matching function from a set of patterns"""
234 """build a matching function from a set of patterns"""
235 if pats:
235 if pats:
236 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
236 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
237 return re.compile(pat).match
237 return re.compile(pat).match
238
238
239 def globprefix(pat):
239 def globprefix(pat):
240 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
240 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
241 root = []
241 root = []
242 for p in pat.split(os.sep):
242 for p in pat.split(os.sep):
243 if patkind(p)[0] == 'glob': break
243 if patkind(p)[0] == 'glob': break
244 root.append(p)
244 root.append(p)
245 return '/'.join(root)
245 return '/'.join(root)
246
246
247 pats = []
247 pats = []
248 files = []
248 files = []
249 roots = []
249 roots = []
250 for kind, name in map(patkind, names):
250 for kind, name in map(patkind, names):
251 if kind in ('glob', 'relpath'):
251 if kind in ('glob', 'relpath'):
252 name = canonpath(canonroot, cwd, name)
252 name = canonpath(canonroot, cwd, name)
253 if name == '':
253 if name == '':
254 kind, name = 'glob', '**'
254 kind, name = 'glob', '**'
255 if kind in ('glob', 'path', 're'):
255 if kind in ('glob', 'path', 're'):
256 pats.append((kind, name))
256 pats.append((kind, name))
257 if kind == 'glob':
257 if kind == 'glob':
258 root = globprefix(name)
258 root = globprefix(name)
259 if root: roots.append(root)
259 if root: roots.append(root)
260 elif kind == 'relpath':
260 elif kind == 'relpath':
261 files.append((kind, name))
261 files.append((kind, name))
262 roots.append(name)
262 roots.append(name)
263
263
264 patmatch = matchfn(pats, '$') or always
264 patmatch = matchfn(pats, '$') or always
265 filematch = matchfn(files, '(?:/|$)') or always
265 filematch = matchfn(files, '(?:/|$)') or always
266 incmatch = always
266 incmatch = always
267 if inc:
267 if inc:
268 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
268 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
269 excmatch = lambda fn: False
269 excmatch = lambda fn: False
270 if exc:
270 if exc:
271 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
271 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
272
272
273 return (roots,
273 return (roots,
274 lambda fn: (incmatch(fn) and not excmatch(fn) and
274 lambda fn: (incmatch(fn) and not excmatch(fn) and
275 (fn.endswith('/') or
275 (fn.endswith('/') or
276 (not pats and not files) or
276 (not pats and not files) or
277 (pats and patmatch(fn)) or
277 (pats and patmatch(fn)) or
278 (files and filematch(fn)))),
278 (files and filematch(fn)))),
279 (inc or exc or (pats and pats != [('glob', '**')])) and True)
279 (inc or exc or (pats and pats != [('glob', '**')])) and True)
280
280
281 def system(cmd, errprefix=None):
281 def system(cmd, errprefix=None):
282 """execute a shell command that must succeed"""
282 """execute a shell command that must succeed"""
283 rc = os.system(cmd)
283 rc = os.system(cmd)
284 if rc:
284 if rc:
285 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
285 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
286 explain_exit(rc)[0])
286 explain_exit(rc)[0])
287 if errprefix:
287 if errprefix:
288 errmsg = "%s: %s" % (errprefix, errmsg)
288 errmsg = "%s: %s" % (errprefix, errmsg)
289 raise Abort(errmsg)
289 raise Abort(errmsg)
290
290
291 def rename(src, dst):
291 def rename(src, dst):
292 """forcibly rename a file"""
292 """forcibly rename a file"""
293 try:
293 try:
294 os.rename(src, dst)
294 os.rename(src, dst)
295 except:
295 except:
296 os.unlink(dst)
296 os.unlink(dst)
297 os.rename(src, dst)
297 os.rename(src, dst)
298
298
299 def copyfiles(src, dst, hardlink=None):
299 def copyfiles(src, dst, hardlink=None):
300 """Copy a directory tree using hardlinks if possible"""
300 """Copy a directory tree using hardlinks if possible"""
301
301
302 if hardlink is None:
302 if hardlink is None:
303 hardlink = (os.stat(src).st_dev ==
303 hardlink = (os.stat(src).st_dev ==
304 os.stat(os.path.dirname(dst)).st_dev)
304 os.stat(os.path.dirname(dst)).st_dev)
305
305
306 if os.path.isdir(src):
306 if os.path.isdir(src):
307 os.mkdir(dst)
307 os.mkdir(dst)
308 for name in os.listdir(src):
308 for name in os.listdir(src):
309 srcname = os.path.join(src, name)
309 srcname = os.path.join(src, name)
310 dstname = os.path.join(dst, name)
310 dstname = os.path.join(dst, name)
311 copyfiles(srcname, dstname, hardlink)
311 copyfiles(srcname, dstname, hardlink)
312 else:
312 else:
313 if hardlink:
313 if hardlink:
314 try:
314 try:
315 os_link(src, dst)
315 os_link(src, dst)
316 except:
316 except:
317 hardlink = False
317 hardlink = False
318 shutil.copy2(src, dst)
318 shutil.copy2(src, dst)
319 else:
319 else:
320 shutil.copy2(src, dst)
320 shutil.copy2(src, dst)
321
321
322 def opener(base):
322 def opener(base):
323 """
323 """
324 return a function that opens files relative to base
324 return a function that opens files relative to base
325
325
326 this function is used to hide the details of COW semantics and
326 this function is used to hide the details of COW semantics and
327 remote file access from higher level code.
327 remote file access from higher level code.
328 """
328 """
329 p = base
329 p = base
330 def o(path, mode="r", text=False):
330 def o(path, mode="r", text=False):
331 f = os.path.join(p, path)
331 f = os.path.join(p, path)
332
332
333 if not text:
333 if not text:
334 mode += "b" # for that other OS
334 mode += "b" # for that other OS
335
335
336 if mode[0] != "r":
336 if mode[0] != "r":
337 try:
337 try:
338 nlink = nlinks(f)
338 nlink = nlinks(f)
339 except OSError:
339 except OSError:
340 d = os.path.dirname(f)
340 d = os.path.dirname(f)
341 if not os.path.isdir(d):
341 if not os.path.isdir(d):
342 os.makedirs(d)
342 os.makedirs(d)
343 else:
343 else:
344 if nlink > 1:
344 if nlink > 1:
345 file(f + ".tmp", "wb").write(file(f, "rb").read())
345 file(f + ".tmp", "wb").write(file(f, "rb").read())
346 rename(f+".tmp", f)
346 rename(f+".tmp", f)
347
347
348 return file(f, mode)
348 return file(f, mode)
349
349
350 return o
350 return o
351
351
352 def _makelock_file(info, pathname):
352 def _makelock_file(info, pathname):
353 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
353 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
354 os.write(ld, info)
354 os.write(ld, info)
355 os.close(ld)
355 os.close(ld)
356
356
357 def _readlock_file(pathname):
357 def _readlock_file(pathname):
358 return file(pathname).read()
358 return file(pathname).read()
359
359
360 def nlinks(pathname):
360 def nlinks(pathname):
361 """Return number of hardlinks for the given file."""
361 """Return number of hardlinks for the given file."""
362 return os.stat(pathname).st_nlink
362 return os.stat(pathname).st_nlink
363
363
364 if hasattr(os, 'link'):
364 if hasattr(os, 'link'):
365 os_link = os.link
365 os_link = os.link
366 else:
366 else:
367 def os_link(src, dst):
367 def os_link(src, dst):
368 raise OSError(0, "Hardlinks not supported")
368 raise OSError(0, "Hardlinks not supported")
369
369
370 # Platform specific variants
370 # Platform specific variants
371 if os.name == 'nt':
371 if os.name == 'nt':
372 nulldev = 'NUL:'
372 nulldev = 'NUL:'
373
373
374 rcpath = (r'c:\mercurial\mercurial.ini',
374 rcpath = (r'c:\mercurial\mercurial.ini',
375 os.path.join(os.path.expanduser('~'), 'mercurial.ini'))
375 os.path.join(os.path.expanduser('~'), 'mercurial.ini'))
376
376
377 def parse_patch_output(output_line):
377 def parse_patch_output(output_line):
378 """parses the output produced by patch and returns the file name"""
378 """parses the output produced by patch and returns the file name"""
379 pf = output_line[14:]
379 pf = output_line[14:]
380 if pf[0] == '`':
380 if pf[0] == '`':
381 pf = pf[1:-1] # Remove the quotes
381 pf = pf[1:-1] # Remove the quotes
382 return pf
382 return pf
383
383
384 try: # ActivePython can create hard links using win32file module
384 try: # ActivePython can create hard links using win32file module
385 import win32file
385 import win32file
386
386
387 def os_link(src, dst): # NB will only succeed on NTFS
387 def os_link(src, dst): # NB will only succeed on NTFS
388 win32file.CreateHardLink(dst, src)
388 win32file.CreateHardLink(dst, src)
389
389
390 def nlinks(pathname):
390 def nlinks(pathname):
391 """Return number of hardlinks for the given file."""
391 """Return number of hardlinks for the given file."""
392 try:
392 try:
393 fh = win32file.CreateFile(pathname,
393 fh = win32file.CreateFile(pathname,
394 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
394 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
395 None, win32file.OPEN_EXISTING, 0, None)
395 None, win32file.OPEN_EXISTING, 0, None)
396 res = win32file.GetFileInformationByHandle(fh)
396 res = win32file.GetFileInformationByHandle(fh)
397 fh.Close()
397 fh.Close()
398 return res[7]
398 return res[7]
399 except:
399 except:
400 return os.stat(pathname).st_nlink
400 return os.stat(pathname).st_nlink
401
401
402 except ImportError:
402 except ImportError:
403 pass
403 pass
404
404
405 def is_exec(f, last):
405 def is_exec(f, last):
406 return last
406 return last
407
407
408 def set_exec(f, mode):
408 def set_exec(f, mode):
409 pass
409 pass
410
410
411 def pconvert(path):
411 def pconvert(path):
412 return path.replace("\\", "/")
412 return path.replace("\\", "/")
413
413
414 def localpath(path):
414 def localpath(path):
415 return path.replace('/', '\\')
415 return path.replace('/', '\\')
416
416
417 def normpath(path):
417 def normpath(path):
418 return pconvert(os.path.normpath(path))
418 return pconvert(os.path.normpath(path))
419
419
420 makelock = _makelock_file
420 makelock = _makelock_file
421 readlock = _readlock_file
421 readlock = _readlock_file
422
422
423 def explain_exit(code):
423 def explain_exit(code):
424 return "exited with status %d" % code, code
424 return "exited with status %d" % code, code
425
425
426 else:
426 else:
427 nulldev = '/dev/null'
427 nulldev = '/dev/null'
428
428
429 hgrcd = '/etc/mercurial/hgrc.d'
429 hgrcd = '/etc/mercurial/hgrc.d'
430 hgrcs = []
430 hgrcs = []
431 if os.path.isdir(hgrcd):
431 if os.path.isdir(hgrcd):
432 hgrcs = [f for f in os.listdir(hgrcd) if f.endswith(".rc")]
432 hgrcs = [f for f in os.listdir(hgrcd) if f.endswith(".rc")]
433 rcpath = map(os.path.normpath, hgrcs +
433 rcpath = map(os.path.normpath, hgrcs +
434 ['/etc/mercurial/hgrc', os.path.expanduser('~/.hgrc')])
434 ['/etc/mercurial/hgrc', os.path.expanduser('~/.hgrc')])
435
435
436 def parse_patch_output(output_line):
436 def parse_patch_output(output_line):
437 """parses the output produced by patch and returns the file name"""
437 """parses the output produced by patch and returns the file name"""
438 return output_line[14:]
438 return output_line[14:]
439
439
440 def is_exec(f, last):
440 def is_exec(f, last):
441 """check whether a file is executable"""
441 """check whether a file is executable"""
442 return (os.stat(f).st_mode & 0100 != 0)
442 return (os.stat(f).st_mode & 0100 != 0)
443
443
444 def set_exec(f, mode):
444 def set_exec(f, mode):
445 s = os.stat(f).st_mode
445 s = os.stat(f).st_mode
446 if (s & 0100 != 0) == mode:
446 if (s & 0100 != 0) == mode:
447 return
447 return
448 if mode:
448 if mode:
449 # Turn on +x for every +r bit when making a file executable
449 # Turn on +x for every +r bit when making a file executable
450 # and obey umask.
450 # and obey umask.
451 umask = os.umask(0)
451 umask = os.umask(0)
452 os.umask(umask)
452 os.umask(umask)
453 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
453 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
454 else:
454 else:
455 os.chmod(f, s & 0666)
455 os.chmod(f, s & 0666)
456
456
457 def pconvert(path):
457 def pconvert(path):
458 return path
458 return path
459
459
460 def localpath(path):
460 def localpath(path):
461 return path
461 return path
462
462
463 normpath = os.path.normpath
463 normpath = os.path.normpath
464
464
465 def makelock(info, pathname):
465 def makelock(info, pathname):
466 try:
466 try:
467 os.symlink(info, pathname)
467 os.symlink(info, pathname)
468 except OSError, why:
468 except OSError, why:
469 if why.errno == errno.EEXIST:
469 if why.errno == errno.EEXIST:
470 raise
470 raise
471 else:
471 else:
472 _makelock_file(info, pathname)
472 _makelock_file(info, pathname)
473
473
474 def readlock(pathname):
474 def readlock(pathname):
475 try:
475 try:
476 return os.readlink(pathname)
476 return os.readlink(pathname)
477 except OSError, why:
477 except OSError, why:
478 if why.errno == errno.EINVAL:
478 if why.errno == errno.EINVAL:
479 return _readlock_file(pathname)
479 return _readlock_file(pathname)
480 else:
480 else:
481 raise
481 raise
482
482
483 def explain_exit(code):
483 def explain_exit(code):
484 """return a 2-tuple (desc, code) describing a process's status"""
484 """return a 2-tuple (desc, code) describing a process's status"""
485 if os.WIFEXITED(code):
485 if os.WIFEXITED(code):
486 val = os.WEXITSTATUS(code)
486 val = os.WEXITSTATUS(code)
487 return "exited with status %d" % val, val
487 return "exited with status %d" % val, val
488 elif os.WIFSIGNALED(code):
488 elif os.WIFSIGNALED(code):
489 val = os.WTERMSIG(code)
489 val = os.WTERMSIG(code)
490 return "killed by signal %d" % val, val
490 return "killed by signal %d" % val, val
491 elif os.WIFSTOPPED(code):
491 elif os.WIFSTOPPED(code):
492 val = os.WSTOPSIG(code)
492 val = os.WSTOPSIG(code)
493 return "stopped by signal %d" % val, val
493 return "stopped by signal %d" % val, val
494 raise ValueError("invalid exit code")
494 raise ValueError("invalid exit code")
495
495
496 class chunkbuffer(object):
496 class chunkbuffer(object):
497 """Allow arbitrary sized chunks of data to be efficiently read from an
497 """Allow arbitrary sized chunks of data to be efficiently read from an
498 iterator over chunks of arbitrary size."""
498 iterator over chunks of arbitrary size."""
499
499
500 def __init__(self, in_iter, targetsize = 2**16):
500 def __init__(self, in_iter, targetsize = 2**16):
501 """in_iter is the iterator that's iterating over the input chunks.
501 """in_iter is the iterator that's iterating over the input chunks.
502 targetsize is how big a buffer to try to maintain."""
502 targetsize is how big a buffer to try to maintain."""
503 self.in_iter = iter(in_iter)
503 self.in_iter = iter(in_iter)
504 self.buf = ''
504 self.buf = ''
505 self.targetsize = int(targetsize)
505 self.targetsize = int(targetsize)
506 if self.targetsize <= 0:
506 if self.targetsize <= 0:
507 raise ValueError("targetsize must be greater than 0, was %d" %
507 raise ValueError("targetsize must be greater than 0, was %d" %
508 targetsize)
508 targetsize)
509 self.iterempty = False
509 self.iterempty = False
510
510
511 def fillbuf(self):
511 def fillbuf(self):
512 """Ignore target size; read every chunk from iterator until empty."""
512 """Ignore target size; read every chunk from iterator until empty."""
513 if not self.iterempty:
513 if not self.iterempty:
514 collector = cStringIO.StringIO()
514 collector = cStringIO.StringIO()
515 collector.write(self.buf)
515 collector.write(self.buf)
516 for ch in self.in_iter:
516 for ch in self.in_iter:
517 collector.write(ch)
517 collector.write(ch)
518 self.buf = collector.getvalue()
518 self.buf = collector.getvalue()
519 self.iterempty = True
519 self.iterempty = True
520
520
521 def read(self, l):
521 def read(self, l):
522 """Read L bytes of data from the iterator of chunks of data.
522 """Read L bytes of data from the iterator of chunks of data.
523 Returns less than L bytes if the iterator runs dry."""
523 Returns less than L bytes if the iterator runs dry."""
524 if l > len(self.buf) and not self.iterempty:
524 if l > len(self.buf) and not self.iterempty:
525 # Clamp to a multiple of self.targetsize
525 # Clamp to a multiple of self.targetsize
526 targetsize = self.targetsize * ((l // self.targetsize) + 1)
526 targetsize = self.targetsize * ((l // self.targetsize) + 1)
527 collector = cStringIO.StringIO()
527 collector = cStringIO.StringIO()
528 collector.write(self.buf)
528 collector.write(self.buf)
529 collected = len(self.buf)
529 collected = len(self.buf)
530 for chunk in self.in_iter:
530 for chunk in self.in_iter:
531 collector.write(chunk)
531 collector.write(chunk)
532 collected += len(chunk)
532 collected += len(chunk)
533 if collected >= targetsize:
533 if collected >= targetsize:
534 break
534 break
535 if collected < targetsize:
535 if collected < targetsize:
536 self.iterempty = True
536 self.iterempty = True
537 self.buf = collector.getvalue()
537 self.buf = collector.getvalue()
538 s, self.buf = self.buf[:l], buffer(self.buf, l)
538 s, self.buf = self.buf[:l], buffer(self.buf, l)
539 return s
539 return s
540
540
541 def filechunkiter(f, size = 65536):
541 def filechunkiter(f, size = 65536):
542 """Create a generator that produces all the data in the file size
542 """Create a generator that produces all the data in the file size
543 (default 65536) bytes at a time. Chunks may be less than size
543 (default 65536) bytes at a time. Chunks may be less than size
544 bytes if the chunk is the last chunk in the file, or the file is a
544 bytes if the chunk is the last chunk in the file, or the file is a
545 socket or some other type of file that sometimes reads less data
545 socket or some other type of file that sometimes reads less data
546 than is requested."""
546 than is requested."""
547 s = f.read(size)
547 s = f.read(size)
548 while len(s) > 0:
548 while len(s) > 0:
549 yield s
549 yield s
550 s = f.read(size)
550 s = f.read(size)
551
551
552 def makedate():
552 def makedate():
553 t = time.time()
553 t = time.time()
554 if time.daylight: tz = time.altzone
554 if time.daylight: tz = time.altzone
555 else: tz = time.timezone
555 else: tz = time.timezone
556 return t, tz
556 return t, tz
557
557
558 def datestr(date=None, format='%c'):
558 def datestr(date=None, format='%c'):
559 """represent a (unixtime, offset) tuple as a localized time.
559 """represent a (unixtime, offset) tuple as a localized time.
560 unixtime is seconds since the epoch, and offset is the time zone's
560 unixtime is seconds since the epoch, and offset is the time zone's
561 number of seconds away from UTC."""
561 number of seconds away from UTC."""
562 t, tz = date or makedate()
562 t, tz = date or makedate()
563 return ("%s %+03d%02d" %
563 return ("%s %+03d%02d" %
564 (time.strftime(format, time.gmtime(float(t) - tz)),
564 (time.strftime(format, time.gmtime(float(t) - tz)),
565 -tz / 3600,
565 -tz / 3600,
566 ((-tz % 3600) / 60)))
566 ((-tz % 3600) / 60)))
General Comments 0
You need to be logged in to leave comments. Login now