##// END OF EJS Templates
util: add removedirs as platform depending function...
FUJIWARA Katsunori -
r24692:144883a8 default
parent child Browse files
Show More
@@ -1,618 +1,619
1 # posix.py - Posix utility function implementations for Mercurial
1 # posix.py - Posix utility function implementations for Mercurial
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import encoding
9 import encoding
10 import os, sys, errno, stat, getpass, pwd, grp, socket, tempfile, unicodedata
10 import os, sys, errno, stat, getpass, pwd, grp, socket, tempfile, unicodedata
11 import fcntl, re
11 import fcntl, re
12
12
13 posixfile = open
13 posixfile = open
14 normpath = os.path.normpath
14 normpath = os.path.normpath
15 samestat = os.path.samestat
15 samestat = os.path.samestat
16 oslink = os.link
16 oslink = os.link
17 unlink = os.unlink
17 unlink = os.unlink
18 rename = os.rename
18 rename = os.rename
19 removedirs = os.removedirs
19 expandglobs = False
20 expandglobs = False
20
21
21 umask = os.umask(0)
22 umask = os.umask(0)
22 os.umask(umask)
23 os.umask(umask)
23
24
24 def split(p):
25 def split(p):
25 '''Same as posixpath.split, but faster
26 '''Same as posixpath.split, but faster
26
27
27 >>> import posixpath
28 >>> import posixpath
28 >>> for f in ['/absolute/path/to/file',
29 >>> for f in ['/absolute/path/to/file',
29 ... 'relative/path/to/file',
30 ... 'relative/path/to/file',
30 ... 'file_alone',
31 ... 'file_alone',
31 ... 'path/to/directory/',
32 ... 'path/to/directory/',
32 ... '/multiple/path//separators',
33 ... '/multiple/path//separators',
33 ... '/file_at_root',
34 ... '/file_at_root',
34 ... '///multiple_leading_separators_at_root',
35 ... '///multiple_leading_separators_at_root',
35 ... '']:
36 ... '']:
36 ... assert split(f) == posixpath.split(f), f
37 ... assert split(f) == posixpath.split(f), f
37 '''
38 '''
38 ht = p.rsplit('/', 1)
39 ht = p.rsplit('/', 1)
39 if len(ht) == 1:
40 if len(ht) == 1:
40 return '', p
41 return '', p
41 nh = ht[0].rstrip('/')
42 nh = ht[0].rstrip('/')
42 if nh:
43 if nh:
43 return nh, ht[1]
44 return nh, ht[1]
44 return ht[0] + '/', ht[1]
45 return ht[0] + '/', ht[1]
45
46
46 def openhardlinks():
47 def openhardlinks():
47 '''return true if it is safe to hold open file handles to hardlinks'''
48 '''return true if it is safe to hold open file handles to hardlinks'''
48 return True
49 return True
49
50
50 def nlinks(name):
51 def nlinks(name):
51 '''return number of hardlinks for the given file'''
52 '''return number of hardlinks for the given file'''
52 return os.lstat(name).st_nlink
53 return os.lstat(name).st_nlink
53
54
54 def parsepatchoutput(output_line):
55 def parsepatchoutput(output_line):
55 """parses the output produced by patch and returns the filename"""
56 """parses the output produced by patch and returns the filename"""
56 pf = output_line[14:]
57 pf = output_line[14:]
57 if os.sys.platform == 'OpenVMS':
58 if os.sys.platform == 'OpenVMS':
58 if pf[0] == '`':
59 if pf[0] == '`':
59 pf = pf[1:-1] # Remove the quotes
60 pf = pf[1:-1] # Remove the quotes
60 else:
61 else:
61 if pf.startswith("'") and pf.endswith("'") and " " in pf:
62 if pf.startswith("'") and pf.endswith("'") and " " in pf:
62 pf = pf[1:-1] # Remove the quotes
63 pf = pf[1:-1] # Remove the quotes
63 return pf
64 return pf
64
65
65 def sshargs(sshcmd, host, user, port):
66 def sshargs(sshcmd, host, user, port):
66 '''Build argument list for ssh'''
67 '''Build argument list for ssh'''
67 args = user and ("%s@%s" % (user, host)) or host
68 args = user and ("%s@%s" % (user, host)) or host
68 return port and ("%s -p %s" % (args, port)) or args
69 return port and ("%s -p %s" % (args, port)) or args
69
70
70 def isexec(f):
71 def isexec(f):
71 """check whether a file is executable"""
72 """check whether a file is executable"""
72 return (os.lstat(f).st_mode & 0100 != 0)
73 return (os.lstat(f).st_mode & 0100 != 0)
73
74
74 def setflags(f, l, x):
75 def setflags(f, l, x):
75 s = os.lstat(f).st_mode
76 s = os.lstat(f).st_mode
76 if l:
77 if l:
77 if not stat.S_ISLNK(s):
78 if not stat.S_ISLNK(s):
78 # switch file to link
79 # switch file to link
79 fp = open(f)
80 fp = open(f)
80 data = fp.read()
81 data = fp.read()
81 fp.close()
82 fp.close()
82 os.unlink(f)
83 os.unlink(f)
83 try:
84 try:
84 os.symlink(data, f)
85 os.symlink(data, f)
85 except OSError:
86 except OSError:
86 # failed to make a link, rewrite file
87 # failed to make a link, rewrite file
87 fp = open(f, "w")
88 fp = open(f, "w")
88 fp.write(data)
89 fp.write(data)
89 fp.close()
90 fp.close()
90 # no chmod needed at this point
91 # no chmod needed at this point
91 return
92 return
92 if stat.S_ISLNK(s):
93 if stat.S_ISLNK(s):
93 # switch link to file
94 # switch link to file
94 data = os.readlink(f)
95 data = os.readlink(f)
95 os.unlink(f)
96 os.unlink(f)
96 fp = open(f, "w")
97 fp = open(f, "w")
97 fp.write(data)
98 fp.write(data)
98 fp.close()
99 fp.close()
99 s = 0666 & ~umask # avoid restatting for chmod
100 s = 0666 & ~umask # avoid restatting for chmod
100
101
101 sx = s & 0100
102 sx = s & 0100
102 if x and not sx:
103 if x and not sx:
103 # Turn on +x for every +r bit when making a file executable
104 # Turn on +x for every +r bit when making a file executable
104 # and obey umask.
105 # and obey umask.
105 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
106 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
106 elif not x and sx:
107 elif not x and sx:
107 # Turn off all +x bits
108 # Turn off all +x bits
108 os.chmod(f, s & 0666)
109 os.chmod(f, s & 0666)
109
110
110 def copymode(src, dst, mode=None):
111 def copymode(src, dst, mode=None):
111 '''Copy the file mode from the file at path src to dst.
112 '''Copy the file mode from the file at path src to dst.
112 If src doesn't exist, we're using mode instead. If mode is None, we're
113 If src doesn't exist, we're using mode instead. If mode is None, we're
113 using umask.'''
114 using umask.'''
114 try:
115 try:
115 st_mode = os.lstat(src).st_mode & 0777
116 st_mode = os.lstat(src).st_mode & 0777
116 except OSError, inst:
117 except OSError, inst:
117 if inst.errno != errno.ENOENT:
118 if inst.errno != errno.ENOENT:
118 raise
119 raise
119 st_mode = mode
120 st_mode = mode
120 if st_mode is None:
121 if st_mode is None:
121 st_mode = ~umask
122 st_mode = ~umask
122 st_mode &= 0666
123 st_mode &= 0666
123 os.chmod(dst, st_mode)
124 os.chmod(dst, st_mode)
124
125
125 def checkexec(path):
126 def checkexec(path):
126 """
127 """
127 Check whether the given path is on a filesystem with UNIX-like exec flags
128 Check whether the given path is on a filesystem with UNIX-like exec flags
128
129
129 Requires a directory (like /foo/.hg)
130 Requires a directory (like /foo/.hg)
130 """
131 """
131
132
132 # VFAT on some Linux versions can flip mode but it doesn't persist
133 # VFAT on some Linux versions can flip mode but it doesn't persist
133 # a FS remount. Frequently we can detect it if files are created
134 # a FS remount. Frequently we can detect it if files are created
134 # with exec bit on.
135 # with exec bit on.
135
136
136 try:
137 try:
137 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
138 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
138 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
139 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
139 try:
140 try:
140 os.close(fh)
141 os.close(fh)
141 m = os.stat(fn).st_mode & 0777
142 m = os.stat(fn).st_mode & 0777
142 new_file_has_exec = m & EXECFLAGS
143 new_file_has_exec = m & EXECFLAGS
143 os.chmod(fn, m ^ EXECFLAGS)
144 os.chmod(fn, m ^ EXECFLAGS)
144 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
145 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
145 finally:
146 finally:
146 os.unlink(fn)
147 os.unlink(fn)
147 except (IOError, OSError):
148 except (IOError, OSError):
148 # we don't care, the user probably won't be able to commit anyway
149 # we don't care, the user probably won't be able to commit anyway
149 return False
150 return False
150 return not (new_file_has_exec or exec_flags_cannot_flip)
151 return not (new_file_has_exec or exec_flags_cannot_flip)
151
152
152 def checklink(path):
153 def checklink(path):
153 """check whether the given path is on a symlink-capable filesystem"""
154 """check whether the given path is on a symlink-capable filesystem"""
154 # mktemp is not racy because symlink creation will fail if the
155 # mktemp is not racy because symlink creation will fail if the
155 # file already exists
156 # file already exists
156 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
157 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
157 try:
158 try:
158 fd = tempfile.NamedTemporaryFile(dir=path, prefix='hg-checklink-')
159 fd = tempfile.NamedTemporaryFile(dir=path, prefix='hg-checklink-')
159 try:
160 try:
160 os.symlink(os.path.basename(fd.name), name)
161 os.symlink(os.path.basename(fd.name), name)
161 os.unlink(name)
162 os.unlink(name)
162 return True
163 return True
163 finally:
164 finally:
164 fd.close()
165 fd.close()
165 except AttributeError:
166 except AttributeError:
166 return False
167 return False
167 except OSError, inst:
168 except OSError, inst:
168 # sshfs might report failure while successfully creating the link
169 # sshfs might report failure while successfully creating the link
169 if inst[0] == errno.EIO and os.path.exists(name):
170 if inst[0] == errno.EIO and os.path.exists(name):
170 os.unlink(name)
171 os.unlink(name)
171 return False
172 return False
172
173
173 def checkosfilename(path):
174 def checkosfilename(path):
174 '''Check that the base-relative path is a valid filename on this platform.
175 '''Check that the base-relative path is a valid filename on this platform.
175 Returns None if the path is ok, or a UI string describing the problem.'''
176 Returns None if the path is ok, or a UI string describing the problem.'''
176 pass # on posix platforms, every path is ok
177 pass # on posix platforms, every path is ok
177
178
178 def setbinary(fd):
179 def setbinary(fd):
179 pass
180 pass
180
181
181 def pconvert(path):
182 def pconvert(path):
182 return path
183 return path
183
184
184 def localpath(path):
185 def localpath(path):
185 return path
186 return path
186
187
187 def samefile(fpath1, fpath2):
188 def samefile(fpath1, fpath2):
188 """Returns whether path1 and path2 refer to the same file. This is only
189 """Returns whether path1 and path2 refer to the same file. This is only
189 guaranteed to work for files, not directories."""
190 guaranteed to work for files, not directories."""
190 return os.path.samefile(fpath1, fpath2)
191 return os.path.samefile(fpath1, fpath2)
191
192
192 def samedevice(fpath1, fpath2):
193 def samedevice(fpath1, fpath2):
193 """Returns whether fpath1 and fpath2 are on the same device. This is only
194 """Returns whether fpath1 and fpath2 are on the same device. This is only
194 guaranteed to work for files, not directories."""
195 guaranteed to work for files, not directories."""
195 st1 = os.lstat(fpath1)
196 st1 = os.lstat(fpath1)
196 st2 = os.lstat(fpath2)
197 st2 = os.lstat(fpath2)
197 return st1.st_dev == st2.st_dev
198 return st1.st_dev == st2.st_dev
198
199
199 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
200 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
200 def normcase(path):
201 def normcase(path):
201 return path.lower()
202 return path.lower()
202
203
203 # what normcase does to ASCII strings
204 # what normcase does to ASCII strings
204 normcasespec = encoding.normcasespecs.lower
205 normcasespec = encoding.normcasespecs.lower
205 # fallback normcase function for non-ASCII strings
206 # fallback normcase function for non-ASCII strings
206 normcasefallback = normcase
207 normcasefallback = normcase
207
208
208 if sys.platform == 'darwin':
209 if sys.platform == 'darwin':
209
210
210 def normcase(path):
211 def normcase(path):
211 '''
212 '''
212 Normalize a filename for OS X-compatible comparison:
213 Normalize a filename for OS X-compatible comparison:
213 - escape-encode invalid characters
214 - escape-encode invalid characters
214 - decompose to NFD
215 - decompose to NFD
215 - lowercase
216 - lowercase
216 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
217 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
217
218
218 >>> normcase('UPPER')
219 >>> normcase('UPPER')
219 'upper'
220 'upper'
220 >>> normcase('Caf\xc3\xa9')
221 >>> normcase('Caf\xc3\xa9')
221 'cafe\\xcc\\x81'
222 'cafe\\xcc\\x81'
222 >>> normcase('\xc3\x89')
223 >>> normcase('\xc3\x89')
223 'e\\xcc\\x81'
224 'e\\xcc\\x81'
224 >>> normcase('\xb8\xca\xc3\xca\xbe\xc8.JPG') # issue3918
225 >>> normcase('\xb8\xca\xc3\xca\xbe\xc8.JPG') # issue3918
225 '%b8%ca%c3\\xca\\xbe%c8.jpg'
226 '%b8%ca%c3\\xca\\xbe%c8.jpg'
226 '''
227 '''
227
228
228 try:
229 try:
229 return encoding.asciilower(path) # exception for non-ASCII
230 return encoding.asciilower(path) # exception for non-ASCII
230 except UnicodeDecodeError:
231 except UnicodeDecodeError:
231 return normcasefallback(path)
232 return normcasefallback(path)
232
233
233 normcasespec = encoding.normcasespecs.lower
234 normcasespec = encoding.normcasespecs.lower
234
235
235 def normcasefallback(path):
236 def normcasefallback(path):
236 try:
237 try:
237 u = path.decode('utf-8')
238 u = path.decode('utf-8')
238 except UnicodeDecodeError:
239 except UnicodeDecodeError:
239 # OS X percent-encodes any bytes that aren't valid utf-8
240 # OS X percent-encodes any bytes that aren't valid utf-8
240 s = ''
241 s = ''
241 g = ''
242 g = ''
242 l = 0
243 l = 0
243 for c in path:
244 for c in path:
244 o = ord(c)
245 o = ord(c)
245 if l and o < 128 or o >= 192:
246 if l and o < 128 or o >= 192:
246 # we want a continuation byte, but didn't get one
247 # we want a continuation byte, but didn't get one
247 s += ''.join(["%%%02X" % ord(x) for x in g])
248 s += ''.join(["%%%02X" % ord(x) for x in g])
248 g = ''
249 g = ''
249 l = 0
250 l = 0
250 if l == 0 and o < 128:
251 if l == 0 and o < 128:
251 # ascii
252 # ascii
252 s += c
253 s += c
253 elif l == 0 and 194 <= o < 245:
254 elif l == 0 and 194 <= o < 245:
254 # valid leading bytes
255 # valid leading bytes
255 if o < 224:
256 if o < 224:
256 l = 1
257 l = 1
257 elif o < 240:
258 elif o < 240:
258 l = 2
259 l = 2
259 else:
260 else:
260 l = 3
261 l = 3
261 g = c
262 g = c
262 elif l > 0 and 128 <= o < 192:
263 elif l > 0 and 128 <= o < 192:
263 # valid continuations
264 # valid continuations
264 g += c
265 g += c
265 l -= 1
266 l -= 1
266 if not l:
267 if not l:
267 s += g
268 s += g
268 g = ''
269 g = ''
269 else:
270 else:
270 # invalid
271 # invalid
271 s += "%%%02X" % o
272 s += "%%%02X" % o
272
273
273 # any remaining partial characters
274 # any remaining partial characters
274 s += ''.join(["%%%02X" % ord(x) for x in g])
275 s += ''.join(["%%%02X" % ord(x) for x in g])
275 u = s.decode('utf-8')
276 u = s.decode('utf-8')
276
277
277 # Decompose then lowercase (HFS+ technote specifies lower)
278 # Decompose then lowercase (HFS+ technote specifies lower)
278 enc = unicodedata.normalize('NFD', u).lower().encode('utf-8')
279 enc = unicodedata.normalize('NFD', u).lower().encode('utf-8')
279 # drop HFS+ ignored characters
280 # drop HFS+ ignored characters
280 return encoding.hfsignoreclean(enc)
281 return encoding.hfsignoreclean(enc)
281
282
282 if sys.platform == 'cygwin':
283 if sys.platform == 'cygwin':
283 # workaround for cygwin, in which mount point part of path is
284 # workaround for cygwin, in which mount point part of path is
284 # treated as case sensitive, even though underlying NTFS is case
285 # treated as case sensitive, even though underlying NTFS is case
285 # insensitive.
286 # insensitive.
286
287
287 # default mount points
288 # default mount points
288 cygwinmountpoints = sorted([
289 cygwinmountpoints = sorted([
289 "/usr/bin",
290 "/usr/bin",
290 "/usr/lib",
291 "/usr/lib",
291 "/cygdrive",
292 "/cygdrive",
292 ], reverse=True)
293 ], reverse=True)
293
294
294 # use upper-ing as normcase as same as NTFS workaround
295 # use upper-ing as normcase as same as NTFS workaround
295 def normcase(path):
296 def normcase(path):
296 pathlen = len(path)
297 pathlen = len(path)
297 if (pathlen == 0) or (path[0] != os.sep):
298 if (pathlen == 0) or (path[0] != os.sep):
298 # treat as relative
299 # treat as relative
299 return encoding.upper(path)
300 return encoding.upper(path)
300
301
301 # to preserve case of mountpoint part
302 # to preserve case of mountpoint part
302 for mp in cygwinmountpoints:
303 for mp in cygwinmountpoints:
303 if not path.startswith(mp):
304 if not path.startswith(mp):
304 continue
305 continue
305
306
306 mplen = len(mp)
307 mplen = len(mp)
307 if mplen == pathlen: # mount point itself
308 if mplen == pathlen: # mount point itself
308 return mp
309 return mp
309 if path[mplen] == os.sep:
310 if path[mplen] == os.sep:
310 return mp + encoding.upper(path[mplen:])
311 return mp + encoding.upper(path[mplen:])
311
312
312 return encoding.upper(path)
313 return encoding.upper(path)
313
314
314 normcasespec = encoding.normcasespecs.other
315 normcasespec = encoding.normcasespecs.other
315 normcasefallback = normcase
316 normcasefallback = normcase
316
317
317 # Cygwin translates native ACLs to POSIX permissions,
318 # Cygwin translates native ACLs to POSIX permissions,
318 # but these translations are not supported by native
319 # but these translations are not supported by native
319 # tools, so the exec bit tends to be set erroneously.
320 # tools, so the exec bit tends to be set erroneously.
320 # Therefore, disable executable bit access on Cygwin.
321 # Therefore, disable executable bit access on Cygwin.
321 def checkexec(path):
322 def checkexec(path):
322 return False
323 return False
323
324
324 # Similarly, Cygwin's symlink emulation is likely to create
325 # Similarly, Cygwin's symlink emulation is likely to create
325 # problems when Mercurial is used from both Cygwin and native
326 # problems when Mercurial is used from both Cygwin and native
326 # Windows, with other native tools, or on shared volumes
327 # Windows, with other native tools, or on shared volumes
327 def checklink(path):
328 def checklink(path):
328 return False
329 return False
329
330
330 _needsshellquote = None
331 _needsshellquote = None
331 def shellquote(s):
332 def shellquote(s):
332 if os.sys.platform == 'OpenVMS':
333 if os.sys.platform == 'OpenVMS':
333 return '"%s"' % s
334 return '"%s"' % s
334 global _needsshellquote
335 global _needsshellquote
335 if _needsshellquote is None:
336 if _needsshellquote is None:
336 _needsshellquote = re.compile(r'[^a-zA-Z0-9._/-]').search
337 _needsshellquote = re.compile(r'[^a-zA-Z0-9._/-]').search
337 if s and not _needsshellquote(s):
338 if s and not _needsshellquote(s):
338 # "s" shouldn't have to be quoted
339 # "s" shouldn't have to be quoted
339 return s
340 return s
340 else:
341 else:
341 return "'%s'" % s.replace("'", "'\\''")
342 return "'%s'" % s.replace("'", "'\\''")
342
343
343 def quotecommand(cmd):
344 def quotecommand(cmd):
344 return cmd
345 return cmd
345
346
346 def popen(command, mode='r'):
347 def popen(command, mode='r'):
347 return os.popen(command, mode)
348 return os.popen(command, mode)
348
349
349 def testpid(pid):
350 def testpid(pid):
350 '''return False if pid dead, True if running or not sure'''
351 '''return False if pid dead, True if running or not sure'''
351 if os.sys.platform == 'OpenVMS':
352 if os.sys.platform == 'OpenVMS':
352 return True
353 return True
353 try:
354 try:
354 os.kill(pid, 0)
355 os.kill(pid, 0)
355 return True
356 return True
356 except OSError, inst:
357 except OSError, inst:
357 return inst.errno != errno.ESRCH
358 return inst.errno != errno.ESRCH
358
359
359 def explainexit(code):
360 def explainexit(code):
360 """return a 2-tuple (desc, code) describing a subprocess status
361 """return a 2-tuple (desc, code) describing a subprocess status
361 (codes from kill are negative - not os.system/wait encoding)"""
362 (codes from kill are negative - not os.system/wait encoding)"""
362 if code >= 0:
363 if code >= 0:
363 return _("exited with status %d") % code, code
364 return _("exited with status %d") % code, code
364 return _("killed by signal %d") % -code, -code
365 return _("killed by signal %d") % -code, -code
365
366
366 def isowner(st):
367 def isowner(st):
367 """Return True if the stat object st is from the current user."""
368 """Return True if the stat object st is from the current user."""
368 return st.st_uid == os.getuid()
369 return st.st_uid == os.getuid()
369
370
370 def findexe(command):
371 def findexe(command):
371 '''Find executable for command searching like which does.
372 '''Find executable for command searching like which does.
372 If command is a basename then PATH is searched for command.
373 If command is a basename then PATH is searched for command.
373 PATH isn't searched if command is an absolute or relative path.
374 PATH isn't searched if command is an absolute or relative path.
374 If command isn't found None is returned.'''
375 If command isn't found None is returned.'''
375 if sys.platform == 'OpenVMS':
376 if sys.platform == 'OpenVMS':
376 return command
377 return command
377
378
378 def findexisting(executable):
379 def findexisting(executable):
379 'Will return executable if existing file'
380 'Will return executable if existing file'
380 if os.path.isfile(executable) and os.access(executable, os.X_OK):
381 if os.path.isfile(executable) and os.access(executable, os.X_OK):
381 return executable
382 return executable
382 return None
383 return None
383
384
384 if os.sep in command:
385 if os.sep in command:
385 return findexisting(command)
386 return findexisting(command)
386
387
387 if sys.platform == 'plan9':
388 if sys.platform == 'plan9':
388 return findexisting(os.path.join('/bin', command))
389 return findexisting(os.path.join('/bin', command))
389
390
390 for path in os.environ.get('PATH', '').split(os.pathsep):
391 for path in os.environ.get('PATH', '').split(os.pathsep):
391 executable = findexisting(os.path.join(path, command))
392 executable = findexisting(os.path.join(path, command))
392 if executable is not None:
393 if executable is not None:
393 return executable
394 return executable
394 return None
395 return None
395
396
396 def setsignalhandler():
397 def setsignalhandler():
397 pass
398 pass
398
399
399 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
400 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
400
401
401 def statfiles(files):
402 def statfiles(files):
402 '''Stat each file in files. Yield each stat, or None if a file does not
403 '''Stat each file in files. Yield each stat, or None if a file does not
403 exist or has a type we don't care about.'''
404 exist or has a type we don't care about.'''
404 lstat = os.lstat
405 lstat = os.lstat
405 getkind = stat.S_IFMT
406 getkind = stat.S_IFMT
406 for nf in files:
407 for nf in files:
407 try:
408 try:
408 st = lstat(nf)
409 st = lstat(nf)
409 if getkind(st.st_mode) not in _wantedkinds:
410 if getkind(st.st_mode) not in _wantedkinds:
410 st = None
411 st = None
411 except OSError, err:
412 except OSError, err:
412 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
413 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
413 raise
414 raise
414 st = None
415 st = None
415 yield st
416 yield st
416
417
417 def getuser():
418 def getuser():
418 '''return name of current user'''
419 '''return name of current user'''
419 return getpass.getuser()
420 return getpass.getuser()
420
421
421 def username(uid=None):
422 def username(uid=None):
422 """Return the name of the user with the given uid.
423 """Return the name of the user with the given uid.
423
424
424 If uid is None, return the name of the current user."""
425 If uid is None, return the name of the current user."""
425
426
426 if uid is None:
427 if uid is None:
427 uid = os.getuid()
428 uid = os.getuid()
428 try:
429 try:
429 return pwd.getpwuid(uid)[0]
430 return pwd.getpwuid(uid)[0]
430 except KeyError:
431 except KeyError:
431 return str(uid)
432 return str(uid)
432
433
433 def groupname(gid=None):
434 def groupname(gid=None):
434 """Return the name of the group with the given gid.
435 """Return the name of the group with the given gid.
435
436
436 If gid is None, return the name of the current group."""
437 If gid is None, return the name of the current group."""
437
438
438 if gid is None:
439 if gid is None:
439 gid = os.getgid()
440 gid = os.getgid()
440 try:
441 try:
441 return grp.getgrgid(gid)[0]
442 return grp.getgrgid(gid)[0]
442 except KeyError:
443 except KeyError:
443 return str(gid)
444 return str(gid)
444
445
445 def groupmembers(name):
446 def groupmembers(name):
446 """Return the list of members of the group with the given
447 """Return the list of members of the group with the given
447 name, KeyError if the group does not exist.
448 name, KeyError if the group does not exist.
448 """
449 """
449 return list(grp.getgrnam(name).gr_mem)
450 return list(grp.getgrnam(name).gr_mem)
450
451
451 def spawndetached(args):
452 def spawndetached(args):
452 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
453 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
453 args[0], args)
454 args[0], args)
454
455
455 def gethgcmd():
456 def gethgcmd():
456 return sys.argv[:1]
457 return sys.argv[:1]
457
458
458 def termwidth():
459 def termwidth():
459 try:
460 try:
460 import termios, array
461 import termios, array
461 for dev in (sys.stderr, sys.stdout, sys.stdin):
462 for dev in (sys.stderr, sys.stdout, sys.stdin):
462 try:
463 try:
463 try:
464 try:
464 fd = dev.fileno()
465 fd = dev.fileno()
465 except AttributeError:
466 except AttributeError:
466 continue
467 continue
467 if not os.isatty(fd):
468 if not os.isatty(fd):
468 continue
469 continue
469 try:
470 try:
470 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
471 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
471 width = array.array('h', arri)[1]
472 width = array.array('h', arri)[1]
472 if width > 0:
473 if width > 0:
473 return width
474 return width
474 except AttributeError:
475 except AttributeError:
475 pass
476 pass
476 except ValueError:
477 except ValueError:
477 pass
478 pass
478 except IOError, e:
479 except IOError, e:
479 if e[0] == errno.EINVAL:
480 if e[0] == errno.EINVAL:
480 pass
481 pass
481 else:
482 else:
482 raise
483 raise
483 except ImportError:
484 except ImportError:
484 pass
485 pass
485 return 80
486 return 80
486
487
487 def makedir(path, notindexed):
488 def makedir(path, notindexed):
488 os.mkdir(path)
489 os.mkdir(path)
489
490
490 def unlinkpath(f, ignoremissing=False):
491 def unlinkpath(f, ignoremissing=False):
491 """unlink and remove the directory if it is empty"""
492 """unlink and remove the directory if it is empty"""
492 try:
493 try:
493 os.unlink(f)
494 os.unlink(f)
494 except OSError, e:
495 except OSError, e:
495 if not (ignoremissing and e.errno == errno.ENOENT):
496 if not (ignoremissing and e.errno == errno.ENOENT):
496 raise
497 raise
497 # try removing directories that might now be empty
498 # try removing directories that might now be empty
498 try:
499 try:
499 os.removedirs(os.path.dirname(f))
500 os.removedirs(os.path.dirname(f))
500 except OSError:
501 except OSError:
501 pass
502 pass
502
503
503 def lookupreg(key, name=None, scope=None):
504 def lookupreg(key, name=None, scope=None):
504 return None
505 return None
505
506
506 def hidewindow():
507 def hidewindow():
507 """Hide current shell window.
508 """Hide current shell window.
508
509
509 Used to hide the window opened when starting asynchronous
510 Used to hide the window opened when starting asynchronous
510 child process under Windows, unneeded on other systems.
511 child process under Windows, unneeded on other systems.
511 """
512 """
512 pass
513 pass
513
514
514 class cachestat(object):
515 class cachestat(object):
515 def __init__(self, path):
516 def __init__(self, path):
516 self.stat = os.stat(path)
517 self.stat = os.stat(path)
517
518
518 def cacheable(self):
519 def cacheable(self):
519 return bool(self.stat.st_ino)
520 return bool(self.stat.st_ino)
520
521
521 __hash__ = object.__hash__
522 __hash__ = object.__hash__
522
523
523 def __eq__(self, other):
524 def __eq__(self, other):
524 try:
525 try:
525 # Only dev, ino, size, mtime and atime are likely to change. Out
526 # Only dev, ino, size, mtime and atime are likely to change. Out
526 # of these, we shouldn't compare atime but should compare the
527 # of these, we shouldn't compare atime but should compare the
527 # rest. However, one of the other fields changing indicates
528 # rest. However, one of the other fields changing indicates
528 # something fishy going on, so return False if anything but atime
529 # something fishy going on, so return False if anything but atime
529 # changes.
530 # changes.
530 return (self.stat.st_mode == other.stat.st_mode and
531 return (self.stat.st_mode == other.stat.st_mode and
531 self.stat.st_ino == other.stat.st_ino and
532 self.stat.st_ino == other.stat.st_ino and
532 self.stat.st_dev == other.stat.st_dev and
533 self.stat.st_dev == other.stat.st_dev and
533 self.stat.st_nlink == other.stat.st_nlink and
534 self.stat.st_nlink == other.stat.st_nlink and
534 self.stat.st_uid == other.stat.st_uid and
535 self.stat.st_uid == other.stat.st_uid and
535 self.stat.st_gid == other.stat.st_gid and
536 self.stat.st_gid == other.stat.st_gid and
536 self.stat.st_size == other.stat.st_size and
537 self.stat.st_size == other.stat.st_size and
537 self.stat.st_mtime == other.stat.st_mtime and
538 self.stat.st_mtime == other.stat.st_mtime and
538 self.stat.st_ctime == other.stat.st_ctime)
539 self.stat.st_ctime == other.stat.st_ctime)
539 except AttributeError:
540 except AttributeError:
540 return False
541 return False
541
542
542 def __ne__(self, other):
543 def __ne__(self, other):
543 return not self == other
544 return not self == other
544
545
545 def executablepath():
546 def executablepath():
546 return None # available on Windows only
547 return None # available on Windows only
547
548
548 class unixdomainserver(socket.socket):
549 class unixdomainserver(socket.socket):
549 def __init__(self, join, subsystem):
550 def __init__(self, join, subsystem):
550 '''Create a unix domain socket with the given prefix.'''
551 '''Create a unix domain socket with the given prefix.'''
551 super(unixdomainserver, self).__init__(socket.AF_UNIX)
552 super(unixdomainserver, self).__init__(socket.AF_UNIX)
552 sockname = subsystem + '.sock'
553 sockname = subsystem + '.sock'
553 self.realpath = self.path = join(sockname)
554 self.realpath = self.path = join(sockname)
554 if os.path.islink(self.path):
555 if os.path.islink(self.path):
555 if os.path.exists(self.path):
556 if os.path.exists(self.path):
556 self.realpath = os.readlink(self.path)
557 self.realpath = os.readlink(self.path)
557 else:
558 else:
558 os.unlink(self.path)
559 os.unlink(self.path)
559 try:
560 try:
560 self.bind(self.realpath)
561 self.bind(self.realpath)
561 except socket.error, err:
562 except socket.error, err:
562 if err.args[0] == 'AF_UNIX path too long':
563 if err.args[0] == 'AF_UNIX path too long':
563 tmpdir = tempfile.mkdtemp(prefix='hg-%s-' % subsystem)
564 tmpdir = tempfile.mkdtemp(prefix='hg-%s-' % subsystem)
564 self.realpath = os.path.join(tmpdir, sockname)
565 self.realpath = os.path.join(tmpdir, sockname)
565 try:
566 try:
566 self.bind(self.realpath)
567 self.bind(self.realpath)
567 os.symlink(self.realpath, self.path)
568 os.symlink(self.realpath, self.path)
568 except (OSError, socket.error):
569 except (OSError, socket.error):
569 self.cleanup()
570 self.cleanup()
570 raise
571 raise
571 else:
572 else:
572 raise
573 raise
573 self.listen(5)
574 self.listen(5)
574
575
575 def cleanup(self):
576 def cleanup(self):
576 def okayifmissing(f, path):
577 def okayifmissing(f, path):
577 try:
578 try:
578 f(path)
579 f(path)
579 except OSError, err:
580 except OSError, err:
580 if err.errno != errno.ENOENT:
581 if err.errno != errno.ENOENT:
581 raise
582 raise
582
583
583 okayifmissing(os.unlink, self.path)
584 okayifmissing(os.unlink, self.path)
584 if self.realpath != self.path:
585 if self.realpath != self.path:
585 okayifmissing(os.unlink, self.realpath)
586 okayifmissing(os.unlink, self.realpath)
586 okayifmissing(os.rmdir, os.path.dirname(self.realpath))
587 okayifmissing(os.rmdir, os.path.dirname(self.realpath))
587
588
588 def statislink(st):
589 def statislink(st):
589 '''check whether a stat result is a symlink'''
590 '''check whether a stat result is a symlink'''
590 return st and stat.S_ISLNK(st.st_mode)
591 return st and stat.S_ISLNK(st.st_mode)
591
592
592 def statisexec(st):
593 def statisexec(st):
593 '''check whether a stat result is an executable file'''
594 '''check whether a stat result is an executable file'''
594 return st and (st.st_mode & 0100 != 0)
595 return st and (st.st_mode & 0100 != 0)
595
596
596 def readpipe(pipe):
597 def readpipe(pipe):
597 """Read all available data from a pipe."""
598 """Read all available data from a pipe."""
598 # We can't fstat() a pipe because Linux will always report 0.
599 # We can't fstat() a pipe because Linux will always report 0.
599 # So, we set the pipe to non-blocking mode and read everything
600 # So, we set the pipe to non-blocking mode and read everything
600 # that's available.
601 # that's available.
601 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
602 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
602 flags |= os.O_NONBLOCK
603 flags |= os.O_NONBLOCK
603 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
604 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
604
605
605 try:
606 try:
606 chunks = []
607 chunks = []
607 while True:
608 while True:
608 try:
609 try:
609 s = pipe.read()
610 s = pipe.read()
610 if not s:
611 if not s:
611 break
612 break
612 chunks.append(s)
613 chunks.append(s)
613 except IOError:
614 except IOError:
614 break
615 break
615
616
616 return ''.join(chunks)
617 return ''.join(chunks)
617 finally:
618 finally:
618 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
619 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
@@ -1,2289 +1,2290
1 # util.py - Mercurial utility functions and platform specific implementations
1 # util.py - Mercurial utility functions and platform specific implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Mercurial utility functions and platform specific implementations.
10 """Mercurial utility functions and platform specific implementations.
11
11
12 This contains helper routines that are independent of the SCM core and
12 This contains helper routines that are independent of the SCM core and
13 hide platform-specific details from the core.
13 hide platform-specific details from the core.
14 """
14 """
15
15
16 import i18n
16 import i18n
17 _ = i18n._
17 _ = i18n._
18 import error, osutil, encoding, parsers
18 import error, osutil, encoding, parsers
19 import errno, shutil, sys, tempfile, traceback
19 import errno, shutil, sys, tempfile, traceback
20 import re as remod
20 import re as remod
21 import os, time, datetime, calendar, textwrap, signal, collections
21 import os, time, datetime, calendar, textwrap, signal, collections
22 import imp, socket, urllib, struct
22 import imp, socket, urllib, struct
23 import gc
23 import gc
24
24
25 if os.name == 'nt':
25 if os.name == 'nt':
26 import windows as platform
26 import windows as platform
27 else:
27 else:
28 import posix as platform
28 import posix as platform
29
29
30 cachestat = platform.cachestat
30 cachestat = platform.cachestat
31 checkexec = platform.checkexec
31 checkexec = platform.checkexec
32 checklink = platform.checklink
32 checklink = platform.checklink
33 copymode = platform.copymode
33 copymode = platform.copymode
34 executablepath = platform.executablepath
34 executablepath = platform.executablepath
35 expandglobs = platform.expandglobs
35 expandglobs = platform.expandglobs
36 explainexit = platform.explainexit
36 explainexit = platform.explainexit
37 findexe = platform.findexe
37 findexe = platform.findexe
38 gethgcmd = platform.gethgcmd
38 gethgcmd = platform.gethgcmd
39 getuser = platform.getuser
39 getuser = platform.getuser
40 groupmembers = platform.groupmembers
40 groupmembers = platform.groupmembers
41 groupname = platform.groupname
41 groupname = platform.groupname
42 hidewindow = platform.hidewindow
42 hidewindow = platform.hidewindow
43 isexec = platform.isexec
43 isexec = platform.isexec
44 isowner = platform.isowner
44 isowner = platform.isowner
45 localpath = platform.localpath
45 localpath = platform.localpath
46 lookupreg = platform.lookupreg
46 lookupreg = platform.lookupreg
47 makedir = platform.makedir
47 makedir = platform.makedir
48 nlinks = platform.nlinks
48 nlinks = platform.nlinks
49 normpath = platform.normpath
49 normpath = platform.normpath
50 normcase = platform.normcase
50 normcase = platform.normcase
51 normcasespec = platform.normcasespec
51 normcasespec = platform.normcasespec
52 normcasefallback = platform.normcasefallback
52 normcasefallback = platform.normcasefallback
53 openhardlinks = platform.openhardlinks
53 openhardlinks = platform.openhardlinks
54 oslink = platform.oslink
54 oslink = platform.oslink
55 parsepatchoutput = platform.parsepatchoutput
55 parsepatchoutput = platform.parsepatchoutput
56 pconvert = platform.pconvert
56 pconvert = platform.pconvert
57 popen = platform.popen
57 popen = platform.popen
58 posixfile = platform.posixfile
58 posixfile = platform.posixfile
59 quotecommand = platform.quotecommand
59 quotecommand = platform.quotecommand
60 readpipe = platform.readpipe
60 readpipe = platform.readpipe
61 rename = platform.rename
61 rename = platform.rename
62 removedirs = platform.removedirs
62 samedevice = platform.samedevice
63 samedevice = platform.samedevice
63 samefile = platform.samefile
64 samefile = platform.samefile
64 samestat = platform.samestat
65 samestat = platform.samestat
65 setbinary = platform.setbinary
66 setbinary = platform.setbinary
66 setflags = platform.setflags
67 setflags = platform.setflags
67 setsignalhandler = platform.setsignalhandler
68 setsignalhandler = platform.setsignalhandler
68 shellquote = platform.shellquote
69 shellquote = platform.shellquote
69 spawndetached = platform.spawndetached
70 spawndetached = platform.spawndetached
70 split = platform.split
71 split = platform.split
71 sshargs = platform.sshargs
72 sshargs = platform.sshargs
72 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
73 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
73 statisexec = platform.statisexec
74 statisexec = platform.statisexec
74 statislink = platform.statislink
75 statislink = platform.statislink
75 termwidth = platform.termwidth
76 termwidth = platform.termwidth
76 testpid = platform.testpid
77 testpid = platform.testpid
77 umask = platform.umask
78 umask = platform.umask
78 unlink = platform.unlink
79 unlink = platform.unlink
79 unlinkpath = platform.unlinkpath
80 unlinkpath = platform.unlinkpath
80 username = platform.username
81 username = platform.username
81
82
82 # Python compatibility
83 # Python compatibility
83
84
84 _notset = object()
85 _notset = object()
85
86
86 def safehasattr(thing, attr):
87 def safehasattr(thing, attr):
87 return getattr(thing, attr, _notset) is not _notset
88 return getattr(thing, attr, _notset) is not _notset
88
89
89 def sha1(s=''):
90 def sha1(s=''):
90 '''
91 '''
91 Low-overhead wrapper around Python's SHA support
92 Low-overhead wrapper around Python's SHA support
92
93
93 >>> f = _fastsha1
94 >>> f = _fastsha1
94 >>> a = sha1()
95 >>> a = sha1()
95 >>> a = f()
96 >>> a = f()
96 >>> a.hexdigest()
97 >>> a.hexdigest()
97 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
98 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
98 '''
99 '''
99
100
100 return _fastsha1(s)
101 return _fastsha1(s)
101
102
102 def _fastsha1(s=''):
103 def _fastsha1(s=''):
103 # This function will import sha1 from hashlib or sha (whichever is
104 # This function will import sha1 from hashlib or sha (whichever is
104 # available) and overwrite itself with it on the first call.
105 # available) and overwrite itself with it on the first call.
105 # Subsequent calls will go directly to the imported function.
106 # Subsequent calls will go directly to the imported function.
106 if sys.version_info >= (2, 5):
107 if sys.version_info >= (2, 5):
107 from hashlib import sha1 as _sha1
108 from hashlib import sha1 as _sha1
108 else:
109 else:
109 from sha import sha as _sha1
110 from sha import sha as _sha1
110 global _fastsha1, sha1
111 global _fastsha1, sha1
111 _fastsha1 = sha1 = _sha1
112 _fastsha1 = sha1 = _sha1
112 return _sha1(s)
113 return _sha1(s)
113
114
114 def md5(s=''):
115 def md5(s=''):
115 try:
116 try:
116 from hashlib import md5 as _md5
117 from hashlib import md5 as _md5
117 except ImportError:
118 except ImportError:
118 from md5 import md5 as _md5
119 from md5 import md5 as _md5
119 global md5
120 global md5
120 md5 = _md5
121 md5 = _md5
121 return _md5(s)
122 return _md5(s)
122
123
123 DIGESTS = {
124 DIGESTS = {
124 'md5': md5,
125 'md5': md5,
125 'sha1': sha1,
126 'sha1': sha1,
126 }
127 }
127 # List of digest types from strongest to weakest
128 # List of digest types from strongest to weakest
128 DIGESTS_BY_STRENGTH = ['sha1', 'md5']
129 DIGESTS_BY_STRENGTH = ['sha1', 'md5']
129
130
130 try:
131 try:
131 import hashlib
132 import hashlib
132 DIGESTS.update({
133 DIGESTS.update({
133 'sha512': hashlib.sha512,
134 'sha512': hashlib.sha512,
134 })
135 })
135 DIGESTS_BY_STRENGTH.insert(0, 'sha512')
136 DIGESTS_BY_STRENGTH.insert(0, 'sha512')
136 except ImportError:
137 except ImportError:
137 pass
138 pass
138
139
139 for k in DIGESTS_BY_STRENGTH:
140 for k in DIGESTS_BY_STRENGTH:
140 assert k in DIGESTS
141 assert k in DIGESTS
141
142
142 class digester(object):
143 class digester(object):
143 """helper to compute digests.
144 """helper to compute digests.
144
145
145 This helper can be used to compute one or more digests given their name.
146 This helper can be used to compute one or more digests given their name.
146
147
147 >>> d = digester(['md5', 'sha1'])
148 >>> d = digester(['md5', 'sha1'])
148 >>> d.update('foo')
149 >>> d.update('foo')
149 >>> [k for k in sorted(d)]
150 >>> [k for k in sorted(d)]
150 ['md5', 'sha1']
151 ['md5', 'sha1']
151 >>> d['md5']
152 >>> d['md5']
152 'acbd18db4cc2f85cedef654fccc4a4d8'
153 'acbd18db4cc2f85cedef654fccc4a4d8'
153 >>> d['sha1']
154 >>> d['sha1']
154 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
155 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
155 >>> digester.preferred(['md5', 'sha1'])
156 >>> digester.preferred(['md5', 'sha1'])
156 'sha1'
157 'sha1'
157 """
158 """
158
159
159 def __init__(self, digests, s=''):
160 def __init__(self, digests, s=''):
160 self._hashes = {}
161 self._hashes = {}
161 for k in digests:
162 for k in digests:
162 if k not in DIGESTS:
163 if k not in DIGESTS:
163 raise Abort(_('unknown digest type: %s') % k)
164 raise Abort(_('unknown digest type: %s') % k)
164 self._hashes[k] = DIGESTS[k]()
165 self._hashes[k] = DIGESTS[k]()
165 if s:
166 if s:
166 self.update(s)
167 self.update(s)
167
168
168 def update(self, data):
169 def update(self, data):
169 for h in self._hashes.values():
170 for h in self._hashes.values():
170 h.update(data)
171 h.update(data)
171
172
172 def __getitem__(self, key):
173 def __getitem__(self, key):
173 if key not in DIGESTS:
174 if key not in DIGESTS:
174 raise Abort(_('unknown digest type: %s') % k)
175 raise Abort(_('unknown digest type: %s') % k)
175 return self._hashes[key].hexdigest()
176 return self._hashes[key].hexdigest()
176
177
177 def __iter__(self):
178 def __iter__(self):
178 return iter(self._hashes)
179 return iter(self._hashes)
179
180
180 @staticmethod
181 @staticmethod
181 def preferred(supported):
182 def preferred(supported):
182 """returns the strongest digest type in both supported and DIGESTS."""
183 """returns the strongest digest type in both supported and DIGESTS."""
183
184
184 for k in DIGESTS_BY_STRENGTH:
185 for k in DIGESTS_BY_STRENGTH:
185 if k in supported:
186 if k in supported:
186 return k
187 return k
187 return None
188 return None
188
189
189 class digestchecker(object):
190 class digestchecker(object):
190 """file handle wrapper that additionally checks content against a given
191 """file handle wrapper that additionally checks content against a given
191 size and digests.
192 size and digests.
192
193
193 d = digestchecker(fh, size, {'md5': '...'})
194 d = digestchecker(fh, size, {'md5': '...'})
194
195
195 When multiple digests are given, all of them are validated.
196 When multiple digests are given, all of them are validated.
196 """
197 """
197
198
198 def __init__(self, fh, size, digests):
199 def __init__(self, fh, size, digests):
199 self._fh = fh
200 self._fh = fh
200 self._size = size
201 self._size = size
201 self._got = 0
202 self._got = 0
202 self._digests = dict(digests)
203 self._digests = dict(digests)
203 self._digester = digester(self._digests.keys())
204 self._digester = digester(self._digests.keys())
204
205
205 def read(self, length=-1):
206 def read(self, length=-1):
206 content = self._fh.read(length)
207 content = self._fh.read(length)
207 self._digester.update(content)
208 self._digester.update(content)
208 self._got += len(content)
209 self._got += len(content)
209 return content
210 return content
210
211
211 def validate(self):
212 def validate(self):
212 if self._size != self._got:
213 if self._size != self._got:
213 raise Abort(_('size mismatch: expected %d, got %d') %
214 raise Abort(_('size mismatch: expected %d, got %d') %
214 (self._size, self._got))
215 (self._size, self._got))
215 for k, v in self._digests.items():
216 for k, v in self._digests.items():
216 if v != self._digester[k]:
217 if v != self._digester[k]:
217 # i18n: first parameter is a digest name
218 # i18n: first parameter is a digest name
218 raise Abort(_('%s mismatch: expected %s, got %s') %
219 raise Abort(_('%s mismatch: expected %s, got %s') %
219 (k, v, self._digester[k]))
220 (k, v, self._digester[k]))
220
221
221 try:
222 try:
222 buffer = buffer
223 buffer = buffer
223 except NameError:
224 except NameError:
224 if sys.version_info[0] < 3:
225 if sys.version_info[0] < 3:
225 def buffer(sliceable, offset=0):
226 def buffer(sliceable, offset=0):
226 return sliceable[offset:]
227 return sliceable[offset:]
227 else:
228 else:
228 def buffer(sliceable, offset=0):
229 def buffer(sliceable, offset=0):
229 return memoryview(sliceable)[offset:]
230 return memoryview(sliceable)[offset:]
230
231
231 import subprocess
232 import subprocess
232 closefds = os.name == 'posix'
233 closefds = os.name == 'posix'
233
234
234 def unpacker(fmt):
235 def unpacker(fmt):
235 """create a struct unpacker for the specified format"""
236 """create a struct unpacker for the specified format"""
236 try:
237 try:
237 # 2.5+
238 # 2.5+
238 return struct.Struct(fmt).unpack
239 return struct.Struct(fmt).unpack
239 except AttributeError:
240 except AttributeError:
240 # 2.4
241 # 2.4
241 return lambda buf: struct.unpack(fmt, buf)
242 return lambda buf: struct.unpack(fmt, buf)
242
243
243 def popen2(cmd, env=None, newlines=False):
244 def popen2(cmd, env=None, newlines=False):
244 # Setting bufsize to -1 lets the system decide the buffer size.
245 # Setting bufsize to -1 lets the system decide the buffer size.
245 # The default for bufsize is 0, meaning unbuffered. This leads to
246 # The default for bufsize is 0, meaning unbuffered. This leads to
246 # poor performance on Mac OS X: http://bugs.python.org/issue4194
247 # poor performance on Mac OS X: http://bugs.python.org/issue4194
247 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
248 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
248 close_fds=closefds,
249 close_fds=closefds,
249 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
250 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
250 universal_newlines=newlines,
251 universal_newlines=newlines,
251 env=env)
252 env=env)
252 return p.stdin, p.stdout
253 return p.stdin, p.stdout
253
254
254 def popen3(cmd, env=None, newlines=False):
255 def popen3(cmd, env=None, newlines=False):
255 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
256 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
256 return stdin, stdout, stderr
257 return stdin, stdout, stderr
257
258
258 def popen4(cmd, env=None, newlines=False):
259 def popen4(cmd, env=None, newlines=False):
259 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
260 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
260 close_fds=closefds,
261 close_fds=closefds,
261 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
262 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
262 stderr=subprocess.PIPE,
263 stderr=subprocess.PIPE,
263 universal_newlines=newlines,
264 universal_newlines=newlines,
264 env=env)
265 env=env)
265 return p.stdin, p.stdout, p.stderr, p
266 return p.stdin, p.stdout, p.stderr, p
266
267
267 def version():
268 def version():
268 """Return version information if available."""
269 """Return version information if available."""
269 try:
270 try:
270 import __version__
271 import __version__
271 return __version__.version
272 return __version__.version
272 except ImportError:
273 except ImportError:
273 return 'unknown'
274 return 'unknown'
274
275
275 # used by parsedate
276 # used by parsedate
276 defaultdateformats = (
277 defaultdateformats = (
277 '%Y-%m-%d %H:%M:%S',
278 '%Y-%m-%d %H:%M:%S',
278 '%Y-%m-%d %I:%M:%S%p',
279 '%Y-%m-%d %I:%M:%S%p',
279 '%Y-%m-%d %H:%M',
280 '%Y-%m-%d %H:%M',
280 '%Y-%m-%d %I:%M%p',
281 '%Y-%m-%d %I:%M%p',
281 '%Y-%m-%d',
282 '%Y-%m-%d',
282 '%m-%d',
283 '%m-%d',
283 '%m/%d',
284 '%m/%d',
284 '%m/%d/%y',
285 '%m/%d/%y',
285 '%m/%d/%Y',
286 '%m/%d/%Y',
286 '%a %b %d %H:%M:%S %Y',
287 '%a %b %d %H:%M:%S %Y',
287 '%a %b %d %I:%M:%S%p %Y',
288 '%a %b %d %I:%M:%S%p %Y',
288 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
289 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
289 '%b %d %H:%M:%S %Y',
290 '%b %d %H:%M:%S %Y',
290 '%b %d %I:%M:%S%p %Y',
291 '%b %d %I:%M:%S%p %Y',
291 '%b %d %H:%M:%S',
292 '%b %d %H:%M:%S',
292 '%b %d %I:%M:%S%p',
293 '%b %d %I:%M:%S%p',
293 '%b %d %H:%M',
294 '%b %d %H:%M',
294 '%b %d %I:%M%p',
295 '%b %d %I:%M%p',
295 '%b %d %Y',
296 '%b %d %Y',
296 '%b %d',
297 '%b %d',
297 '%H:%M:%S',
298 '%H:%M:%S',
298 '%I:%M:%S%p',
299 '%I:%M:%S%p',
299 '%H:%M',
300 '%H:%M',
300 '%I:%M%p',
301 '%I:%M%p',
301 )
302 )
302
303
303 extendeddateformats = defaultdateformats + (
304 extendeddateformats = defaultdateformats + (
304 "%Y",
305 "%Y",
305 "%Y-%m",
306 "%Y-%m",
306 "%b",
307 "%b",
307 "%b %Y",
308 "%b %Y",
308 )
309 )
309
310
310 def cachefunc(func):
311 def cachefunc(func):
311 '''cache the result of function calls'''
312 '''cache the result of function calls'''
312 # XXX doesn't handle keywords args
313 # XXX doesn't handle keywords args
313 if func.func_code.co_argcount == 0:
314 if func.func_code.co_argcount == 0:
314 cache = []
315 cache = []
315 def f():
316 def f():
316 if len(cache) == 0:
317 if len(cache) == 0:
317 cache.append(func())
318 cache.append(func())
318 return cache[0]
319 return cache[0]
319 return f
320 return f
320 cache = {}
321 cache = {}
321 if func.func_code.co_argcount == 1:
322 if func.func_code.co_argcount == 1:
322 # we gain a small amount of time because
323 # we gain a small amount of time because
323 # we don't need to pack/unpack the list
324 # we don't need to pack/unpack the list
324 def f(arg):
325 def f(arg):
325 if arg not in cache:
326 if arg not in cache:
326 cache[arg] = func(arg)
327 cache[arg] = func(arg)
327 return cache[arg]
328 return cache[arg]
328 else:
329 else:
329 def f(*args):
330 def f(*args):
330 if args not in cache:
331 if args not in cache:
331 cache[args] = func(*args)
332 cache[args] = func(*args)
332 return cache[args]
333 return cache[args]
333
334
334 return f
335 return f
335
336
336 try:
337 try:
337 collections.deque.remove
338 collections.deque.remove
338 deque = collections.deque
339 deque = collections.deque
339 except AttributeError:
340 except AttributeError:
340 # python 2.4 lacks deque.remove
341 # python 2.4 lacks deque.remove
341 class deque(collections.deque):
342 class deque(collections.deque):
342 def remove(self, val):
343 def remove(self, val):
343 for i, v in enumerate(self):
344 for i, v in enumerate(self):
344 if v == val:
345 if v == val:
345 del self[i]
346 del self[i]
346 break
347 break
347
348
348 class sortdict(dict):
349 class sortdict(dict):
349 '''a simple sorted dictionary'''
350 '''a simple sorted dictionary'''
350 def __init__(self, data=None):
351 def __init__(self, data=None):
351 self._list = []
352 self._list = []
352 if data:
353 if data:
353 self.update(data)
354 self.update(data)
354 def copy(self):
355 def copy(self):
355 return sortdict(self)
356 return sortdict(self)
356 def __setitem__(self, key, val):
357 def __setitem__(self, key, val):
357 if key in self:
358 if key in self:
358 self._list.remove(key)
359 self._list.remove(key)
359 self._list.append(key)
360 self._list.append(key)
360 dict.__setitem__(self, key, val)
361 dict.__setitem__(self, key, val)
361 def __iter__(self):
362 def __iter__(self):
362 return self._list.__iter__()
363 return self._list.__iter__()
363 def update(self, src):
364 def update(self, src):
364 if isinstance(src, dict):
365 if isinstance(src, dict):
365 src = src.iteritems()
366 src = src.iteritems()
366 for k, v in src:
367 for k, v in src:
367 self[k] = v
368 self[k] = v
368 def clear(self):
369 def clear(self):
369 dict.clear(self)
370 dict.clear(self)
370 self._list = []
371 self._list = []
371 def items(self):
372 def items(self):
372 return [(k, self[k]) for k in self._list]
373 return [(k, self[k]) for k in self._list]
373 def __delitem__(self, key):
374 def __delitem__(self, key):
374 dict.__delitem__(self, key)
375 dict.__delitem__(self, key)
375 self._list.remove(key)
376 self._list.remove(key)
376 def pop(self, key, *args, **kwargs):
377 def pop(self, key, *args, **kwargs):
377 dict.pop(self, key, *args, **kwargs)
378 dict.pop(self, key, *args, **kwargs)
378 try:
379 try:
379 self._list.remove(key)
380 self._list.remove(key)
380 except ValueError:
381 except ValueError:
381 pass
382 pass
382 def keys(self):
383 def keys(self):
383 return self._list
384 return self._list
384 def iterkeys(self):
385 def iterkeys(self):
385 return self._list.__iter__()
386 return self._list.__iter__()
386 def iteritems(self):
387 def iteritems(self):
387 for k in self._list:
388 for k in self._list:
388 yield k, self[k]
389 yield k, self[k]
389 def insert(self, index, key, val):
390 def insert(self, index, key, val):
390 self._list.insert(index, key)
391 self._list.insert(index, key)
391 dict.__setitem__(self, key, val)
392 dict.__setitem__(self, key, val)
392
393
393 class lrucachedict(object):
394 class lrucachedict(object):
394 '''cache most recent gets from or sets to this dictionary'''
395 '''cache most recent gets from or sets to this dictionary'''
395 def __init__(self, maxsize):
396 def __init__(self, maxsize):
396 self._cache = {}
397 self._cache = {}
397 self._maxsize = maxsize
398 self._maxsize = maxsize
398 self._order = deque()
399 self._order = deque()
399
400
400 def __getitem__(self, key):
401 def __getitem__(self, key):
401 value = self._cache[key]
402 value = self._cache[key]
402 self._order.remove(key)
403 self._order.remove(key)
403 self._order.append(key)
404 self._order.append(key)
404 return value
405 return value
405
406
406 def __setitem__(self, key, value):
407 def __setitem__(self, key, value):
407 if key not in self._cache:
408 if key not in self._cache:
408 if len(self._cache) >= self._maxsize:
409 if len(self._cache) >= self._maxsize:
409 del self._cache[self._order.popleft()]
410 del self._cache[self._order.popleft()]
410 else:
411 else:
411 self._order.remove(key)
412 self._order.remove(key)
412 self._cache[key] = value
413 self._cache[key] = value
413 self._order.append(key)
414 self._order.append(key)
414
415
415 def __contains__(self, key):
416 def __contains__(self, key):
416 return key in self._cache
417 return key in self._cache
417
418
418 def clear(self):
419 def clear(self):
419 self._cache.clear()
420 self._cache.clear()
420 self._order = deque()
421 self._order = deque()
421
422
422 def lrucachefunc(func):
423 def lrucachefunc(func):
423 '''cache most recent results of function calls'''
424 '''cache most recent results of function calls'''
424 cache = {}
425 cache = {}
425 order = deque()
426 order = deque()
426 if func.func_code.co_argcount == 1:
427 if func.func_code.co_argcount == 1:
427 def f(arg):
428 def f(arg):
428 if arg not in cache:
429 if arg not in cache:
429 if len(cache) > 20:
430 if len(cache) > 20:
430 del cache[order.popleft()]
431 del cache[order.popleft()]
431 cache[arg] = func(arg)
432 cache[arg] = func(arg)
432 else:
433 else:
433 order.remove(arg)
434 order.remove(arg)
434 order.append(arg)
435 order.append(arg)
435 return cache[arg]
436 return cache[arg]
436 else:
437 else:
437 def f(*args):
438 def f(*args):
438 if args not in cache:
439 if args not in cache:
439 if len(cache) > 20:
440 if len(cache) > 20:
440 del cache[order.popleft()]
441 del cache[order.popleft()]
441 cache[args] = func(*args)
442 cache[args] = func(*args)
442 else:
443 else:
443 order.remove(args)
444 order.remove(args)
444 order.append(args)
445 order.append(args)
445 return cache[args]
446 return cache[args]
446
447
447 return f
448 return f
448
449
449 class propertycache(object):
450 class propertycache(object):
450 def __init__(self, func):
451 def __init__(self, func):
451 self.func = func
452 self.func = func
452 self.name = func.__name__
453 self.name = func.__name__
453 def __get__(self, obj, type=None):
454 def __get__(self, obj, type=None):
454 result = self.func(obj)
455 result = self.func(obj)
455 self.cachevalue(obj, result)
456 self.cachevalue(obj, result)
456 return result
457 return result
457
458
458 def cachevalue(self, obj, value):
459 def cachevalue(self, obj, value):
459 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
460 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
460 obj.__dict__[self.name] = value
461 obj.__dict__[self.name] = value
461
462
462 def pipefilter(s, cmd):
463 def pipefilter(s, cmd):
463 '''filter string S through command CMD, returning its output'''
464 '''filter string S through command CMD, returning its output'''
464 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
465 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
465 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
466 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
466 pout, perr = p.communicate(s)
467 pout, perr = p.communicate(s)
467 return pout
468 return pout
468
469
469 def tempfilter(s, cmd):
470 def tempfilter(s, cmd):
470 '''filter string S through a pair of temporary files with CMD.
471 '''filter string S through a pair of temporary files with CMD.
471 CMD is used as a template to create the real command to be run,
472 CMD is used as a template to create the real command to be run,
472 with the strings INFILE and OUTFILE replaced by the real names of
473 with the strings INFILE and OUTFILE replaced by the real names of
473 the temporary files generated.'''
474 the temporary files generated.'''
474 inname, outname = None, None
475 inname, outname = None, None
475 try:
476 try:
476 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
477 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
477 fp = os.fdopen(infd, 'wb')
478 fp = os.fdopen(infd, 'wb')
478 fp.write(s)
479 fp.write(s)
479 fp.close()
480 fp.close()
480 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
481 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
481 os.close(outfd)
482 os.close(outfd)
482 cmd = cmd.replace('INFILE', inname)
483 cmd = cmd.replace('INFILE', inname)
483 cmd = cmd.replace('OUTFILE', outname)
484 cmd = cmd.replace('OUTFILE', outname)
484 code = os.system(cmd)
485 code = os.system(cmd)
485 if sys.platform == 'OpenVMS' and code & 1:
486 if sys.platform == 'OpenVMS' and code & 1:
486 code = 0
487 code = 0
487 if code:
488 if code:
488 raise Abort(_("command '%s' failed: %s") %
489 raise Abort(_("command '%s' failed: %s") %
489 (cmd, explainexit(code)))
490 (cmd, explainexit(code)))
490 fp = open(outname, 'rb')
491 fp = open(outname, 'rb')
491 r = fp.read()
492 r = fp.read()
492 fp.close()
493 fp.close()
493 return r
494 return r
494 finally:
495 finally:
495 try:
496 try:
496 if inname:
497 if inname:
497 os.unlink(inname)
498 os.unlink(inname)
498 except OSError:
499 except OSError:
499 pass
500 pass
500 try:
501 try:
501 if outname:
502 if outname:
502 os.unlink(outname)
503 os.unlink(outname)
503 except OSError:
504 except OSError:
504 pass
505 pass
505
506
506 filtertable = {
507 filtertable = {
507 'tempfile:': tempfilter,
508 'tempfile:': tempfilter,
508 'pipe:': pipefilter,
509 'pipe:': pipefilter,
509 }
510 }
510
511
511 def filter(s, cmd):
512 def filter(s, cmd):
512 "filter a string through a command that transforms its input to its output"
513 "filter a string through a command that transforms its input to its output"
513 for name, fn in filtertable.iteritems():
514 for name, fn in filtertable.iteritems():
514 if cmd.startswith(name):
515 if cmd.startswith(name):
515 return fn(s, cmd[len(name):].lstrip())
516 return fn(s, cmd[len(name):].lstrip())
516 return pipefilter(s, cmd)
517 return pipefilter(s, cmd)
517
518
518 def binary(s):
519 def binary(s):
519 """return true if a string is binary data"""
520 """return true if a string is binary data"""
520 return bool(s and '\0' in s)
521 return bool(s and '\0' in s)
521
522
522 def increasingchunks(source, min=1024, max=65536):
523 def increasingchunks(source, min=1024, max=65536):
523 '''return no less than min bytes per chunk while data remains,
524 '''return no less than min bytes per chunk while data remains,
524 doubling min after each chunk until it reaches max'''
525 doubling min after each chunk until it reaches max'''
525 def log2(x):
526 def log2(x):
526 if not x:
527 if not x:
527 return 0
528 return 0
528 i = 0
529 i = 0
529 while x:
530 while x:
530 x >>= 1
531 x >>= 1
531 i += 1
532 i += 1
532 return i - 1
533 return i - 1
533
534
534 buf = []
535 buf = []
535 blen = 0
536 blen = 0
536 for chunk in source:
537 for chunk in source:
537 buf.append(chunk)
538 buf.append(chunk)
538 blen += len(chunk)
539 blen += len(chunk)
539 if blen >= min:
540 if blen >= min:
540 if min < max:
541 if min < max:
541 min = min << 1
542 min = min << 1
542 nmin = 1 << log2(blen)
543 nmin = 1 << log2(blen)
543 if nmin > min:
544 if nmin > min:
544 min = nmin
545 min = nmin
545 if min > max:
546 if min > max:
546 min = max
547 min = max
547 yield ''.join(buf)
548 yield ''.join(buf)
548 blen = 0
549 blen = 0
549 buf = []
550 buf = []
550 if buf:
551 if buf:
551 yield ''.join(buf)
552 yield ''.join(buf)
552
553
553 Abort = error.Abort
554 Abort = error.Abort
554
555
555 def always(fn):
556 def always(fn):
556 return True
557 return True
557
558
558 def never(fn):
559 def never(fn):
559 return False
560 return False
560
561
561 def nogc(func):
562 def nogc(func):
562 """disable garbage collector
563 """disable garbage collector
563
564
564 Python's garbage collector triggers a GC each time a certain number of
565 Python's garbage collector triggers a GC each time a certain number of
565 container objects (the number being defined by gc.get_threshold()) are
566 container objects (the number being defined by gc.get_threshold()) are
566 allocated even when marked not to be tracked by the collector. Tracking has
567 allocated even when marked not to be tracked by the collector. Tracking has
567 no effect on when GCs are triggered, only on what objects the GC looks
568 no effect on when GCs are triggered, only on what objects the GC looks
568 into. As a workaround, disable GC while building complex (huge)
569 into. As a workaround, disable GC while building complex (huge)
569 containers.
570 containers.
570
571
571 This garbage collector issue have been fixed in 2.7.
572 This garbage collector issue have been fixed in 2.7.
572 """
573 """
573 def wrapper(*args, **kwargs):
574 def wrapper(*args, **kwargs):
574 gcenabled = gc.isenabled()
575 gcenabled = gc.isenabled()
575 gc.disable()
576 gc.disable()
576 try:
577 try:
577 return func(*args, **kwargs)
578 return func(*args, **kwargs)
578 finally:
579 finally:
579 if gcenabled:
580 if gcenabled:
580 gc.enable()
581 gc.enable()
581 return wrapper
582 return wrapper
582
583
583 def pathto(root, n1, n2):
584 def pathto(root, n1, n2):
584 '''return the relative path from one place to another.
585 '''return the relative path from one place to another.
585 root should use os.sep to separate directories
586 root should use os.sep to separate directories
586 n1 should use os.sep to separate directories
587 n1 should use os.sep to separate directories
587 n2 should use "/" to separate directories
588 n2 should use "/" to separate directories
588 returns an os.sep-separated path.
589 returns an os.sep-separated path.
589
590
590 If n1 is a relative path, it's assumed it's
591 If n1 is a relative path, it's assumed it's
591 relative to root.
592 relative to root.
592 n2 should always be relative to root.
593 n2 should always be relative to root.
593 '''
594 '''
594 if not n1:
595 if not n1:
595 return localpath(n2)
596 return localpath(n2)
596 if os.path.isabs(n1):
597 if os.path.isabs(n1):
597 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
598 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
598 return os.path.join(root, localpath(n2))
599 return os.path.join(root, localpath(n2))
599 n2 = '/'.join((pconvert(root), n2))
600 n2 = '/'.join((pconvert(root), n2))
600 a, b = splitpath(n1), n2.split('/')
601 a, b = splitpath(n1), n2.split('/')
601 a.reverse()
602 a.reverse()
602 b.reverse()
603 b.reverse()
603 while a and b and a[-1] == b[-1]:
604 while a and b and a[-1] == b[-1]:
604 a.pop()
605 a.pop()
605 b.pop()
606 b.pop()
606 b.reverse()
607 b.reverse()
607 return os.sep.join((['..'] * len(a)) + b) or '.'
608 return os.sep.join((['..'] * len(a)) + b) or '.'
608
609
609 def mainfrozen():
610 def mainfrozen():
610 """return True if we are a frozen executable.
611 """return True if we are a frozen executable.
611
612
612 The code supports py2exe (most common, Windows only) and tools/freeze
613 The code supports py2exe (most common, Windows only) and tools/freeze
613 (portable, not much used).
614 (portable, not much used).
614 """
615 """
615 return (safehasattr(sys, "frozen") or # new py2exe
616 return (safehasattr(sys, "frozen") or # new py2exe
616 safehasattr(sys, "importers") or # old py2exe
617 safehasattr(sys, "importers") or # old py2exe
617 imp.is_frozen("__main__")) # tools/freeze
618 imp.is_frozen("__main__")) # tools/freeze
618
619
619 # the location of data files matching the source code
620 # the location of data files matching the source code
620 if mainfrozen():
621 if mainfrozen():
621 # executable version (py2exe) doesn't support __file__
622 # executable version (py2exe) doesn't support __file__
622 datapath = os.path.dirname(sys.executable)
623 datapath = os.path.dirname(sys.executable)
623 else:
624 else:
624 datapath = os.path.dirname(__file__)
625 datapath = os.path.dirname(__file__)
625
626
626 i18n.setdatapath(datapath)
627 i18n.setdatapath(datapath)
627
628
628 _hgexecutable = None
629 _hgexecutable = None
629
630
630 def hgexecutable():
631 def hgexecutable():
631 """return location of the 'hg' executable.
632 """return location of the 'hg' executable.
632
633
633 Defaults to $HG or 'hg' in the search path.
634 Defaults to $HG or 'hg' in the search path.
634 """
635 """
635 if _hgexecutable is None:
636 if _hgexecutable is None:
636 hg = os.environ.get('HG')
637 hg = os.environ.get('HG')
637 mainmod = sys.modules['__main__']
638 mainmod = sys.modules['__main__']
638 if hg:
639 if hg:
639 _sethgexecutable(hg)
640 _sethgexecutable(hg)
640 elif mainfrozen():
641 elif mainfrozen():
641 _sethgexecutable(sys.executable)
642 _sethgexecutable(sys.executable)
642 elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
643 elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
643 _sethgexecutable(mainmod.__file__)
644 _sethgexecutable(mainmod.__file__)
644 else:
645 else:
645 exe = findexe('hg') or os.path.basename(sys.argv[0])
646 exe = findexe('hg') or os.path.basename(sys.argv[0])
646 _sethgexecutable(exe)
647 _sethgexecutable(exe)
647 return _hgexecutable
648 return _hgexecutable
648
649
649 def _sethgexecutable(path):
650 def _sethgexecutable(path):
650 """set location of the 'hg' executable"""
651 """set location of the 'hg' executable"""
651 global _hgexecutable
652 global _hgexecutable
652 _hgexecutable = path
653 _hgexecutable = path
653
654
654 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
655 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
655 '''enhanced shell command execution.
656 '''enhanced shell command execution.
656 run with environment maybe modified, maybe in different dir.
657 run with environment maybe modified, maybe in different dir.
657
658
658 if command fails and onerr is None, return status, else raise onerr
659 if command fails and onerr is None, return status, else raise onerr
659 object as exception.
660 object as exception.
660
661
661 if out is specified, it is assumed to be a file-like object that has a
662 if out is specified, it is assumed to be a file-like object that has a
662 write() method. stdout and stderr will be redirected to out.'''
663 write() method. stdout and stderr will be redirected to out.'''
663 try:
664 try:
664 sys.stdout.flush()
665 sys.stdout.flush()
665 except Exception:
666 except Exception:
666 pass
667 pass
667 def py2shell(val):
668 def py2shell(val):
668 'convert python object into string that is useful to shell'
669 'convert python object into string that is useful to shell'
669 if val is None or val is False:
670 if val is None or val is False:
670 return '0'
671 return '0'
671 if val is True:
672 if val is True:
672 return '1'
673 return '1'
673 return str(val)
674 return str(val)
674 origcmd = cmd
675 origcmd = cmd
675 cmd = quotecommand(cmd)
676 cmd = quotecommand(cmd)
676 if sys.platform == 'plan9' and (sys.version_info[0] == 2
677 if sys.platform == 'plan9' and (sys.version_info[0] == 2
677 and sys.version_info[1] < 7):
678 and sys.version_info[1] < 7):
678 # subprocess kludge to work around issues in half-baked Python
679 # subprocess kludge to work around issues in half-baked Python
679 # ports, notably bichued/python:
680 # ports, notably bichued/python:
680 if not cwd is None:
681 if not cwd is None:
681 os.chdir(cwd)
682 os.chdir(cwd)
682 rc = os.system(cmd)
683 rc = os.system(cmd)
683 else:
684 else:
684 env = dict(os.environ)
685 env = dict(os.environ)
685 env.update((k, py2shell(v)) for k, v in environ.iteritems())
686 env.update((k, py2shell(v)) for k, v in environ.iteritems())
686 env['HG'] = hgexecutable()
687 env['HG'] = hgexecutable()
687 if out is None or out == sys.__stdout__:
688 if out is None or out == sys.__stdout__:
688 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
689 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
689 env=env, cwd=cwd)
690 env=env, cwd=cwd)
690 else:
691 else:
691 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
692 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
692 env=env, cwd=cwd, stdout=subprocess.PIPE,
693 env=env, cwd=cwd, stdout=subprocess.PIPE,
693 stderr=subprocess.STDOUT)
694 stderr=subprocess.STDOUT)
694 while True:
695 while True:
695 line = proc.stdout.readline()
696 line = proc.stdout.readline()
696 if not line:
697 if not line:
697 break
698 break
698 out.write(line)
699 out.write(line)
699 proc.wait()
700 proc.wait()
700 rc = proc.returncode
701 rc = proc.returncode
701 if sys.platform == 'OpenVMS' and rc & 1:
702 if sys.platform == 'OpenVMS' and rc & 1:
702 rc = 0
703 rc = 0
703 if rc and onerr:
704 if rc and onerr:
704 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
705 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
705 explainexit(rc)[0])
706 explainexit(rc)[0])
706 if errprefix:
707 if errprefix:
707 errmsg = '%s: %s' % (errprefix, errmsg)
708 errmsg = '%s: %s' % (errprefix, errmsg)
708 raise onerr(errmsg)
709 raise onerr(errmsg)
709 return rc
710 return rc
710
711
711 def checksignature(func):
712 def checksignature(func):
712 '''wrap a function with code to check for calling errors'''
713 '''wrap a function with code to check for calling errors'''
713 def check(*args, **kwargs):
714 def check(*args, **kwargs):
714 try:
715 try:
715 return func(*args, **kwargs)
716 return func(*args, **kwargs)
716 except TypeError:
717 except TypeError:
717 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
718 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
718 raise error.SignatureError
719 raise error.SignatureError
719 raise
720 raise
720
721
721 return check
722 return check
722
723
723 def copyfile(src, dest, hardlink=False):
724 def copyfile(src, dest, hardlink=False):
724 "copy a file, preserving mode and atime/mtime"
725 "copy a file, preserving mode and atime/mtime"
725 if os.path.lexists(dest):
726 if os.path.lexists(dest):
726 unlink(dest)
727 unlink(dest)
727 # hardlinks are problematic on CIFS, quietly ignore this flag
728 # hardlinks are problematic on CIFS, quietly ignore this flag
728 # until we find a way to work around it cleanly (issue4546)
729 # until we find a way to work around it cleanly (issue4546)
729 if False and hardlink:
730 if False and hardlink:
730 try:
731 try:
731 oslink(src, dest)
732 oslink(src, dest)
732 return
733 return
733 except (IOError, OSError):
734 except (IOError, OSError):
734 pass # fall back to normal copy
735 pass # fall back to normal copy
735 if os.path.islink(src):
736 if os.path.islink(src):
736 os.symlink(os.readlink(src), dest)
737 os.symlink(os.readlink(src), dest)
737 else:
738 else:
738 try:
739 try:
739 shutil.copyfile(src, dest)
740 shutil.copyfile(src, dest)
740 shutil.copymode(src, dest)
741 shutil.copymode(src, dest)
741 except shutil.Error, inst:
742 except shutil.Error, inst:
742 raise Abort(str(inst))
743 raise Abort(str(inst))
743
744
744 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
745 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
745 """Copy a directory tree using hardlinks if possible."""
746 """Copy a directory tree using hardlinks if possible."""
746 num = 0
747 num = 0
747
748
748 if hardlink is None:
749 if hardlink is None:
749 hardlink = (os.stat(src).st_dev ==
750 hardlink = (os.stat(src).st_dev ==
750 os.stat(os.path.dirname(dst)).st_dev)
751 os.stat(os.path.dirname(dst)).st_dev)
751 if hardlink:
752 if hardlink:
752 topic = _('linking')
753 topic = _('linking')
753 else:
754 else:
754 topic = _('copying')
755 topic = _('copying')
755
756
756 if os.path.isdir(src):
757 if os.path.isdir(src):
757 os.mkdir(dst)
758 os.mkdir(dst)
758 for name, kind in osutil.listdir(src):
759 for name, kind in osutil.listdir(src):
759 srcname = os.path.join(src, name)
760 srcname = os.path.join(src, name)
760 dstname = os.path.join(dst, name)
761 dstname = os.path.join(dst, name)
761 def nprog(t, pos):
762 def nprog(t, pos):
762 if pos is not None:
763 if pos is not None:
763 return progress(t, pos + num)
764 return progress(t, pos + num)
764 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
765 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
765 num += n
766 num += n
766 else:
767 else:
767 if hardlink:
768 if hardlink:
768 try:
769 try:
769 oslink(src, dst)
770 oslink(src, dst)
770 except (IOError, OSError):
771 except (IOError, OSError):
771 hardlink = False
772 hardlink = False
772 shutil.copy(src, dst)
773 shutil.copy(src, dst)
773 else:
774 else:
774 shutil.copy(src, dst)
775 shutil.copy(src, dst)
775 num += 1
776 num += 1
776 progress(topic, num)
777 progress(topic, num)
777 progress(topic, None)
778 progress(topic, None)
778
779
779 return hardlink, num
780 return hardlink, num
780
781
781 _winreservednames = '''con prn aux nul
782 _winreservednames = '''con prn aux nul
782 com1 com2 com3 com4 com5 com6 com7 com8 com9
783 com1 com2 com3 com4 com5 com6 com7 com8 com9
783 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
784 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
784 _winreservedchars = ':*?"<>|'
785 _winreservedchars = ':*?"<>|'
785 def checkwinfilename(path):
786 def checkwinfilename(path):
786 r'''Check that the base-relative path is a valid filename on Windows.
787 r'''Check that the base-relative path is a valid filename on Windows.
787 Returns None if the path is ok, or a UI string describing the problem.
788 Returns None if the path is ok, or a UI string describing the problem.
788
789
789 >>> checkwinfilename("just/a/normal/path")
790 >>> checkwinfilename("just/a/normal/path")
790 >>> checkwinfilename("foo/bar/con.xml")
791 >>> checkwinfilename("foo/bar/con.xml")
791 "filename contains 'con', which is reserved on Windows"
792 "filename contains 'con', which is reserved on Windows"
792 >>> checkwinfilename("foo/con.xml/bar")
793 >>> checkwinfilename("foo/con.xml/bar")
793 "filename contains 'con', which is reserved on Windows"
794 "filename contains 'con', which is reserved on Windows"
794 >>> checkwinfilename("foo/bar/xml.con")
795 >>> checkwinfilename("foo/bar/xml.con")
795 >>> checkwinfilename("foo/bar/AUX/bla.txt")
796 >>> checkwinfilename("foo/bar/AUX/bla.txt")
796 "filename contains 'AUX', which is reserved on Windows"
797 "filename contains 'AUX', which is reserved on Windows"
797 >>> checkwinfilename("foo/bar/bla:.txt")
798 >>> checkwinfilename("foo/bar/bla:.txt")
798 "filename contains ':', which is reserved on Windows"
799 "filename contains ':', which is reserved on Windows"
799 >>> checkwinfilename("foo/bar/b\07la.txt")
800 >>> checkwinfilename("foo/bar/b\07la.txt")
800 "filename contains '\\x07', which is invalid on Windows"
801 "filename contains '\\x07', which is invalid on Windows"
801 >>> checkwinfilename("foo/bar/bla ")
802 >>> checkwinfilename("foo/bar/bla ")
802 "filename ends with ' ', which is not allowed on Windows"
803 "filename ends with ' ', which is not allowed on Windows"
803 >>> checkwinfilename("../bar")
804 >>> checkwinfilename("../bar")
804 >>> checkwinfilename("foo\\")
805 >>> checkwinfilename("foo\\")
805 "filename ends with '\\', which is invalid on Windows"
806 "filename ends with '\\', which is invalid on Windows"
806 >>> checkwinfilename("foo\\/bar")
807 >>> checkwinfilename("foo\\/bar")
807 "directory name ends with '\\', which is invalid on Windows"
808 "directory name ends with '\\', which is invalid on Windows"
808 '''
809 '''
809 if path.endswith('\\'):
810 if path.endswith('\\'):
810 return _("filename ends with '\\', which is invalid on Windows")
811 return _("filename ends with '\\', which is invalid on Windows")
811 if '\\/' in path:
812 if '\\/' in path:
812 return _("directory name ends with '\\', which is invalid on Windows")
813 return _("directory name ends with '\\', which is invalid on Windows")
813 for n in path.replace('\\', '/').split('/'):
814 for n in path.replace('\\', '/').split('/'):
814 if not n:
815 if not n:
815 continue
816 continue
816 for c in n:
817 for c in n:
817 if c in _winreservedchars:
818 if c in _winreservedchars:
818 return _("filename contains '%s', which is reserved "
819 return _("filename contains '%s', which is reserved "
819 "on Windows") % c
820 "on Windows") % c
820 if ord(c) <= 31:
821 if ord(c) <= 31:
821 return _("filename contains %r, which is invalid "
822 return _("filename contains %r, which is invalid "
822 "on Windows") % c
823 "on Windows") % c
823 base = n.split('.')[0]
824 base = n.split('.')[0]
824 if base and base.lower() in _winreservednames:
825 if base and base.lower() in _winreservednames:
825 return _("filename contains '%s', which is reserved "
826 return _("filename contains '%s', which is reserved "
826 "on Windows") % base
827 "on Windows") % base
827 t = n[-1]
828 t = n[-1]
828 if t in '. ' and n not in '..':
829 if t in '. ' and n not in '..':
829 return _("filename ends with '%s', which is not allowed "
830 return _("filename ends with '%s', which is not allowed "
830 "on Windows") % t
831 "on Windows") % t
831
832
832 if os.name == 'nt':
833 if os.name == 'nt':
833 checkosfilename = checkwinfilename
834 checkosfilename = checkwinfilename
834 else:
835 else:
835 checkosfilename = platform.checkosfilename
836 checkosfilename = platform.checkosfilename
836
837
837 def makelock(info, pathname):
838 def makelock(info, pathname):
838 try:
839 try:
839 return os.symlink(info, pathname)
840 return os.symlink(info, pathname)
840 except OSError, why:
841 except OSError, why:
841 if why.errno == errno.EEXIST:
842 if why.errno == errno.EEXIST:
842 raise
843 raise
843 except AttributeError: # no symlink in os
844 except AttributeError: # no symlink in os
844 pass
845 pass
845
846
846 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
847 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
847 os.write(ld, info)
848 os.write(ld, info)
848 os.close(ld)
849 os.close(ld)
849
850
850 def readlock(pathname):
851 def readlock(pathname):
851 try:
852 try:
852 return os.readlink(pathname)
853 return os.readlink(pathname)
853 except OSError, why:
854 except OSError, why:
854 if why.errno not in (errno.EINVAL, errno.ENOSYS):
855 if why.errno not in (errno.EINVAL, errno.ENOSYS):
855 raise
856 raise
856 except AttributeError: # no symlink in os
857 except AttributeError: # no symlink in os
857 pass
858 pass
858 fp = posixfile(pathname)
859 fp = posixfile(pathname)
859 r = fp.read()
860 r = fp.read()
860 fp.close()
861 fp.close()
861 return r
862 return r
862
863
863 def fstat(fp):
864 def fstat(fp):
864 '''stat file object that may not have fileno method.'''
865 '''stat file object that may not have fileno method.'''
865 try:
866 try:
866 return os.fstat(fp.fileno())
867 return os.fstat(fp.fileno())
867 except AttributeError:
868 except AttributeError:
868 return os.stat(fp.name)
869 return os.stat(fp.name)
869
870
870 # File system features
871 # File system features
871
872
872 def checkcase(path):
873 def checkcase(path):
873 """
874 """
874 Return true if the given path is on a case-sensitive filesystem
875 Return true if the given path is on a case-sensitive filesystem
875
876
876 Requires a path (like /foo/.hg) ending with a foldable final
877 Requires a path (like /foo/.hg) ending with a foldable final
877 directory component.
878 directory component.
878 """
879 """
879 s1 = os.stat(path)
880 s1 = os.stat(path)
880 d, b = os.path.split(path)
881 d, b = os.path.split(path)
881 b2 = b.upper()
882 b2 = b.upper()
882 if b == b2:
883 if b == b2:
883 b2 = b.lower()
884 b2 = b.lower()
884 if b == b2:
885 if b == b2:
885 return True # no evidence against case sensitivity
886 return True # no evidence against case sensitivity
886 p2 = os.path.join(d, b2)
887 p2 = os.path.join(d, b2)
887 try:
888 try:
888 s2 = os.stat(p2)
889 s2 = os.stat(p2)
889 if s2 == s1:
890 if s2 == s1:
890 return False
891 return False
891 return True
892 return True
892 except OSError:
893 except OSError:
893 return True
894 return True
894
895
895 try:
896 try:
896 import re2
897 import re2
897 _re2 = None
898 _re2 = None
898 except ImportError:
899 except ImportError:
899 _re2 = False
900 _re2 = False
900
901
901 class _re(object):
902 class _re(object):
902 def _checkre2(self):
903 def _checkre2(self):
903 global _re2
904 global _re2
904 try:
905 try:
905 # check if match works, see issue3964
906 # check if match works, see issue3964
906 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
907 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
907 except ImportError:
908 except ImportError:
908 _re2 = False
909 _re2 = False
909
910
910 def compile(self, pat, flags=0):
911 def compile(self, pat, flags=0):
911 '''Compile a regular expression, using re2 if possible
912 '''Compile a regular expression, using re2 if possible
912
913
913 For best performance, use only re2-compatible regexp features. The
914 For best performance, use only re2-compatible regexp features. The
914 only flags from the re module that are re2-compatible are
915 only flags from the re module that are re2-compatible are
915 IGNORECASE and MULTILINE.'''
916 IGNORECASE and MULTILINE.'''
916 if _re2 is None:
917 if _re2 is None:
917 self._checkre2()
918 self._checkre2()
918 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
919 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
919 if flags & remod.IGNORECASE:
920 if flags & remod.IGNORECASE:
920 pat = '(?i)' + pat
921 pat = '(?i)' + pat
921 if flags & remod.MULTILINE:
922 if flags & remod.MULTILINE:
922 pat = '(?m)' + pat
923 pat = '(?m)' + pat
923 try:
924 try:
924 return re2.compile(pat)
925 return re2.compile(pat)
925 except re2.error:
926 except re2.error:
926 pass
927 pass
927 return remod.compile(pat, flags)
928 return remod.compile(pat, flags)
928
929
929 @propertycache
930 @propertycache
930 def escape(self):
931 def escape(self):
931 '''Return the version of escape corresponding to self.compile.
932 '''Return the version of escape corresponding to self.compile.
932
933
933 This is imperfect because whether re2 or re is used for a particular
934 This is imperfect because whether re2 or re is used for a particular
934 function depends on the flags, etc, but it's the best we can do.
935 function depends on the flags, etc, but it's the best we can do.
935 '''
936 '''
936 global _re2
937 global _re2
937 if _re2 is None:
938 if _re2 is None:
938 self._checkre2()
939 self._checkre2()
939 if _re2:
940 if _re2:
940 return re2.escape
941 return re2.escape
941 else:
942 else:
942 return remod.escape
943 return remod.escape
943
944
944 re = _re()
945 re = _re()
945
946
946 _fspathcache = {}
947 _fspathcache = {}
947 def fspath(name, root):
948 def fspath(name, root):
948 '''Get name in the case stored in the filesystem
949 '''Get name in the case stored in the filesystem
949
950
950 The name should be relative to root, and be normcase-ed for efficiency.
951 The name should be relative to root, and be normcase-ed for efficiency.
951
952
952 Note that this function is unnecessary, and should not be
953 Note that this function is unnecessary, and should not be
953 called, for case-sensitive filesystems (simply because it's expensive).
954 called, for case-sensitive filesystems (simply because it's expensive).
954
955
955 The root should be normcase-ed, too.
956 The root should be normcase-ed, too.
956 '''
957 '''
957 def _makefspathcacheentry(dir):
958 def _makefspathcacheentry(dir):
958 return dict((normcase(n), n) for n in os.listdir(dir))
959 return dict((normcase(n), n) for n in os.listdir(dir))
959
960
960 seps = os.sep
961 seps = os.sep
961 if os.altsep:
962 if os.altsep:
962 seps = seps + os.altsep
963 seps = seps + os.altsep
963 # Protect backslashes. This gets silly very quickly.
964 # Protect backslashes. This gets silly very quickly.
964 seps.replace('\\','\\\\')
965 seps.replace('\\','\\\\')
965 pattern = remod.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
966 pattern = remod.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
966 dir = os.path.normpath(root)
967 dir = os.path.normpath(root)
967 result = []
968 result = []
968 for part, sep in pattern.findall(name):
969 for part, sep in pattern.findall(name):
969 if sep:
970 if sep:
970 result.append(sep)
971 result.append(sep)
971 continue
972 continue
972
973
973 if dir not in _fspathcache:
974 if dir not in _fspathcache:
974 _fspathcache[dir] = _makefspathcacheentry(dir)
975 _fspathcache[dir] = _makefspathcacheentry(dir)
975 contents = _fspathcache[dir]
976 contents = _fspathcache[dir]
976
977
977 found = contents.get(part)
978 found = contents.get(part)
978 if not found:
979 if not found:
979 # retry "once per directory" per "dirstate.walk" which
980 # retry "once per directory" per "dirstate.walk" which
980 # may take place for each patches of "hg qpush", for example
981 # may take place for each patches of "hg qpush", for example
981 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
982 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
982 found = contents.get(part)
983 found = contents.get(part)
983
984
984 result.append(found or part)
985 result.append(found or part)
985 dir = os.path.join(dir, part)
986 dir = os.path.join(dir, part)
986
987
987 return ''.join(result)
988 return ''.join(result)
988
989
989 def checknlink(testfile):
990 def checknlink(testfile):
990 '''check whether hardlink count reporting works properly'''
991 '''check whether hardlink count reporting works properly'''
991
992
992 # testfile may be open, so we need a separate file for checking to
993 # testfile may be open, so we need a separate file for checking to
993 # work around issue2543 (or testfile may get lost on Samba shares)
994 # work around issue2543 (or testfile may get lost on Samba shares)
994 f1 = testfile + ".hgtmp1"
995 f1 = testfile + ".hgtmp1"
995 if os.path.lexists(f1):
996 if os.path.lexists(f1):
996 return False
997 return False
997 try:
998 try:
998 posixfile(f1, 'w').close()
999 posixfile(f1, 'w').close()
999 except IOError:
1000 except IOError:
1000 return False
1001 return False
1001
1002
1002 f2 = testfile + ".hgtmp2"
1003 f2 = testfile + ".hgtmp2"
1003 fd = None
1004 fd = None
1004 try:
1005 try:
1005 try:
1006 try:
1006 oslink(f1, f2)
1007 oslink(f1, f2)
1007 except OSError:
1008 except OSError:
1008 return False
1009 return False
1009
1010
1010 # nlinks() may behave differently for files on Windows shares if
1011 # nlinks() may behave differently for files on Windows shares if
1011 # the file is open.
1012 # the file is open.
1012 fd = posixfile(f2)
1013 fd = posixfile(f2)
1013 return nlinks(f2) > 1
1014 return nlinks(f2) > 1
1014 finally:
1015 finally:
1015 if fd is not None:
1016 if fd is not None:
1016 fd.close()
1017 fd.close()
1017 for f in (f1, f2):
1018 for f in (f1, f2):
1018 try:
1019 try:
1019 os.unlink(f)
1020 os.unlink(f)
1020 except OSError:
1021 except OSError:
1021 pass
1022 pass
1022
1023
1023 def endswithsep(path):
1024 def endswithsep(path):
1024 '''Check path ends with os.sep or os.altsep.'''
1025 '''Check path ends with os.sep or os.altsep.'''
1025 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1026 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1026
1027
1027 def splitpath(path):
1028 def splitpath(path):
1028 '''Split path by os.sep.
1029 '''Split path by os.sep.
1029 Note that this function does not use os.altsep because this is
1030 Note that this function does not use os.altsep because this is
1030 an alternative of simple "xxx.split(os.sep)".
1031 an alternative of simple "xxx.split(os.sep)".
1031 It is recommended to use os.path.normpath() before using this
1032 It is recommended to use os.path.normpath() before using this
1032 function if need.'''
1033 function if need.'''
1033 return path.split(os.sep)
1034 return path.split(os.sep)
1034
1035
1035 def gui():
1036 def gui():
1036 '''Are we running in a GUI?'''
1037 '''Are we running in a GUI?'''
1037 if sys.platform == 'darwin':
1038 if sys.platform == 'darwin':
1038 if 'SSH_CONNECTION' in os.environ:
1039 if 'SSH_CONNECTION' in os.environ:
1039 # handle SSH access to a box where the user is logged in
1040 # handle SSH access to a box where the user is logged in
1040 return False
1041 return False
1041 elif getattr(osutil, 'isgui', None):
1042 elif getattr(osutil, 'isgui', None):
1042 # check if a CoreGraphics session is available
1043 # check if a CoreGraphics session is available
1043 return osutil.isgui()
1044 return osutil.isgui()
1044 else:
1045 else:
1045 # pure build; use a safe default
1046 # pure build; use a safe default
1046 return True
1047 return True
1047 else:
1048 else:
1048 return os.name == "nt" or os.environ.get("DISPLAY")
1049 return os.name == "nt" or os.environ.get("DISPLAY")
1049
1050
1050 def mktempcopy(name, emptyok=False, createmode=None):
1051 def mktempcopy(name, emptyok=False, createmode=None):
1051 """Create a temporary file with the same contents from name
1052 """Create a temporary file with the same contents from name
1052
1053
1053 The permission bits are copied from the original file.
1054 The permission bits are copied from the original file.
1054
1055
1055 If the temporary file is going to be truncated immediately, you
1056 If the temporary file is going to be truncated immediately, you
1056 can use emptyok=True as an optimization.
1057 can use emptyok=True as an optimization.
1057
1058
1058 Returns the name of the temporary file.
1059 Returns the name of the temporary file.
1059 """
1060 """
1060 d, fn = os.path.split(name)
1061 d, fn = os.path.split(name)
1061 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1062 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1062 os.close(fd)
1063 os.close(fd)
1063 # Temporary files are created with mode 0600, which is usually not
1064 # Temporary files are created with mode 0600, which is usually not
1064 # what we want. If the original file already exists, just copy
1065 # what we want. If the original file already exists, just copy
1065 # its mode. Otherwise, manually obey umask.
1066 # its mode. Otherwise, manually obey umask.
1066 copymode(name, temp, createmode)
1067 copymode(name, temp, createmode)
1067 if emptyok:
1068 if emptyok:
1068 return temp
1069 return temp
1069 try:
1070 try:
1070 try:
1071 try:
1071 ifp = posixfile(name, "rb")
1072 ifp = posixfile(name, "rb")
1072 except IOError, inst:
1073 except IOError, inst:
1073 if inst.errno == errno.ENOENT:
1074 if inst.errno == errno.ENOENT:
1074 return temp
1075 return temp
1075 if not getattr(inst, 'filename', None):
1076 if not getattr(inst, 'filename', None):
1076 inst.filename = name
1077 inst.filename = name
1077 raise
1078 raise
1078 ofp = posixfile(temp, "wb")
1079 ofp = posixfile(temp, "wb")
1079 for chunk in filechunkiter(ifp):
1080 for chunk in filechunkiter(ifp):
1080 ofp.write(chunk)
1081 ofp.write(chunk)
1081 ifp.close()
1082 ifp.close()
1082 ofp.close()
1083 ofp.close()
1083 except: # re-raises
1084 except: # re-raises
1084 try: os.unlink(temp)
1085 try: os.unlink(temp)
1085 except OSError: pass
1086 except OSError: pass
1086 raise
1087 raise
1087 return temp
1088 return temp
1088
1089
1089 class atomictempfile(object):
1090 class atomictempfile(object):
1090 '''writable file object that atomically updates a file
1091 '''writable file object that atomically updates a file
1091
1092
1092 All writes will go to a temporary copy of the original file. Call
1093 All writes will go to a temporary copy of the original file. Call
1093 close() when you are done writing, and atomictempfile will rename
1094 close() when you are done writing, and atomictempfile will rename
1094 the temporary copy to the original name, making the changes
1095 the temporary copy to the original name, making the changes
1095 visible. If the object is destroyed without being closed, all your
1096 visible. If the object is destroyed without being closed, all your
1096 writes are discarded.
1097 writes are discarded.
1097 '''
1098 '''
1098 def __init__(self, name, mode='w+b', createmode=None):
1099 def __init__(self, name, mode='w+b', createmode=None):
1099 self.__name = name # permanent name
1100 self.__name = name # permanent name
1100 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1101 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1101 createmode=createmode)
1102 createmode=createmode)
1102 self._fp = posixfile(self._tempname, mode)
1103 self._fp = posixfile(self._tempname, mode)
1103
1104
1104 # delegated methods
1105 # delegated methods
1105 self.write = self._fp.write
1106 self.write = self._fp.write
1106 self.seek = self._fp.seek
1107 self.seek = self._fp.seek
1107 self.tell = self._fp.tell
1108 self.tell = self._fp.tell
1108 self.fileno = self._fp.fileno
1109 self.fileno = self._fp.fileno
1109
1110
1110 def close(self):
1111 def close(self):
1111 if not self._fp.closed:
1112 if not self._fp.closed:
1112 self._fp.close()
1113 self._fp.close()
1113 rename(self._tempname, localpath(self.__name))
1114 rename(self._tempname, localpath(self.__name))
1114
1115
1115 def discard(self):
1116 def discard(self):
1116 if not self._fp.closed:
1117 if not self._fp.closed:
1117 try:
1118 try:
1118 os.unlink(self._tempname)
1119 os.unlink(self._tempname)
1119 except OSError:
1120 except OSError:
1120 pass
1121 pass
1121 self._fp.close()
1122 self._fp.close()
1122
1123
1123 def __del__(self):
1124 def __del__(self):
1124 if safehasattr(self, '_fp'): # constructor actually did something
1125 if safehasattr(self, '_fp'): # constructor actually did something
1125 self.discard()
1126 self.discard()
1126
1127
1127 def makedirs(name, mode=None, notindexed=False):
1128 def makedirs(name, mode=None, notindexed=False):
1128 """recursive directory creation with parent mode inheritance"""
1129 """recursive directory creation with parent mode inheritance"""
1129 try:
1130 try:
1130 makedir(name, notindexed)
1131 makedir(name, notindexed)
1131 except OSError, err:
1132 except OSError, err:
1132 if err.errno == errno.EEXIST:
1133 if err.errno == errno.EEXIST:
1133 return
1134 return
1134 if err.errno != errno.ENOENT or not name:
1135 if err.errno != errno.ENOENT or not name:
1135 raise
1136 raise
1136 parent = os.path.dirname(os.path.abspath(name))
1137 parent = os.path.dirname(os.path.abspath(name))
1137 if parent == name:
1138 if parent == name:
1138 raise
1139 raise
1139 makedirs(parent, mode, notindexed)
1140 makedirs(parent, mode, notindexed)
1140 makedir(name, notindexed)
1141 makedir(name, notindexed)
1141 if mode is not None:
1142 if mode is not None:
1142 os.chmod(name, mode)
1143 os.chmod(name, mode)
1143
1144
1144 def ensuredirs(name, mode=None, notindexed=False):
1145 def ensuredirs(name, mode=None, notindexed=False):
1145 """race-safe recursive directory creation
1146 """race-safe recursive directory creation
1146
1147
1147 Newly created directories are marked as "not to be indexed by
1148 Newly created directories are marked as "not to be indexed by
1148 the content indexing service", if ``notindexed`` is specified
1149 the content indexing service", if ``notindexed`` is specified
1149 for "write" mode access.
1150 for "write" mode access.
1150 """
1151 """
1151 if os.path.isdir(name):
1152 if os.path.isdir(name):
1152 return
1153 return
1153 parent = os.path.dirname(os.path.abspath(name))
1154 parent = os.path.dirname(os.path.abspath(name))
1154 if parent != name:
1155 if parent != name:
1155 ensuredirs(parent, mode, notindexed)
1156 ensuredirs(parent, mode, notindexed)
1156 try:
1157 try:
1157 makedir(name, notindexed)
1158 makedir(name, notindexed)
1158 except OSError, err:
1159 except OSError, err:
1159 if err.errno == errno.EEXIST and os.path.isdir(name):
1160 if err.errno == errno.EEXIST and os.path.isdir(name):
1160 # someone else seems to have won a directory creation race
1161 # someone else seems to have won a directory creation race
1161 return
1162 return
1162 raise
1163 raise
1163 if mode is not None:
1164 if mode is not None:
1164 os.chmod(name, mode)
1165 os.chmod(name, mode)
1165
1166
1166 def readfile(path):
1167 def readfile(path):
1167 fp = open(path, 'rb')
1168 fp = open(path, 'rb')
1168 try:
1169 try:
1169 return fp.read()
1170 return fp.read()
1170 finally:
1171 finally:
1171 fp.close()
1172 fp.close()
1172
1173
1173 def writefile(path, text):
1174 def writefile(path, text):
1174 fp = open(path, 'wb')
1175 fp = open(path, 'wb')
1175 try:
1176 try:
1176 fp.write(text)
1177 fp.write(text)
1177 finally:
1178 finally:
1178 fp.close()
1179 fp.close()
1179
1180
1180 def appendfile(path, text):
1181 def appendfile(path, text):
1181 fp = open(path, 'ab')
1182 fp = open(path, 'ab')
1182 try:
1183 try:
1183 fp.write(text)
1184 fp.write(text)
1184 finally:
1185 finally:
1185 fp.close()
1186 fp.close()
1186
1187
1187 class chunkbuffer(object):
1188 class chunkbuffer(object):
1188 """Allow arbitrary sized chunks of data to be efficiently read from an
1189 """Allow arbitrary sized chunks of data to be efficiently read from an
1189 iterator over chunks of arbitrary size."""
1190 iterator over chunks of arbitrary size."""
1190
1191
1191 def __init__(self, in_iter):
1192 def __init__(self, in_iter):
1192 """in_iter is the iterator that's iterating over the input chunks.
1193 """in_iter is the iterator that's iterating over the input chunks.
1193 targetsize is how big a buffer to try to maintain."""
1194 targetsize is how big a buffer to try to maintain."""
1194 def splitbig(chunks):
1195 def splitbig(chunks):
1195 for chunk in chunks:
1196 for chunk in chunks:
1196 if len(chunk) > 2**20:
1197 if len(chunk) > 2**20:
1197 pos = 0
1198 pos = 0
1198 while pos < len(chunk):
1199 while pos < len(chunk):
1199 end = pos + 2 ** 18
1200 end = pos + 2 ** 18
1200 yield chunk[pos:end]
1201 yield chunk[pos:end]
1201 pos = end
1202 pos = end
1202 else:
1203 else:
1203 yield chunk
1204 yield chunk
1204 self.iter = splitbig(in_iter)
1205 self.iter = splitbig(in_iter)
1205 self._queue = deque()
1206 self._queue = deque()
1206
1207
1207 def read(self, l=None):
1208 def read(self, l=None):
1208 """Read L bytes of data from the iterator of chunks of data.
1209 """Read L bytes of data from the iterator of chunks of data.
1209 Returns less than L bytes if the iterator runs dry.
1210 Returns less than L bytes if the iterator runs dry.
1210
1211
1211 If size parameter is omitted, read everything"""
1212 If size parameter is omitted, read everything"""
1212 left = l
1213 left = l
1213 buf = []
1214 buf = []
1214 queue = self._queue
1215 queue = self._queue
1215 while left is None or left > 0:
1216 while left is None or left > 0:
1216 # refill the queue
1217 # refill the queue
1217 if not queue:
1218 if not queue:
1218 target = 2**18
1219 target = 2**18
1219 for chunk in self.iter:
1220 for chunk in self.iter:
1220 queue.append(chunk)
1221 queue.append(chunk)
1221 target -= len(chunk)
1222 target -= len(chunk)
1222 if target <= 0:
1223 if target <= 0:
1223 break
1224 break
1224 if not queue:
1225 if not queue:
1225 break
1226 break
1226
1227
1227 chunk = queue.popleft()
1228 chunk = queue.popleft()
1228 if left is not None:
1229 if left is not None:
1229 left -= len(chunk)
1230 left -= len(chunk)
1230 if left is not None and left < 0:
1231 if left is not None and left < 0:
1231 queue.appendleft(chunk[left:])
1232 queue.appendleft(chunk[left:])
1232 buf.append(chunk[:left])
1233 buf.append(chunk[:left])
1233 else:
1234 else:
1234 buf.append(chunk)
1235 buf.append(chunk)
1235
1236
1236 return ''.join(buf)
1237 return ''.join(buf)
1237
1238
1238 def filechunkiter(f, size=65536, limit=None):
1239 def filechunkiter(f, size=65536, limit=None):
1239 """Create a generator that produces the data in the file size
1240 """Create a generator that produces the data in the file size
1240 (default 65536) bytes at a time, up to optional limit (default is
1241 (default 65536) bytes at a time, up to optional limit (default is
1241 to read all data). Chunks may be less than size bytes if the
1242 to read all data). Chunks may be less than size bytes if the
1242 chunk is the last chunk in the file, or the file is a socket or
1243 chunk is the last chunk in the file, or the file is a socket or
1243 some other type of file that sometimes reads less data than is
1244 some other type of file that sometimes reads less data than is
1244 requested."""
1245 requested."""
1245 assert size >= 0
1246 assert size >= 0
1246 assert limit is None or limit >= 0
1247 assert limit is None or limit >= 0
1247 while True:
1248 while True:
1248 if limit is None:
1249 if limit is None:
1249 nbytes = size
1250 nbytes = size
1250 else:
1251 else:
1251 nbytes = min(limit, size)
1252 nbytes = min(limit, size)
1252 s = nbytes and f.read(nbytes)
1253 s = nbytes and f.read(nbytes)
1253 if not s:
1254 if not s:
1254 break
1255 break
1255 if limit:
1256 if limit:
1256 limit -= len(s)
1257 limit -= len(s)
1257 yield s
1258 yield s
1258
1259
1259 def makedate(timestamp=None):
1260 def makedate(timestamp=None):
1260 '''Return a unix timestamp (or the current time) as a (unixtime,
1261 '''Return a unix timestamp (or the current time) as a (unixtime,
1261 offset) tuple based off the local timezone.'''
1262 offset) tuple based off the local timezone.'''
1262 if timestamp is None:
1263 if timestamp is None:
1263 timestamp = time.time()
1264 timestamp = time.time()
1264 if timestamp < 0:
1265 if timestamp < 0:
1265 hint = _("check your clock")
1266 hint = _("check your clock")
1266 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1267 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1267 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1268 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1268 datetime.datetime.fromtimestamp(timestamp))
1269 datetime.datetime.fromtimestamp(timestamp))
1269 tz = delta.days * 86400 + delta.seconds
1270 tz = delta.days * 86400 + delta.seconds
1270 return timestamp, tz
1271 return timestamp, tz
1271
1272
1272 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1273 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1273 """represent a (unixtime, offset) tuple as a localized time.
1274 """represent a (unixtime, offset) tuple as a localized time.
1274 unixtime is seconds since the epoch, and offset is the time zone's
1275 unixtime is seconds since the epoch, and offset is the time zone's
1275 number of seconds away from UTC. if timezone is false, do not
1276 number of seconds away from UTC. if timezone is false, do not
1276 append time zone to string."""
1277 append time zone to string."""
1277 t, tz = date or makedate()
1278 t, tz = date or makedate()
1278 if t < 0:
1279 if t < 0:
1279 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1280 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1280 tz = 0
1281 tz = 0
1281 if "%1" in format or "%2" in format or "%z" in format:
1282 if "%1" in format or "%2" in format or "%z" in format:
1282 sign = (tz > 0) and "-" or "+"
1283 sign = (tz > 0) and "-" or "+"
1283 minutes = abs(tz) // 60
1284 minutes = abs(tz) // 60
1284 format = format.replace("%z", "%1%2")
1285 format = format.replace("%z", "%1%2")
1285 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1286 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1286 format = format.replace("%2", "%02d" % (minutes % 60))
1287 format = format.replace("%2", "%02d" % (minutes % 60))
1287 try:
1288 try:
1288 t = time.gmtime(float(t) - tz)
1289 t = time.gmtime(float(t) - tz)
1289 except ValueError:
1290 except ValueError:
1290 # time was out of range
1291 # time was out of range
1291 t = time.gmtime(sys.maxint)
1292 t = time.gmtime(sys.maxint)
1292 s = time.strftime(format, t)
1293 s = time.strftime(format, t)
1293 return s
1294 return s
1294
1295
1295 def shortdate(date=None):
1296 def shortdate(date=None):
1296 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1297 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1297 return datestr(date, format='%Y-%m-%d')
1298 return datestr(date, format='%Y-%m-%d')
1298
1299
1299 def strdate(string, format, defaults=[]):
1300 def strdate(string, format, defaults=[]):
1300 """parse a localized time string and return a (unixtime, offset) tuple.
1301 """parse a localized time string and return a (unixtime, offset) tuple.
1301 if the string cannot be parsed, ValueError is raised."""
1302 if the string cannot be parsed, ValueError is raised."""
1302 def timezone(string):
1303 def timezone(string):
1303 tz = string.split()[-1]
1304 tz = string.split()[-1]
1304 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1305 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1305 sign = (tz[0] == "+") and 1 or -1
1306 sign = (tz[0] == "+") and 1 or -1
1306 hours = int(tz[1:3])
1307 hours = int(tz[1:3])
1307 minutes = int(tz[3:5])
1308 minutes = int(tz[3:5])
1308 return -sign * (hours * 60 + minutes) * 60
1309 return -sign * (hours * 60 + minutes) * 60
1309 if tz == "GMT" or tz == "UTC":
1310 if tz == "GMT" or tz == "UTC":
1310 return 0
1311 return 0
1311 return None
1312 return None
1312
1313
1313 # NOTE: unixtime = localunixtime + offset
1314 # NOTE: unixtime = localunixtime + offset
1314 offset, date = timezone(string), string
1315 offset, date = timezone(string), string
1315 if offset is not None:
1316 if offset is not None:
1316 date = " ".join(string.split()[:-1])
1317 date = " ".join(string.split()[:-1])
1317
1318
1318 # add missing elements from defaults
1319 # add missing elements from defaults
1319 usenow = False # default to using biased defaults
1320 usenow = False # default to using biased defaults
1320 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1321 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1321 found = [True for p in part if ("%"+p) in format]
1322 found = [True for p in part if ("%"+p) in format]
1322 if not found:
1323 if not found:
1323 date += "@" + defaults[part][usenow]
1324 date += "@" + defaults[part][usenow]
1324 format += "@%" + part[0]
1325 format += "@%" + part[0]
1325 else:
1326 else:
1326 # We've found a specific time element, less specific time
1327 # We've found a specific time element, less specific time
1327 # elements are relative to today
1328 # elements are relative to today
1328 usenow = True
1329 usenow = True
1329
1330
1330 timetuple = time.strptime(date, format)
1331 timetuple = time.strptime(date, format)
1331 localunixtime = int(calendar.timegm(timetuple))
1332 localunixtime = int(calendar.timegm(timetuple))
1332 if offset is None:
1333 if offset is None:
1333 # local timezone
1334 # local timezone
1334 unixtime = int(time.mktime(timetuple))
1335 unixtime = int(time.mktime(timetuple))
1335 offset = unixtime - localunixtime
1336 offset = unixtime - localunixtime
1336 else:
1337 else:
1337 unixtime = localunixtime + offset
1338 unixtime = localunixtime + offset
1338 return unixtime, offset
1339 return unixtime, offset
1339
1340
1340 def parsedate(date, formats=None, bias={}):
1341 def parsedate(date, formats=None, bias={}):
1341 """parse a localized date/time and return a (unixtime, offset) tuple.
1342 """parse a localized date/time and return a (unixtime, offset) tuple.
1342
1343
1343 The date may be a "unixtime offset" string or in one of the specified
1344 The date may be a "unixtime offset" string or in one of the specified
1344 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1345 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1345
1346
1346 >>> parsedate(' today ') == parsedate(\
1347 >>> parsedate(' today ') == parsedate(\
1347 datetime.date.today().strftime('%b %d'))
1348 datetime.date.today().strftime('%b %d'))
1348 True
1349 True
1349 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1350 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1350 datetime.timedelta(days=1)\
1351 datetime.timedelta(days=1)\
1351 ).strftime('%b %d'))
1352 ).strftime('%b %d'))
1352 True
1353 True
1353 >>> now, tz = makedate()
1354 >>> now, tz = makedate()
1354 >>> strnow, strtz = parsedate('now')
1355 >>> strnow, strtz = parsedate('now')
1355 >>> (strnow - now) < 1
1356 >>> (strnow - now) < 1
1356 True
1357 True
1357 >>> tz == strtz
1358 >>> tz == strtz
1358 True
1359 True
1359 """
1360 """
1360 if not date:
1361 if not date:
1361 return 0, 0
1362 return 0, 0
1362 if isinstance(date, tuple) and len(date) == 2:
1363 if isinstance(date, tuple) and len(date) == 2:
1363 return date
1364 return date
1364 if not formats:
1365 if not formats:
1365 formats = defaultdateformats
1366 formats = defaultdateformats
1366 date = date.strip()
1367 date = date.strip()
1367
1368
1368 if date == 'now' or date == _('now'):
1369 if date == 'now' or date == _('now'):
1369 return makedate()
1370 return makedate()
1370 if date == 'today' or date == _('today'):
1371 if date == 'today' or date == _('today'):
1371 date = datetime.date.today().strftime('%b %d')
1372 date = datetime.date.today().strftime('%b %d')
1372 elif date == 'yesterday' or date == _('yesterday'):
1373 elif date == 'yesterday' or date == _('yesterday'):
1373 date = (datetime.date.today() -
1374 date = (datetime.date.today() -
1374 datetime.timedelta(days=1)).strftime('%b %d')
1375 datetime.timedelta(days=1)).strftime('%b %d')
1375
1376
1376 try:
1377 try:
1377 when, offset = map(int, date.split(' '))
1378 when, offset = map(int, date.split(' '))
1378 except ValueError:
1379 except ValueError:
1379 # fill out defaults
1380 # fill out defaults
1380 now = makedate()
1381 now = makedate()
1381 defaults = {}
1382 defaults = {}
1382 for part in ("d", "mb", "yY", "HI", "M", "S"):
1383 for part in ("d", "mb", "yY", "HI", "M", "S"):
1383 # this piece is for rounding the specific end of unknowns
1384 # this piece is for rounding the specific end of unknowns
1384 b = bias.get(part)
1385 b = bias.get(part)
1385 if b is None:
1386 if b is None:
1386 if part[0] in "HMS":
1387 if part[0] in "HMS":
1387 b = "00"
1388 b = "00"
1388 else:
1389 else:
1389 b = "0"
1390 b = "0"
1390
1391
1391 # this piece is for matching the generic end to today's date
1392 # this piece is for matching the generic end to today's date
1392 n = datestr(now, "%" + part[0])
1393 n = datestr(now, "%" + part[0])
1393
1394
1394 defaults[part] = (b, n)
1395 defaults[part] = (b, n)
1395
1396
1396 for format in formats:
1397 for format in formats:
1397 try:
1398 try:
1398 when, offset = strdate(date, format, defaults)
1399 when, offset = strdate(date, format, defaults)
1399 except (ValueError, OverflowError):
1400 except (ValueError, OverflowError):
1400 pass
1401 pass
1401 else:
1402 else:
1402 break
1403 break
1403 else:
1404 else:
1404 raise Abort(_('invalid date: %r') % date)
1405 raise Abort(_('invalid date: %r') % date)
1405 # validate explicit (probably user-specified) date and
1406 # validate explicit (probably user-specified) date and
1406 # time zone offset. values must fit in signed 32 bits for
1407 # time zone offset. values must fit in signed 32 bits for
1407 # current 32-bit linux runtimes. timezones go from UTC-12
1408 # current 32-bit linux runtimes. timezones go from UTC-12
1408 # to UTC+14
1409 # to UTC+14
1409 if abs(when) > 0x7fffffff:
1410 if abs(when) > 0x7fffffff:
1410 raise Abort(_('date exceeds 32 bits: %d') % when)
1411 raise Abort(_('date exceeds 32 bits: %d') % when)
1411 if when < 0:
1412 if when < 0:
1412 raise Abort(_('negative date value: %d') % when)
1413 raise Abort(_('negative date value: %d') % when)
1413 if offset < -50400 or offset > 43200:
1414 if offset < -50400 or offset > 43200:
1414 raise Abort(_('impossible time zone offset: %d') % offset)
1415 raise Abort(_('impossible time zone offset: %d') % offset)
1415 return when, offset
1416 return when, offset
1416
1417
1417 def matchdate(date):
1418 def matchdate(date):
1418 """Return a function that matches a given date match specifier
1419 """Return a function that matches a given date match specifier
1419
1420
1420 Formats include:
1421 Formats include:
1421
1422
1422 '{date}' match a given date to the accuracy provided
1423 '{date}' match a given date to the accuracy provided
1423
1424
1424 '<{date}' on or before a given date
1425 '<{date}' on or before a given date
1425
1426
1426 '>{date}' on or after a given date
1427 '>{date}' on or after a given date
1427
1428
1428 >>> p1 = parsedate("10:29:59")
1429 >>> p1 = parsedate("10:29:59")
1429 >>> p2 = parsedate("10:30:00")
1430 >>> p2 = parsedate("10:30:00")
1430 >>> p3 = parsedate("10:30:59")
1431 >>> p3 = parsedate("10:30:59")
1431 >>> p4 = parsedate("10:31:00")
1432 >>> p4 = parsedate("10:31:00")
1432 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1433 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1433 >>> f = matchdate("10:30")
1434 >>> f = matchdate("10:30")
1434 >>> f(p1[0])
1435 >>> f(p1[0])
1435 False
1436 False
1436 >>> f(p2[0])
1437 >>> f(p2[0])
1437 True
1438 True
1438 >>> f(p3[0])
1439 >>> f(p3[0])
1439 True
1440 True
1440 >>> f(p4[0])
1441 >>> f(p4[0])
1441 False
1442 False
1442 >>> f(p5[0])
1443 >>> f(p5[0])
1443 False
1444 False
1444 """
1445 """
1445
1446
1446 def lower(date):
1447 def lower(date):
1447 d = {'mb': "1", 'd': "1"}
1448 d = {'mb': "1", 'd': "1"}
1448 return parsedate(date, extendeddateformats, d)[0]
1449 return parsedate(date, extendeddateformats, d)[0]
1449
1450
1450 def upper(date):
1451 def upper(date):
1451 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
1452 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
1452 for days in ("31", "30", "29"):
1453 for days in ("31", "30", "29"):
1453 try:
1454 try:
1454 d["d"] = days
1455 d["d"] = days
1455 return parsedate(date, extendeddateformats, d)[0]
1456 return parsedate(date, extendeddateformats, d)[0]
1456 except Abort:
1457 except Abort:
1457 pass
1458 pass
1458 d["d"] = "28"
1459 d["d"] = "28"
1459 return parsedate(date, extendeddateformats, d)[0]
1460 return parsedate(date, extendeddateformats, d)[0]
1460
1461
1461 date = date.strip()
1462 date = date.strip()
1462
1463
1463 if not date:
1464 if not date:
1464 raise Abort(_("dates cannot consist entirely of whitespace"))
1465 raise Abort(_("dates cannot consist entirely of whitespace"))
1465 elif date[0] == "<":
1466 elif date[0] == "<":
1466 if not date[1:]:
1467 if not date[1:]:
1467 raise Abort(_("invalid day spec, use '<DATE'"))
1468 raise Abort(_("invalid day spec, use '<DATE'"))
1468 when = upper(date[1:])
1469 when = upper(date[1:])
1469 return lambda x: x <= when
1470 return lambda x: x <= when
1470 elif date[0] == ">":
1471 elif date[0] == ">":
1471 if not date[1:]:
1472 if not date[1:]:
1472 raise Abort(_("invalid day spec, use '>DATE'"))
1473 raise Abort(_("invalid day spec, use '>DATE'"))
1473 when = lower(date[1:])
1474 when = lower(date[1:])
1474 return lambda x: x >= when
1475 return lambda x: x >= when
1475 elif date[0] == "-":
1476 elif date[0] == "-":
1476 try:
1477 try:
1477 days = int(date[1:])
1478 days = int(date[1:])
1478 except ValueError:
1479 except ValueError:
1479 raise Abort(_("invalid day spec: %s") % date[1:])
1480 raise Abort(_("invalid day spec: %s") % date[1:])
1480 if days < 0:
1481 if days < 0:
1481 raise Abort(_('%s must be nonnegative (see "hg help dates")')
1482 raise Abort(_('%s must be nonnegative (see "hg help dates")')
1482 % date[1:])
1483 % date[1:])
1483 when = makedate()[0] - days * 3600 * 24
1484 when = makedate()[0] - days * 3600 * 24
1484 return lambda x: x >= when
1485 return lambda x: x >= when
1485 elif " to " in date:
1486 elif " to " in date:
1486 a, b = date.split(" to ")
1487 a, b = date.split(" to ")
1487 start, stop = lower(a), upper(b)
1488 start, stop = lower(a), upper(b)
1488 return lambda x: x >= start and x <= stop
1489 return lambda x: x >= start and x <= stop
1489 else:
1490 else:
1490 start, stop = lower(date), upper(date)
1491 start, stop = lower(date), upper(date)
1491 return lambda x: x >= start and x <= stop
1492 return lambda x: x >= start and x <= stop
1492
1493
1493 def shortuser(user):
1494 def shortuser(user):
1494 """Return a short representation of a user name or email address."""
1495 """Return a short representation of a user name or email address."""
1495 f = user.find('@')
1496 f = user.find('@')
1496 if f >= 0:
1497 if f >= 0:
1497 user = user[:f]
1498 user = user[:f]
1498 f = user.find('<')
1499 f = user.find('<')
1499 if f >= 0:
1500 if f >= 0:
1500 user = user[f + 1:]
1501 user = user[f + 1:]
1501 f = user.find(' ')
1502 f = user.find(' ')
1502 if f >= 0:
1503 if f >= 0:
1503 user = user[:f]
1504 user = user[:f]
1504 f = user.find('.')
1505 f = user.find('.')
1505 if f >= 0:
1506 if f >= 0:
1506 user = user[:f]
1507 user = user[:f]
1507 return user
1508 return user
1508
1509
1509 def emailuser(user):
1510 def emailuser(user):
1510 """Return the user portion of an email address."""
1511 """Return the user portion of an email address."""
1511 f = user.find('@')
1512 f = user.find('@')
1512 if f >= 0:
1513 if f >= 0:
1513 user = user[:f]
1514 user = user[:f]
1514 f = user.find('<')
1515 f = user.find('<')
1515 if f >= 0:
1516 if f >= 0:
1516 user = user[f + 1:]
1517 user = user[f + 1:]
1517 return user
1518 return user
1518
1519
1519 def email(author):
1520 def email(author):
1520 '''get email of author.'''
1521 '''get email of author.'''
1521 r = author.find('>')
1522 r = author.find('>')
1522 if r == -1:
1523 if r == -1:
1523 r = None
1524 r = None
1524 return author[author.find('<') + 1:r]
1525 return author[author.find('<') + 1:r]
1525
1526
1526 def ellipsis(text, maxlength=400):
1527 def ellipsis(text, maxlength=400):
1527 """Trim string to at most maxlength (default: 400) columns in display."""
1528 """Trim string to at most maxlength (default: 400) columns in display."""
1528 return encoding.trim(text, maxlength, ellipsis='...')
1529 return encoding.trim(text, maxlength, ellipsis='...')
1529
1530
1530 def unitcountfn(*unittable):
1531 def unitcountfn(*unittable):
1531 '''return a function that renders a readable count of some quantity'''
1532 '''return a function that renders a readable count of some quantity'''
1532
1533
1533 def go(count):
1534 def go(count):
1534 for multiplier, divisor, format in unittable:
1535 for multiplier, divisor, format in unittable:
1535 if count >= divisor * multiplier:
1536 if count >= divisor * multiplier:
1536 return format % (count / float(divisor))
1537 return format % (count / float(divisor))
1537 return unittable[-1][2] % count
1538 return unittable[-1][2] % count
1538
1539
1539 return go
1540 return go
1540
1541
1541 bytecount = unitcountfn(
1542 bytecount = unitcountfn(
1542 (100, 1 << 30, _('%.0f GB')),
1543 (100, 1 << 30, _('%.0f GB')),
1543 (10, 1 << 30, _('%.1f GB')),
1544 (10, 1 << 30, _('%.1f GB')),
1544 (1, 1 << 30, _('%.2f GB')),
1545 (1, 1 << 30, _('%.2f GB')),
1545 (100, 1 << 20, _('%.0f MB')),
1546 (100, 1 << 20, _('%.0f MB')),
1546 (10, 1 << 20, _('%.1f MB')),
1547 (10, 1 << 20, _('%.1f MB')),
1547 (1, 1 << 20, _('%.2f MB')),
1548 (1, 1 << 20, _('%.2f MB')),
1548 (100, 1 << 10, _('%.0f KB')),
1549 (100, 1 << 10, _('%.0f KB')),
1549 (10, 1 << 10, _('%.1f KB')),
1550 (10, 1 << 10, _('%.1f KB')),
1550 (1, 1 << 10, _('%.2f KB')),
1551 (1, 1 << 10, _('%.2f KB')),
1551 (1, 1, _('%.0f bytes')),
1552 (1, 1, _('%.0f bytes')),
1552 )
1553 )
1553
1554
1554 def uirepr(s):
1555 def uirepr(s):
1555 # Avoid double backslash in Windows path repr()
1556 # Avoid double backslash in Windows path repr()
1556 return repr(s).replace('\\\\', '\\')
1557 return repr(s).replace('\\\\', '\\')
1557
1558
1558 # delay import of textwrap
1559 # delay import of textwrap
1559 def MBTextWrapper(**kwargs):
1560 def MBTextWrapper(**kwargs):
1560 class tw(textwrap.TextWrapper):
1561 class tw(textwrap.TextWrapper):
1561 """
1562 """
1562 Extend TextWrapper for width-awareness.
1563 Extend TextWrapper for width-awareness.
1563
1564
1564 Neither number of 'bytes' in any encoding nor 'characters' is
1565 Neither number of 'bytes' in any encoding nor 'characters' is
1565 appropriate to calculate terminal columns for specified string.
1566 appropriate to calculate terminal columns for specified string.
1566
1567
1567 Original TextWrapper implementation uses built-in 'len()' directly,
1568 Original TextWrapper implementation uses built-in 'len()' directly,
1568 so overriding is needed to use width information of each characters.
1569 so overriding is needed to use width information of each characters.
1569
1570
1570 In addition, characters classified into 'ambiguous' width are
1571 In addition, characters classified into 'ambiguous' width are
1571 treated as wide in East Asian area, but as narrow in other.
1572 treated as wide in East Asian area, but as narrow in other.
1572
1573
1573 This requires use decision to determine width of such characters.
1574 This requires use decision to determine width of such characters.
1574 """
1575 """
1575 def __init__(self, **kwargs):
1576 def __init__(self, **kwargs):
1576 textwrap.TextWrapper.__init__(self, **kwargs)
1577 textwrap.TextWrapper.__init__(self, **kwargs)
1577
1578
1578 # for compatibility between 2.4 and 2.6
1579 # for compatibility between 2.4 and 2.6
1579 if getattr(self, 'drop_whitespace', None) is None:
1580 if getattr(self, 'drop_whitespace', None) is None:
1580 self.drop_whitespace = kwargs.get('drop_whitespace', True)
1581 self.drop_whitespace = kwargs.get('drop_whitespace', True)
1581
1582
1582 def _cutdown(self, ucstr, space_left):
1583 def _cutdown(self, ucstr, space_left):
1583 l = 0
1584 l = 0
1584 colwidth = encoding.ucolwidth
1585 colwidth = encoding.ucolwidth
1585 for i in xrange(len(ucstr)):
1586 for i in xrange(len(ucstr)):
1586 l += colwidth(ucstr[i])
1587 l += colwidth(ucstr[i])
1587 if space_left < l:
1588 if space_left < l:
1588 return (ucstr[:i], ucstr[i:])
1589 return (ucstr[:i], ucstr[i:])
1589 return ucstr, ''
1590 return ucstr, ''
1590
1591
1591 # overriding of base class
1592 # overriding of base class
1592 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1593 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1593 space_left = max(width - cur_len, 1)
1594 space_left = max(width - cur_len, 1)
1594
1595
1595 if self.break_long_words:
1596 if self.break_long_words:
1596 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1597 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1597 cur_line.append(cut)
1598 cur_line.append(cut)
1598 reversed_chunks[-1] = res
1599 reversed_chunks[-1] = res
1599 elif not cur_line:
1600 elif not cur_line:
1600 cur_line.append(reversed_chunks.pop())
1601 cur_line.append(reversed_chunks.pop())
1601
1602
1602 # this overriding code is imported from TextWrapper of python 2.6
1603 # this overriding code is imported from TextWrapper of python 2.6
1603 # to calculate columns of string by 'encoding.ucolwidth()'
1604 # to calculate columns of string by 'encoding.ucolwidth()'
1604 def _wrap_chunks(self, chunks):
1605 def _wrap_chunks(self, chunks):
1605 colwidth = encoding.ucolwidth
1606 colwidth = encoding.ucolwidth
1606
1607
1607 lines = []
1608 lines = []
1608 if self.width <= 0:
1609 if self.width <= 0:
1609 raise ValueError("invalid width %r (must be > 0)" % self.width)
1610 raise ValueError("invalid width %r (must be > 0)" % self.width)
1610
1611
1611 # Arrange in reverse order so items can be efficiently popped
1612 # Arrange in reverse order so items can be efficiently popped
1612 # from a stack of chucks.
1613 # from a stack of chucks.
1613 chunks.reverse()
1614 chunks.reverse()
1614
1615
1615 while chunks:
1616 while chunks:
1616
1617
1617 # Start the list of chunks that will make up the current line.
1618 # Start the list of chunks that will make up the current line.
1618 # cur_len is just the length of all the chunks in cur_line.
1619 # cur_len is just the length of all the chunks in cur_line.
1619 cur_line = []
1620 cur_line = []
1620 cur_len = 0
1621 cur_len = 0
1621
1622
1622 # Figure out which static string will prefix this line.
1623 # Figure out which static string will prefix this line.
1623 if lines:
1624 if lines:
1624 indent = self.subsequent_indent
1625 indent = self.subsequent_indent
1625 else:
1626 else:
1626 indent = self.initial_indent
1627 indent = self.initial_indent
1627
1628
1628 # Maximum width for this line.
1629 # Maximum width for this line.
1629 width = self.width - len(indent)
1630 width = self.width - len(indent)
1630
1631
1631 # First chunk on line is whitespace -- drop it, unless this
1632 # First chunk on line is whitespace -- drop it, unless this
1632 # is the very beginning of the text (i.e. no lines started yet).
1633 # is the very beginning of the text (i.e. no lines started yet).
1633 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
1634 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
1634 del chunks[-1]
1635 del chunks[-1]
1635
1636
1636 while chunks:
1637 while chunks:
1637 l = colwidth(chunks[-1])
1638 l = colwidth(chunks[-1])
1638
1639
1639 # Can at least squeeze this chunk onto the current line.
1640 # Can at least squeeze this chunk onto the current line.
1640 if cur_len + l <= width:
1641 if cur_len + l <= width:
1641 cur_line.append(chunks.pop())
1642 cur_line.append(chunks.pop())
1642 cur_len += l
1643 cur_len += l
1643
1644
1644 # Nope, this line is full.
1645 # Nope, this line is full.
1645 else:
1646 else:
1646 break
1647 break
1647
1648
1648 # The current line is full, and the next chunk is too big to
1649 # The current line is full, and the next chunk is too big to
1649 # fit on *any* line (not just this one).
1650 # fit on *any* line (not just this one).
1650 if chunks and colwidth(chunks[-1]) > width:
1651 if chunks and colwidth(chunks[-1]) > width:
1651 self._handle_long_word(chunks, cur_line, cur_len, width)
1652 self._handle_long_word(chunks, cur_line, cur_len, width)
1652
1653
1653 # If the last chunk on this line is all whitespace, drop it.
1654 # If the last chunk on this line is all whitespace, drop it.
1654 if (self.drop_whitespace and
1655 if (self.drop_whitespace and
1655 cur_line and cur_line[-1].strip() == ''):
1656 cur_line and cur_line[-1].strip() == ''):
1656 del cur_line[-1]
1657 del cur_line[-1]
1657
1658
1658 # Convert current line back to a string and store it in list
1659 # Convert current line back to a string and store it in list
1659 # of all lines (return value).
1660 # of all lines (return value).
1660 if cur_line:
1661 if cur_line:
1661 lines.append(indent + ''.join(cur_line))
1662 lines.append(indent + ''.join(cur_line))
1662
1663
1663 return lines
1664 return lines
1664
1665
1665 global MBTextWrapper
1666 global MBTextWrapper
1666 MBTextWrapper = tw
1667 MBTextWrapper = tw
1667 return tw(**kwargs)
1668 return tw(**kwargs)
1668
1669
1669 def wrap(line, width, initindent='', hangindent=''):
1670 def wrap(line, width, initindent='', hangindent=''):
1670 maxindent = max(len(hangindent), len(initindent))
1671 maxindent = max(len(hangindent), len(initindent))
1671 if width <= maxindent:
1672 if width <= maxindent:
1672 # adjust for weird terminal size
1673 # adjust for weird terminal size
1673 width = max(78, maxindent + 1)
1674 width = max(78, maxindent + 1)
1674 line = line.decode(encoding.encoding, encoding.encodingmode)
1675 line = line.decode(encoding.encoding, encoding.encodingmode)
1675 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
1676 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
1676 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
1677 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
1677 wrapper = MBTextWrapper(width=width,
1678 wrapper = MBTextWrapper(width=width,
1678 initial_indent=initindent,
1679 initial_indent=initindent,
1679 subsequent_indent=hangindent)
1680 subsequent_indent=hangindent)
1680 return wrapper.fill(line).encode(encoding.encoding)
1681 return wrapper.fill(line).encode(encoding.encoding)
1681
1682
1682 def iterlines(iterator):
1683 def iterlines(iterator):
1683 for chunk in iterator:
1684 for chunk in iterator:
1684 for line in chunk.splitlines():
1685 for line in chunk.splitlines():
1685 yield line
1686 yield line
1686
1687
1687 def expandpath(path):
1688 def expandpath(path):
1688 return os.path.expanduser(os.path.expandvars(path))
1689 return os.path.expanduser(os.path.expandvars(path))
1689
1690
1690 def hgcmd():
1691 def hgcmd():
1691 """Return the command used to execute current hg
1692 """Return the command used to execute current hg
1692
1693
1693 This is different from hgexecutable() because on Windows we want
1694 This is different from hgexecutable() because on Windows we want
1694 to avoid things opening new shell windows like batch files, so we
1695 to avoid things opening new shell windows like batch files, so we
1695 get either the python call or current executable.
1696 get either the python call or current executable.
1696 """
1697 """
1697 if mainfrozen():
1698 if mainfrozen():
1698 return [sys.executable]
1699 return [sys.executable]
1699 return gethgcmd()
1700 return gethgcmd()
1700
1701
1701 def rundetached(args, condfn):
1702 def rundetached(args, condfn):
1702 """Execute the argument list in a detached process.
1703 """Execute the argument list in a detached process.
1703
1704
1704 condfn is a callable which is called repeatedly and should return
1705 condfn is a callable which is called repeatedly and should return
1705 True once the child process is known to have started successfully.
1706 True once the child process is known to have started successfully.
1706 At this point, the child process PID is returned. If the child
1707 At this point, the child process PID is returned. If the child
1707 process fails to start or finishes before condfn() evaluates to
1708 process fails to start or finishes before condfn() evaluates to
1708 True, return -1.
1709 True, return -1.
1709 """
1710 """
1710 # Windows case is easier because the child process is either
1711 # Windows case is easier because the child process is either
1711 # successfully starting and validating the condition or exiting
1712 # successfully starting and validating the condition or exiting
1712 # on failure. We just poll on its PID. On Unix, if the child
1713 # on failure. We just poll on its PID. On Unix, if the child
1713 # process fails to start, it will be left in a zombie state until
1714 # process fails to start, it will be left in a zombie state until
1714 # the parent wait on it, which we cannot do since we expect a long
1715 # the parent wait on it, which we cannot do since we expect a long
1715 # running process on success. Instead we listen for SIGCHLD telling
1716 # running process on success. Instead we listen for SIGCHLD telling
1716 # us our child process terminated.
1717 # us our child process terminated.
1717 terminated = set()
1718 terminated = set()
1718 def handler(signum, frame):
1719 def handler(signum, frame):
1719 terminated.add(os.wait())
1720 terminated.add(os.wait())
1720 prevhandler = None
1721 prevhandler = None
1721 SIGCHLD = getattr(signal, 'SIGCHLD', None)
1722 SIGCHLD = getattr(signal, 'SIGCHLD', None)
1722 if SIGCHLD is not None:
1723 if SIGCHLD is not None:
1723 prevhandler = signal.signal(SIGCHLD, handler)
1724 prevhandler = signal.signal(SIGCHLD, handler)
1724 try:
1725 try:
1725 pid = spawndetached(args)
1726 pid = spawndetached(args)
1726 while not condfn():
1727 while not condfn():
1727 if ((pid in terminated or not testpid(pid))
1728 if ((pid in terminated or not testpid(pid))
1728 and not condfn()):
1729 and not condfn()):
1729 return -1
1730 return -1
1730 time.sleep(0.1)
1731 time.sleep(0.1)
1731 return pid
1732 return pid
1732 finally:
1733 finally:
1733 if prevhandler is not None:
1734 if prevhandler is not None:
1734 signal.signal(signal.SIGCHLD, prevhandler)
1735 signal.signal(signal.SIGCHLD, prevhandler)
1735
1736
1736 try:
1737 try:
1737 any, all = any, all
1738 any, all = any, all
1738 except NameError:
1739 except NameError:
1739 def any(iterable):
1740 def any(iterable):
1740 for i in iterable:
1741 for i in iterable:
1741 if i:
1742 if i:
1742 return True
1743 return True
1743 return False
1744 return False
1744
1745
1745 def all(iterable):
1746 def all(iterable):
1746 for i in iterable:
1747 for i in iterable:
1747 if not i:
1748 if not i:
1748 return False
1749 return False
1749 return True
1750 return True
1750
1751
1751 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1752 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1752 """Return the result of interpolating items in the mapping into string s.
1753 """Return the result of interpolating items in the mapping into string s.
1753
1754
1754 prefix is a single character string, or a two character string with
1755 prefix is a single character string, or a two character string with
1755 a backslash as the first character if the prefix needs to be escaped in
1756 a backslash as the first character if the prefix needs to be escaped in
1756 a regular expression.
1757 a regular expression.
1757
1758
1758 fn is an optional function that will be applied to the replacement text
1759 fn is an optional function that will be applied to the replacement text
1759 just before replacement.
1760 just before replacement.
1760
1761
1761 escape_prefix is an optional flag that allows using doubled prefix for
1762 escape_prefix is an optional flag that allows using doubled prefix for
1762 its escaping.
1763 its escaping.
1763 """
1764 """
1764 fn = fn or (lambda s: s)
1765 fn = fn or (lambda s: s)
1765 patterns = '|'.join(mapping.keys())
1766 patterns = '|'.join(mapping.keys())
1766 if escape_prefix:
1767 if escape_prefix:
1767 patterns += '|' + prefix
1768 patterns += '|' + prefix
1768 if len(prefix) > 1:
1769 if len(prefix) > 1:
1769 prefix_char = prefix[1:]
1770 prefix_char = prefix[1:]
1770 else:
1771 else:
1771 prefix_char = prefix
1772 prefix_char = prefix
1772 mapping[prefix_char] = prefix_char
1773 mapping[prefix_char] = prefix_char
1773 r = remod.compile(r'%s(%s)' % (prefix, patterns))
1774 r = remod.compile(r'%s(%s)' % (prefix, patterns))
1774 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1775 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1775
1776
1776 def getport(port):
1777 def getport(port):
1777 """Return the port for a given network service.
1778 """Return the port for a given network service.
1778
1779
1779 If port is an integer, it's returned as is. If it's a string, it's
1780 If port is an integer, it's returned as is. If it's a string, it's
1780 looked up using socket.getservbyname(). If there's no matching
1781 looked up using socket.getservbyname(). If there's no matching
1781 service, util.Abort is raised.
1782 service, util.Abort is raised.
1782 """
1783 """
1783 try:
1784 try:
1784 return int(port)
1785 return int(port)
1785 except ValueError:
1786 except ValueError:
1786 pass
1787 pass
1787
1788
1788 try:
1789 try:
1789 return socket.getservbyname(port)
1790 return socket.getservbyname(port)
1790 except socket.error:
1791 except socket.error:
1791 raise Abort(_("no port number associated with service '%s'") % port)
1792 raise Abort(_("no port number associated with service '%s'") % port)
1792
1793
1793 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1794 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1794 '0': False, 'no': False, 'false': False, 'off': False,
1795 '0': False, 'no': False, 'false': False, 'off': False,
1795 'never': False}
1796 'never': False}
1796
1797
1797 def parsebool(s):
1798 def parsebool(s):
1798 """Parse s into a boolean.
1799 """Parse s into a boolean.
1799
1800
1800 If s is not a valid boolean, returns None.
1801 If s is not a valid boolean, returns None.
1801 """
1802 """
1802 return _booleans.get(s.lower(), None)
1803 return _booleans.get(s.lower(), None)
1803
1804
1804 _hexdig = '0123456789ABCDEFabcdef'
1805 _hexdig = '0123456789ABCDEFabcdef'
1805 _hextochr = dict((a + b, chr(int(a + b, 16)))
1806 _hextochr = dict((a + b, chr(int(a + b, 16)))
1806 for a in _hexdig for b in _hexdig)
1807 for a in _hexdig for b in _hexdig)
1807
1808
1808 def _urlunquote(s):
1809 def _urlunquote(s):
1809 """Decode HTTP/HTML % encoding.
1810 """Decode HTTP/HTML % encoding.
1810
1811
1811 >>> _urlunquote('abc%20def')
1812 >>> _urlunquote('abc%20def')
1812 'abc def'
1813 'abc def'
1813 """
1814 """
1814 res = s.split('%')
1815 res = s.split('%')
1815 # fastpath
1816 # fastpath
1816 if len(res) == 1:
1817 if len(res) == 1:
1817 return s
1818 return s
1818 s = res[0]
1819 s = res[0]
1819 for item in res[1:]:
1820 for item in res[1:]:
1820 try:
1821 try:
1821 s += _hextochr[item[:2]] + item[2:]
1822 s += _hextochr[item[:2]] + item[2:]
1822 except KeyError:
1823 except KeyError:
1823 s += '%' + item
1824 s += '%' + item
1824 except UnicodeDecodeError:
1825 except UnicodeDecodeError:
1825 s += unichr(int(item[:2], 16)) + item[2:]
1826 s += unichr(int(item[:2], 16)) + item[2:]
1826 return s
1827 return s
1827
1828
1828 class url(object):
1829 class url(object):
1829 r"""Reliable URL parser.
1830 r"""Reliable URL parser.
1830
1831
1831 This parses URLs and provides attributes for the following
1832 This parses URLs and provides attributes for the following
1832 components:
1833 components:
1833
1834
1834 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
1835 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
1835
1836
1836 Missing components are set to None. The only exception is
1837 Missing components are set to None. The only exception is
1837 fragment, which is set to '' if present but empty.
1838 fragment, which is set to '' if present but empty.
1838
1839
1839 If parsefragment is False, fragment is included in query. If
1840 If parsefragment is False, fragment is included in query. If
1840 parsequery is False, query is included in path. If both are
1841 parsequery is False, query is included in path. If both are
1841 False, both fragment and query are included in path.
1842 False, both fragment and query are included in path.
1842
1843
1843 See http://www.ietf.org/rfc/rfc2396.txt for more information.
1844 See http://www.ietf.org/rfc/rfc2396.txt for more information.
1844
1845
1845 Note that for backward compatibility reasons, bundle URLs do not
1846 Note that for backward compatibility reasons, bundle URLs do not
1846 take host names. That means 'bundle://../' has a path of '../'.
1847 take host names. That means 'bundle://../' has a path of '../'.
1847
1848
1848 Examples:
1849 Examples:
1849
1850
1850 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
1851 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
1851 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
1852 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
1852 >>> url('ssh://[::1]:2200//home/joe/repo')
1853 >>> url('ssh://[::1]:2200//home/joe/repo')
1853 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
1854 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
1854 >>> url('file:///home/joe/repo')
1855 >>> url('file:///home/joe/repo')
1855 <url scheme: 'file', path: '/home/joe/repo'>
1856 <url scheme: 'file', path: '/home/joe/repo'>
1856 >>> url('file:///c:/temp/foo/')
1857 >>> url('file:///c:/temp/foo/')
1857 <url scheme: 'file', path: 'c:/temp/foo/'>
1858 <url scheme: 'file', path: 'c:/temp/foo/'>
1858 >>> url('bundle:foo')
1859 >>> url('bundle:foo')
1859 <url scheme: 'bundle', path: 'foo'>
1860 <url scheme: 'bundle', path: 'foo'>
1860 >>> url('bundle://../foo')
1861 >>> url('bundle://../foo')
1861 <url scheme: 'bundle', path: '../foo'>
1862 <url scheme: 'bundle', path: '../foo'>
1862 >>> url(r'c:\foo\bar')
1863 >>> url(r'c:\foo\bar')
1863 <url path: 'c:\\foo\\bar'>
1864 <url path: 'c:\\foo\\bar'>
1864 >>> url(r'\\blah\blah\blah')
1865 >>> url(r'\\blah\blah\blah')
1865 <url path: '\\\\blah\\blah\\blah'>
1866 <url path: '\\\\blah\\blah\\blah'>
1866 >>> url(r'\\blah\blah\blah#baz')
1867 >>> url(r'\\blah\blah\blah#baz')
1867 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
1868 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
1868 >>> url(r'file:///C:\users\me')
1869 >>> url(r'file:///C:\users\me')
1869 <url scheme: 'file', path: 'C:\\users\\me'>
1870 <url scheme: 'file', path: 'C:\\users\\me'>
1870
1871
1871 Authentication credentials:
1872 Authentication credentials:
1872
1873
1873 >>> url('ssh://joe:xyz@x/repo')
1874 >>> url('ssh://joe:xyz@x/repo')
1874 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
1875 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
1875 >>> url('ssh://joe@x/repo')
1876 >>> url('ssh://joe@x/repo')
1876 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
1877 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
1877
1878
1878 Query strings and fragments:
1879 Query strings and fragments:
1879
1880
1880 >>> url('http://host/a?b#c')
1881 >>> url('http://host/a?b#c')
1881 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
1882 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
1882 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
1883 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
1883 <url scheme: 'http', host: 'host', path: 'a?b#c'>
1884 <url scheme: 'http', host: 'host', path: 'a?b#c'>
1884 """
1885 """
1885
1886
1886 _safechars = "!~*'()+"
1887 _safechars = "!~*'()+"
1887 _safepchars = "/!~*'()+:\\"
1888 _safepchars = "/!~*'()+:\\"
1888 _matchscheme = remod.compile(r'^[a-zA-Z0-9+.\-]+:').match
1889 _matchscheme = remod.compile(r'^[a-zA-Z0-9+.\-]+:').match
1889
1890
1890 def __init__(self, path, parsequery=True, parsefragment=True):
1891 def __init__(self, path, parsequery=True, parsefragment=True):
1891 # We slowly chomp away at path until we have only the path left
1892 # We slowly chomp away at path until we have only the path left
1892 self.scheme = self.user = self.passwd = self.host = None
1893 self.scheme = self.user = self.passwd = self.host = None
1893 self.port = self.path = self.query = self.fragment = None
1894 self.port = self.path = self.query = self.fragment = None
1894 self._localpath = True
1895 self._localpath = True
1895 self._hostport = ''
1896 self._hostport = ''
1896 self._origpath = path
1897 self._origpath = path
1897
1898
1898 if parsefragment and '#' in path:
1899 if parsefragment and '#' in path:
1899 path, self.fragment = path.split('#', 1)
1900 path, self.fragment = path.split('#', 1)
1900 if not path:
1901 if not path:
1901 path = None
1902 path = None
1902
1903
1903 # special case for Windows drive letters and UNC paths
1904 # special case for Windows drive letters and UNC paths
1904 if hasdriveletter(path) or path.startswith(r'\\'):
1905 if hasdriveletter(path) or path.startswith(r'\\'):
1905 self.path = path
1906 self.path = path
1906 return
1907 return
1907
1908
1908 # For compatibility reasons, we can't handle bundle paths as
1909 # For compatibility reasons, we can't handle bundle paths as
1909 # normal URLS
1910 # normal URLS
1910 if path.startswith('bundle:'):
1911 if path.startswith('bundle:'):
1911 self.scheme = 'bundle'
1912 self.scheme = 'bundle'
1912 path = path[7:]
1913 path = path[7:]
1913 if path.startswith('//'):
1914 if path.startswith('//'):
1914 path = path[2:]
1915 path = path[2:]
1915 self.path = path
1916 self.path = path
1916 return
1917 return
1917
1918
1918 if self._matchscheme(path):
1919 if self._matchscheme(path):
1919 parts = path.split(':', 1)
1920 parts = path.split(':', 1)
1920 if parts[0]:
1921 if parts[0]:
1921 self.scheme, path = parts
1922 self.scheme, path = parts
1922 self._localpath = False
1923 self._localpath = False
1923
1924
1924 if not path:
1925 if not path:
1925 path = None
1926 path = None
1926 if self._localpath:
1927 if self._localpath:
1927 self.path = ''
1928 self.path = ''
1928 return
1929 return
1929 else:
1930 else:
1930 if self._localpath:
1931 if self._localpath:
1931 self.path = path
1932 self.path = path
1932 return
1933 return
1933
1934
1934 if parsequery and '?' in path:
1935 if parsequery and '?' in path:
1935 path, self.query = path.split('?', 1)
1936 path, self.query = path.split('?', 1)
1936 if not path:
1937 if not path:
1937 path = None
1938 path = None
1938 if not self.query:
1939 if not self.query:
1939 self.query = None
1940 self.query = None
1940
1941
1941 # // is required to specify a host/authority
1942 # // is required to specify a host/authority
1942 if path and path.startswith('//'):
1943 if path and path.startswith('//'):
1943 parts = path[2:].split('/', 1)
1944 parts = path[2:].split('/', 1)
1944 if len(parts) > 1:
1945 if len(parts) > 1:
1945 self.host, path = parts
1946 self.host, path = parts
1946 else:
1947 else:
1947 self.host = parts[0]
1948 self.host = parts[0]
1948 path = None
1949 path = None
1949 if not self.host:
1950 if not self.host:
1950 self.host = None
1951 self.host = None
1951 # path of file:///d is /d
1952 # path of file:///d is /d
1952 # path of file:///d:/ is d:/, not /d:/
1953 # path of file:///d:/ is d:/, not /d:/
1953 if path and not hasdriveletter(path):
1954 if path and not hasdriveletter(path):
1954 path = '/' + path
1955 path = '/' + path
1955
1956
1956 if self.host and '@' in self.host:
1957 if self.host and '@' in self.host:
1957 self.user, self.host = self.host.rsplit('@', 1)
1958 self.user, self.host = self.host.rsplit('@', 1)
1958 if ':' in self.user:
1959 if ':' in self.user:
1959 self.user, self.passwd = self.user.split(':', 1)
1960 self.user, self.passwd = self.user.split(':', 1)
1960 if not self.host:
1961 if not self.host:
1961 self.host = None
1962 self.host = None
1962
1963
1963 # Don't split on colons in IPv6 addresses without ports
1964 # Don't split on colons in IPv6 addresses without ports
1964 if (self.host and ':' in self.host and
1965 if (self.host and ':' in self.host and
1965 not (self.host.startswith('[') and self.host.endswith(']'))):
1966 not (self.host.startswith('[') and self.host.endswith(']'))):
1966 self._hostport = self.host
1967 self._hostport = self.host
1967 self.host, self.port = self.host.rsplit(':', 1)
1968 self.host, self.port = self.host.rsplit(':', 1)
1968 if not self.host:
1969 if not self.host:
1969 self.host = None
1970 self.host = None
1970
1971
1971 if (self.host and self.scheme == 'file' and
1972 if (self.host and self.scheme == 'file' and
1972 self.host not in ('localhost', '127.0.0.1', '[::1]')):
1973 self.host not in ('localhost', '127.0.0.1', '[::1]')):
1973 raise Abort(_('file:// URLs can only refer to localhost'))
1974 raise Abort(_('file:// URLs can only refer to localhost'))
1974
1975
1975 self.path = path
1976 self.path = path
1976
1977
1977 # leave the query string escaped
1978 # leave the query string escaped
1978 for a in ('user', 'passwd', 'host', 'port',
1979 for a in ('user', 'passwd', 'host', 'port',
1979 'path', 'fragment'):
1980 'path', 'fragment'):
1980 v = getattr(self, a)
1981 v = getattr(self, a)
1981 if v is not None:
1982 if v is not None:
1982 setattr(self, a, _urlunquote(v))
1983 setattr(self, a, _urlunquote(v))
1983
1984
1984 def __repr__(self):
1985 def __repr__(self):
1985 attrs = []
1986 attrs = []
1986 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
1987 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
1987 'query', 'fragment'):
1988 'query', 'fragment'):
1988 v = getattr(self, a)
1989 v = getattr(self, a)
1989 if v is not None:
1990 if v is not None:
1990 attrs.append('%s: %r' % (a, v))
1991 attrs.append('%s: %r' % (a, v))
1991 return '<url %s>' % ', '.join(attrs)
1992 return '<url %s>' % ', '.join(attrs)
1992
1993
1993 def __str__(self):
1994 def __str__(self):
1994 r"""Join the URL's components back into a URL string.
1995 r"""Join the URL's components back into a URL string.
1995
1996
1996 Examples:
1997 Examples:
1997
1998
1998 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
1999 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
1999 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2000 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2000 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2001 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2001 'http://user:pw@host:80/?foo=bar&baz=42'
2002 'http://user:pw@host:80/?foo=bar&baz=42'
2002 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2003 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2003 'http://user:pw@host:80/?foo=bar%3dbaz'
2004 'http://user:pw@host:80/?foo=bar%3dbaz'
2004 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2005 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2005 'ssh://user:pw@[::1]:2200//home/joe#'
2006 'ssh://user:pw@[::1]:2200//home/joe#'
2006 >>> str(url('http://localhost:80//'))
2007 >>> str(url('http://localhost:80//'))
2007 'http://localhost:80//'
2008 'http://localhost:80//'
2008 >>> str(url('http://localhost:80/'))
2009 >>> str(url('http://localhost:80/'))
2009 'http://localhost:80/'
2010 'http://localhost:80/'
2010 >>> str(url('http://localhost:80'))
2011 >>> str(url('http://localhost:80'))
2011 'http://localhost:80/'
2012 'http://localhost:80/'
2012 >>> str(url('bundle:foo'))
2013 >>> str(url('bundle:foo'))
2013 'bundle:foo'
2014 'bundle:foo'
2014 >>> str(url('bundle://../foo'))
2015 >>> str(url('bundle://../foo'))
2015 'bundle:../foo'
2016 'bundle:../foo'
2016 >>> str(url('path'))
2017 >>> str(url('path'))
2017 'path'
2018 'path'
2018 >>> str(url('file:///tmp/foo/bar'))
2019 >>> str(url('file:///tmp/foo/bar'))
2019 'file:///tmp/foo/bar'
2020 'file:///tmp/foo/bar'
2020 >>> str(url('file:///c:/tmp/foo/bar'))
2021 >>> str(url('file:///c:/tmp/foo/bar'))
2021 'file:///c:/tmp/foo/bar'
2022 'file:///c:/tmp/foo/bar'
2022 >>> print url(r'bundle:foo\bar')
2023 >>> print url(r'bundle:foo\bar')
2023 bundle:foo\bar
2024 bundle:foo\bar
2024 >>> print url(r'file:///D:\data\hg')
2025 >>> print url(r'file:///D:\data\hg')
2025 file:///D:\data\hg
2026 file:///D:\data\hg
2026 """
2027 """
2027 if self._localpath:
2028 if self._localpath:
2028 s = self.path
2029 s = self.path
2029 if self.scheme == 'bundle':
2030 if self.scheme == 'bundle':
2030 s = 'bundle:' + s
2031 s = 'bundle:' + s
2031 if self.fragment:
2032 if self.fragment:
2032 s += '#' + self.fragment
2033 s += '#' + self.fragment
2033 return s
2034 return s
2034
2035
2035 s = self.scheme + ':'
2036 s = self.scheme + ':'
2036 if self.user or self.passwd or self.host:
2037 if self.user or self.passwd or self.host:
2037 s += '//'
2038 s += '//'
2038 elif self.scheme and (not self.path or self.path.startswith('/')
2039 elif self.scheme and (not self.path or self.path.startswith('/')
2039 or hasdriveletter(self.path)):
2040 or hasdriveletter(self.path)):
2040 s += '//'
2041 s += '//'
2041 if hasdriveletter(self.path):
2042 if hasdriveletter(self.path):
2042 s += '/'
2043 s += '/'
2043 if self.user:
2044 if self.user:
2044 s += urllib.quote(self.user, safe=self._safechars)
2045 s += urllib.quote(self.user, safe=self._safechars)
2045 if self.passwd:
2046 if self.passwd:
2046 s += ':' + urllib.quote(self.passwd, safe=self._safechars)
2047 s += ':' + urllib.quote(self.passwd, safe=self._safechars)
2047 if self.user or self.passwd:
2048 if self.user or self.passwd:
2048 s += '@'
2049 s += '@'
2049 if self.host:
2050 if self.host:
2050 if not (self.host.startswith('[') and self.host.endswith(']')):
2051 if not (self.host.startswith('[') and self.host.endswith(']')):
2051 s += urllib.quote(self.host)
2052 s += urllib.quote(self.host)
2052 else:
2053 else:
2053 s += self.host
2054 s += self.host
2054 if self.port:
2055 if self.port:
2055 s += ':' + urllib.quote(self.port)
2056 s += ':' + urllib.quote(self.port)
2056 if self.host:
2057 if self.host:
2057 s += '/'
2058 s += '/'
2058 if self.path:
2059 if self.path:
2059 # TODO: similar to the query string, we should not unescape the
2060 # TODO: similar to the query string, we should not unescape the
2060 # path when we store it, the path might contain '%2f' = '/',
2061 # path when we store it, the path might contain '%2f' = '/',
2061 # which we should *not* escape.
2062 # which we should *not* escape.
2062 s += urllib.quote(self.path, safe=self._safepchars)
2063 s += urllib.quote(self.path, safe=self._safepchars)
2063 if self.query:
2064 if self.query:
2064 # we store the query in escaped form.
2065 # we store the query in escaped form.
2065 s += '?' + self.query
2066 s += '?' + self.query
2066 if self.fragment is not None:
2067 if self.fragment is not None:
2067 s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
2068 s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
2068 return s
2069 return s
2069
2070
2070 def authinfo(self):
2071 def authinfo(self):
2071 user, passwd = self.user, self.passwd
2072 user, passwd = self.user, self.passwd
2072 try:
2073 try:
2073 self.user, self.passwd = None, None
2074 self.user, self.passwd = None, None
2074 s = str(self)
2075 s = str(self)
2075 finally:
2076 finally:
2076 self.user, self.passwd = user, passwd
2077 self.user, self.passwd = user, passwd
2077 if not self.user:
2078 if not self.user:
2078 return (s, None)
2079 return (s, None)
2079 # authinfo[1] is passed to urllib2 password manager, and its
2080 # authinfo[1] is passed to urllib2 password manager, and its
2080 # URIs must not contain credentials. The host is passed in the
2081 # URIs must not contain credentials. The host is passed in the
2081 # URIs list because Python < 2.4.3 uses only that to search for
2082 # URIs list because Python < 2.4.3 uses only that to search for
2082 # a password.
2083 # a password.
2083 return (s, (None, (s, self.host),
2084 return (s, (None, (s, self.host),
2084 self.user, self.passwd or ''))
2085 self.user, self.passwd or ''))
2085
2086
2086 def isabs(self):
2087 def isabs(self):
2087 if self.scheme and self.scheme != 'file':
2088 if self.scheme and self.scheme != 'file':
2088 return True # remote URL
2089 return True # remote URL
2089 if hasdriveletter(self.path):
2090 if hasdriveletter(self.path):
2090 return True # absolute for our purposes - can't be joined()
2091 return True # absolute for our purposes - can't be joined()
2091 if self.path.startswith(r'\\'):
2092 if self.path.startswith(r'\\'):
2092 return True # Windows UNC path
2093 return True # Windows UNC path
2093 if self.path.startswith('/'):
2094 if self.path.startswith('/'):
2094 return True # POSIX-style
2095 return True # POSIX-style
2095 return False
2096 return False
2096
2097
2097 def localpath(self):
2098 def localpath(self):
2098 if self.scheme == 'file' or self.scheme == 'bundle':
2099 if self.scheme == 'file' or self.scheme == 'bundle':
2099 path = self.path or '/'
2100 path = self.path or '/'
2100 # For Windows, we need to promote hosts containing drive
2101 # For Windows, we need to promote hosts containing drive
2101 # letters to paths with drive letters.
2102 # letters to paths with drive letters.
2102 if hasdriveletter(self._hostport):
2103 if hasdriveletter(self._hostport):
2103 path = self._hostport + '/' + self.path
2104 path = self._hostport + '/' + self.path
2104 elif (self.host is not None and self.path
2105 elif (self.host is not None and self.path
2105 and not hasdriveletter(path)):
2106 and not hasdriveletter(path)):
2106 path = '/' + path
2107 path = '/' + path
2107 return path
2108 return path
2108 return self._origpath
2109 return self._origpath
2109
2110
2110 def islocal(self):
2111 def islocal(self):
2111 '''whether localpath will return something that posixfile can open'''
2112 '''whether localpath will return something that posixfile can open'''
2112 return (not self.scheme or self.scheme == 'file'
2113 return (not self.scheme or self.scheme == 'file'
2113 or self.scheme == 'bundle')
2114 or self.scheme == 'bundle')
2114
2115
2115 def hasscheme(path):
2116 def hasscheme(path):
2116 return bool(url(path).scheme)
2117 return bool(url(path).scheme)
2117
2118
2118 def hasdriveletter(path):
2119 def hasdriveletter(path):
2119 return path and path[1:2] == ':' and path[0:1].isalpha()
2120 return path and path[1:2] == ':' and path[0:1].isalpha()
2120
2121
2121 def urllocalpath(path):
2122 def urllocalpath(path):
2122 return url(path, parsequery=False, parsefragment=False).localpath()
2123 return url(path, parsequery=False, parsefragment=False).localpath()
2123
2124
2124 def hidepassword(u):
2125 def hidepassword(u):
2125 '''hide user credential in a url string'''
2126 '''hide user credential in a url string'''
2126 u = url(u)
2127 u = url(u)
2127 if u.passwd:
2128 if u.passwd:
2128 u.passwd = '***'
2129 u.passwd = '***'
2129 return str(u)
2130 return str(u)
2130
2131
2131 def removeauth(u):
2132 def removeauth(u):
2132 '''remove all authentication information from a url string'''
2133 '''remove all authentication information from a url string'''
2133 u = url(u)
2134 u = url(u)
2134 u.user = u.passwd = None
2135 u.user = u.passwd = None
2135 return str(u)
2136 return str(u)
2136
2137
2137 def isatty(fd):
2138 def isatty(fd):
2138 try:
2139 try:
2139 return fd.isatty()
2140 return fd.isatty()
2140 except AttributeError:
2141 except AttributeError:
2141 return False
2142 return False
2142
2143
2143 timecount = unitcountfn(
2144 timecount = unitcountfn(
2144 (1, 1e3, _('%.0f s')),
2145 (1, 1e3, _('%.0f s')),
2145 (100, 1, _('%.1f s')),
2146 (100, 1, _('%.1f s')),
2146 (10, 1, _('%.2f s')),
2147 (10, 1, _('%.2f s')),
2147 (1, 1, _('%.3f s')),
2148 (1, 1, _('%.3f s')),
2148 (100, 0.001, _('%.1f ms')),
2149 (100, 0.001, _('%.1f ms')),
2149 (10, 0.001, _('%.2f ms')),
2150 (10, 0.001, _('%.2f ms')),
2150 (1, 0.001, _('%.3f ms')),
2151 (1, 0.001, _('%.3f ms')),
2151 (100, 0.000001, _('%.1f us')),
2152 (100, 0.000001, _('%.1f us')),
2152 (10, 0.000001, _('%.2f us')),
2153 (10, 0.000001, _('%.2f us')),
2153 (1, 0.000001, _('%.3f us')),
2154 (1, 0.000001, _('%.3f us')),
2154 (100, 0.000000001, _('%.1f ns')),
2155 (100, 0.000000001, _('%.1f ns')),
2155 (10, 0.000000001, _('%.2f ns')),
2156 (10, 0.000000001, _('%.2f ns')),
2156 (1, 0.000000001, _('%.3f ns')),
2157 (1, 0.000000001, _('%.3f ns')),
2157 )
2158 )
2158
2159
2159 _timenesting = [0]
2160 _timenesting = [0]
2160
2161
2161 def timed(func):
2162 def timed(func):
2162 '''Report the execution time of a function call to stderr.
2163 '''Report the execution time of a function call to stderr.
2163
2164
2164 During development, use as a decorator when you need to measure
2165 During development, use as a decorator when you need to measure
2165 the cost of a function, e.g. as follows:
2166 the cost of a function, e.g. as follows:
2166
2167
2167 @util.timed
2168 @util.timed
2168 def foo(a, b, c):
2169 def foo(a, b, c):
2169 pass
2170 pass
2170 '''
2171 '''
2171
2172
2172 def wrapper(*args, **kwargs):
2173 def wrapper(*args, **kwargs):
2173 start = time.time()
2174 start = time.time()
2174 indent = 2
2175 indent = 2
2175 _timenesting[0] += indent
2176 _timenesting[0] += indent
2176 try:
2177 try:
2177 return func(*args, **kwargs)
2178 return func(*args, **kwargs)
2178 finally:
2179 finally:
2179 elapsed = time.time() - start
2180 elapsed = time.time() - start
2180 _timenesting[0] -= indent
2181 _timenesting[0] -= indent
2181 sys.stderr.write('%s%s: %s\n' %
2182 sys.stderr.write('%s%s: %s\n' %
2182 (' ' * _timenesting[0], func.__name__,
2183 (' ' * _timenesting[0], func.__name__,
2183 timecount(elapsed)))
2184 timecount(elapsed)))
2184 return wrapper
2185 return wrapper
2185
2186
2186 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2187 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2187 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2188 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2188
2189
2189 def sizetoint(s):
2190 def sizetoint(s):
2190 '''Convert a space specifier to a byte count.
2191 '''Convert a space specifier to a byte count.
2191
2192
2192 >>> sizetoint('30')
2193 >>> sizetoint('30')
2193 30
2194 30
2194 >>> sizetoint('2.2kb')
2195 >>> sizetoint('2.2kb')
2195 2252
2196 2252
2196 >>> sizetoint('6M')
2197 >>> sizetoint('6M')
2197 6291456
2198 6291456
2198 '''
2199 '''
2199 t = s.strip().lower()
2200 t = s.strip().lower()
2200 try:
2201 try:
2201 for k, u in _sizeunits:
2202 for k, u in _sizeunits:
2202 if t.endswith(k):
2203 if t.endswith(k):
2203 return int(float(t[:-len(k)]) * u)
2204 return int(float(t[:-len(k)]) * u)
2204 return int(t)
2205 return int(t)
2205 except ValueError:
2206 except ValueError:
2206 raise error.ParseError(_("couldn't parse size: %s") % s)
2207 raise error.ParseError(_("couldn't parse size: %s") % s)
2207
2208
2208 class hooks(object):
2209 class hooks(object):
2209 '''A collection of hook functions that can be used to extend a
2210 '''A collection of hook functions that can be used to extend a
2210 function's behaviour. Hooks are called in lexicographic order,
2211 function's behaviour. Hooks are called in lexicographic order,
2211 based on the names of their sources.'''
2212 based on the names of their sources.'''
2212
2213
2213 def __init__(self):
2214 def __init__(self):
2214 self._hooks = []
2215 self._hooks = []
2215
2216
2216 def add(self, source, hook):
2217 def add(self, source, hook):
2217 self._hooks.append((source, hook))
2218 self._hooks.append((source, hook))
2218
2219
2219 def __call__(self, *args):
2220 def __call__(self, *args):
2220 self._hooks.sort(key=lambda x: x[0])
2221 self._hooks.sort(key=lambda x: x[0])
2221 results = []
2222 results = []
2222 for source, hook in self._hooks:
2223 for source, hook in self._hooks:
2223 results.append(hook(*args))
2224 results.append(hook(*args))
2224 return results
2225 return results
2225
2226
2226 def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr, otherf=sys.stdout):
2227 def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr, otherf=sys.stdout):
2227 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2228 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2228 Skips the 'skip' last entries. By default it will flush stdout first.
2229 Skips the 'skip' last entries. By default it will flush stdout first.
2229 It can be used everywhere and do intentionally not require an ui object.
2230 It can be used everywhere and do intentionally not require an ui object.
2230 Not be used in production code but very convenient while developing.
2231 Not be used in production code but very convenient while developing.
2231 '''
2232 '''
2232 if otherf:
2233 if otherf:
2233 otherf.flush()
2234 otherf.flush()
2234 f.write('%s at:\n' % msg)
2235 f.write('%s at:\n' % msg)
2235 entries = [('%s:%s' % (fn, ln), func)
2236 entries = [('%s:%s' % (fn, ln), func)
2236 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
2237 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
2237 if entries:
2238 if entries:
2238 fnmax = max(len(entry[0]) for entry in entries)
2239 fnmax = max(len(entry[0]) for entry in entries)
2239 for fnln, func in entries:
2240 for fnln, func in entries:
2240 f.write(' %-*s in %s\n' % (fnmax, fnln, func))
2241 f.write(' %-*s in %s\n' % (fnmax, fnln, func))
2241 f.flush()
2242 f.flush()
2242
2243
2243 class dirs(object):
2244 class dirs(object):
2244 '''a multiset of directory names from a dirstate or manifest'''
2245 '''a multiset of directory names from a dirstate or manifest'''
2245
2246
2246 def __init__(self, map, skip=None):
2247 def __init__(self, map, skip=None):
2247 self._dirs = {}
2248 self._dirs = {}
2248 addpath = self.addpath
2249 addpath = self.addpath
2249 if safehasattr(map, 'iteritems') and skip is not None:
2250 if safehasattr(map, 'iteritems') and skip is not None:
2250 for f, s in map.iteritems():
2251 for f, s in map.iteritems():
2251 if s[0] != skip:
2252 if s[0] != skip:
2252 addpath(f)
2253 addpath(f)
2253 else:
2254 else:
2254 for f in map:
2255 for f in map:
2255 addpath(f)
2256 addpath(f)
2256
2257
2257 def addpath(self, path):
2258 def addpath(self, path):
2258 dirs = self._dirs
2259 dirs = self._dirs
2259 for base in finddirs(path):
2260 for base in finddirs(path):
2260 if base in dirs:
2261 if base in dirs:
2261 dirs[base] += 1
2262 dirs[base] += 1
2262 return
2263 return
2263 dirs[base] = 1
2264 dirs[base] = 1
2264
2265
2265 def delpath(self, path):
2266 def delpath(self, path):
2266 dirs = self._dirs
2267 dirs = self._dirs
2267 for base in finddirs(path):
2268 for base in finddirs(path):
2268 if dirs[base] > 1:
2269 if dirs[base] > 1:
2269 dirs[base] -= 1
2270 dirs[base] -= 1
2270 return
2271 return
2271 del dirs[base]
2272 del dirs[base]
2272
2273
2273 def __iter__(self):
2274 def __iter__(self):
2274 return self._dirs.iterkeys()
2275 return self._dirs.iterkeys()
2275
2276
2276 def __contains__(self, d):
2277 def __contains__(self, d):
2277 return d in self._dirs
2278 return d in self._dirs
2278
2279
2279 if safehasattr(parsers, 'dirs'):
2280 if safehasattr(parsers, 'dirs'):
2280 dirs = parsers.dirs
2281 dirs = parsers.dirs
2281
2282
2282 def finddirs(path):
2283 def finddirs(path):
2283 pos = path.rfind('/')
2284 pos = path.rfind('/')
2284 while pos != -1:
2285 while pos != -1:
2285 yield path[:pos]
2286 yield path[:pos]
2286 pos = path.rfind('/', 0, pos)
2287 pos = path.rfind('/', 0, pos)
2287
2288
2288 # convenient shortcut
2289 # convenient shortcut
2289 dst = debugstacktrace
2290 dst = debugstacktrace
@@ -1,375 +1,375
1 # windows.py - Windows utility function implementations for Mercurial
1 # windows.py - Windows utility function implementations for Mercurial
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import osutil, encoding
9 import osutil, encoding
10 import errno, msvcrt, os, re, stat, sys, _winreg
10 import errno, msvcrt, os, re, stat, sys, _winreg
11
11
12 import win32
12 import win32
13 executablepath = win32.executablepath
13 executablepath = win32.executablepath
14 getuser = win32.getuser
14 getuser = win32.getuser
15 hidewindow = win32.hidewindow
15 hidewindow = win32.hidewindow
16 makedir = win32.makedir
16 makedir = win32.makedir
17 nlinks = win32.nlinks
17 nlinks = win32.nlinks
18 oslink = win32.oslink
18 oslink = win32.oslink
19 samedevice = win32.samedevice
19 samedevice = win32.samedevice
20 samefile = win32.samefile
20 samefile = win32.samefile
21 setsignalhandler = win32.setsignalhandler
21 setsignalhandler = win32.setsignalhandler
22 spawndetached = win32.spawndetached
22 spawndetached = win32.spawndetached
23 split = os.path.split
23 split = os.path.split
24 termwidth = win32.termwidth
24 termwidth = win32.termwidth
25 testpid = win32.testpid
25 testpid = win32.testpid
26 unlink = win32.unlink
26 unlink = win32.unlink
27
27
28 umask = 0022
28 umask = 0022
29 _SEEK_END = 2 # os.SEEK_END was introduced in Python 2.5
29 _SEEK_END = 2 # os.SEEK_END was introduced in Python 2.5
30
30
31 def posixfile(name, mode='r', buffering=-1):
31 def posixfile(name, mode='r', buffering=-1):
32 '''Open a file with even more POSIX-like semantics'''
32 '''Open a file with even more POSIX-like semantics'''
33 try:
33 try:
34 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
34 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
35
35
36 # The position when opening in append mode is implementation defined, so
36 # The position when opening in append mode is implementation defined, so
37 # make it consistent with other platforms, which position at EOF.
37 # make it consistent with other platforms, which position at EOF.
38 if 'a' in mode:
38 if 'a' in mode:
39 fp.seek(0, _SEEK_END)
39 fp.seek(0, _SEEK_END)
40
40
41 return fp
41 return fp
42 except WindowsError, err:
42 except WindowsError, err:
43 # convert to a friendlier exception
43 # convert to a friendlier exception
44 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
44 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
45
45
46 class winstdout(object):
46 class winstdout(object):
47 '''stdout on windows misbehaves if sent through a pipe'''
47 '''stdout on windows misbehaves if sent through a pipe'''
48
48
49 def __init__(self, fp):
49 def __init__(self, fp):
50 self.fp = fp
50 self.fp = fp
51
51
52 def __getattr__(self, key):
52 def __getattr__(self, key):
53 return getattr(self.fp, key)
53 return getattr(self.fp, key)
54
54
55 def close(self):
55 def close(self):
56 try:
56 try:
57 self.fp.close()
57 self.fp.close()
58 except IOError:
58 except IOError:
59 pass
59 pass
60
60
61 def write(self, s):
61 def write(self, s):
62 try:
62 try:
63 # This is workaround for "Not enough space" error on
63 # This is workaround for "Not enough space" error on
64 # writing large size of data to console.
64 # writing large size of data to console.
65 limit = 16000
65 limit = 16000
66 l = len(s)
66 l = len(s)
67 start = 0
67 start = 0
68 self.softspace = 0
68 self.softspace = 0
69 while start < l:
69 while start < l:
70 end = start + limit
70 end = start + limit
71 self.fp.write(s[start:end])
71 self.fp.write(s[start:end])
72 start = end
72 start = end
73 except IOError, inst:
73 except IOError, inst:
74 if inst.errno != 0:
74 if inst.errno != 0:
75 raise
75 raise
76 self.close()
76 self.close()
77 raise IOError(errno.EPIPE, 'Broken pipe')
77 raise IOError(errno.EPIPE, 'Broken pipe')
78
78
79 def flush(self):
79 def flush(self):
80 try:
80 try:
81 return self.fp.flush()
81 return self.fp.flush()
82 except IOError, inst:
82 except IOError, inst:
83 if inst.errno != errno.EINVAL:
83 if inst.errno != errno.EINVAL:
84 raise
84 raise
85 self.close()
85 self.close()
86 raise IOError(errno.EPIPE, 'Broken pipe')
86 raise IOError(errno.EPIPE, 'Broken pipe')
87
87
88 sys.__stdout__ = sys.stdout = winstdout(sys.stdout)
88 sys.__stdout__ = sys.stdout = winstdout(sys.stdout)
89
89
90 def _is_win_9x():
90 def _is_win_9x():
91 '''return true if run on windows 95, 98 or me.'''
91 '''return true if run on windows 95, 98 or me.'''
92 try:
92 try:
93 return sys.getwindowsversion()[3] == 1
93 return sys.getwindowsversion()[3] == 1
94 except AttributeError:
94 except AttributeError:
95 return 'command' in os.environ.get('comspec', '')
95 return 'command' in os.environ.get('comspec', '')
96
96
97 def openhardlinks():
97 def openhardlinks():
98 return not _is_win_9x()
98 return not _is_win_9x()
99
99
100 def parsepatchoutput(output_line):
100 def parsepatchoutput(output_line):
101 """parses the output produced by patch and returns the filename"""
101 """parses the output produced by patch and returns the filename"""
102 pf = output_line[14:]
102 pf = output_line[14:]
103 if pf[0] == '`':
103 if pf[0] == '`':
104 pf = pf[1:-1] # Remove the quotes
104 pf = pf[1:-1] # Remove the quotes
105 return pf
105 return pf
106
106
107 def sshargs(sshcmd, host, user, port):
107 def sshargs(sshcmd, host, user, port):
108 '''Build argument list for ssh or Plink'''
108 '''Build argument list for ssh or Plink'''
109 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
109 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
110 args = user and ("%s@%s" % (user, host)) or host
110 args = user and ("%s@%s" % (user, host)) or host
111 return port and ("%s %s %s" % (args, pflag, port)) or args
111 return port and ("%s %s %s" % (args, pflag, port)) or args
112
112
113 def setflags(f, l, x):
113 def setflags(f, l, x):
114 pass
114 pass
115
115
116 def copymode(src, dst, mode=None):
116 def copymode(src, dst, mode=None):
117 pass
117 pass
118
118
119 def checkexec(path):
119 def checkexec(path):
120 return False
120 return False
121
121
122 def checklink(path):
122 def checklink(path):
123 return False
123 return False
124
124
125 def setbinary(fd):
125 def setbinary(fd):
126 # When run without console, pipes may expose invalid
126 # When run without console, pipes may expose invalid
127 # fileno(), usually set to -1.
127 # fileno(), usually set to -1.
128 fno = getattr(fd, 'fileno', None)
128 fno = getattr(fd, 'fileno', None)
129 if fno is not None and fno() >= 0:
129 if fno is not None and fno() >= 0:
130 msvcrt.setmode(fno(), os.O_BINARY)
130 msvcrt.setmode(fno(), os.O_BINARY)
131
131
132 def pconvert(path):
132 def pconvert(path):
133 return path.replace(os.sep, '/')
133 return path.replace(os.sep, '/')
134
134
135 def localpath(path):
135 def localpath(path):
136 return path.replace('/', '\\')
136 return path.replace('/', '\\')
137
137
138 def normpath(path):
138 def normpath(path):
139 return pconvert(os.path.normpath(path))
139 return pconvert(os.path.normpath(path))
140
140
141 def normcase(path):
141 def normcase(path):
142 return encoding.upper(path)
142 return encoding.upper(path)
143
143
144 # see posix.py for definitions
144 # see posix.py for definitions
145 normcasespec = encoding.normcasespecs.upper
145 normcasespec = encoding.normcasespecs.upper
146 normcasefallback = encoding.upperfallback
146 normcasefallback = encoding.upperfallback
147
147
148 def samestat(s1, s2):
148 def samestat(s1, s2):
149 return False
149 return False
150
150
151 # A sequence of backslashes is special iff it precedes a double quote:
151 # A sequence of backslashes is special iff it precedes a double quote:
152 # - if there's an even number of backslashes, the double quote is not
152 # - if there's an even number of backslashes, the double quote is not
153 # quoted (i.e. it ends the quoted region)
153 # quoted (i.e. it ends the quoted region)
154 # - if there's an odd number of backslashes, the double quote is quoted
154 # - if there's an odd number of backslashes, the double quote is quoted
155 # - in both cases, every pair of backslashes is unquoted into a single
155 # - in both cases, every pair of backslashes is unquoted into a single
156 # backslash
156 # backslash
157 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
157 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
158 # So, to quote a string, we must surround it in double quotes, double
158 # So, to quote a string, we must surround it in double quotes, double
159 # the number of backslashes that precede double quotes and add another
159 # the number of backslashes that precede double quotes and add another
160 # backslash before every double quote (being careful with the double
160 # backslash before every double quote (being careful with the double
161 # quote we've appended to the end)
161 # quote we've appended to the end)
162 _quotere = None
162 _quotere = None
163 _needsshellquote = None
163 _needsshellquote = None
164 def shellquote(s):
164 def shellquote(s):
165 global _quotere
165 global _quotere
166 if _quotere is None:
166 if _quotere is None:
167 _quotere = re.compile(r'(\\*)("|\\$)')
167 _quotere = re.compile(r'(\\*)("|\\$)')
168 global _needsshellquote
168 global _needsshellquote
169 if _needsshellquote is None:
169 if _needsshellquote is None:
170 # ":" and "\\" are also treated as "safe character", because
170 # ":" and "\\" are also treated as "safe character", because
171 # they are used as a part of path name (and the latter doesn't
171 # they are used as a part of path name (and the latter doesn't
172 # work as "escape character", like one on posix) on Windows
172 # work as "escape character", like one on posix) on Windows
173 _needsshellquote = re.compile(r'[^a-zA-Z0-9._:/\\-]').search
173 _needsshellquote = re.compile(r'[^a-zA-Z0-9._:/\\-]').search
174 if s and not _needsshellquote(s) and not _quotere.search(s):
174 if s and not _needsshellquote(s) and not _quotere.search(s):
175 # "s" shouldn't have to be quoted
175 # "s" shouldn't have to be quoted
176 return s
176 return s
177 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
177 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
178
178
179 def quotecommand(cmd):
179 def quotecommand(cmd):
180 """Build a command string suitable for os.popen* calls."""
180 """Build a command string suitable for os.popen* calls."""
181 if sys.version_info < (2, 7, 1):
181 if sys.version_info < (2, 7, 1):
182 # Python versions since 2.7.1 do this extra quoting themselves
182 # Python versions since 2.7.1 do this extra quoting themselves
183 return '"' + cmd + '"'
183 return '"' + cmd + '"'
184 return cmd
184 return cmd
185
185
186 def popen(command, mode='r'):
186 def popen(command, mode='r'):
187 # Work around "popen spawned process may not write to stdout
187 # Work around "popen spawned process may not write to stdout
188 # under windows"
188 # under windows"
189 # http://bugs.python.org/issue1366
189 # http://bugs.python.org/issue1366
190 command += " 2> %s" % os.devnull
190 command += " 2> %s" % os.devnull
191 return os.popen(quotecommand(command), mode)
191 return os.popen(quotecommand(command), mode)
192
192
193 def explainexit(code):
193 def explainexit(code):
194 return _("exited with status %d") % code, code
194 return _("exited with status %d") % code, code
195
195
196 # if you change this stub into a real check, please try to implement the
196 # if you change this stub into a real check, please try to implement the
197 # username and groupname functions above, too.
197 # username and groupname functions above, too.
198 def isowner(st):
198 def isowner(st):
199 return True
199 return True
200
200
201 def findexe(command):
201 def findexe(command):
202 '''Find executable for command searching like cmd.exe does.
202 '''Find executable for command searching like cmd.exe does.
203 If command is a basename then PATH is searched for command.
203 If command is a basename then PATH is searched for command.
204 PATH isn't searched if command is an absolute or relative path.
204 PATH isn't searched if command is an absolute or relative path.
205 An extension from PATHEXT is found and added if not present.
205 An extension from PATHEXT is found and added if not present.
206 If command isn't found None is returned.'''
206 If command isn't found None is returned.'''
207 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
207 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
208 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
208 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
209 if os.path.splitext(command)[1].lower() in pathexts:
209 if os.path.splitext(command)[1].lower() in pathexts:
210 pathexts = ['']
210 pathexts = ['']
211
211
212 def findexisting(pathcommand):
212 def findexisting(pathcommand):
213 'Will append extension (if needed) and return existing file'
213 'Will append extension (if needed) and return existing file'
214 for ext in pathexts:
214 for ext in pathexts:
215 executable = pathcommand + ext
215 executable = pathcommand + ext
216 if os.path.exists(executable):
216 if os.path.exists(executable):
217 return executable
217 return executable
218 return None
218 return None
219
219
220 if os.sep in command:
220 if os.sep in command:
221 return findexisting(command)
221 return findexisting(command)
222
222
223 for path in os.environ.get('PATH', '').split(os.pathsep):
223 for path in os.environ.get('PATH', '').split(os.pathsep):
224 executable = findexisting(os.path.join(path, command))
224 executable = findexisting(os.path.join(path, command))
225 if executable is not None:
225 if executable is not None:
226 return executable
226 return executable
227 return findexisting(os.path.expanduser(os.path.expandvars(command)))
227 return findexisting(os.path.expanduser(os.path.expandvars(command)))
228
228
229 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
229 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
230
230
231 def statfiles(files):
231 def statfiles(files):
232 '''Stat each file in files. Yield each stat, or None if a file
232 '''Stat each file in files. Yield each stat, or None if a file
233 does not exist or has a type we don't care about.
233 does not exist or has a type we don't care about.
234
234
235 Cluster and cache stat per directory to minimize number of OS stat calls.'''
235 Cluster and cache stat per directory to minimize number of OS stat calls.'''
236 dircache = {} # dirname -> filename -> status | None if file does not exist
236 dircache = {} # dirname -> filename -> status | None if file does not exist
237 getkind = stat.S_IFMT
237 getkind = stat.S_IFMT
238 for nf in files:
238 for nf in files:
239 nf = normcase(nf)
239 nf = normcase(nf)
240 dir, base = os.path.split(nf)
240 dir, base = os.path.split(nf)
241 if not dir:
241 if not dir:
242 dir = '.'
242 dir = '.'
243 cache = dircache.get(dir, None)
243 cache = dircache.get(dir, None)
244 if cache is None:
244 if cache is None:
245 try:
245 try:
246 dmap = dict([(normcase(n), s)
246 dmap = dict([(normcase(n), s)
247 for n, k, s in osutil.listdir(dir, True)
247 for n, k, s in osutil.listdir(dir, True)
248 if getkind(s.st_mode) in _wantedkinds])
248 if getkind(s.st_mode) in _wantedkinds])
249 except OSError, err:
249 except OSError, err:
250 # handle directory not found in Python version prior to 2.5
250 # handle directory not found in Python version prior to 2.5
251 # Python <= 2.4 returns native Windows code 3 in errno
251 # Python <= 2.4 returns native Windows code 3 in errno
252 # Python >= 2.5 returns ENOENT and adds winerror field
252 # Python >= 2.5 returns ENOENT and adds winerror field
253 # EINVAL is raised if dir is not a directory.
253 # EINVAL is raised if dir is not a directory.
254 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
254 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
255 errno.ENOTDIR):
255 errno.ENOTDIR):
256 raise
256 raise
257 dmap = {}
257 dmap = {}
258 cache = dircache.setdefault(dir, dmap)
258 cache = dircache.setdefault(dir, dmap)
259 yield cache.get(base, None)
259 yield cache.get(base, None)
260
260
261 def username(uid=None):
261 def username(uid=None):
262 """Return the name of the user with the given uid.
262 """Return the name of the user with the given uid.
263
263
264 If uid is None, return the name of the current user."""
264 If uid is None, return the name of the current user."""
265 return None
265 return None
266
266
267 def groupname(gid=None):
267 def groupname(gid=None):
268 """Return the name of the group with the given gid.
268 """Return the name of the group with the given gid.
269
269
270 If gid is None, return the name of the current group."""
270 If gid is None, return the name of the current group."""
271 return None
271 return None
272
272
273 def _removedirs(name):
273 def removedirs(name):
274 """special version of os.removedirs that does not remove symlinked
274 """special version of os.removedirs that does not remove symlinked
275 directories or junction points if they actually contain files"""
275 directories or junction points if they actually contain files"""
276 if osutil.listdir(name):
276 if osutil.listdir(name):
277 return
277 return
278 os.rmdir(name)
278 os.rmdir(name)
279 head, tail = os.path.split(name)
279 head, tail = os.path.split(name)
280 if not tail:
280 if not tail:
281 head, tail = os.path.split(head)
281 head, tail = os.path.split(head)
282 while head and tail:
282 while head and tail:
283 try:
283 try:
284 if osutil.listdir(head):
284 if osutil.listdir(head):
285 return
285 return
286 os.rmdir(head)
286 os.rmdir(head)
287 except (ValueError, OSError):
287 except (ValueError, OSError):
288 break
288 break
289 head, tail = os.path.split(head)
289 head, tail = os.path.split(head)
290
290
291 def unlinkpath(f, ignoremissing=False):
291 def unlinkpath(f, ignoremissing=False):
292 """unlink and remove the directory if it is empty"""
292 """unlink and remove the directory if it is empty"""
293 try:
293 try:
294 unlink(f)
294 unlink(f)
295 except OSError, e:
295 except OSError, e:
296 if not (ignoremissing and e.errno == errno.ENOENT):
296 if not (ignoremissing and e.errno == errno.ENOENT):
297 raise
297 raise
298 # try removing directories that might now be empty
298 # try removing directories that might now be empty
299 try:
299 try:
300 _removedirs(os.path.dirname(f))
300 removedirs(os.path.dirname(f))
301 except OSError:
301 except OSError:
302 pass
302 pass
303
303
304 def rename(src, dst):
304 def rename(src, dst):
305 '''atomically rename file src to dst, replacing dst if it exists'''
305 '''atomically rename file src to dst, replacing dst if it exists'''
306 try:
306 try:
307 os.rename(src, dst)
307 os.rename(src, dst)
308 except OSError, e:
308 except OSError, e:
309 if e.errno != errno.EEXIST:
309 if e.errno != errno.EEXIST:
310 raise
310 raise
311 unlink(dst)
311 unlink(dst)
312 os.rename(src, dst)
312 os.rename(src, dst)
313
313
314 def gethgcmd():
314 def gethgcmd():
315 return [sys.executable] + sys.argv[:1]
315 return [sys.executable] + sys.argv[:1]
316
316
317 def groupmembers(name):
317 def groupmembers(name):
318 # Don't support groups on Windows for now
318 # Don't support groups on Windows for now
319 raise KeyError
319 raise KeyError
320
320
321 def isexec(f):
321 def isexec(f):
322 return False
322 return False
323
323
324 class cachestat(object):
324 class cachestat(object):
325 def __init__(self, path):
325 def __init__(self, path):
326 pass
326 pass
327
327
328 def cacheable(self):
328 def cacheable(self):
329 return False
329 return False
330
330
331 def lookupreg(key, valname=None, scope=None):
331 def lookupreg(key, valname=None, scope=None):
332 ''' Look up a key/value name in the Windows registry.
332 ''' Look up a key/value name in the Windows registry.
333
333
334 valname: value name. If unspecified, the default value for the key
334 valname: value name. If unspecified, the default value for the key
335 is used.
335 is used.
336 scope: optionally specify scope for registry lookup, this can be
336 scope: optionally specify scope for registry lookup, this can be
337 a sequence of scopes to look up in order. Default (CURRENT_USER,
337 a sequence of scopes to look up in order. Default (CURRENT_USER,
338 LOCAL_MACHINE).
338 LOCAL_MACHINE).
339 '''
339 '''
340 if scope is None:
340 if scope is None:
341 scope = (_winreg.HKEY_CURRENT_USER, _winreg.HKEY_LOCAL_MACHINE)
341 scope = (_winreg.HKEY_CURRENT_USER, _winreg.HKEY_LOCAL_MACHINE)
342 elif not isinstance(scope, (list, tuple)):
342 elif not isinstance(scope, (list, tuple)):
343 scope = (scope,)
343 scope = (scope,)
344 for s in scope:
344 for s in scope:
345 try:
345 try:
346 val = _winreg.QueryValueEx(_winreg.OpenKey(s, key), valname)[0]
346 val = _winreg.QueryValueEx(_winreg.OpenKey(s, key), valname)[0]
347 # never let a Unicode string escape into the wild
347 # never let a Unicode string escape into the wild
348 return encoding.tolocal(val.encode('UTF-8'))
348 return encoding.tolocal(val.encode('UTF-8'))
349 except EnvironmentError:
349 except EnvironmentError:
350 pass
350 pass
351
351
352 expandglobs = True
352 expandglobs = True
353
353
354 def statislink(st):
354 def statislink(st):
355 '''check whether a stat result is a symlink'''
355 '''check whether a stat result is a symlink'''
356 return False
356 return False
357
357
358 def statisexec(st):
358 def statisexec(st):
359 '''check whether a stat result is an executable file'''
359 '''check whether a stat result is an executable file'''
360 return False
360 return False
361
361
362 def readpipe(pipe):
362 def readpipe(pipe):
363 """Read all available data from a pipe."""
363 """Read all available data from a pipe."""
364 chunks = []
364 chunks = []
365 while True:
365 while True:
366 size = win32.peekpipe(pipe)
366 size = win32.peekpipe(pipe)
367 if not size:
367 if not size:
368 break
368 break
369
369
370 s = pipe.read(size)
370 s = pipe.read(size)
371 if not s:
371 if not s:
372 break
372 break
373 chunks.append(s)
373 chunks.append(s)
374
374
375 return ''.join(chunks)
375 return ''.join(chunks)
General Comments 0
You need to be logged in to leave comments. Login now