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