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