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