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