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