##// END OF EJS Templates
util: return boolean result directly in util.binary
Martin Geisler -
r8118:35f7fda5 default
parent child Browse files
Show More
@@ -1,1503 +1,1501 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 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import _
15 from i18n import _
16 import cStringIO, errno, re, shutil, sys, tempfile, traceback, error
16 import cStringIO, errno, re, shutil, sys, tempfile, traceback, error
17 import os, stat, threading, time, calendar, ConfigParser, glob, osutil
17 import os, stat, threading, time, calendar, ConfigParser, glob, osutil
18 import imp
18 import imp
19
19
20 # Python compatibility
20 # Python compatibility
21
21
22 try:
22 try:
23 set = set
23 set = set
24 frozenset = frozenset
24 frozenset = frozenset
25 except NameError:
25 except NameError:
26 from sets import Set as set, ImmutableSet as frozenset
26 from sets import Set as set, ImmutableSet as frozenset
27
27
28 _md5 = None
28 _md5 = None
29 def md5(s):
29 def md5(s):
30 global _md5
30 global _md5
31 if _md5 is None:
31 if _md5 is None:
32 try:
32 try:
33 import hashlib
33 import hashlib
34 _md5 = hashlib.md5
34 _md5 = hashlib.md5
35 except ImportError:
35 except ImportError:
36 import md5
36 import md5
37 _md5 = md5.md5
37 _md5 = md5.md5
38 return _md5(s)
38 return _md5(s)
39
39
40 _sha1 = None
40 _sha1 = None
41 def sha1(s):
41 def sha1(s):
42 global _sha1
42 global _sha1
43 if _sha1 is None:
43 if _sha1 is None:
44 try:
44 try:
45 import hashlib
45 import hashlib
46 _sha1 = hashlib.sha1
46 _sha1 = hashlib.sha1
47 except ImportError:
47 except ImportError:
48 import sha
48 import sha
49 _sha1 = sha.sha
49 _sha1 = sha.sha
50 return _sha1(s)
50 return _sha1(s)
51
51
52 try:
52 try:
53 import subprocess
53 import subprocess
54 subprocess.Popen # trigger ImportError early
54 subprocess.Popen # trigger ImportError early
55 closefds = os.name == 'posix'
55 closefds = os.name == 'posix'
56 def popen2(cmd, mode='t', bufsize=-1):
56 def popen2(cmd, mode='t', bufsize=-1):
57 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
57 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
58 close_fds=closefds,
58 close_fds=closefds,
59 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
59 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
60 return p.stdin, p.stdout
60 return p.stdin, p.stdout
61 def popen3(cmd, mode='t', bufsize=-1):
61 def popen3(cmd, mode='t', bufsize=-1):
62 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
62 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
63 close_fds=closefds,
63 close_fds=closefds,
64 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
64 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
65 stderr=subprocess.PIPE)
65 stderr=subprocess.PIPE)
66 return p.stdin, p.stdout, p.stderr
66 return p.stdin, p.stdout, p.stderr
67 def Popen3(cmd, capturestderr=False, bufsize=-1):
67 def Popen3(cmd, capturestderr=False, bufsize=-1):
68 stderr = capturestderr and subprocess.PIPE or None
68 stderr = capturestderr and subprocess.PIPE or None
69 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
69 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
70 close_fds=closefds,
70 close_fds=closefds,
71 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
71 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
72 stderr=stderr)
72 stderr=stderr)
73 p.fromchild = p.stdout
73 p.fromchild = p.stdout
74 p.tochild = p.stdin
74 p.tochild = p.stdin
75 p.childerr = p.stderr
75 p.childerr = p.stderr
76 return p
76 return p
77 except ImportError:
77 except ImportError:
78 subprocess = None
78 subprocess = None
79 from popen2 import Popen3
79 from popen2 import Popen3
80 popen2 = os.popen2
80 popen2 = os.popen2
81 popen3 = os.popen3
81 popen3 = os.popen3
82
82
83
83
84 def version():
84 def version():
85 """Return version information if available."""
85 """Return version information if available."""
86 try:
86 try:
87 import __version__
87 import __version__
88 return __version__.version
88 return __version__.version
89 except ImportError:
89 except ImportError:
90 return 'unknown'
90 return 'unknown'
91
91
92 # used by parsedate
92 # used by parsedate
93 defaultdateformats = (
93 defaultdateformats = (
94 '%Y-%m-%d %H:%M:%S',
94 '%Y-%m-%d %H:%M:%S',
95 '%Y-%m-%d %I:%M:%S%p',
95 '%Y-%m-%d %I:%M:%S%p',
96 '%Y-%m-%d %H:%M',
96 '%Y-%m-%d %H:%M',
97 '%Y-%m-%d %I:%M%p',
97 '%Y-%m-%d %I:%M%p',
98 '%Y-%m-%d',
98 '%Y-%m-%d',
99 '%m-%d',
99 '%m-%d',
100 '%m/%d',
100 '%m/%d',
101 '%m/%d/%y',
101 '%m/%d/%y',
102 '%m/%d/%Y',
102 '%m/%d/%Y',
103 '%a %b %d %H:%M:%S %Y',
103 '%a %b %d %H:%M:%S %Y',
104 '%a %b %d %I:%M:%S%p %Y',
104 '%a %b %d %I:%M:%S%p %Y',
105 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
105 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
106 '%b %d %H:%M:%S %Y',
106 '%b %d %H:%M:%S %Y',
107 '%b %d %I:%M:%S%p %Y',
107 '%b %d %I:%M:%S%p %Y',
108 '%b %d %H:%M:%S',
108 '%b %d %H:%M:%S',
109 '%b %d %I:%M:%S%p',
109 '%b %d %I:%M:%S%p',
110 '%b %d %H:%M',
110 '%b %d %H:%M',
111 '%b %d %I:%M%p',
111 '%b %d %I:%M%p',
112 '%b %d %Y',
112 '%b %d %Y',
113 '%b %d',
113 '%b %d',
114 '%H:%M:%S',
114 '%H:%M:%S',
115 '%I:%M:%SP',
115 '%I:%M:%SP',
116 '%H:%M',
116 '%H:%M',
117 '%I:%M%p',
117 '%I:%M%p',
118 )
118 )
119
119
120 extendeddateformats = defaultdateformats + (
120 extendeddateformats = defaultdateformats + (
121 "%Y",
121 "%Y",
122 "%Y-%m",
122 "%Y-%m",
123 "%b",
123 "%b",
124 "%b %Y",
124 "%b %Y",
125 )
125 )
126
126
127 # differences from SafeConfigParser:
127 # differences from SafeConfigParser:
128 # - case-sensitive keys
128 # - case-sensitive keys
129 # - allows values that are not strings (this means that you may not
129 # - allows values that are not strings (this means that you may not
130 # be able to save the configuration to a file)
130 # be able to save the configuration to a file)
131 class configparser(ConfigParser.SafeConfigParser):
131 class configparser(ConfigParser.SafeConfigParser):
132 def optionxform(self, optionstr):
132 def optionxform(self, optionstr):
133 return optionstr
133 return optionstr
134
134
135 def set(self, section, option, value):
135 def set(self, section, option, value):
136 return ConfigParser.ConfigParser.set(self, section, option, value)
136 return ConfigParser.ConfigParser.set(self, section, option, value)
137
137
138 def _interpolate(self, section, option, rawval, vars):
138 def _interpolate(self, section, option, rawval, vars):
139 if not isinstance(rawval, basestring):
139 if not isinstance(rawval, basestring):
140 return rawval
140 return rawval
141 return ConfigParser.SafeConfigParser._interpolate(self, section,
141 return ConfigParser.SafeConfigParser._interpolate(self, section,
142 option, rawval, vars)
142 option, rawval, vars)
143
143
144 def cachefunc(func):
144 def cachefunc(func):
145 '''cache the result of function calls'''
145 '''cache the result of function calls'''
146 # XXX doesn't handle keywords args
146 # XXX doesn't handle keywords args
147 cache = {}
147 cache = {}
148 if func.func_code.co_argcount == 1:
148 if func.func_code.co_argcount == 1:
149 # we gain a small amount of time because
149 # we gain a small amount of time because
150 # we don't need to pack/unpack the list
150 # we don't need to pack/unpack the list
151 def f(arg):
151 def f(arg):
152 if arg not in cache:
152 if arg not in cache:
153 cache[arg] = func(arg)
153 cache[arg] = func(arg)
154 return cache[arg]
154 return cache[arg]
155 else:
155 else:
156 def f(*args):
156 def f(*args):
157 if args not in cache:
157 if args not in cache:
158 cache[args] = func(*args)
158 cache[args] = func(*args)
159 return cache[args]
159 return cache[args]
160
160
161 return f
161 return f
162
162
163 def pipefilter(s, cmd):
163 def pipefilter(s, cmd):
164 '''filter string S through command CMD, returning its output'''
164 '''filter string S through command CMD, returning its output'''
165 (pin, pout) = popen2(cmd, 'b')
165 (pin, pout) = popen2(cmd, 'b')
166 def writer():
166 def writer():
167 try:
167 try:
168 pin.write(s)
168 pin.write(s)
169 pin.close()
169 pin.close()
170 except IOError, inst:
170 except IOError, inst:
171 if inst.errno != errno.EPIPE:
171 if inst.errno != errno.EPIPE:
172 raise
172 raise
173
173
174 # we should use select instead on UNIX, but this will work on most
174 # we should use select instead on UNIX, but this will work on most
175 # systems, including Windows
175 # systems, including Windows
176 w = threading.Thread(target=writer)
176 w = threading.Thread(target=writer)
177 w.start()
177 w.start()
178 f = pout.read()
178 f = pout.read()
179 pout.close()
179 pout.close()
180 w.join()
180 w.join()
181 return f
181 return f
182
182
183 def tempfilter(s, cmd):
183 def tempfilter(s, cmd):
184 '''filter string S through a pair of temporary files with CMD.
184 '''filter string S through a pair of temporary files with CMD.
185 CMD is used as a template to create the real command to be run,
185 CMD is used as a template to create the real command to be run,
186 with the strings INFILE and OUTFILE replaced by the real names of
186 with the strings INFILE and OUTFILE replaced by the real names of
187 the temporary files generated.'''
187 the temporary files generated.'''
188 inname, outname = None, None
188 inname, outname = None, None
189 try:
189 try:
190 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
190 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
191 fp = os.fdopen(infd, 'wb')
191 fp = os.fdopen(infd, 'wb')
192 fp.write(s)
192 fp.write(s)
193 fp.close()
193 fp.close()
194 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
194 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
195 os.close(outfd)
195 os.close(outfd)
196 cmd = cmd.replace('INFILE', inname)
196 cmd = cmd.replace('INFILE', inname)
197 cmd = cmd.replace('OUTFILE', outname)
197 cmd = cmd.replace('OUTFILE', outname)
198 code = os.system(cmd)
198 code = os.system(cmd)
199 if sys.platform == 'OpenVMS' and code & 1:
199 if sys.platform == 'OpenVMS' and code & 1:
200 code = 0
200 code = 0
201 if code: raise Abort(_("command '%s' failed: %s") %
201 if code: raise Abort(_("command '%s' failed: %s") %
202 (cmd, explain_exit(code)))
202 (cmd, explain_exit(code)))
203 return open(outname, 'rb').read()
203 return open(outname, 'rb').read()
204 finally:
204 finally:
205 try:
205 try:
206 if inname: os.unlink(inname)
206 if inname: os.unlink(inname)
207 except: pass
207 except: pass
208 try:
208 try:
209 if outname: os.unlink(outname)
209 if outname: os.unlink(outname)
210 except: pass
210 except: pass
211
211
212 filtertable = {
212 filtertable = {
213 'tempfile:': tempfilter,
213 'tempfile:': tempfilter,
214 'pipe:': pipefilter,
214 'pipe:': pipefilter,
215 }
215 }
216
216
217 def filter(s, cmd):
217 def filter(s, cmd):
218 "filter a string through a command that transforms its input to its output"
218 "filter a string through a command that transforms its input to its output"
219 for name, fn in filtertable.iteritems():
219 for name, fn in filtertable.iteritems():
220 if cmd.startswith(name):
220 if cmd.startswith(name):
221 return fn(s, cmd[len(name):].lstrip())
221 return fn(s, cmd[len(name):].lstrip())
222 return pipefilter(s, cmd)
222 return pipefilter(s, cmd)
223
223
224 def binary(s):
224 def binary(s):
225 """return true if a string is binary data"""
225 """return true if a string is binary data"""
226 if s and '\0' in s:
226 return bool(s and '\0' in s)
227 return True
228 return False
229
227
230 def unique(g):
228 def unique(g):
231 """return the uniq elements of iterable g"""
229 """return the uniq elements of iterable g"""
232 return dict.fromkeys(g).keys()
230 return dict.fromkeys(g).keys()
233
231
234 def sort(l):
232 def sort(l):
235 if not isinstance(l, list):
233 if not isinstance(l, list):
236 l = list(l)
234 l = list(l)
237 l.sort()
235 l.sort()
238 return l
236 return l
239
237
240 def increasingchunks(source, min=1024, max=65536):
238 def increasingchunks(source, min=1024, max=65536):
241 '''return no less than min bytes per chunk while data remains,
239 '''return no less than min bytes per chunk while data remains,
242 doubling min after each chunk until it reaches max'''
240 doubling min after each chunk until it reaches max'''
243 def log2(x):
241 def log2(x):
244 if not x:
242 if not x:
245 return 0
243 return 0
246 i = 0
244 i = 0
247 while x:
245 while x:
248 x >>= 1
246 x >>= 1
249 i += 1
247 i += 1
250 return i - 1
248 return i - 1
251
249
252 buf = []
250 buf = []
253 blen = 0
251 blen = 0
254 for chunk in source:
252 for chunk in source:
255 buf.append(chunk)
253 buf.append(chunk)
256 blen += len(chunk)
254 blen += len(chunk)
257 if blen >= min:
255 if blen >= min:
258 if min < max:
256 if min < max:
259 min = min << 1
257 min = min << 1
260 nmin = 1 << log2(blen)
258 nmin = 1 << log2(blen)
261 if nmin > min:
259 if nmin > min:
262 min = nmin
260 min = nmin
263 if min > max:
261 if min > max:
264 min = max
262 min = max
265 yield ''.join(buf)
263 yield ''.join(buf)
266 blen = 0
264 blen = 0
267 buf = []
265 buf = []
268 if buf:
266 if buf:
269 yield ''.join(buf)
267 yield ''.join(buf)
270
268
271 Abort = error.Abort
269 Abort = error.Abort
272
270
273 def always(fn): return True
271 def always(fn): return True
274 def never(fn): return False
272 def never(fn): return False
275
273
276 def patkind(name, default):
274 def patkind(name, default):
277 """Split a string into an optional pattern kind prefix and the
275 """Split a string into an optional pattern kind prefix and the
278 actual pattern."""
276 actual pattern."""
279 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
277 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
280 if name.startswith(prefix + ':'): return name.split(':', 1)
278 if name.startswith(prefix + ':'): return name.split(':', 1)
281 return default, name
279 return default, name
282
280
283 def globre(pat, head='^', tail='$'):
281 def globre(pat, head='^', tail='$'):
284 "convert a glob pattern into a regexp"
282 "convert a glob pattern into a regexp"
285 i, n = 0, len(pat)
283 i, n = 0, len(pat)
286 res = ''
284 res = ''
287 group = 0
285 group = 0
288 def peek(): return i < n and pat[i]
286 def peek(): return i < n and pat[i]
289 while i < n:
287 while i < n:
290 c = pat[i]
288 c = pat[i]
291 i = i+1
289 i = i+1
292 if c == '*':
290 if c == '*':
293 if peek() == '*':
291 if peek() == '*':
294 i += 1
292 i += 1
295 res += '.*'
293 res += '.*'
296 else:
294 else:
297 res += '[^/]*'
295 res += '[^/]*'
298 elif c == '?':
296 elif c == '?':
299 res += '.'
297 res += '.'
300 elif c == '[':
298 elif c == '[':
301 j = i
299 j = i
302 if j < n and pat[j] in '!]':
300 if j < n and pat[j] in '!]':
303 j += 1
301 j += 1
304 while j < n and pat[j] != ']':
302 while j < n and pat[j] != ']':
305 j += 1
303 j += 1
306 if j >= n:
304 if j >= n:
307 res += '\\['
305 res += '\\['
308 else:
306 else:
309 stuff = pat[i:j].replace('\\','\\\\')
307 stuff = pat[i:j].replace('\\','\\\\')
310 i = j + 1
308 i = j + 1
311 if stuff[0] == '!':
309 if stuff[0] == '!':
312 stuff = '^' + stuff[1:]
310 stuff = '^' + stuff[1:]
313 elif stuff[0] == '^':
311 elif stuff[0] == '^':
314 stuff = '\\' + stuff
312 stuff = '\\' + stuff
315 res = '%s[%s]' % (res, stuff)
313 res = '%s[%s]' % (res, stuff)
316 elif c == '{':
314 elif c == '{':
317 group += 1
315 group += 1
318 res += '(?:'
316 res += '(?:'
319 elif c == '}' and group:
317 elif c == '}' and group:
320 res += ')'
318 res += ')'
321 group -= 1
319 group -= 1
322 elif c == ',' and group:
320 elif c == ',' and group:
323 res += '|'
321 res += '|'
324 elif c == '\\':
322 elif c == '\\':
325 p = peek()
323 p = peek()
326 if p:
324 if p:
327 i += 1
325 i += 1
328 res += re.escape(p)
326 res += re.escape(p)
329 else:
327 else:
330 res += re.escape(c)
328 res += re.escape(c)
331 else:
329 else:
332 res += re.escape(c)
330 res += re.escape(c)
333 return head + res + tail
331 return head + res + tail
334
332
335 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
333 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
336
334
337 def pathto(root, n1, n2):
335 def pathto(root, n1, n2):
338 '''return the relative path from one place to another.
336 '''return the relative path from one place to another.
339 root should use os.sep to separate directories
337 root should use os.sep to separate directories
340 n1 should use os.sep to separate directories
338 n1 should use os.sep to separate directories
341 n2 should use "/" to separate directories
339 n2 should use "/" to separate directories
342 returns an os.sep-separated path.
340 returns an os.sep-separated path.
343
341
344 If n1 is a relative path, it's assumed it's
342 If n1 is a relative path, it's assumed it's
345 relative to root.
343 relative to root.
346 n2 should always be relative to root.
344 n2 should always be relative to root.
347 '''
345 '''
348 if not n1: return localpath(n2)
346 if not n1: return localpath(n2)
349 if os.path.isabs(n1):
347 if os.path.isabs(n1):
350 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
348 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
351 return os.path.join(root, localpath(n2))
349 return os.path.join(root, localpath(n2))
352 n2 = '/'.join((pconvert(root), n2))
350 n2 = '/'.join((pconvert(root), n2))
353 a, b = splitpath(n1), n2.split('/')
351 a, b = splitpath(n1), n2.split('/')
354 a.reverse()
352 a.reverse()
355 b.reverse()
353 b.reverse()
356 while a and b and a[-1] == b[-1]:
354 while a and b and a[-1] == b[-1]:
357 a.pop()
355 a.pop()
358 b.pop()
356 b.pop()
359 b.reverse()
357 b.reverse()
360 return os.sep.join((['..'] * len(a)) + b) or '.'
358 return os.sep.join((['..'] * len(a)) + b) or '.'
361
359
362 def canonpath(root, cwd, myname):
360 def canonpath(root, cwd, myname):
363 """return the canonical path of myname, given cwd and root"""
361 """return the canonical path of myname, given cwd and root"""
364 if root == os.sep:
362 if root == os.sep:
365 rootsep = os.sep
363 rootsep = os.sep
366 elif endswithsep(root):
364 elif endswithsep(root):
367 rootsep = root
365 rootsep = root
368 else:
366 else:
369 rootsep = root + os.sep
367 rootsep = root + os.sep
370 name = myname
368 name = myname
371 if not os.path.isabs(name):
369 if not os.path.isabs(name):
372 name = os.path.join(root, cwd, name)
370 name = os.path.join(root, cwd, name)
373 name = os.path.normpath(name)
371 name = os.path.normpath(name)
374 audit_path = path_auditor(root)
372 audit_path = path_auditor(root)
375 if name != rootsep and name.startswith(rootsep):
373 if name != rootsep and name.startswith(rootsep):
376 name = name[len(rootsep):]
374 name = name[len(rootsep):]
377 audit_path(name)
375 audit_path(name)
378 return pconvert(name)
376 return pconvert(name)
379 elif name == root:
377 elif name == root:
380 return ''
378 return ''
381 else:
379 else:
382 # Determine whether `name' is in the hierarchy at or beneath `root',
380 # Determine whether `name' is in the hierarchy at or beneath `root',
383 # by iterating name=dirname(name) until that causes no change (can't
381 # by iterating name=dirname(name) until that causes no change (can't
384 # check name == '/', because that doesn't work on windows). For each
382 # check name == '/', because that doesn't work on windows). For each
385 # `name', compare dev/inode numbers. If they match, the list `rel'
383 # `name', compare dev/inode numbers. If they match, the list `rel'
386 # holds the reversed list of components making up the relative file
384 # holds the reversed list of components making up the relative file
387 # name we want.
385 # name we want.
388 root_st = os.stat(root)
386 root_st = os.stat(root)
389 rel = []
387 rel = []
390 while True:
388 while True:
391 try:
389 try:
392 name_st = os.stat(name)
390 name_st = os.stat(name)
393 except OSError:
391 except OSError:
394 break
392 break
395 if samestat(name_st, root_st):
393 if samestat(name_st, root_st):
396 if not rel:
394 if not rel:
397 # name was actually the same as root (maybe a symlink)
395 # name was actually the same as root (maybe a symlink)
398 return ''
396 return ''
399 rel.reverse()
397 rel.reverse()
400 name = os.path.join(*rel)
398 name = os.path.join(*rel)
401 audit_path(name)
399 audit_path(name)
402 return pconvert(name)
400 return pconvert(name)
403 dirname, basename = os.path.split(name)
401 dirname, basename = os.path.split(name)
404 rel.append(basename)
402 rel.append(basename)
405 if dirname == name:
403 if dirname == name:
406 break
404 break
407 name = dirname
405 name = dirname
408
406
409 raise Abort('%s not under root' % myname)
407 raise Abort('%s not under root' % myname)
410
408
411 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
409 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
412 """build a function to match a set of file patterns
410 """build a function to match a set of file patterns
413
411
414 arguments:
412 arguments:
415 canonroot - the canonical root of the tree you're matching against
413 canonroot - the canonical root of the tree you're matching against
416 cwd - the current working directory, if relevant
414 cwd - the current working directory, if relevant
417 names - patterns to find
415 names - patterns to find
418 inc - patterns to include
416 inc - patterns to include
419 exc - patterns to exclude
417 exc - patterns to exclude
420 dflt_pat - if a pattern in names has no explicit type, assume this one
418 dflt_pat - if a pattern in names has no explicit type, assume this one
421 src - where these patterns came from (e.g. .hgignore)
419 src - where these patterns came from (e.g. .hgignore)
422
420
423 a pattern is one of:
421 a pattern is one of:
424 'glob:<glob>' - a glob relative to cwd
422 'glob:<glob>' - a glob relative to cwd
425 're:<regexp>' - a regular expression
423 're:<regexp>' - a regular expression
426 'path:<path>' - a path relative to canonroot
424 'path:<path>' - a path relative to canonroot
427 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
425 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
428 'relpath:<path>' - a path relative to cwd
426 'relpath:<path>' - a path relative to cwd
429 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
427 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
430 '<something>' - one of the cases above, selected by the dflt_pat argument
428 '<something>' - one of the cases above, selected by the dflt_pat argument
431
429
432 returns:
430 returns:
433 a 3-tuple containing
431 a 3-tuple containing
434 - list of roots (places where one should start a recursive walk of the fs);
432 - list of roots (places where one should start a recursive walk of the fs);
435 this often matches the explicit non-pattern names passed in, but also
433 this often matches the explicit non-pattern names passed in, but also
436 includes the initial part of glob: patterns that has no glob characters
434 includes the initial part of glob: patterns that has no glob characters
437 - a bool match(filename) function
435 - a bool match(filename) function
438 - a bool indicating if any patterns were passed in
436 - a bool indicating if any patterns were passed in
439 """
437 """
440
438
441 # a common case: no patterns at all
439 # a common case: no patterns at all
442 if not names and not inc and not exc:
440 if not names and not inc and not exc:
443 return [], always, False
441 return [], always, False
444
442
445 def contains_glob(name):
443 def contains_glob(name):
446 for c in name:
444 for c in name:
447 if c in _globchars: return True
445 if c in _globchars: return True
448 return False
446 return False
449
447
450 def regex(kind, name, tail):
448 def regex(kind, name, tail):
451 '''convert a pattern into a regular expression'''
449 '''convert a pattern into a regular expression'''
452 if not name:
450 if not name:
453 return ''
451 return ''
454 if kind == 're':
452 if kind == 're':
455 return name
453 return name
456 elif kind == 'path':
454 elif kind == 'path':
457 return '^' + re.escape(name) + '(?:/|$)'
455 return '^' + re.escape(name) + '(?:/|$)'
458 elif kind == 'relglob':
456 elif kind == 'relglob':
459 return globre(name, '(?:|.*/)', tail)
457 return globre(name, '(?:|.*/)', tail)
460 elif kind == 'relpath':
458 elif kind == 'relpath':
461 return re.escape(name) + '(?:/|$)'
459 return re.escape(name) + '(?:/|$)'
462 elif kind == 'relre':
460 elif kind == 'relre':
463 if name.startswith('^'):
461 if name.startswith('^'):
464 return name
462 return name
465 return '.*' + name
463 return '.*' + name
466 return globre(name, '', tail)
464 return globre(name, '', tail)
467
465
468 def matchfn(pats, tail):
466 def matchfn(pats, tail):
469 """build a matching function from a set of patterns"""
467 """build a matching function from a set of patterns"""
470 if not pats:
468 if not pats:
471 return
469 return
472 try:
470 try:
473 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
471 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
474 if len(pat) > 20000:
472 if len(pat) > 20000:
475 raise OverflowError()
473 raise OverflowError()
476 return re.compile(pat).match
474 return re.compile(pat).match
477 except OverflowError:
475 except OverflowError:
478 # We're using a Python with a tiny regex engine and we
476 # We're using a Python with a tiny regex engine and we
479 # made it explode, so we'll divide the pattern list in two
477 # made it explode, so we'll divide the pattern list in two
480 # until it works
478 # until it works
481 l = len(pats)
479 l = len(pats)
482 if l < 2:
480 if l < 2:
483 raise
481 raise
484 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
482 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
485 return lambda s: a(s) or b(s)
483 return lambda s: a(s) or b(s)
486 except re.error:
484 except re.error:
487 for k, p in pats:
485 for k, p in pats:
488 try:
486 try:
489 re.compile('(?:%s)' % regex(k, p, tail))
487 re.compile('(?:%s)' % regex(k, p, tail))
490 except re.error:
488 except re.error:
491 if src:
489 if src:
492 raise Abort("%s: invalid pattern (%s): %s" %
490 raise Abort("%s: invalid pattern (%s): %s" %
493 (src, k, p))
491 (src, k, p))
494 else:
492 else:
495 raise Abort("invalid pattern (%s): %s" % (k, p))
493 raise Abort("invalid pattern (%s): %s" % (k, p))
496 raise Abort("invalid pattern")
494 raise Abort("invalid pattern")
497
495
498 def globprefix(pat):
496 def globprefix(pat):
499 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
497 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
500 root = []
498 root = []
501 for p in pat.split('/'):
499 for p in pat.split('/'):
502 if contains_glob(p): break
500 if contains_glob(p): break
503 root.append(p)
501 root.append(p)
504 return '/'.join(root) or '.'
502 return '/'.join(root) or '.'
505
503
506 def normalizepats(names, default):
504 def normalizepats(names, default):
507 pats = []
505 pats = []
508 roots = []
506 roots = []
509 anypats = False
507 anypats = False
510 for kind, name in [patkind(p, default) for p in names]:
508 for kind, name in [patkind(p, default) for p in names]:
511 if kind in ('glob', 'relpath'):
509 if kind in ('glob', 'relpath'):
512 name = canonpath(canonroot, cwd, name)
510 name = canonpath(canonroot, cwd, name)
513 elif kind in ('relglob', 'path'):
511 elif kind in ('relglob', 'path'):
514 name = normpath(name)
512 name = normpath(name)
515
513
516 pats.append((kind, name))
514 pats.append((kind, name))
517
515
518 if kind in ('glob', 're', 'relglob', 'relre'):
516 if kind in ('glob', 're', 'relglob', 'relre'):
519 anypats = True
517 anypats = True
520
518
521 if kind == 'glob':
519 if kind == 'glob':
522 root = globprefix(name)
520 root = globprefix(name)
523 roots.append(root)
521 roots.append(root)
524 elif kind in ('relpath', 'path'):
522 elif kind in ('relpath', 'path'):
525 roots.append(name or '.')
523 roots.append(name or '.')
526 elif kind == 'relglob':
524 elif kind == 'relglob':
527 roots.append('.')
525 roots.append('.')
528 return roots, pats, anypats
526 return roots, pats, anypats
529
527
530 roots, pats, anypats = normalizepats(names, dflt_pat)
528 roots, pats, anypats = normalizepats(names, dflt_pat)
531
529
532 patmatch = matchfn(pats, '$') or always
530 patmatch = matchfn(pats, '$') or always
533 incmatch = always
531 incmatch = always
534 if inc:
532 if inc:
535 dummy, inckinds, dummy = normalizepats(inc, 'glob')
533 dummy, inckinds, dummy = normalizepats(inc, 'glob')
536 incmatch = matchfn(inckinds, '(?:/|$)')
534 incmatch = matchfn(inckinds, '(?:/|$)')
537 excmatch = never
535 excmatch = never
538 if exc:
536 if exc:
539 dummy, exckinds, dummy = normalizepats(exc, 'glob')
537 dummy, exckinds, dummy = normalizepats(exc, 'glob')
540 excmatch = matchfn(exckinds, '(?:/|$)')
538 excmatch = matchfn(exckinds, '(?:/|$)')
541
539
542 if not names and inc and not exc:
540 if not names and inc and not exc:
543 # common case: hgignore patterns
541 # common case: hgignore patterns
544 match = incmatch
542 match = incmatch
545 else:
543 else:
546 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
544 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
547
545
548 return (roots, match, (inc or exc or anypats) and True)
546 return (roots, match, (inc or exc or anypats) and True)
549
547
550 _hgexecutable = None
548 _hgexecutable = None
551
549
552 def main_is_frozen():
550 def main_is_frozen():
553 """return True if we are a frozen executable.
551 """return True if we are a frozen executable.
554
552
555 The code supports py2exe (most common, Windows only) and tools/freeze
553 The code supports py2exe (most common, Windows only) and tools/freeze
556 (portable, not much used).
554 (portable, not much used).
557 """
555 """
558 return (hasattr(sys, "frozen") or # new py2exe
556 return (hasattr(sys, "frozen") or # new py2exe
559 hasattr(sys, "importers") or # old py2exe
557 hasattr(sys, "importers") or # old py2exe
560 imp.is_frozen("__main__")) # tools/freeze
558 imp.is_frozen("__main__")) # tools/freeze
561
559
562 def hgexecutable():
560 def hgexecutable():
563 """return location of the 'hg' executable.
561 """return location of the 'hg' executable.
564
562
565 Defaults to $HG or 'hg' in the search path.
563 Defaults to $HG or 'hg' in the search path.
566 """
564 """
567 if _hgexecutable is None:
565 if _hgexecutable is None:
568 hg = os.environ.get('HG')
566 hg = os.environ.get('HG')
569 if hg:
567 if hg:
570 set_hgexecutable(hg)
568 set_hgexecutable(hg)
571 elif main_is_frozen():
569 elif main_is_frozen():
572 set_hgexecutable(sys.executable)
570 set_hgexecutable(sys.executable)
573 else:
571 else:
574 set_hgexecutable(find_exe('hg') or 'hg')
572 set_hgexecutable(find_exe('hg') or 'hg')
575 return _hgexecutable
573 return _hgexecutable
576
574
577 def set_hgexecutable(path):
575 def set_hgexecutable(path):
578 """set location of the 'hg' executable"""
576 """set location of the 'hg' executable"""
579 global _hgexecutable
577 global _hgexecutable
580 _hgexecutable = path
578 _hgexecutable = path
581
579
582 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
580 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
583 '''enhanced shell command execution.
581 '''enhanced shell command execution.
584 run with environment maybe modified, maybe in different dir.
582 run with environment maybe modified, maybe in different dir.
585
583
586 if command fails and onerr is None, return status. if ui object,
584 if command fails and onerr is None, return status. if ui object,
587 print error message and return status, else raise onerr object as
585 print error message and return status, else raise onerr object as
588 exception.'''
586 exception.'''
589 def py2shell(val):
587 def py2shell(val):
590 'convert python object into string that is useful to shell'
588 'convert python object into string that is useful to shell'
591 if val in (None, False):
589 if val in (None, False):
592 return '0'
590 return '0'
593 if val == True:
591 if val == True:
594 return '1'
592 return '1'
595 return str(val)
593 return str(val)
596 oldenv = {}
594 oldenv = {}
597 for k in environ:
595 for k in environ:
598 oldenv[k] = os.environ.get(k)
596 oldenv[k] = os.environ.get(k)
599 if cwd is not None:
597 if cwd is not None:
600 oldcwd = os.getcwd()
598 oldcwd = os.getcwd()
601 origcmd = cmd
599 origcmd = cmd
602 if os.name == 'nt':
600 if os.name == 'nt':
603 cmd = '"%s"' % cmd
601 cmd = '"%s"' % cmd
604 try:
602 try:
605 for k, v in environ.iteritems():
603 for k, v in environ.iteritems():
606 os.environ[k] = py2shell(v)
604 os.environ[k] = py2shell(v)
607 os.environ['HG'] = hgexecutable()
605 os.environ['HG'] = hgexecutable()
608 if cwd is not None and oldcwd != cwd:
606 if cwd is not None and oldcwd != cwd:
609 os.chdir(cwd)
607 os.chdir(cwd)
610 rc = os.system(cmd)
608 rc = os.system(cmd)
611 if sys.platform == 'OpenVMS' and rc & 1:
609 if sys.platform == 'OpenVMS' and rc & 1:
612 rc = 0
610 rc = 0
613 if rc and onerr:
611 if rc and onerr:
614 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
612 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
615 explain_exit(rc)[0])
613 explain_exit(rc)[0])
616 if errprefix:
614 if errprefix:
617 errmsg = '%s: %s' % (errprefix, errmsg)
615 errmsg = '%s: %s' % (errprefix, errmsg)
618 try:
616 try:
619 onerr.warn(errmsg + '\n')
617 onerr.warn(errmsg + '\n')
620 except AttributeError:
618 except AttributeError:
621 raise onerr(errmsg)
619 raise onerr(errmsg)
622 return rc
620 return rc
623 finally:
621 finally:
624 for k, v in oldenv.iteritems():
622 for k, v in oldenv.iteritems():
625 if v is None:
623 if v is None:
626 del os.environ[k]
624 del os.environ[k]
627 else:
625 else:
628 os.environ[k] = v
626 os.environ[k] = v
629 if cwd is not None and oldcwd != cwd:
627 if cwd is not None and oldcwd != cwd:
630 os.chdir(oldcwd)
628 os.chdir(oldcwd)
631
629
632 def checksignature(func):
630 def checksignature(func):
633 '''wrap a function with code to check for calling errors'''
631 '''wrap a function with code to check for calling errors'''
634 def check(*args, **kwargs):
632 def check(*args, **kwargs):
635 try:
633 try:
636 return func(*args, **kwargs)
634 return func(*args, **kwargs)
637 except TypeError:
635 except TypeError:
638 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
636 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
639 raise error.SignatureError
637 raise error.SignatureError
640 raise
638 raise
641
639
642 return check
640 return check
643
641
644 # os.path.lexists is not available on python2.3
642 # os.path.lexists is not available on python2.3
645 def lexists(filename):
643 def lexists(filename):
646 "test whether a file with this name exists. does not follow symlinks"
644 "test whether a file with this name exists. does not follow symlinks"
647 try:
645 try:
648 os.lstat(filename)
646 os.lstat(filename)
649 except:
647 except:
650 return False
648 return False
651 return True
649 return True
652
650
653 def rename(src, dst):
651 def rename(src, dst):
654 """forcibly rename a file"""
652 """forcibly rename a file"""
655 try:
653 try:
656 os.rename(src, dst)
654 os.rename(src, dst)
657 except OSError, err: # FIXME: check err (EEXIST ?)
655 except OSError, err: # FIXME: check err (EEXIST ?)
658 # on windows, rename to existing file is not allowed, so we
656 # on windows, rename to existing file is not allowed, so we
659 # must delete destination first. but if file is open, unlink
657 # must delete destination first. but if file is open, unlink
660 # schedules it for delete but does not delete it. rename
658 # schedules it for delete but does not delete it. rename
661 # happens immediately even for open files, so we rename
659 # happens immediately even for open files, so we rename
662 # destination to a temporary name, then delete that. then
660 # destination to a temporary name, then delete that. then
663 # rename is safe to do.
661 # rename is safe to do.
664 temp = dst + "-force-rename"
662 temp = dst + "-force-rename"
665 os.rename(dst, temp)
663 os.rename(dst, temp)
666 os.unlink(temp)
664 os.unlink(temp)
667 os.rename(src, dst)
665 os.rename(src, dst)
668
666
669 def unlink(f):
667 def unlink(f):
670 """unlink and remove the directory if it is empty"""
668 """unlink and remove the directory if it is empty"""
671 os.unlink(f)
669 os.unlink(f)
672 # try removing directories that might now be empty
670 # try removing directories that might now be empty
673 try:
671 try:
674 os.removedirs(os.path.dirname(f))
672 os.removedirs(os.path.dirname(f))
675 except OSError:
673 except OSError:
676 pass
674 pass
677
675
678 def copyfile(src, dest):
676 def copyfile(src, dest):
679 "copy a file, preserving mode and atime/mtime"
677 "copy a file, preserving mode and atime/mtime"
680 if os.path.islink(src):
678 if os.path.islink(src):
681 try:
679 try:
682 os.unlink(dest)
680 os.unlink(dest)
683 except:
681 except:
684 pass
682 pass
685 os.symlink(os.readlink(src), dest)
683 os.symlink(os.readlink(src), dest)
686 else:
684 else:
687 try:
685 try:
688 shutil.copyfile(src, dest)
686 shutil.copyfile(src, dest)
689 shutil.copystat(src, dest)
687 shutil.copystat(src, dest)
690 except shutil.Error, inst:
688 except shutil.Error, inst:
691 raise Abort(str(inst))
689 raise Abort(str(inst))
692
690
693 def copyfiles(src, dst, hardlink=None):
691 def copyfiles(src, dst, hardlink=None):
694 """Copy a directory tree using hardlinks if possible"""
692 """Copy a directory tree using hardlinks if possible"""
695
693
696 if hardlink is None:
694 if hardlink is None:
697 hardlink = (os.stat(src).st_dev ==
695 hardlink = (os.stat(src).st_dev ==
698 os.stat(os.path.dirname(dst)).st_dev)
696 os.stat(os.path.dirname(dst)).st_dev)
699
697
700 if os.path.isdir(src):
698 if os.path.isdir(src):
701 os.mkdir(dst)
699 os.mkdir(dst)
702 for name, kind in osutil.listdir(src):
700 for name, kind in osutil.listdir(src):
703 srcname = os.path.join(src, name)
701 srcname = os.path.join(src, name)
704 dstname = os.path.join(dst, name)
702 dstname = os.path.join(dst, name)
705 copyfiles(srcname, dstname, hardlink)
703 copyfiles(srcname, dstname, hardlink)
706 else:
704 else:
707 if hardlink:
705 if hardlink:
708 try:
706 try:
709 os_link(src, dst)
707 os_link(src, dst)
710 except (IOError, OSError):
708 except (IOError, OSError):
711 hardlink = False
709 hardlink = False
712 shutil.copy(src, dst)
710 shutil.copy(src, dst)
713 else:
711 else:
714 shutil.copy(src, dst)
712 shutil.copy(src, dst)
715
713
716 class path_auditor(object):
714 class path_auditor(object):
717 '''ensure that a filesystem path contains no banned components.
715 '''ensure that a filesystem path contains no banned components.
718 the following properties of a path are checked:
716 the following properties of a path are checked:
719
717
720 - under top-level .hg
718 - under top-level .hg
721 - starts at the root of a windows drive
719 - starts at the root of a windows drive
722 - contains ".."
720 - contains ".."
723 - traverses a symlink (e.g. a/symlink_here/b)
721 - traverses a symlink (e.g. a/symlink_here/b)
724 - inside a nested repository'''
722 - inside a nested repository'''
725
723
726 def __init__(self, root):
724 def __init__(self, root):
727 self.audited = set()
725 self.audited = set()
728 self.auditeddir = set()
726 self.auditeddir = set()
729 self.root = root
727 self.root = root
730
728
731 def __call__(self, path):
729 def __call__(self, path):
732 if path in self.audited:
730 if path in self.audited:
733 return
731 return
734 normpath = os.path.normcase(path)
732 normpath = os.path.normcase(path)
735 parts = splitpath(normpath)
733 parts = splitpath(normpath)
736 if (os.path.splitdrive(path)[0]
734 if (os.path.splitdrive(path)[0]
737 or parts[0].lower() in ('.hg', '.hg.', '')
735 or parts[0].lower() in ('.hg', '.hg.', '')
738 or os.pardir in parts):
736 or os.pardir in parts):
739 raise Abort(_("path contains illegal component: %s") % path)
737 raise Abort(_("path contains illegal component: %s") % path)
740 if '.hg' in path.lower():
738 if '.hg' in path.lower():
741 lparts = [p.lower() for p in parts]
739 lparts = [p.lower() for p in parts]
742 for p in '.hg', '.hg.':
740 for p in '.hg', '.hg.':
743 if p in lparts[1:]:
741 if p in lparts[1:]:
744 pos = lparts.index(p)
742 pos = lparts.index(p)
745 base = os.path.join(*parts[:pos])
743 base = os.path.join(*parts[:pos])
746 raise Abort(_('path %r is inside repo %r') % (path, base))
744 raise Abort(_('path %r is inside repo %r') % (path, base))
747 def check(prefix):
745 def check(prefix):
748 curpath = os.path.join(self.root, prefix)
746 curpath = os.path.join(self.root, prefix)
749 try:
747 try:
750 st = os.lstat(curpath)
748 st = os.lstat(curpath)
751 except OSError, err:
749 except OSError, err:
752 # EINVAL can be raised as invalid path syntax under win32.
750 # EINVAL can be raised as invalid path syntax under win32.
753 # They must be ignored for patterns can be checked too.
751 # They must be ignored for patterns can be checked too.
754 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
752 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
755 raise
753 raise
756 else:
754 else:
757 if stat.S_ISLNK(st.st_mode):
755 if stat.S_ISLNK(st.st_mode):
758 raise Abort(_('path %r traverses symbolic link %r') %
756 raise Abort(_('path %r traverses symbolic link %r') %
759 (path, prefix))
757 (path, prefix))
760 elif (stat.S_ISDIR(st.st_mode) and
758 elif (stat.S_ISDIR(st.st_mode) and
761 os.path.isdir(os.path.join(curpath, '.hg'))):
759 os.path.isdir(os.path.join(curpath, '.hg'))):
762 raise Abort(_('path %r is inside repo %r') %
760 raise Abort(_('path %r is inside repo %r') %
763 (path, prefix))
761 (path, prefix))
764 parts.pop()
762 parts.pop()
765 prefixes = []
763 prefixes = []
766 for n in range(len(parts)):
764 for n in range(len(parts)):
767 prefix = os.sep.join(parts)
765 prefix = os.sep.join(parts)
768 if prefix in self.auditeddir:
766 if prefix in self.auditeddir:
769 break
767 break
770 check(prefix)
768 check(prefix)
771 prefixes.append(prefix)
769 prefixes.append(prefix)
772 parts.pop()
770 parts.pop()
773
771
774 self.audited.add(path)
772 self.audited.add(path)
775 # only add prefixes to the cache after checking everything: we don't
773 # only add prefixes to the cache after checking everything: we don't
776 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
774 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
777 self.auditeddir.update(prefixes)
775 self.auditeddir.update(prefixes)
778
776
779 def nlinks(pathname):
777 def nlinks(pathname):
780 """Return number of hardlinks for the given file."""
778 """Return number of hardlinks for the given file."""
781 return os.lstat(pathname).st_nlink
779 return os.lstat(pathname).st_nlink
782
780
783 if hasattr(os, 'link'):
781 if hasattr(os, 'link'):
784 os_link = os.link
782 os_link = os.link
785 else:
783 else:
786 def os_link(src, dst):
784 def os_link(src, dst):
787 raise OSError(0, _("Hardlinks not supported"))
785 raise OSError(0, _("Hardlinks not supported"))
788
786
789 def lookup_reg(key, name=None, scope=None):
787 def lookup_reg(key, name=None, scope=None):
790 return None
788 return None
791
789
792 if os.name == 'nt':
790 if os.name == 'nt':
793 from windows import *
791 from windows import *
794 def expand_glob(pats):
792 def expand_glob(pats):
795 '''On Windows, expand the implicit globs in a list of patterns'''
793 '''On Windows, expand the implicit globs in a list of patterns'''
796 ret = []
794 ret = []
797 for p in pats:
795 for p in pats:
798 kind, name = patkind(p, None)
796 kind, name = patkind(p, None)
799 if kind is None:
797 if kind is None:
800 globbed = glob.glob(name)
798 globbed = glob.glob(name)
801 if globbed:
799 if globbed:
802 ret.extend(globbed)
800 ret.extend(globbed)
803 continue
801 continue
804 # if we couldn't expand the glob, just keep it around
802 # if we couldn't expand the glob, just keep it around
805 ret.append(p)
803 ret.append(p)
806 return ret
804 return ret
807 else:
805 else:
808 from posix import *
806 from posix import *
809
807
810 def makelock(info, pathname):
808 def makelock(info, pathname):
811 try:
809 try:
812 return os.symlink(info, pathname)
810 return os.symlink(info, pathname)
813 except OSError, why:
811 except OSError, why:
814 if why.errno == errno.EEXIST:
812 if why.errno == errno.EEXIST:
815 raise
813 raise
816 except AttributeError: # no symlink in os
814 except AttributeError: # no symlink in os
817 pass
815 pass
818
816
819 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
817 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
820 os.write(ld, info)
818 os.write(ld, info)
821 os.close(ld)
819 os.close(ld)
822
820
823 def readlock(pathname):
821 def readlock(pathname):
824 try:
822 try:
825 return os.readlink(pathname)
823 return os.readlink(pathname)
826 except OSError, why:
824 except OSError, why:
827 if why.errno not in (errno.EINVAL, errno.ENOSYS):
825 if why.errno not in (errno.EINVAL, errno.ENOSYS):
828 raise
826 raise
829 except AttributeError: # no symlink in os
827 except AttributeError: # no symlink in os
830 pass
828 pass
831 return posixfile(pathname).read()
829 return posixfile(pathname).read()
832
830
833 def fstat(fp):
831 def fstat(fp):
834 '''stat file object that may not have fileno method.'''
832 '''stat file object that may not have fileno method.'''
835 try:
833 try:
836 return os.fstat(fp.fileno())
834 return os.fstat(fp.fileno())
837 except AttributeError:
835 except AttributeError:
838 return os.stat(fp.name)
836 return os.stat(fp.name)
839
837
840 # File system features
838 # File system features
841
839
842 def checkcase(path):
840 def checkcase(path):
843 """
841 """
844 Check whether the given path is on a case-sensitive filesystem
842 Check whether the given path is on a case-sensitive filesystem
845
843
846 Requires a path (like /foo/.hg) ending with a foldable final
844 Requires a path (like /foo/.hg) ending with a foldable final
847 directory component.
845 directory component.
848 """
846 """
849 s1 = os.stat(path)
847 s1 = os.stat(path)
850 d, b = os.path.split(path)
848 d, b = os.path.split(path)
851 p2 = os.path.join(d, b.upper())
849 p2 = os.path.join(d, b.upper())
852 if path == p2:
850 if path == p2:
853 p2 = os.path.join(d, b.lower())
851 p2 = os.path.join(d, b.lower())
854 try:
852 try:
855 s2 = os.stat(p2)
853 s2 = os.stat(p2)
856 if s2 == s1:
854 if s2 == s1:
857 return False
855 return False
858 return True
856 return True
859 except:
857 except:
860 return True
858 return True
861
859
862 _fspathcache = {}
860 _fspathcache = {}
863 def fspath(name, root):
861 def fspath(name, root):
864 '''Get name in the case stored in the filesystem
862 '''Get name in the case stored in the filesystem
865
863
866 The name is either relative to root, or it is an absolute path starting
864 The name is either relative to root, or it is an absolute path starting
867 with root. Note that this function is unnecessary, and should not be
865 with root. Note that this function is unnecessary, and should not be
868 called, for case-sensitive filesystems (simply because it's expensive).
866 called, for case-sensitive filesystems (simply because it's expensive).
869 '''
867 '''
870 # If name is absolute, make it relative
868 # If name is absolute, make it relative
871 if name.lower().startswith(root.lower()):
869 if name.lower().startswith(root.lower()):
872 l = len(root)
870 l = len(root)
873 if name[l] == os.sep or name[l] == os.altsep:
871 if name[l] == os.sep or name[l] == os.altsep:
874 l = l + 1
872 l = l + 1
875 name = name[l:]
873 name = name[l:]
876
874
877 if not os.path.exists(os.path.join(root, name)):
875 if not os.path.exists(os.path.join(root, name)):
878 return None
876 return None
879
877
880 seps = os.sep
878 seps = os.sep
881 if os.altsep:
879 if os.altsep:
882 seps = seps + os.altsep
880 seps = seps + os.altsep
883 # Protect backslashes. This gets silly very quickly.
881 # Protect backslashes. This gets silly very quickly.
884 seps.replace('\\','\\\\')
882 seps.replace('\\','\\\\')
885 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
883 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
886 dir = os.path.normcase(os.path.normpath(root))
884 dir = os.path.normcase(os.path.normpath(root))
887 result = []
885 result = []
888 for part, sep in pattern.findall(name):
886 for part, sep in pattern.findall(name):
889 if sep:
887 if sep:
890 result.append(sep)
888 result.append(sep)
891 continue
889 continue
892
890
893 if dir not in _fspathcache:
891 if dir not in _fspathcache:
894 _fspathcache[dir] = os.listdir(dir)
892 _fspathcache[dir] = os.listdir(dir)
895 contents = _fspathcache[dir]
893 contents = _fspathcache[dir]
896
894
897 lpart = part.lower()
895 lpart = part.lower()
898 for n in contents:
896 for n in contents:
899 if n.lower() == lpart:
897 if n.lower() == lpart:
900 result.append(n)
898 result.append(n)
901 break
899 break
902 else:
900 else:
903 # Cannot happen, as the file exists!
901 # Cannot happen, as the file exists!
904 result.append(part)
902 result.append(part)
905 dir = os.path.join(dir, lpart)
903 dir = os.path.join(dir, lpart)
906
904
907 return ''.join(result)
905 return ''.join(result)
908
906
909 def checkexec(path):
907 def checkexec(path):
910 """
908 """
911 Check whether the given path is on a filesystem with UNIX-like exec flags
909 Check whether the given path is on a filesystem with UNIX-like exec flags
912
910
913 Requires a directory (like /foo/.hg)
911 Requires a directory (like /foo/.hg)
914 """
912 """
915
913
916 # VFAT on some Linux versions can flip mode but it doesn't persist
914 # VFAT on some Linux versions can flip mode but it doesn't persist
917 # a FS remount. Frequently we can detect it if files are created
915 # a FS remount. Frequently we can detect it if files are created
918 # with exec bit on.
916 # with exec bit on.
919
917
920 try:
918 try:
921 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
919 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
922 fh, fn = tempfile.mkstemp("", "", path)
920 fh, fn = tempfile.mkstemp("", "", path)
923 try:
921 try:
924 os.close(fh)
922 os.close(fh)
925 m = os.stat(fn).st_mode & 0777
923 m = os.stat(fn).st_mode & 0777
926 new_file_has_exec = m & EXECFLAGS
924 new_file_has_exec = m & EXECFLAGS
927 os.chmod(fn, m ^ EXECFLAGS)
925 os.chmod(fn, m ^ EXECFLAGS)
928 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
926 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
929 finally:
927 finally:
930 os.unlink(fn)
928 os.unlink(fn)
931 except (IOError, OSError):
929 except (IOError, OSError):
932 # we don't care, the user probably won't be able to commit anyway
930 # we don't care, the user probably won't be able to commit anyway
933 return False
931 return False
934 return not (new_file_has_exec or exec_flags_cannot_flip)
932 return not (new_file_has_exec or exec_flags_cannot_flip)
935
933
936 def checklink(path):
934 def checklink(path):
937 """check whether the given path is on a symlink-capable filesystem"""
935 """check whether the given path is on a symlink-capable filesystem"""
938 # mktemp is not racy because symlink creation will fail if the
936 # mktemp is not racy because symlink creation will fail if the
939 # file already exists
937 # file already exists
940 name = tempfile.mktemp(dir=path)
938 name = tempfile.mktemp(dir=path)
941 try:
939 try:
942 os.symlink(".", name)
940 os.symlink(".", name)
943 os.unlink(name)
941 os.unlink(name)
944 return True
942 return True
945 except (OSError, AttributeError):
943 except (OSError, AttributeError):
946 return False
944 return False
947
945
948 def needbinarypatch():
946 def needbinarypatch():
949 """return True if patches should be applied in binary mode by default."""
947 """return True if patches should be applied in binary mode by default."""
950 return os.name == 'nt'
948 return os.name == 'nt'
951
949
952 def endswithsep(path):
950 def endswithsep(path):
953 '''Check path ends with os.sep or os.altsep.'''
951 '''Check path ends with os.sep or os.altsep.'''
954 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
952 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
955
953
956 def splitpath(path):
954 def splitpath(path):
957 '''Split path by os.sep.
955 '''Split path by os.sep.
958 Note that this function does not use os.altsep because this is
956 Note that this function does not use os.altsep because this is
959 an alternative of simple "xxx.split(os.sep)".
957 an alternative of simple "xxx.split(os.sep)".
960 It is recommended to use os.path.normpath() before using this
958 It is recommended to use os.path.normpath() before using this
961 function if need.'''
959 function if need.'''
962 return path.split(os.sep)
960 return path.split(os.sep)
963
961
964 def gui():
962 def gui():
965 '''Are we running in a GUI?'''
963 '''Are we running in a GUI?'''
966 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
964 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
967
965
968 def mktempcopy(name, emptyok=False, createmode=None):
966 def mktempcopy(name, emptyok=False, createmode=None):
969 """Create a temporary file with the same contents from name
967 """Create a temporary file with the same contents from name
970
968
971 The permission bits are copied from the original file.
969 The permission bits are copied from the original file.
972
970
973 If the temporary file is going to be truncated immediately, you
971 If the temporary file is going to be truncated immediately, you
974 can use emptyok=True as an optimization.
972 can use emptyok=True as an optimization.
975
973
976 Returns the name of the temporary file.
974 Returns the name of the temporary file.
977 """
975 """
978 d, fn = os.path.split(name)
976 d, fn = os.path.split(name)
979 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
977 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
980 os.close(fd)
978 os.close(fd)
981 # Temporary files are created with mode 0600, which is usually not
979 # Temporary files are created with mode 0600, which is usually not
982 # what we want. If the original file already exists, just copy
980 # what we want. If the original file already exists, just copy
983 # its mode. Otherwise, manually obey umask.
981 # its mode. Otherwise, manually obey umask.
984 try:
982 try:
985 st_mode = os.lstat(name).st_mode & 0777
983 st_mode = os.lstat(name).st_mode & 0777
986 except OSError, inst:
984 except OSError, inst:
987 if inst.errno != errno.ENOENT:
985 if inst.errno != errno.ENOENT:
988 raise
986 raise
989 st_mode = createmode
987 st_mode = createmode
990 if st_mode is None:
988 if st_mode is None:
991 st_mode = ~umask
989 st_mode = ~umask
992 st_mode &= 0666
990 st_mode &= 0666
993 os.chmod(temp, st_mode)
991 os.chmod(temp, st_mode)
994 if emptyok:
992 if emptyok:
995 return temp
993 return temp
996 try:
994 try:
997 try:
995 try:
998 ifp = posixfile(name, "rb")
996 ifp = posixfile(name, "rb")
999 except IOError, inst:
997 except IOError, inst:
1000 if inst.errno == errno.ENOENT:
998 if inst.errno == errno.ENOENT:
1001 return temp
999 return temp
1002 if not getattr(inst, 'filename', None):
1000 if not getattr(inst, 'filename', None):
1003 inst.filename = name
1001 inst.filename = name
1004 raise
1002 raise
1005 ofp = posixfile(temp, "wb")
1003 ofp = posixfile(temp, "wb")
1006 for chunk in filechunkiter(ifp):
1004 for chunk in filechunkiter(ifp):
1007 ofp.write(chunk)
1005 ofp.write(chunk)
1008 ifp.close()
1006 ifp.close()
1009 ofp.close()
1007 ofp.close()
1010 except:
1008 except:
1011 try: os.unlink(temp)
1009 try: os.unlink(temp)
1012 except: pass
1010 except: pass
1013 raise
1011 raise
1014 return temp
1012 return temp
1015
1013
1016 class atomictempfile(posixfile):
1014 class atomictempfile(posixfile):
1017 """file-like object that atomically updates a file
1015 """file-like object that atomically updates a file
1018
1016
1019 All writes will be redirected to a temporary copy of the original
1017 All writes will be redirected to a temporary copy of the original
1020 file. When rename is called, the copy is renamed to the original
1018 file. When rename is called, the copy is renamed to the original
1021 name, making the changes visible.
1019 name, making the changes visible.
1022 """
1020 """
1023 def __init__(self, name, mode, createmode):
1021 def __init__(self, name, mode, createmode):
1024 self.__name = name
1022 self.__name = name
1025 self.temp = mktempcopy(name, emptyok=('w' in mode),
1023 self.temp = mktempcopy(name, emptyok=('w' in mode),
1026 createmode=createmode)
1024 createmode=createmode)
1027 posixfile.__init__(self, self.temp, mode)
1025 posixfile.__init__(self, self.temp, mode)
1028
1026
1029 def rename(self):
1027 def rename(self):
1030 if not self.closed:
1028 if not self.closed:
1031 posixfile.close(self)
1029 posixfile.close(self)
1032 rename(self.temp, localpath(self.__name))
1030 rename(self.temp, localpath(self.__name))
1033
1031
1034 def __del__(self):
1032 def __del__(self):
1035 if not self.closed:
1033 if not self.closed:
1036 try:
1034 try:
1037 os.unlink(self.temp)
1035 os.unlink(self.temp)
1038 except: pass
1036 except: pass
1039 posixfile.close(self)
1037 posixfile.close(self)
1040
1038
1041 def makedirs(name, mode=None):
1039 def makedirs(name, mode=None):
1042 """recursive directory creation with parent mode inheritance"""
1040 """recursive directory creation with parent mode inheritance"""
1043 try:
1041 try:
1044 os.mkdir(name)
1042 os.mkdir(name)
1045 if mode is not None:
1043 if mode is not None:
1046 os.chmod(name, mode)
1044 os.chmod(name, mode)
1047 return
1045 return
1048 except OSError, err:
1046 except OSError, err:
1049 if err.errno == errno.EEXIST:
1047 if err.errno == errno.EEXIST:
1050 return
1048 return
1051 if err.errno != errno.ENOENT:
1049 if err.errno != errno.ENOENT:
1052 raise
1050 raise
1053 parent = os.path.abspath(os.path.dirname(name))
1051 parent = os.path.abspath(os.path.dirname(name))
1054 makedirs(parent, mode)
1052 makedirs(parent, mode)
1055 makedirs(name, mode)
1053 makedirs(name, mode)
1056
1054
1057 class opener(object):
1055 class opener(object):
1058 """Open files relative to a base directory
1056 """Open files relative to a base directory
1059
1057
1060 This class is used to hide the details of COW semantics and
1058 This class is used to hide the details of COW semantics and
1061 remote file access from higher level code.
1059 remote file access from higher level code.
1062 """
1060 """
1063 def __init__(self, base, audit=True):
1061 def __init__(self, base, audit=True):
1064 self.base = base
1062 self.base = base
1065 if audit:
1063 if audit:
1066 self.audit_path = path_auditor(base)
1064 self.audit_path = path_auditor(base)
1067 else:
1065 else:
1068 self.audit_path = always
1066 self.audit_path = always
1069 self.createmode = None
1067 self.createmode = None
1070
1068
1071 def __getattr__(self, name):
1069 def __getattr__(self, name):
1072 if name == '_can_symlink':
1070 if name == '_can_symlink':
1073 self._can_symlink = checklink(self.base)
1071 self._can_symlink = checklink(self.base)
1074 return self._can_symlink
1072 return self._can_symlink
1075 raise AttributeError(name)
1073 raise AttributeError(name)
1076
1074
1077 def _fixfilemode(self, name):
1075 def _fixfilemode(self, name):
1078 if self.createmode is None:
1076 if self.createmode is None:
1079 return
1077 return
1080 os.chmod(name, self.createmode & 0666)
1078 os.chmod(name, self.createmode & 0666)
1081
1079
1082 def __call__(self, path, mode="r", text=False, atomictemp=False):
1080 def __call__(self, path, mode="r", text=False, atomictemp=False):
1083 self.audit_path(path)
1081 self.audit_path(path)
1084 f = os.path.join(self.base, path)
1082 f = os.path.join(self.base, path)
1085
1083
1086 if not text and "b" not in mode:
1084 if not text and "b" not in mode:
1087 mode += "b" # for that other OS
1085 mode += "b" # for that other OS
1088
1086
1089 nlink = -1
1087 nlink = -1
1090 if mode not in ("r", "rb"):
1088 if mode not in ("r", "rb"):
1091 try:
1089 try:
1092 nlink = nlinks(f)
1090 nlink = nlinks(f)
1093 except OSError:
1091 except OSError:
1094 nlink = 0
1092 nlink = 0
1095 d = os.path.dirname(f)
1093 d = os.path.dirname(f)
1096 if not os.path.isdir(d):
1094 if not os.path.isdir(d):
1097 makedirs(d, self.createmode)
1095 makedirs(d, self.createmode)
1098 if atomictemp:
1096 if atomictemp:
1099 return atomictempfile(f, mode, self.createmode)
1097 return atomictempfile(f, mode, self.createmode)
1100 if nlink > 1:
1098 if nlink > 1:
1101 rename(mktempcopy(f), f)
1099 rename(mktempcopy(f), f)
1102 fp = posixfile(f, mode)
1100 fp = posixfile(f, mode)
1103 if nlink == 0:
1101 if nlink == 0:
1104 self._fixfilemode(f)
1102 self._fixfilemode(f)
1105 return fp
1103 return fp
1106
1104
1107 def symlink(self, src, dst):
1105 def symlink(self, src, dst):
1108 self.audit_path(dst)
1106 self.audit_path(dst)
1109 linkname = os.path.join(self.base, dst)
1107 linkname = os.path.join(self.base, dst)
1110 try:
1108 try:
1111 os.unlink(linkname)
1109 os.unlink(linkname)
1112 except OSError:
1110 except OSError:
1113 pass
1111 pass
1114
1112
1115 dirname = os.path.dirname(linkname)
1113 dirname = os.path.dirname(linkname)
1116 if not os.path.exists(dirname):
1114 if not os.path.exists(dirname):
1117 makedirs(dirname, self.createmode)
1115 makedirs(dirname, self.createmode)
1118
1116
1119 if self._can_symlink:
1117 if self._can_symlink:
1120 try:
1118 try:
1121 os.symlink(src, linkname)
1119 os.symlink(src, linkname)
1122 except OSError, err:
1120 except OSError, err:
1123 raise OSError(err.errno, _('could not symlink to %r: %s') %
1121 raise OSError(err.errno, _('could not symlink to %r: %s') %
1124 (src, err.strerror), linkname)
1122 (src, err.strerror), linkname)
1125 else:
1123 else:
1126 f = self(dst, "w")
1124 f = self(dst, "w")
1127 f.write(src)
1125 f.write(src)
1128 f.close()
1126 f.close()
1129 self._fixfilemode(dst)
1127 self._fixfilemode(dst)
1130
1128
1131 class chunkbuffer(object):
1129 class chunkbuffer(object):
1132 """Allow arbitrary sized chunks of data to be efficiently read from an
1130 """Allow arbitrary sized chunks of data to be efficiently read from an
1133 iterator over chunks of arbitrary size."""
1131 iterator over chunks of arbitrary size."""
1134
1132
1135 def __init__(self, in_iter):
1133 def __init__(self, in_iter):
1136 """in_iter is the iterator that's iterating over the input chunks.
1134 """in_iter is the iterator that's iterating over the input chunks.
1137 targetsize is how big a buffer to try to maintain."""
1135 targetsize is how big a buffer to try to maintain."""
1138 self.iter = iter(in_iter)
1136 self.iter = iter(in_iter)
1139 self.buf = ''
1137 self.buf = ''
1140 self.targetsize = 2**16
1138 self.targetsize = 2**16
1141
1139
1142 def read(self, l):
1140 def read(self, l):
1143 """Read L bytes of data from the iterator of chunks of data.
1141 """Read L bytes of data from the iterator of chunks of data.
1144 Returns less than L bytes if the iterator runs dry."""
1142 Returns less than L bytes if the iterator runs dry."""
1145 if l > len(self.buf) and self.iter:
1143 if l > len(self.buf) and self.iter:
1146 # Clamp to a multiple of self.targetsize
1144 # Clamp to a multiple of self.targetsize
1147 targetsize = max(l, self.targetsize)
1145 targetsize = max(l, self.targetsize)
1148 collector = cStringIO.StringIO()
1146 collector = cStringIO.StringIO()
1149 collector.write(self.buf)
1147 collector.write(self.buf)
1150 collected = len(self.buf)
1148 collected = len(self.buf)
1151 for chunk in self.iter:
1149 for chunk in self.iter:
1152 collector.write(chunk)
1150 collector.write(chunk)
1153 collected += len(chunk)
1151 collected += len(chunk)
1154 if collected >= targetsize:
1152 if collected >= targetsize:
1155 break
1153 break
1156 if collected < targetsize:
1154 if collected < targetsize:
1157 self.iter = False
1155 self.iter = False
1158 self.buf = collector.getvalue()
1156 self.buf = collector.getvalue()
1159 if len(self.buf) == l:
1157 if len(self.buf) == l:
1160 s, self.buf = str(self.buf), ''
1158 s, self.buf = str(self.buf), ''
1161 else:
1159 else:
1162 s, self.buf = self.buf[:l], buffer(self.buf, l)
1160 s, self.buf = self.buf[:l], buffer(self.buf, l)
1163 return s
1161 return s
1164
1162
1165 def filechunkiter(f, size=65536, limit=None):
1163 def filechunkiter(f, size=65536, limit=None):
1166 """Create a generator that produces the data in the file size
1164 """Create a generator that produces the data in the file size
1167 (default 65536) bytes at a time, up to optional limit (default is
1165 (default 65536) bytes at a time, up to optional limit (default is
1168 to read all data). Chunks may be less than size bytes if the
1166 to read all data). Chunks may be less than size bytes if the
1169 chunk is the last chunk in the file, or the file is a socket or
1167 chunk is the last chunk in the file, or the file is a socket or
1170 some other type of file that sometimes reads less data than is
1168 some other type of file that sometimes reads less data than is
1171 requested."""
1169 requested."""
1172 assert size >= 0
1170 assert size >= 0
1173 assert limit is None or limit >= 0
1171 assert limit is None or limit >= 0
1174 while True:
1172 while True:
1175 if limit is None: nbytes = size
1173 if limit is None: nbytes = size
1176 else: nbytes = min(limit, size)
1174 else: nbytes = min(limit, size)
1177 s = nbytes and f.read(nbytes)
1175 s = nbytes and f.read(nbytes)
1178 if not s: break
1176 if not s: break
1179 if limit: limit -= len(s)
1177 if limit: limit -= len(s)
1180 yield s
1178 yield s
1181
1179
1182 def makedate():
1180 def makedate():
1183 lt = time.localtime()
1181 lt = time.localtime()
1184 if lt[8] == 1 and time.daylight:
1182 if lt[8] == 1 and time.daylight:
1185 tz = time.altzone
1183 tz = time.altzone
1186 else:
1184 else:
1187 tz = time.timezone
1185 tz = time.timezone
1188 return time.mktime(lt), tz
1186 return time.mktime(lt), tz
1189
1187
1190 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1188 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1191 """represent a (unixtime, offset) tuple as a localized time.
1189 """represent a (unixtime, offset) tuple as a localized time.
1192 unixtime is seconds since the epoch, and offset is the time zone's
1190 unixtime is seconds since the epoch, and offset is the time zone's
1193 number of seconds away from UTC. if timezone is false, do not
1191 number of seconds away from UTC. if timezone is false, do not
1194 append time zone to string."""
1192 append time zone to string."""
1195 t, tz = date or makedate()
1193 t, tz = date or makedate()
1196 if "%1" in format or "%2" in format:
1194 if "%1" in format or "%2" in format:
1197 sign = (tz > 0) and "-" or "+"
1195 sign = (tz > 0) and "-" or "+"
1198 minutes = abs(tz) / 60
1196 minutes = abs(tz) / 60
1199 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1197 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1200 format = format.replace("%2", "%02d" % (minutes % 60))
1198 format = format.replace("%2", "%02d" % (minutes % 60))
1201 s = time.strftime(format, time.gmtime(float(t) - tz))
1199 s = time.strftime(format, time.gmtime(float(t) - tz))
1202 return s
1200 return s
1203
1201
1204 def shortdate(date=None):
1202 def shortdate(date=None):
1205 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1203 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1206 return datestr(date, format='%Y-%m-%d')
1204 return datestr(date, format='%Y-%m-%d')
1207
1205
1208 def strdate(string, format, defaults=[]):
1206 def strdate(string, format, defaults=[]):
1209 """parse a localized time string and return a (unixtime, offset) tuple.
1207 """parse a localized time string and return a (unixtime, offset) tuple.
1210 if the string cannot be parsed, ValueError is raised."""
1208 if the string cannot be parsed, ValueError is raised."""
1211 def timezone(string):
1209 def timezone(string):
1212 tz = string.split()[-1]
1210 tz = string.split()[-1]
1213 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1211 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1214 sign = (tz[0] == "+") and 1 or -1
1212 sign = (tz[0] == "+") and 1 or -1
1215 hours = int(tz[1:3])
1213 hours = int(tz[1:3])
1216 minutes = int(tz[3:5])
1214 minutes = int(tz[3:5])
1217 return -sign * (hours * 60 + minutes) * 60
1215 return -sign * (hours * 60 + minutes) * 60
1218 if tz == "GMT" or tz == "UTC":
1216 if tz == "GMT" or tz == "UTC":
1219 return 0
1217 return 0
1220 return None
1218 return None
1221
1219
1222 # NOTE: unixtime = localunixtime + offset
1220 # NOTE: unixtime = localunixtime + offset
1223 offset, date = timezone(string), string
1221 offset, date = timezone(string), string
1224 if offset != None:
1222 if offset != None:
1225 date = " ".join(string.split()[:-1])
1223 date = " ".join(string.split()[:-1])
1226
1224
1227 # add missing elements from defaults
1225 # add missing elements from defaults
1228 for part in defaults:
1226 for part in defaults:
1229 found = [True for p in part if ("%"+p) in format]
1227 found = [True for p in part if ("%"+p) in format]
1230 if not found:
1228 if not found:
1231 date += "@" + defaults[part]
1229 date += "@" + defaults[part]
1232 format += "@%" + part[0]
1230 format += "@%" + part[0]
1233
1231
1234 timetuple = time.strptime(date, format)
1232 timetuple = time.strptime(date, format)
1235 localunixtime = int(calendar.timegm(timetuple))
1233 localunixtime = int(calendar.timegm(timetuple))
1236 if offset is None:
1234 if offset is None:
1237 # local timezone
1235 # local timezone
1238 unixtime = int(time.mktime(timetuple))
1236 unixtime = int(time.mktime(timetuple))
1239 offset = unixtime - localunixtime
1237 offset = unixtime - localunixtime
1240 else:
1238 else:
1241 unixtime = localunixtime + offset
1239 unixtime = localunixtime + offset
1242 return unixtime, offset
1240 return unixtime, offset
1243
1241
1244 def parsedate(date, formats=None, defaults=None):
1242 def parsedate(date, formats=None, defaults=None):
1245 """parse a localized date/time string and return a (unixtime, offset) tuple.
1243 """parse a localized date/time string and return a (unixtime, offset) tuple.
1246
1244
1247 The date may be a "unixtime offset" string or in one of the specified
1245 The date may be a "unixtime offset" string or in one of the specified
1248 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1246 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1249 """
1247 """
1250 if not date:
1248 if not date:
1251 return 0, 0
1249 return 0, 0
1252 if isinstance(date, tuple) and len(date) == 2:
1250 if isinstance(date, tuple) and len(date) == 2:
1253 return date
1251 return date
1254 if not formats:
1252 if not formats:
1255 formats = defaultdateformats
1253 formats = defaultdateformats
1256 date = date.strip()
1254 date = date.strip()
1257 try:
1255 try:
1258 when, offset = map(int, date.split(' '))
1256 when, offset = map(int, date.split(' '))
1259 except ValueError:
1257 except ValueError:
1260 # fill out defaults
1258 # fill out defaults
1261 if not defaults:
1259 if not defaults:
1262 defaults = {}
1260 defaults = {}
1263 now = makedate()
1261 now = makedate()
1264 for part in "d mb yY HI M S".split():
1262 for part in "d mb yY HI M S".split():
1265 if part not in defaults:
1263 if part not in defaults:
1266 if part[0] in "HMS":
1264 if part[0] in "HMS":
1267 defaults[part] = "00"
1265 defaults[part] = "00"
1268 else:
1266 else:
1269 defaults[part] = datestr(now, "%" + part[0])
1267 defaults[part] = datestr(now, "%" + part[0])
1270
1268
1271 for format in formats:
1269 for format in formats:
1272 try:
1270 try:
1273 when, offset = strdate(date, format, defaults)
1271 when, offset = strdate(date, format, defaults)
1274 except (ValueError, OverflowError):
1272 except (ValueError, OverflowError):
1275 pass
1273 pass
1276 else:
1274 else:
1277 break
1275 break
1278 else:
1276 else:
1279 raise Abort(_('invalid date: %r ') % date)
1277 raise Abort(_('invalid date: %r ') % date)
1280 # validate explicit (probably user-specified) date and
1278 # validate explicit (probably user-specified) date and
1281 # time zone offset. values must fit in signed 32 bits for
1279 # time zone offset. values must fit in signed 32 bits for
1282 # current 32-bit linux runtimes. timezones go from UTC-12
1280 # current 32-bit linux runtimes. timezones go from UTC-12
1283 # to UTC+14
1281 # to UTC+14
1284 if abs(when) > 0x7fffffff:
1282 if abs(when) > 0x7fffffff:
1285 raise Abort(_('date exceeds 32 bits: %d') % when)
1283 raise Abort(_('date exceeds 32 bits: %d') % when)
1286 if offset < -50400 or offset > 43200:
1284 if offset < -50400 or offset > 43200:
1287 raise Abort(_('impossible time zone offset: %d') % offset)
1285 raise Abort(_('impossible time zone offset: %d') % offset)
1288 return when, offset
1286 return when, offset
1289
1287
1290 def matchdate(date):
1288 def matchdate(date):
1291 """Return a function that matches a given date match specifier
1289 """Return a function that matches a given date match specifier
1292
1290
1293 Formats include:
1291 Formats include:
1294
1292
1295 '{date}' match a given date to the accuracy provided
1293 '{date}' match a given date to the accuracy provided
1296
1294
1297 '<{date}' on or before a given date
1295 '<{date}' on or before a given date
1298
1296
1299 '>{date}' on or after a given date
1297 '>{date}' on or after a given date
1300
1298
1301 """
1299 """
1302
1300
1303 def lower(date):
1301 def lower(date):
1304 d = dict(mb="1", d="1")
1302 d = dict(mb="1", d="1")
1305 return parsedate(date, extendeddateformats, d)[0]
1303 return parsedate(date, extendeddateformats, d)[0]
1306
1304
1307 def upper(date):
1305 def upper(date):
1308 d = dict(mb="12", HI="23", M="59", S="59")
1306 d = dict(mb="12", HI="23", M="59", S="59")
1309 for days in "31 30 29".split():
1307 for days in "31 30 29".split():
1310 try:
1308 try:
1311 d["d"] = days
1309 d["d"] = days
1312 return parsedate(date, extendeddateformats, d)[0]
1310 return parsedate(date, extendeddateformats, d)[0]
1313 except:
1311 except:
1314 pass
1312 pass
1315 d["d"] = "28"
1313 d["d"] = "28"
1316 return parsedate(date, extendeddateformats, d)[0]
1314 return parsedate(date, extendeddateformats, d)[0]
1317
1315
1318 date = date.strip()
1316 date = date.strip()
1319 if date[0] == "<":
1317 if date[0] == "<":
1320 when = upper(date[1:])
1318 when = upper(date[1:])
1321 return lambda x: x <= when
1319 return lambda x: x <= when
1322 elif date[0] == ">":
1320 elif date[0] == ">":
1323 when = lower(date[1:])
1321 when = lower(date[1:])
1324 return lambda x: x >= when
1322 return lambda x: x >= when
1325 elif date[0] == "-":
1323 elif date[0] == "-":
1326 try:
1324 try:
1327 days = int(date[1:])
1325 days = int(date[1:])
1328 except ValueError:
1326 except ValueError:
1329 raise Abort(_("invalid day spec: %s") % date[1:])
1327 raise Abort(_("invalid day spec: %s") % date[1:])
1330 when = makedate()[0] - days * 3600 * 24
1328 when = makedate()[0] - days * 3600 * 24
1331 return lambda x: x >= when
1329 return lambda x: x >= when
1332 elif " to " in date:
1330 elif " to " in date:
1333 a, b = date.split(" to ")
1331 a, b = date.split(" to ")
1334 start, stop = lower(a), upper(b)
1332 start, stop = lower(a), upper(b)
1335 return lambda x: x >= start and x <= stop
1333 return lambda x: x >= start and x <= stop
1336 else:
1334 else:
1337 start, stop = lower(date), upper(date)
1335 start, stop = lower(date), upper(date)
1338 return lambda x: x >= start and x <= stop
1336 return lambda x: x >= start and x <= stop
1339
1337
1340 def shortuser(user):
1338 def shortuser(user):
1341 """Return a short representation of a user name or email address."""
1339 """Return a short representation of a user name or email address."""
1342 f = user.find('@')
1340 f = user.find('@')
1343 if f >= 0:
1341 if f >= 0:
1344 user = user[:f]
1342 user = user[:f]
1345 f = user.find('<')
1343 f = user.find('<')
1346 if f >= 0:
1344 if f >= 0:
1347 user = user[f+1:]
1345 user = user[f+1:]
1348 f = user.find(' ')
1346 f = user.find(' ')
1349 if f >= 0:
1347 if f >= 0:
1350 user = user[:f]
1348 user = user[:f]
1351 f = user.find('.')
1349 f = user.find('.')
1352 if f >= 0:
1350 if f >= 0:
1353 user = user[:f]
1351 user = user[:f]
1354 return user
1352 return user
1355
1353
1356 def email(author):
1354 def email(author):
1357 '''get email of author.'''
1355 '''get email of author.'''
1358 r = author.find('>')
1356 r = author.find('>')
1359 if r == -1: r = None
1357 if r == -1: r = None
1360 return author[author.find('<')+1:r]
1358 return author[author.find('<')+1:r]
1361
1359
1362 def ellipsis(text, maxlength=400):
1360 def ellipsis(text, maxlength=400):
1363 """Trim string to at most maxlength (default: 400) characters."""
1361 """Trim string to at most maxlength (default: 400) characters."""
1364 if len(text) <= maxlength:
1362 if len(text) <= maxlength:
1365 return text
1363 return text
1366 else:
1364 else:
1367 return "%s..." % (text[:maxlength-3])
1365 return "%s..." % (text[:maxlength-3])
1368
1366
1369 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1367 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1370 '''yield every hg repository under path, recursively.'''
1368 '''yield every hg repository under path, recursively.'''
1371 def errhandler(err):
1369 def errhandler(err):
1372 if err.filename == path:
1370 if err.filename == path:
1373 raise err
1371 raise err
1374 if followsym and hasattr(os.path, 'samestat'):
1372 if followsym and hasattr(os.path, 'samestat'):
1375 def _add_dir_if_not_there(dirlst, dirname):
1373 def _add_dir_if_not_there(dirlst, dirname):
1376 match = False
1374 match = False
1377 samestat = os.path.samestat
1375 samestat = os.path.samestat
1378 dirstat = os.stat(dirname)
1376 dirstat = os.stat(dirname)
1379 for lstdirstat in dirlst:
1377 for lstdirstat in dirlst:
1380 if samestat(dirstat, lstdirstat):
1378 if samestat(dirstat, lstdirstat):
1381 match = True
1379 match = True
1382 break
1380 break
1383 if not match:
1381 if not match:
1384 dirlst.append(dirstat)
1382 dirlst.append(dirstat)
1385 return not match
1383 return not match
1386 else:
1384 else:
1387 followsym = False
1385 followsym = False
1388
1386
1389 if (seen_dirs is None) and followsym:
1387 if (seen_dirs is None) and followsym:
1390 seen_dirs = []
1388 seen_dirs = []
1391 _add_dir_if_not_there(seen_dirs, path)
1389 _add_dir_if_not_there(seen_dirs, path)
1392 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1390 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1393 if '.hg' in dirs:
1391 if '.hg' in dirs:
1394 yield root # found a repository
1392 yield root # found a repository
1395 qroot = os.path.join(root, '.hg', 'patches')
1393 qroot = os.path.join(root, '.hg', 'patches')
1396 if os.path.isdir(os.path.join(qroot, '.hg')):
1394 if os.path.isdir(os.path.join(qroot, '.hg')):
1397 yield qroot # we have a patch queue repo here
1395 yield qroot # we have a patch queue repo here
1398 if recurse:
1396 if recurse:
1399 # avoid recursing inside the .hg directory
1397 # avoid recursing inside the .hg directory
1400 dirs.remove('.hg')
1398 dirs.remove('.hg')
1401 else:
1399 else:
1402 dirs[:] = [] # don't descend further
1400 dirs[:] = [] # don't descend further
1403 elif followsym:
1401 elif followsym:
1404 newdirs = []
1402 newdirs = []
1405 for d in dirs:
1403 for d in dirs:
1406 fname = os.path.join(root, d)
1404 fname = os.path.join(root, d)
1407 if _add_dir_if_not_there(seen_dirs, fname):
1405 if _add_dir_if_not_there(seen_dirs, fname):
1408 if os.path.islink(fname):
1406 if os.path.islink(fname):
1409 for hgname in walkrepos(fname, True, seen_dirs):
1407 for hgname in walkrepos(fname, True, seen_dirs):
1410 yield hgname
1408 yield hgname
1411 else:
1409 else:
1412 newdirs.append(d)
1410 newdirs.append(d)
1413 dirs[:] = newdirs
1411 dirs[:] = newdirs
1414
1412
1415 _rcpath = None
1413 _rcpath = None
1416
1414
1417 def os_rcpath():
1415 def os_rcpath():
1418 '''return default os-specific hgrc search path'''
1416 '''return default os-specific hgrc search path'''
1419 path = system_rcpath()
1417 path = system_rcpath()
1420 path.extend(user_rcpath())
1418 path.extend(user_rcpath())
1421 path = [os.path.normpath(f) for f in path]
1419 path = [os.path.normpath(f) for f in path]
1422 return path
1420 return path
1423
1421
1424 def rcpath():
1422 def rcpath():
1425 '''return hgrc search path. if env var HGRCPATH is set, use it.
1423 '''return hgrc search path. if env var HGRCPATH is set, use it.
1426 for each item in path, if directory, use files ending in .rc,
1424 for each item in path, if directory, use files ending in .rc,
1427 else use item.
1425 else use item.
1428 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1426 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1429 if no HGRCPATH, use default os-specific path.'''
1427 if no HGRCPATH, use default os-specific path.'''
1430 global _rcpath
1428 global _rcpath
1431 if _rcpath is None:
1429 if _rcpath is None:
1432 if 'HGRCPATH' in os.environ:
1430 if 'HGRCPATH' in os.environ:
1433 _rcpath = []
1431 _rcpath = []
1434 for p in os.environ['HGRCPATH'].split(os.pathsep):
1432 for p in os.environ['HGRCPATH'].split(os.pathsep):
1435 if not p: continue
1433 if not p: continue
1436 if os.path.isdir(p):
1434 if os.path.isdir(p):
1437 for f, kind in osutil.listdir(p):
1435 for f, kind in osutil.listdir(p):
1438 if f.endswith('.rc'):
1436 if f.endswith('.rc'):
1439 _rcpath.append(os.path.join(p, f))
1437 _rcpath.append(os.path.join(p, f))
1440 else:
1438 else:
1441 _rcpath.append(p)
1439 _rcpath.append(p)
1442 else:
1440 else:
1443 _rcpath = os_rcpath()
1441 _rcpath = os_rcpath()
1444 return _rcpath
1442 return _rcpath
1445
1443
1446 def bytecount(nbytes):
1444 def bytecount(nbytes):
1447 '''return byte count formatted as readable string, with units'''
1445 '''return byte count formatted as readable string, with units'''
1448
1446
1449 units = (
1447 units = (
1450 (100, 1<<30, _('%.0f GB')),
1448 (100, 1<<30, _('%.0f GB')),
1451 (10, 1<<30, _('%.1f GB')),
1449 (10, 1<<30, _('%.1f GB')),
1452 (1, 1<<30, _('%.2f GB')),
1450 (1, 1<<30, _('%.2f GB')),
1453 (100, 1<<20, _('%.0f MB')),
1451 (100, 1<<20, _('%.0f MB')),
1454 (10, 1<<20, _('%.1f MB')),
1452 (10, 1<<20, _('%.1f MB')),
1455 (1, 1<<20, _('%.2f MB')),
1453 (1, 1<<20, _('%.2f MB')),
1456 (100, 1<<10, _('%.0f KB')),
1454 (100, 1<<10, _('%.0f KB')),
1457 (10, 1<<10, _('%.1f KB')),
1455 (10, 1<<10, _('%.1f KB')),
1458 (1, 1<<10, _('%.2f KB')),
1456 (1, 1<<10, _('%.2f KB')),
1459 (1, 1, _('%.0f bytes')),
1457 (1, 1, _('%.0f bytes')),
1460 )
1458 )
1461
1459
1462 for multiplier, divisor, format in units:
1460 for multiplier, divisor, format in units:
1463 if nbytes >= divisor * multiplier:
1461 if nbytes >= divisor * multiplier:
1464 return format % (nbytes / float(divisor))
1462 return format % (nbytes / float(divisor))
1465 return units[-1][2] % nbytes
1463 return units[-1][2] % nbytes
1466
1464
1467 def drop_scheme(scheme, path):
1465 def drop_scheme(scheme, path):
1468 sc = scheme + ':'
1466 sc = scheme + ':'
1469 if path.startswith(sc):
1467 if path.startswith(sc):
1470 path = path[len(sc):]
1468 path = path[len(sc):]
1471 if path.startswith('//'):
1469 if path.startswith('//'):
1472 path = path[2:]
1470 path = path[2:]
1473 return path
1471 return path
1474
1472
1475 def uirepr(s):
1473 def uirepr(s):
1476 # Avoid double backslash in Windows path repr()
1474 # Avoid double backslash in Windows path repr()
1477 return repr(s).replace('\\\\', '\\')
1475 return repr(s).replace('\\\\', '\\')
1478
1476
1479 def termwidth():
1477 def termwidth():
1480 if 'COLUMNS' in os.environ:
1478 if 'COLUMNS' in os.environ:
1481 try:
1479 try:
1482 return int(os.environ['COLUMNS'])
1480 return int(os.environ['COLUMNS'])
1483 except ValueError:
1481 except ValueError:
1484 pass
1482 pass
1485 try:
1483 try:
1486 import termios, array, fcntl
1484 import termios, array, fcntl
1487 for dev in (sys.stdout, sys.stdin):
1485 for dev in (sys.stdout, sys.stdin):
1488 try:
1486 try:
1489 fd = dev.fileno()
1487 fd = dev.fileno()
1490 if not os.isatty(fd):
1488 if not os.isatty(fd):
1491 continue
1489 continue
1492 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
1490 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
1493 return array.array('h', arri)[1]
1491 return array.array('h', arri)[1]
1494 except ValueError:
1492 except ValueError:
1495 pass
1493 pass
1496 except ImportError:
1494 except ImportError:
1497 pass
1495 pass
1498 return 80
1496 return 80
1499
1497
1500 def iterlines(iterator):
1498 def iterlines(iterator):
1501 for chunk in iterator:
1499 for chunk in iterator:
1502 for line in chunk.splitlines():
1500 for line in chunk.splitlines():
1503 yield line
1501 yield line
General Comments 0
You need to be logged in to leave comments. Login now