##// END OF EJS Templates
util: eliminate wildcard imports
Adrian Buehlmann -
r14926:4e7e63fc default
parent child Browse files
Show More
@@ -1,350 +1,353
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 os, sys, errno, stat, getpass, pwd, grp, tempfile
9 import os, sys, errno, stat, getpass, pwd, grp, tempfile
10
10
11 posixfile = open
11 posixfile = open
12 nulldev = '/dev/null'
12 nulldev = '/dev/null'
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 openhardlinks():
23 def openhardlinks():
24 '''return true if it is safe to hold open file handles to hardlinks'''
24 '''return true if it is safe to hold open file handles to hardlinks'''
25 return True
25 return True
26
26
27 def nlinks(name):
27 def nlinks(name):
28 '''return number of hardlinks for the given file'''
28 '''return number of hardlinks for the given file'''
29 return os.lstat(name).st_nlink
29 return os.lstat(name).st_nlink
30
30
31 def parsepatchoutput(output_line):
31 def parsepatchoutput(output_line):
32 """parses the output produced by patch and returns the filename"""
32 """parses the output produced by patch and returns the filename"""
33 pf = output_line[14:]
33 pf = output_line[14:]
34 if os.sys.platform == 'OpenVMS':
34 if os.sys.platform == 'OpenVMS':
35 if pf[0] == '`':
35 if pf[0] == '`':
36 pf = pf[1:-1] # Remove the quotes
36 pf = pf[1:-1] # Remove the quotes
37 else:
37 else:
38 if pf.startswith("'") and pf.endswith("'") and " " in pf:
38 if pf.startswith("'") and pf.endswith("'") and " " in pf:
39 pf = pf[1:-1] # Remove the quotes
39 pf = pf[1:-1] # Remove the quotes
40 return pf
40 return pf
41
41
42 def sshargs(sshcmd, host, user, port):
42 def sshargs(sshcmd, host, user, port):
43 '''Build argument list for ssh'''
43 '''Build argument list for ssh'''
44 args = user and ("%s@%s" % (user, host)) or host
44 args = user and ("%s@%s" % (user, host)) or host
45 return port and ("%s -p %s" % (args, port)) or args
45 return port and ("%s -p %s" % (args, port)) or args
46
46
47 def isexec(f):
47 def isexec(f):
48 """check whether a file is executable"""
48 """check whether a file is executable"""
49 return (os.lstat(f).st_mode & 0100 != 0)
49 return (os.lstat(f).st_mode & 0100 != 0)
50
50
51 def setflags(f, l, x):
51 def setflags(f, l, x):
52 s = os.lstat(f).st_mode
52 s = os.lstat(f).st_mode
53 if l:
53 if l:
54 if not stat.S_ISLNK(s):
54 if not stat.S_ISLNK(s):
55 # switch file to link
55 # switch file to link
56 fp = open(f)
56 fp = open(f)
57 data = fp.read()
57 data = fp.read()
58 fp.close()
58 fp.close()
59 os.unlink(f)
59 os.unlink(f)
60 try:
60 try:
61 os.symlink(data, f)
61 os.symlink(data, f)
62 except OSError:
62 except OSError:
63 # failed to make a link, rewrite file
63 # failed to make a link, rewrite file
64 fp = open(f, "w")
64 fp = open(f, "w")
65 fp.write(data)
65 fp.write(data)
66 fp.close()
66 fp.close()
67 # no chmod needed at this point
67 # no chmod needed at this point
68 return
68 return
69 if stat.S_ISLNK(s):
69 if stat.S_ISLNK(s):
70 # switch link to file
70 # switch link to file
71 data = os.readlink(f)
71 data = os.readlink(f)
72 os.unlink(f)
72 os.unlink(f)
73 fp = open(f, "w")
73 fp = open(f, "w")
74 fp.write(data)
74 fp.write(data)
75 fp.close()
75 fp.close()
76 s = 0666 & ~umask # avoid restatting for chmod
76 s = 0666 & ~umask # avoid restatting for chmod
77
77
78 sx = s & 0100
78 sx = s & 0100
79 if x and not sx:
79 if x and not sx:
80 # Turn on +x for every +r bit when making a file executable
80 # Turn on +x for every +r bit when making a file executable
81 # and obey umask.
81 # and obey umask.
82 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
82 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
83 elif not x and sx:
83 elif not x and sx:
84 # Turn off all +x bits
84 # Turn off all +x bits
85 os.chmod(f, s & 0666)
85 os.chmod(f, s & 0666)
86
86
87 def checkexec(path):
87 def checkexec(path):
88 """
88 """
89 Check whether the given path is on a filesystem with UNIX-like exec flags
89 Check whether the given path is on a filesystem with UNIX-like exec flags
90
90
91 Requires a directory (like /foo/.hg)
91 Requires a directory (like /foo/.hg)
92 """
92 """
93
93
94 # VFAT on some Linux versions can flip mode but it doesn't persist
94 # VFAT on some Linux versions can flip mode but it doesn't persist
95 # a FS remount. Frequently we can detect it if files are created
95 # a FS remount. Frequently we can detect it if files are created
96 # with exec bit on.
96 # with exec bit on.
97
97
98 try:
98 try:
99 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
99 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
100 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
100 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
101 try:
101 try:
102 os.close(fh)
102 os.close(fh)
103 m = os.stat(fn).st_mode & 0777
103 m = os.stat(fn).st_mode & 0777
104 new_file_has_exec = m & EXECFLAGS
104 new_file_has_exec = m & EXECFLAGS
105 os.chmod(fn, m ^ EXECFLAGS)
105 os.chmod(fn, m ^ EXECFLAGS)
106 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
106 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
107 finally:
107 finally:
108 os.unlink(fn)
108 os.unlink(fn)
109 except (IOError, OSError):
109 except (IOError, OSError):
110 # we don't care, the user probably won't be able to commit anyway
110 # we don't care, the user probably won't be able to commit anyway
111 return False
111 return False
112 return not (new_file_has_exec or exec_flags_cannot_flip)
112 return not (new_file_has_exec or exec_flags_cannot_flip)
113
113
114 def checklink(path):
114 def checklink(path):
115 """check whether the given path is on a symlink-capable filesystem"""
115 """check whether the given path is on a symlink-capable filesystem"""
116 # mktemp is not racy because symlink creation will fail if the
116 # mktemp is not racy because symlink creation will fail if the
117 # file already exists
117 # file already exists
118 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
118 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
119 try:
119 try:
120 os.symlink(".", name)
120 os.symlink(".", name)
121 os.unlink(name)
121 os.unlink(name)
122 return True
122 return True
123 except (OSError, AttributeError):
123 except (OSError, AttributeError):
124 return False
124 return False
125
125
126 def checkosfilename(path):
126 def checkosfilename(path):
127 '''Check that the base-relative path is a valid filename on this platform.
127 '''Check that the base-relative path is a valid filename on this platform.
128 Returns None if the path is ok, or a UI string describing the problem.'''
128 Returns None if the path is ok, or a UI string describing the problem.'''
129 pass # on posix platforms, every path is ok
129 pass # on posix platforms, every path is ok
130
130
131 def setbinary(fd):
131 def setbinary(fd):
132 pass
132 pass
133
133
134 def pconvert(path):
134 def pconvert(path):
135 return path
135 return path
136
136
137 def localpath(path):
137 def localpath(path):
138 return path
138 return path
139
139
140 def samefile(fpath1, fpath2):
140 def samefile(fpath1, fpath2):
141 """Returns whether path1 and path2 refer to the same file. This is only
141 """Returns whether path1 and path2 refer to the same file. This is only
142 guaranteed to work for files, not directories."""
142 guaranteed to work for files, not directories."""
143 return os.path.samefile(fpath1, fpath2)
143 return os.path.samefile(fpath1, fpath2)
144
144
145 def samedevice(fpath1, fpath2):
145 def samedevice(fpath1, fpath2):
146 """Returns whether fpath1 and fpath2 are on the same device. This is only
146 """Returns whether fpath1 and fpath2 are on the same device. This is only
147 guaranteed to work for files, not directories."""
147 guaranteed to work for files, not directories."""
148 st1 = os.lstat(fpath1)
148 st1 = os.lstat(fpath1)
149 st2 = os.lstat(fpath2)
149 st2 = os.lstat(fpath2)
150 return st1.st_dev == st2.st_dev
150 return st1.st_dev == st2.st_dev
151
151
152 if sys.platform == 'darwin':
152 if sys.platform == 'darwin':
153 import fcntl # only needed on darwin, missing on jython
153 import fcntl # only needed on darwin, missing on jython
154 def realpath(path):
154 def realpath(path):
155 '''
155 '''
156 Returns the true, canonical file system path equivalent to the given
156 Returns the true, canonical file system path equivalent to the given
157 path.
157 path.
158
158
159 Equivalent means, in this case, resulting in the same, unique
159 Equivalent means, in this case, resulting in the same, unique
160 file system link to the path. Every file system entry, whether a file,
160 file system link to the path. Every file system entry, whether a file,
161 directory, hard link or symbolic link or special, will have a single
161 directory, hard link or symbolic link or special, will have a single
162 path preferred by the system, but may allow multiple, differing path
162 path preferred by the system, but may allow multiple, differing path
163 lookups to point to it.
163 lookups to point to it.
164
164
165 Most regular UNIX file systems only allow a file system entry to be
165 Most regular UNIX file systems only allow a file system entry to be
166 looked up by its distinct path. Obviously, this does not apply to case
166 looked up by its distinct path. Obviously, this does not apply to case
167 insensitive file systems, whether case preserving or not. The most
167 insensitive file systems, whether case preserving or not. The most
168 complex issue to deal with is file systems transparently reencoding the
168 complex issue to deal with is file systems transparently reencoding the
169 path, such as the non-standard Unicode normalisation required for HFS+
169 path, such as the non-standard Unicode normalisation required for HFS+
170 and HFSX.
170 and HFSX.
171 '''
171 '''
172 # Constants copied from /usr/include/sys/fcntl.h
172 # Constants copied from /usr/include/sys/fcntl.h
173 F_GETPATH = 50
173 F_GETPATH = 50
174 O_SYMLINK = 0x200000
174 O_SYMLINK = 0x200000
175
175
176 try:
176 try:
177 fd = os.open(path, O_SYMLINK)
177 fd = os.open(path, O_SYMLINK)
178 except OSError, err:
178 except OSError, err:
179 if err.errno == errno.ENOENT:
179 if err.errno == errno.ENOENT:
180 return path
180 return path
181 raise
181 raise
182
182
183 try:
183 try:
184 return fcntl.fcntl(fd, F_GETPATH, '\0' * 1024).rstrip('\0')
184 return fcntl.fcntl(fd, F_GETPATH, '\0' * 1024).rstrip('\0')
185 finally:
185 finally:
186 os.close(fd)
186 os.close(fd)
187 else:
187 else:
188 # Fallback to the likely inadequate Python builtin function.
188 # Fallback to the likely inadequate Python builtin function.
189 realpath = os.path.realpath
189 realpath = os.path.realpath
190
190
191 def shellquote(s):
191 def shellquote(s):
192 if os.sys.platform == 'OpenVMS':
192 if os.sys.platform == 'OpenVMS':
193 return '"%s"' % s
193 return '"%s"' % s
194 else:
194 else:
195 return "'%s'" % s.replace("'", "'\\''")
195 return "'%s'" % s.replace("'", "'\\''")
196
196
197 def quotecommand(cmd):
197 def quotecommand(cmd):
198 return cmd
198 return cmd
199
199
200 def popen(command, mode='r'):
200 def popen(command, mode='r'):
201 return os.popen(command, mode)
201 return os.popen(command, mode)
202
202
203 def testpid(pid):
203 def testpid(pid):
204 '''return False if pid dead, True if running or not sure'''
204 '''return False if pid dead, True if running or not sure'''
205 if os.sys.platform == 'OpenVMS':
205 if os.sys.platform == 'OpenVMS':
206 return True
206 return True
207 try:
207 try:
208 os.kill(pid, 0)
208 os.kill(pid, 0)
209 return True
209 return True
210 except OSError, inst:
210 except OSError, inst:
211 return inst.errno != errno.ESRCH
211 return inst.errno != errno.ESRCH
212
212
213 def explainexit(code):
213 def explainexit(code):
214 """return a 2-tuple (desc, code) describing a subprocess status
214 """return a 2-tuple (desc, code) describing a subprocess status
215 (codes from kill are negative - not os.system/wait encoding)"""
215 (codes from kill are negative - not os.system/wait encoding)"""
216 if code >= 0:
216 if code >= 0:
217 return _("exited with status %d") % code, code
217 return _("exited with status %d") % code, code
218 return _("killed by signal %d") % -code, -code
218 return _("killed by signal %d") % -code, -code
219
219
220 def isowner(st):
220 def isowner(st):
221 """Return True if the stat object st is from the current user."""
221 """Return True if the stat object st is from the current user."""
222 return st.st_uid == os.getuid()
222 return st.st_uid == os.getuid()
223
223
224 def findexe(command):
224 def findexe(command):
225 '''Find executable for command searching like which does.
225 '''Find executable for command searching like which does.
226 If command is a basename then PATH is searched for command.
226 If command is a basename then PATH is searched for command.
227 PATH isn't searched if command is an absolute or relative path.
227 PATH isn't searched if command is an absolute or relative path.
228 If command isn't found None is returned.'''
228 If command isn't found None is returned.'''
229 if sys.platform == 'OpenVMS':
229 if sys.platform == 'OpenVMS':
230 return command
230 return command
231
231
232 def findexisting(executable):
232 def findexisting(executable):
233 'Will return executable if existing file'
233 'Will return executable if existing file'
234 if os.path.exists(executable):
234 if os.path.exists(executable):
235 return executable
235 return executable
236 return None
236 return None
237
237
238 if os.sep in command:
238 if os.sep in command:
239 return findexisting(command)
239 return findexisting(command)
240
240
241 for path in os.environ.get('PATH', '').split(os.pathsep):
241 for path in os.environ.get('PATH', '').split(os.pathsep):
242 executable = findexisting(os.path.join(path, command))
242 executable = findexisting(os.path.join(path, command))
243 if executable is not None:
243 if executable is not None:
244 return executable
244 return executable
245 return None
245 return None
246
246
247 def setsignalhandler():
247 def setsignalhandler():
248 pass
248 pass
249
249
250 def statfiles(files):
250 def statfiles(files):
251 'Stat each file in files and yield stat or None if file does not exist.'
251 'Stat each file in files and yield stat or None if file does not exist.'
252 lstat = os.lstat
252 lstat = os.lstat
253 for nf in files:
253 for nf in files:
254 try:
254 try:
255 st = lstat(nf)
255 st = lstat(nf)
256 except OSError, err:
256 except OSError, err:
257 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
257 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
258 raise
258 raise
259 st = None
259 st = None
260 yield st
260 yield st
261
261
262 def getuser():
262 def getuser():
263 '''return name of current user'''
263 '''return name of current user'''
264 return getpass.getuser()
264 return getpass.getuser()
265
265
266 def username(uid=None):
266 def username(uid=None):
267 """Return the name of the user with the given uid.
267 """Return the name of the user with the given uid.
268
268
269 If uid is None, return the name of the current user."""
269 If uid is None, return the name of the current user."""
270
270
271 if uid is None:
271 if uid is None:
272 uid = os.getuid()
272 uid = os.getuid()
273 try:
273 try:
274 return pwd.getpwuid(uid)[0]
274 return pwd.getpwuid(uid)[0]
275 except KeyError:
275 except KeyError:
276 return str(uid)
276 return str(uid)
277
277
278 def groupname(gid=None):
278 def groupname(gid=None):
279 """Return the name of the group with the given gid.
279 """Return the name of the group with the given gid.
280
280
281 If gid is None, return the name of the current group."""
281 If gid is None, return the name of the current group."""
282
282
283 if gid is None:
283 if gid is None:
284 gid = os.getgid()
284 gid = os.getgid()
285 try:
285 try:
286 return grp.getgrgid(gid)[0]
286 return grp.getgrgid(gid)[0]
287 except KeyError:
287 except KeyError:
288 return str(gid)
288 return str(gid)
289
289
290 def groupmembers(name):
290 def groupmembers(name):
291 """Return the list of members of the group with the given
291 """Return the list of members of the group with the given
292 name, KeyError if the group does not exist.
292 name, KeyError if the group does not exist.
293 """
293 """
294 return list(grp.getgrnam(name).gr_mem)
294 return list(grp.getgrnam(name).gr_mem)
295
295
296 def spawndetached(args):
296 def spawndetached(args):
297 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
297 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
298 args[0], args)
298 args[0], args)
299
299
300 def gethgcmd():
300 def gethgcmd():
301 return sys.argv[:1]
301 return sys.argv[:1]
302
302
303 def termwidth():
303 def termwidth():
304 try:
304 try:
305 import termios, array, fcntl
305 import termios, array, fcntl
306 for dev in (sys.stderr, sys.stdout, sys.stdin):
306 for dev in (sys.stderr, sys.stdout, sys.stdin):
307 try:
307 try:
308 try:
308 try:
309 fd = dev.fileno()
309 fd = dev.fileno()
310 except AttributeError:
310 except AttributeError:
311 continue
311 continue
312 if not os.isatty(fd):
312 if not os.isatty(fd):
313 continue
313 continue
314 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
314 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
315 width = array.array('h', arri)[1]
315 width = array.array('h', arri)[1]
316 if width > 0:
316 if width > 0:
317 return width
317 return width
318 except ValueError:
318 except ValueError:
319 pass
319 pass
320 except IOError, e:
320 except IOError, e:
321 if e[0] == errno.EINVAL:
321 if e[0] == errno.EINVAL:
322 pass
322 pass
323 else:
323 else:
324 raise
324 raise
325 except ImportError:
325 except ImportError:
326 pass
326 pass
327 return 80
327 return 80
328
328
329 def makedir(path, notindexed):
329 def makedir(path, notindexed):
330 os.mkdir(path)
330 os.mkdir(path)
331
331
332 def unlinkpath(f):
332 def unlinkpath(f):
333 """unlink and remove the directory if it is empty"""
333 """unlink and remove the directory if it is empty"""
334 os.unlink(f)
334 os.unlink(f)
335 # try removing directories that might now be empty
335 # try removing directories that might now be empty
336 try:
336 try:
337 os.removedirs(os.path.dirname(f))
337 os.removedirs(os.path.dirname(f))
338 except OSError:
338 except OSError:
339 pass
339 pass
340
340
341 def lookupreg(key, name=None, scope=None):
341 def lookupreg(key, name=None, scope=None):
342 return None
342 return None
343
343
344 def hidewindow():
344 def hidewindow():
345 """Hide current shell window.
345 """Hide current shell window.
346
346
347 Used to hide the window opened when starting asynchronous
347 Used to hide the window opened when starting asynchronous
348 child process under Windows, unneeded on other systems.
348 child process under Windows, unneeded on other systems.
349 """
349 """
350 pass
350 pass
351
352 def executablepath():
353 return None # available on Windows only
@@ -1,1593 +1,1640
1 # util.py - Mercurial utility functions and platform specfic implementations
1 # util.py - Mercurial utility functions and platform specfic implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Mercurial utility functions and platform specfic implementations.
10 """Mercurial utility functions and platform specfic implementations.
11
11
12 This contains helper routines that are independent of the SCM core and
12 This contains helper routines that are independent of the SCM core and
13 hide platform-specific details from the core.
13 hide platform-specific details from the core.
14 """
14 """
15
15
16 from i18n import _
16 from i18n import _
17 import error, osutil, 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, calendar, textwrap, unicodedata, signal
19 import os, time, calendar, textwrap, unicodedata, signal
20 import imp, socket, urllib
20 import imp, socket, urllib
21
21
22 if os.name == 'nt':
22 if os.name == 'nt':
23 from windows import *
23 import windows as platform
24 else:
24 else:
25 from posix import *
25 import posix as platform
26
27 checkexec = platform.checkexec
28 checklink = platform.checklink
29 executablepath = platform.executablepath
30 expandglobs = platform.expandglobs
31 explainexit = platform.explainexit
32 findexe = platform.findexe
33 gethgcmd = platform.gethgcmd
34 getuser = platform.getuser
35 groupmembers = platform.groupmembers
36 groupname = platform.groupname
37 hidewindow = platform.hidewindow
38 isexec = platform.isexec
39 isowner = platform.isowner
40 localpath = platform.localpath
41 lookupreg = platform.lookupreg
42 makedir = platform.makedir
43 nlinks = platform.nlinks
44 normpath = platform.normpath
45 nulldev = platform.nulldev
46 openhardlinks = platform.openhardlinks
47 oslink = platform.oslink
48 parsepatchoutput = platform.parsepatchoutput
49 pconvert = platform.pconvert
50 popen = platform.popen
51 posixfile = platform.posixfile
52 quotecommand = platform.quotecommand
53 realpath = platform.realpath
54 rename = platform.rename
55 samedevice = platform.samedevice
56 samefile = platform.samefile
57 samestat = platform.samestat
58 setbinary = platform.setbinary
59 setflags = platform.setflags
60 setsignalhandler = platform.setsignalhandler
61 shellquote = platform.shellquote
62 spawndetached = platform.spawndetached
63 sshargs = platform.sshargs
64 statfiles = platform.statfiles
65 termwidth = platform.termwidth
66 testpid = platform.testpid
67 umask = platform.umask
68 unlink = platform.unlink
69 unlinkpath = platform.unlinkpath
70 username = platform.username
26
71
27 # Python compatibility
72 # Python compatibility
28
73
29 def sha1(s):
74 def sha1(s):
30 return _fastsha1(s)
75 return _fastsha1(s)
31
76
32 def _fastsha1(s):
77 def _fastsha1(s):
33 # This function will import sha1 from hashlib or sha (whichever is
78 # This function will import sha1 from hashlib or sha (whichever is
34 # available) and overwrite itself with it on the first call.
79 # available) and overwrite itself with it on the first call.
35 # Subsequent calls will go directly to the imported function.
80 # Subsequent calls will go directly to the imported function.
36 if sys.version_info >= (2, 5):
81 if sys.version_info >= (2, 5):
37 from hashlib import sha1 as _sha1
82 from hashlib import sha1 as _sha1
38 else:
83 else:
39 from sha import sha as _sha1
84 from sha import sha as _sha1
40 global _fastsha1, sha1
85 global _fastsha1, sha1
41 _fastsha1 = sha1 = _sha1
86 _fastsha1 = sha1 = _sha1
42 return _sha1(s)
87 return _sha1(s)
43
88
44 import __builtin__
89 import __builtin__
45
90
46 if sys.version_info[0] < 3:
91 if sys.version_info[0] < 3:
47 def fakebuffer(sliceable, offset=0):
92 def fakebuffer(sliceable, offset=0):
48 return sliceable[offset:]
93 return sliceable[offset:]
49 else:
94 else:
50 def fakebuffer(sliceable, offset=0):
95 def fakebuffer(sliceable, offset=0):
51 return memoryview(sliceable)[offset:]
96 return memoryview(sliceable)[offset:]
52 try:
97 try:
53 buffer
98 buffer
54 except NameError:
99 except NameError:
55 __builtin__.buffer = fakebuffer
100 __builtin__.buffer = fakebuffer
56
101
57 import subprocess
102 import subprocess
58 closefds = os.name == 'posix'
103 closefds = os.name == 'posix'
59
104
60 def popen2(cmd, env=None, newlines=False):
105 def popen2(cmd, env=None, newlines=False):
61 # Setting bufsize to -1 lets the system decide the buffer size.
106 # Setting bufsize to -1 lets the system decide the buffer size.
62 # The default for bufsize is 0, meaning unbuffered. This leads to
107 # The default for bufsize is 0, meaning unbuffered. This leads to
63 # poor performance on Mac OS X: http://bugs.python.org/issue4194
108 # poor performance on Mac OS X: http://bugs.python.org/issue4194
64 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
109 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
65 close_fds=closefds,
110 close_fds=closefds,
66 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
111 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
67 universal_newlines=newlines,
112 universal_newlines=newlines,
68 env=env)
113 env=env)
69 return p.stdin, p.stdout
114 return p.stdin, p.stdout
70
115
71 def popen3(cmd, env=None, newlines=False):
116 def popen3(cmd, env=None, newlines=False):
72 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
117 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
73 close_fds=closefds,
118 close_fds=closefds,
74 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
119 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
75 stderr=subprocess.PIPE,
120 stderr=subprocess.PIPE,
76 universal_newlines=newlines,
121 universal_newlines=newlines,
77 env=env)
122 env=env)
78 return p.stdin, p.stdout, p.stderr
123 return p.stdin, p.stdout, p.stderr
79
124
80 def version():
125 def version():
81 """Return version information if available."""
126 """Return version information if available."""
82 try:
127 try:
83 import __version__
128 import __version__
84 return __version__.version
129 return __version__.version
85 except ImportError:
130 except ImportError:
86 return 'unknown'
131 return 'unknown'
87
132
88 # used by parsedate
133 # used by parsedate
89 defaultdateformats = (
134 defaultdateformats = (
90 '%Y-%m-%d %H:%M:%S',
135 '%Y-%m-%d %H:%M:%S',
91 '%Y-%m-%d %I:%M:%S%p',
136 '%Y-%m-%d %I:%M:%S%p',
92 '%Y-%m-%d %H:%M',
137 '%Y-%m-%d %H:%M',
93 '%Y-%m-%d %I:%M%p',
138 '%Y-%m-%d %I:%M%p',
94 '%Y-%m-%d',
139 '%Y-%m-%d',
95 '%m-%d',
140 '%m-%d',
96 '%m/%d',
141 '%m/%d',
97 '%m/%d/%y',
142 '%m/%d/%y',
98 '%m/%d/%Y',
143 '%m/%d/%Y',
99 '%a %b %d %H:%M:%S %Y',
144 '%a %b %d %H:%M:%S %Y',
100 '%a %b %d %I:%M:%S%p %Y',
145 '%a %b %d %I:%M:%S%p %Y',
101 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
146 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
102 '%b %d %H:%M:%S %Y',
147 '%b %d %H:%M:%S %Y',
103 '%b %d %I:%M:%S%p %Y',
148 '%b %d %I:%M:%S%p %Y',
104 '%b %d %H:%M:%S',
149 '%b %d %H:%M:%S',
105 '%b %d %I:%M:%S%p',
150 '%b %d %I:%M:%S%p',
106 '%b %d %H:%M',
151 '%b %d %H:%M',
107 '%b %d %I:%M%p',
152 '%b %d %I:%M%p',
108 '%b %d %Y',
153 '%b %d %Y',
109 '%b %d',
154 '%b %d',
110 '%H:%M:%S',
155 '%H:%M:%S',
111 '%I:%M:%S%p',
156 '%I:%M:%S%p',
112 '%H:%M',
157 '%H:%M',
113 '%I:%M%p',
158 '%I:%M%p',
114 )
159 )
115
160
116 extendeddateformats = defaultdateformats + (
161 extendeddateformats = defaultdateformats + (
117 "%Y",
162 "%Y",
118 "%Y-%m",
163 "%Y-%m",
119 "%b",
164 "%b",
120 "%b %Y",
165 "%b %Y",
121 )
166 )
122
167
123 def cachefunc(func):
168 def cachefunc(func):
124 '''cache the result of function calls'''
169 '''cache the result of function calls'''
125 # XXX doesn't handle keywords args
170 # XXX doesn't handle keywords args
126 cache = {}
171 cache = {}
127 if func.func_code.co_argcount == 1:
172 if func.func_code.co_argcount == 1:
128 # we gain a small amount of time because
173 # we gain a small amount of time because
129 # we don't need to pack/unpack the list
174 # we don't need to pack/unpack the list
130 def f(arg):
175 def f(arg):
131 if arg not in cache:
176 if arg not in cache:
132 cache[arg] = func(arg)
177 cache[arg] = func(arg)
133 return cache[arg]
178 return cache[arg]
134 else:
179 else:
135 def f(*args):
180 def f(*args):
136 if args not in cache:
181 if args not in cache:
137 cache[args] = func(*args)
182 cache[args] = func(*args)
138 return cache[args]
183 return cache[args]
139
184
140 return f
185 return f
141
186
142 def lrucachefunc(func):
187 def lrucachefunc(func):
143 '''cache most recent results of function calls'''
188 '''cache most recent results of function calls'''
144 cache = {}
189 cache = {}
145 order = []
190 order = []
146 if func.func_code.co_argcount == 1:
191 if func.func_code.co_argcount == 1:
147 def f(arg):
192 def f(arg):
148 if arg not in cache:
193 if arg not in cache:
149 if len(cache) > 20:
194 if len(cache) > 20:
150 del cache[order.pop(0)]
195 del cache[order.pop(0)]
151 cache[arg] = func(arg)
196 cache[arg] = func(arg)
152 else:
197 else:
153 order.remove(arg)
198 order.remove(arg)
154 order.append(arg)
199 order.append(arg)
155 return cache[arg]
200 return cache[arg]
156 else:
201 else:
157 def f(*args):
202 def f(*args):
158 if args not in cache:
203 if args not in cache:
159 if len(cache) > 20:
204 if len(cache) > 20:
160 del cache[order.pop(0)]
205 del cache[order.pop(0)]
161 cache[args] = func(*args)
206 cache[args] = func(*args)
162 else:
207 else:
163 order.remove(args)
208 order.remove(args)
164 order.append(args)
209 order.append(args)
165 return cache[args]
210 return cache[args]
166
211
167 return f
212 return f
168
213
169 class propertycache(object):
214 class propertycache(object):
170 def __init__(self, func):
215 def __init__(self, func):
171 self.func = func
216 self.func = func
172 self.name = func.__name__
217 self.name = func.__name__
173 def __get__(self, obj, type=None):
218 def __get__(self, obj, type=None):
174 result = self.func(obj)
219 result = self.func(obj)
175 setattr(obj, self.name, result)
220 setattr(obj, self.name, result)
176 return result
221 return result
177
222
178 def pipefilter(s, cmd):
223 def pipefilter(s, cmd):
179 '''filter string S through command CMD, returning its output'''
224 '''filter string S through command CMD, returning its output'''
180 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
225 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
181 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
226 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
182 pout, perr = p.communicate(s)
227 pout, perr = p.communicate(s)
183 return pout
228 return pout
184
229
185 def tempfilter(s, cmd):
230 def tempfilter(s, cmd):
186 '''filter string S through a pair of temporary files with CMD.
231 '''filter string S through a pair of temporary files with CMD.
187 CMD is used as a template to create the real command to be run,
232 CMD is used as a template to create the real command to be run,
188 with the strings INFILE and OUTFILE replaced by the real names of
233 with the strings INFILE and OUTFILE replaced by the real names of
189 the temporary files generated.'''
234 the temporary files generated.'''
190 inname, outname = None, None
235 inname, outname = None, None
191 try:
236 try:
192 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
237 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
193 fp = os.fdopen(infd, 'wb')
238 fp = os.fdopen(infd, 'wb')
194 fp.write(s)
239 fp.write(s)
195 fp.close()
240 fp.close()
196 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
241 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
197 os.close(outfd)
242 os.close(outfd)
198 cmd = cmd.replace('INFILE', inname)
243 cmd = cmd.replace('INFILE', inname)
199 cmd = cmd.replace('OUTFILE', outname)
244 cmd = cmd.replace('OUTFILE', outname)
200 code = os.system(cmd)
245 code = os.system(cmd)
201 if sys.platform == 'OpenVMS' and code & 1:
246 if sys.platform == 'OpenVMS' and code & 1:
202 code = 0
247 code = 0
203 if code:
248 if code:
204 raise Abort(_("command '%s' failed: %s") %
249 raise Abort(_("command '%s' failed: %s") %
205 (cmd, explainexit(code)))
250 (cmd, explainexit(code)))
206 fp = open(outname, 'rb')
251 fp = open(outname, 'rb')
207 r = fp.read()
252 r = fp.read()
208 fp.close()
253 fp.close()
209 return r
254 return r
210 finally:
255 finally:
211 try:
256 try:
212 if inname:
257 if inname:
213 os.unlink(inname)
258 os.unlink(inname)
214 except OSError:
259 except OSError:
215 pass
260 pass
216 try:
261 try:
217 if outname:
262 if outname:
218 os.unlink(outname)
263 os.unlink(outname)
219 except OSError:
264 except OSError:
220 pass
265 pass
221
266
222 filtertable = {
267 filtertable = {
223 'tempfile:': tempfilter,
268 'tempfile:': tempfilter,
224 'pipe:': pipefilter,
269 'pipe:': pipefilter,
225 }
270 }
226
271
227 def filter(s, cmd):
272 def filter(s, cmd):
228 "filter a string through a command that transforms its input to its output"
273 "filter a string through a command that transforms its input to its output"
229 for name, fn in filtertable.iteritems():
274 for name, fn in filtertable.iteritems():
230 if cmd.startswith(name):
275 if cmd.startswith(name):
231 return fn(s, cmd[len(name):].lstrip())
276 return fn(s, cmd[len(name):].lstrip())
232 return pipefilter(s, cmd)
277 return pipefilter(s, cmd)
233
278
234 def binary(s):
279 def binary(s):
235 """return true if a string is binary data"""
280 """return true if a string is binary data"""
236 return bool(s and '\0' in s)
281 return bool(s and '\0' in s)
237
282
238 def increasingchunks(source, min=1024, max=65536):
283 def increasingchunks(source, min=1024, max=65536):
239 '''return no less than min bytes per chunk while data remains,
284 '''return no less than min bytes per chunk while data remains,
240 doubling min after each chunk until it reaches max'''
285 doubling min after each chunk until it reaches max'''
241 def log2(x):
286 def log2(x):
242 if not x:
287 if not x:
243 return 0
288 return 0
244 i = 0
289 i = 0
245 while x:
290 while x:
246 x >>= 1
291 x >>= 1
247 i += 1
292 i += 1
248 return i - 1
293 return i - 1
249
294
250 buf = []
295 buf = []
251 blen = 0
296 blen = 0
252 for chunk in source:
297 for chunk in source:
253 buf.append(chunk)
298 buf.append(chunk)
254 blen += len(chunk)
299 blen += len(chunk)
255 if blen >= min:
300 if blen >= min:
256 if min < max:
301 if min < max:
257 min = min << 1
302 min = min << 1
258 nmin = 1 << log2(blen)
303 nmin = 1 << log2(blen)
259 if nmin > min:
304 if nmin > min:
260 min = nmin
305 min = nmin
261 if min > max:
306 if min > max:
262 min = max
307 min = max
263 yield ''.join(buf)
308 yield ''.join(buf)
264 blen = 0
309 blen = 0
265 buf = []
310 buf = []
266 if buf:
311 if buf:
267 yield ''.join(buf)
312 yield ''.join(buf)
268
313
269 Abort = error.Abort
314 Abort = error.Abort
270
315
271 def always(fn):
316 def always(fn):
272 return True
317 return True
273
318
274 def never(fn):
319 def never(fn):
275 return False
320 return False
276
321
277 def pathto(root, n1, n2):
322 def pathto(root, n1, n2):
278 '''return the relative path from one place to another.
323 '''return the relative path from one place to another.
279 root should use os.sep to separate directories
324 root should use os.sep to separate directories
280 n1 should use os.sep to separate directories
325 n1 should use os.sep to separate directories
281 n2 should use "/" to separate directories
326 n2 should use "/" to separate directories
282 returns an os.sep-separated path.
327 returns an os.sep-separated path.
283
328
284 If n1 is a relative path, it's assumed it's
329 If n1 is a relative path, it's assumed it's
285 relative to root.
330 relative to root.
286 n2 should always be relative to root.
331 n2 should always be relative to root.
287 '''
332 '''
288 if not n1:
333 if not n1:
289 return localpath(n2)
334 return localpath(n2)
290 if os.path.isabs(n1):
335 if os.path.isabs(n1):
291 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
336 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
292 return os.path.join(root, localpath(n2))
337 return os.path.join(root, localpath(n2))
293 n2 = '/'.join((pconvert(root), n2))
338 n2 = '/'.join((pconvert(root), n2))
294 a, b = splitpath(n1), n2.split('/')
339 a, b = splitpath(n1), n2.split('/')
295 a.reverse()
340 a.reverse()
296 b.reverse()
341 b.reverse()
297 while a and b and a[-1] == b[-1]:
342 while a and b and a[-1] == b[-1]:
298 a.pop()
343 a.pop()
299 b.pop()
344 b.pop()
300 b.reverse()
345 b.reverse()
301 return os.sep.join((['..'] * len(a)) + b) or '.'
346 return os.sep.join((['..'] * len(a)) + b) or '.'
302
347
303 _hgexecutable = None
348 _hgexecutable = None
304
349
305 def mainfrozen():
350 def mainfrozen():
306 """return True if we are a frozen executable.
351 """return True if we are a frozen executable.
307
352
308 The code supports py2exe (most common, Windows only) and tools/freeze
353 The code supports py2exe (most common, Windows only) and tools/freeze
309 (portable, not much used).
354 (portable, not much used).
310 """
355 """
311 return (hasattr(sys, "frozen") or # new py2exe
356 return (hasattr(sys, "frozen") or # new py2exe
312 hasattr(sys, "importers") or # old py2exe
357 hasattr(sys, "importers") or # old py2exe
313 imp.is_frozen("__main__")) # tools/freeze
358 imp.is_frozen("__main__")) # tools/freeze
314
359
315 def hgexecutable():
360 def hgexecutable():
316 """return location of the 'hg' executable.
361 """return location of the 'hg' executable.
317
362
318 Defaults to $HG or 'hg' in the search path.
363 Defaults to $HG or 'hg' in the search path.
319 """
364 """
320 if _hgexecutable is None:
365 if _hgexecutable is None:
321 hg = os.environ.get('HG')
366 hg = os.environ.get('HG')
322 if hg:
367 if hg:
323 _sethgexecutable(hg)
368 _sethgexecutable(hg)
324 elif mainfrozen():
369 elif mainfrozen():
325 _sethgexecutable(sys.executable)
370 _sethgexecutable(sys.executable)
326 else:
371 else:
327 exe = findexe('hg') or os.path.basename(sys.argv[0])
372 exe = findexe('hg') or os.path.basename(sys.argv[0])
328 _sethgexecutable(exe)
373 _sethgexecutable(exe)
329 return _hgexecutable
374 return _hgexecutable
330
375
331 def _sethgexecutable(path):
376 def _sethgexecutable(path):
332 """set location of the 'hg' executable"""
377 """set location of the 'hg' executable"""
333 global _hgexecutable
378 global _hgexecutable
334 _hgexecutable = path
379 _hgexecutable = path
335
380
336 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
381 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
337 '''enhanced shell command execution.
382 '''enhanced shell command execution.
338 run with environment maybe modified, maybe in different dir.
383 run with environment maybe modified, maybe in different dir.
339
384
340 if command fails and onerr is None, return status. if ui object,
385 if command fails and onerr is None, return status. if ui object,
341 print error message and return status, else raise onerr object as
386 print error message and return status, else raise onerr object as
342 exception.
387 exception.
343
388
344 if out is specified, it is assumed to be a file-like object that has a
389 if out is specified, it is assumed to be a file-like object that has a
345 write() method. stdout and stderr will be redirected to out.'''
390 write() method. stdout and stderr will be redirected to out.'''
346 try:
391 try:
347 sys.stdout.flush()
392 sys.stdout.flush()
348 except Exception:
393 except Exception:
349 pass
394 pass
350 def py2shell(val):
395 def py2shell(val):
351 'convert python object into string that is useful to shell'
396 'convert python object into string that is useful to shell'
352 if val is None or val is False:
397 if val is None or val is False:
353 return '0'
398 return '0'
354 if val is True:
399 if val is True:
355 return '1'
400 return '1'
356 return str(val)
401 return str(val)
357 origcmd = cmd
402 origcmd = cmd
358 cmd = quotecommand(cmd)
403 cmd = quotecommand(cmd)
359 env = dict(os.environ)
404 env = dict(os.environ)
360 env.update((k, py2shell(v)) for k, v in environ.iteritems())
405 env.update((k, py2shell(v)) for k, v in environ.iteritems())
361 env['HG'] = hgexecutable()
406 env['HG'] = hgexecutable()
362 if out is None or out == sys.__stdout__:
407 if out is None or out == sys.__stdout__:
363 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
408 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
364 env=env, cwd=cwd)
409 env=env, cwd=cwd)
365 else:
410 else:
366 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
411 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
367 env=env, cwd=cwd, stdout=subprocess.PIPE,
412 env=env, cwd=cwd, stdout=subprocess.PIPE,
368 stderr=subprocess.STDOUT)
413 stderr=subprocess.STDOUT)
369 for line in proc.stdout:
414 for line in proc.stdout:
370 out.write(line)
415 out.write(line)
371 proc.wait()
416 proc.wait()
372 rc = proc.returncode
417 rc = proc.returncode
373 if sys.platform == 'OpenVMS' and rc & 1:
418 if sys.platform == 'OpenVMS' and rc & 1:
374 rc = 0
419 rc = 0
375 if rc and onerr:
420 if rc and onerr:
376 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
421 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
377 explainexit(rc)[0])
422 explainexit(rc)[0])
378 if errprefix:
423 if errprefix:
379 errmsg = '%s: %s' % (errprefix, errmsg)
424 errmsg = '%s: %s' % (errprefix, errmsg)
380 try:
425 try:
381 onerr.warn(errmsg + '\n')
426 onerr.warn(errmsg + '\n')
382 except AttributeError:
427 except AttributeError:
383 raise onerr(errmsg)
428 raise onerr(errmsg)
384 return rc
429 return rc
385
430
386 def checksignature(func):
431 def checksignature(func):
387 '''wrap a function with code to check for calling errors'''
432 '''wrap a function with code to check for calling errors'''
388 def check(*args, **kwargs):
433 def check(*args, **kwargs):
389 try:
434 try:
390 return func(*args, **kwargs)
435 return func(*args, **kwargs)
391 except TypeError:
436 except TypeError:
392 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
437 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
393 raise error.SignatureError
438 raise error.SignatureError
394 raise
439 raise
395
440
396 return check
441 return check
397
442
398 def copyfile(src, dest):
443 def copyfile(src, dest):
399 "copy a file, preserving mode and atime/mtime"
444 "copy a file, preserving mode and atime/mtime"
400 if os.path.islink(src):
445 if os.path.islink(src):
401 try:
446 try:
402 os.unlink(dest)
447 os.unlink(dest)
403 except OSError:
448 except OSError:
404 pass
449 pass
405 os.symlink(os.readlink(src), dest)
450 os.symlink(os.readlink(src), dest)
406 else:
451 else:
407 try:
452 try:
408 shutil.copyfile(src, dest)
453 shutil.copyfile(src, dest)
409 shutil.copymode(src, dest)
454 shutil.copymode(src, dest)
410 except shutil.Error, inst:
455 except shutil.Error, inst:
411 raise Abort(str(inst))
456 raise Abort(str(inst))
412
457
413 def copyfiles(src, dst, hardlink=None):
458 def copyfiles(src, dst, hardlink=None):
414 """Copy a directory tree using hardlinks if possible"""
459 """Copy a directory tree using hardlinks if possible"""
415
460
416 if hardlink is None:
461 if hardlink is None:
417 hardlink = (os.stat(src).st_dev ==
462 hardlink = (os.stat(src).st_dev ==
418 os.stat(os.path.dirname(dst)).st_dev)
463 os.stat(os.path.dirname(dst)).st_dev)
419
464
420 num = 0
465 num = 0
421 if os.path.isdir(src):
466 if os.path.isdir(src):
422 os.mkdir(dst)
467 os.mkdir(dst)
423 for name, kind in osutil.listdir(src):
468 for name, kind in osutil.listdir(src):
424 srcname = os.path.join(src, name)
469 srcname = os.path.join(src, name)
425 dstname = os.path.join(dst, name)
470 dstname = os.path.join(dst, name)
426 hardlink, n = copyfiles(srcname, dstname, hardlink)
471 hardlink, n = copyfiles(srcname, dstname, hardlink)
427 num += n
472 num += n
428 else:
473 else:
429 if hardlink:
474 if hardlink:
430 try:
475 try:
431 oslink(src, dst)
476 oslink(src, dst)
432 except (IOError, OSError):
477 except (IOError, OSError):
433 hardlink = False
478 hardlink = False
434 shutil.copy(src, dst)
479 shutil.copy(src, dst)
435 else:
480 else:
436 shutil.copy(src, dst)
481 shutil.copy(src, dst)
437 num += 1
482 num += 1
438
483
439 return hardlink, num
484 return hardlink, num
440
485
441 _winreservednames = '''con prn aux nul
486 _winreservednames = '''con prn aux nul
442 com1 com2 com3 com4 com5 com6 com7 com8 com9
487 com1 com2 com3 com4 com5 com6 com7 com8 com9
443 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
488 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
444 _winreservedchars = ':*?"<>|'
489 _winreservedchars = ':*?"<>|'
445 def checkwinfilename(path):
490 def checkwinfilename(path):
446 '''Check that the base-relative path is a valid filename on Windows.
491 '''Check that the base-relative path is a valid filename on Windows.
447 Returns None if the path is ok, or a UI string describing the problem.
492 Returns None if the path is ok, or a UI string describing the problem.
448
493
449 >>> checkwinfilename("just/a/normal/path")
494 >>> checkwinfilename("just/a/normal/path")
450 >>> checkwinfilename("foo/bar/con.xml")
495 >>> checkwinfilename("foo/bar/con.xml")
451 "filename contains 'con', which is reserved on Windows"
496 "filename contains 'con', which is reserved on Windows"
452 >>> checkwinfilename("foo/con.xml/bar")
497 >>> checkwinfilename("foo/con.xml/bar")
453 "filename contains 'con', which is reserved on Windows"
498 "filename contains 'con', which is reserved on Windows"
454 >>> checkwinfilename("foo/bar/xml.con")
499 >>> checkwinfilename("foo/bar/xml.con")
455 >>> checkwinfilename("foo/bar/AUX/bla.txt")
500 >>> checkwinfilename("foo/bar/AUX/bla.txt")
456 "filename contains 'AUX', which is reserved on Windows"
501 "filename contains 'AUX', which is reserved on Windows"
457 >>> checkwinfilename("foo/bar/bla:.txt")
502 >>> checkwinfilename("foo/bar/bla:.txt")
458 "filename contains ':', which is reserved on Windows"
503 "filename contains ':', which is reserved on Windows"
459 >>> checkwinfilename("foo/bar/b\07la.txt")
504 >>> checkwinfilename("foo/bar/b\07la.txt")
460 "filename contains '\\\\x07', which is invalid on Windows"
505 "filename contains '\\\\x07', which is invalid on Windows"
461 >>> checkwinfilename("foo/bar/bla ")
506 >>> checkwinfilename("foo/bar/bla ")
462 "filename ends with ' ', which is not allowed on Windows"
507 "filename ends with ' ', which is not allowed on Windows"
463 '''
508 '''
464 for n in path.replace('\\', '/').split('/'):
509 for n in path.replace('\\', '/').split('/'):
465 if not n:
510 if not n:
466 continue
511 continue
467 for c in n:
512 for c in n:
468 if c in _winreservedchars:
513 if c in _winreservedchars:
469 return _("filename contains '%s', which is reserved "
514 return _("filename contains '%s', which is reserved "
470 "on Windows") % c
515 "on Windows") % c
471 if ord(c) <= 31:
516 if ord(c) <= 31:
472 return _("filename contains %r, which is invalid "
517 return _("filename contains %r, which is invalid "
473 "on Windows") % c
518 "on Windows") % c
474 base = n.split('.')[0]
519 base = n.split('.')[0]
475 if base and base.lower() in _winreservednames:
520 if base and base.lower() in _winreservednames:
476 return _("filename contains '%s', which is reserved "
521 return _("filename contains '%s', which is reserved "
477 "on Windows") % base
522 "on Windows") % base
478 t = n[-1]
523 t = n[-1]
479 if t in '. ':
524 if t in '. ':
480 return _("filename ends with '%s', which is not allowed "
525 return _("filename ends with '%s', which is not allowed "
481 "on Windows") % t
526 "on Windows") % t
482
527
483 if os.name == 'nt':
528 if os.name == 'nt':
484 checkosfilename = checkwinfilename
529 checkosfilename = checkwinfilename
530 else:
531 checkosfilename = platform.checkosfilename
485
532
486 def makelock(info, pathname):
533 def makelock(info, pathname):
487 try:
534 try:
488 return os.symlink(info, pathname)
535 return os.symlink(info, pathname)
489 except OSError, why:
536 except OSError, why:
490 if why.errno == errno.EEXIST:
537 if why.errno == errno.EEXIST:
491 raise
538 raise
492 except AttributeError: # no symlink in os
539 except AttributeError: # no symlink in os
493 pass
540 pass
494
541
495 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
542 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
496 os.write(ld, info)
543 os.write(ld, info)
497 os.close(ld)
544 os.close(ld)
498
545
499 def readlock(pathname):
546 def readlock(pathname):
500 try:
547 try:
501 return os.readlink(pathname)
548 return os.readlink(pathname)
502 except OSError, why:
549 except OSError, why:
503 if why.errno not in (errno.EINVAL, errno.ENOSYS):
550 if why.errno not in (errno.EINVAL, errno.ENOSYS):
504 raise
551 raise
505 except AttributeError: # no symlink in os
552 except AttributeError: # no symlink in os
506 pass
553 pass
507 fp = posixfile(pathname)
554 fp = posixfile(pathname)
508 r = fp.read()
555 r = fp.read()
509 fp.close()
556 fp.close()
510 return r
557 return r
511
558
512 def fstat(fp):
559 def fstat(fp):
513 '''stat file object that may not have fileno method.'''
560 '''stat file object that may not have fileno method.'''
514 try:
561 try:
515 return os.fstat(fp.fileno())
562 return os.fstat(fp.fileno())
516 except AttributeError:
563 except AttributeError:
517 return os.stat(fp.name)
564 return os.stat(fp.name)
518
565
519 # File system features
566 # File system features
520
567
521 def checkcase(path):
568 def checkcase(path):
522 """
569 """
523 Check whether the given path is on a case-sensitive filesystem
570 Check whether the given path is on a case-sensitive filesystem
524
571
525 Requires a path (like /foo/.hg) ending with a foldable final
572 Requires a path (like /foo/.hg) ending with a foldable final
526 directory component.
573 directory component.
527 """
574 """
528 s1 = os.stat(path)
575 s1 = os.stat(path)
529 d, b = os.path.split(path)
576 d, b = os.path.split(path)
530 p2 = os.path.join(d, b.upper())
577 p2 = os.path.join(d, b.upper())
531 if path == p2:
578 if path == p2:
532 p2 = os.path.join(d, b.lower())
579 p2 = os.path.join(d, b.lower())
533 try:
580 try:
534 s2 = os.stat(p2)
581 s2 = os.stat(p2)
535 if s2 == s1:
582 if s2 == s1:
536 return False
583 return False
537 return True
584 return True
538 except OSError:
585 except OSError:
539 return True
586 return True
540
587
541 _fspathcache = {}
588 _fspathcache = {}
542 def fspath(name, root):
589 def fspath(name, root):
543 '''Get name in the case stored in the filesystem
590 '''Get name in the case stored in the filesystem
544
591
545 The name is either relative to root, or it is an absolute path starting
592 The name is either relative to root, or it is an absolute path starting
546 with root. Note that this function is unnecessary, and should not be
593 with root. Note that this function is unnecessary, and should not be
547 called, for case-sensitive filesystems (simply because it's expensive).
594 called, for case-sensitive filesystems (simply because it's expensive).
548 '''
595 '''
549 # If name is absolute, make it relative
596 # If name is absolute, make it relative
550 if name.lower().startswith(root.lower()):
597 if name.lower().startswith(root.lower()):
551 l = len(root)
598 l = len(root)
552 if name[l] == os.sep or name[l] == os.altsep:
599 if name[l] == os.sep or name[l] == os.altsep:
553 l = l + 1
600 l = l + 1
554 name = name[l:]
601 name = name[l:]
555
602
556 if not os.path.lexists(os.path.join(root, name)):
603 if not os.path.lexists(os.path.join(root, name)):
557 return None
604 return None
558
605
559 seps = os.sep
606 seps = os.sep
560 if os.altsep:
607 if os.altsep:
561 seps = seps + os.altsep
608 seps = seps + os.altsep
562 # Protect backslashes. This gets silly very quickly.
609 # Protect backslashes. This gets silly very quickly.
563 seps.replace('\\','\\\\')
610 seps.replace('\\','\\\\')
564 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
611 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
565 dir = os.path.normcase(os.path.normpath(root))
612 dir = os.path.normcase(os.path.normpath(root))
566 result = []
613 result = []
567 for part, sep in pattern.findall(name):
614 for part, sep in pattern.findall(name):
568 if sep:
615 if sep:
569 result.append(sep)
616 result.append(sep)
570 continue
617 continue
571
618
572 if dir not in _fspathcache:
619 if dir not in _fspathcache:
573 _fspathcache[dir] = os.listdir(dir)
620 _fspathcache[dir] = os.listdir(dir)
574 contents = _fspathcache[dir]
621 contents = _fspathcache[dir]
575
622
576 lpart = part.lower()
623 lpart = part.lower()
577 lenp = len(part)
624 lenp = len(part)
578 for n in contents:
625 for n in contents:
579 if lenp == len(n) and n.lower() == lpart:
626 if lenp == len(n) and n.lower() == lpart:
580 result.append(n)
627 result.append(n)
581 break
628 break
582 else:
629 else:
583 # Cannot happen, as the file exists!
630 # Cannot happen, as the file exists!
584 result.append(part)
631 result.append(part)
585 dir = os.path.join(dir, lpart)
632 dir = os.path.join(dir, lpart)
586
633
587 return ''.join(result)
634 return ''.join(result)
588
635
589 def checknlink(testfile):
636 def checknlink(testfile):
590 '''check whether hardlink count reporting works properly'''
637 '''check whether hardlink count reporting works properly'''
591
638
592 # testfile may be open, so we need a separate file for checking to
639 # testfile may be open, so we need a separate file for checking to
593 # work around issue2543 (or testfile may get lost on Samba shares)
640 # work around issue2543 (or testfile may get lost on Samba shares)
594 f1 = testfile + ".hgtmp1"
641 f1 = testfile + ".hgtmp1"
595 if os.path.lexists(f1):
642 if os.path.lexists(f1):
596 return False
643 return False
597 try:
644 try:
598 posixfile(f1, 'w').close()
645 posixfile(f1, 'w').close()
599 except IOError:
646 except IOError:
600 return False
647 return False
601
648
602 f2 = testfile + ".hgtmp2"
649 f2 = testfile + ".hgtmp2"
603 fd = None
650 fd = None
604 try:
651 try:
605 try:
652 try:
606 oslink(f1, f2)
653 oslink(f1, f2)
607 except OSError:
654 except OSError:
608 return False
655 return False
609
656
610 # nlinks() may behave differently for files on Windows shares if
657 # nlinks() may behave differently for files on Windows shares if
611 # the file is open.
658 # the file is open.
612 fd = posixfile(f2)
659 fd = posixfile(f2)
613 return nlinks(f2) > 1
660 return nlinks(f2) > 1
614 finally:
661 finally:
615 if fd is not None:
662 if fd is not None:
616 fd.close()
663 fd.close()
617 for f in (f1, f2):
664 for f in (f1, f2):
618 try:
665 try:
619 os.unlink(f)
666 os.unlink(f)
620 except OSError:
667 except OSError:
621 pass
668 pass
622
669
623 return False
670 return False
624
671
625 def endswithsep(path):
672 def endswithsep(path):
626 '''Check path ends with os.sep or os.altsep.'''
673 '''Check path ends with os.sep or os.altsep.'''
627 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
674 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
628
675
629 def splitpath(path):
676 def splitpath(path):
630 '''Split path by os.sep.
677 '''Split path by os.sep.
631 Note that this function does not use os.altsep because this is
678 Note that this function does not use os.altsep because this is
632 an alternative of simple "xxx.split(os.sep)".
679 an alternative of simple "xxx.split(os.sep)".
633 It is recommended to use os.path.normpath() before using this
680 It is recommended to use os.path.normpath() before using this
634 function if need.'''
681 function if need.'''
635 return path.split(os.sep)
682 return path.split(os.sep)
636
683
637 def gui():
684 def gui():
638 '''Are we running in a GUI?'''
685 '''Are we running in a GUI?'''
639 if sys.platform == 'darwin':
686 if sys.platform == 'darwin':
640 if 'SSH_CONNECTION' in os.environ:
687 if 'SSH_CONNECTION' in os.environ:
641 # handle SSH access to a box where the user is logged in
688 # handle SSH access to a box where the user is logged in
642 return False
689 return False
643 elif getattr(osutil, 'isgui', None):
690 elif getattr(osutil, 'isgui', None):
644 # check if a CoreGraphics session is available
691 # check if a CoreGraphics session is available
645 return osutil.isgui()
692 return osutil.isgui()
646 else:
693 else:
647 # pure build; use a safe default
694 # pure build; use a safe default
648 return True
695 return True
649 else:
696 else:
650 return os.name == "nt" or os.environ.get("DISPLAY")
697 return os.name == "nt" or os.environ.get("DISPLAY")
651
698
652 def mktempcopy(name, emptyok=False, createmode=None):
699 def mktempcopy(name, emptyok=False, createmode=None):
653 """Create a temporary file with the same contents from name
700 """Create a temporary file with the same contents from name
654
701
655 The permission bits are copied from the original file.
702 The permission bits are copied from the original file.
656
703
657 If the temporary file is going to be truncated immediately, you
704 If the temporary file is going to be truncated immediately, you
658 can use emptyok=True as an optimization.
705 can use emptyok=True as an optimization.
659
706
660 Returns the name of the temporary file.
707 Returns the name of the temporary file.
661 """
708 """
662 d, fn = os.path.split(name)
709 d, fn = os.path.split(name)
663 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
710 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
664 os.close(fd)
711 os.close(fd)
665 # Temporary files are created with mode 0600, which is usually not
712 # Temporary files are created with mode 0600, which is usually not
666 # what we want. If the original file already exists, just copy
713 # what we want. If the original file already exists, just copy
667 # its mode. Otherwise, manually obey umask.
714 # its mode. Otherwise, manually obey umask.
668 try:
715 try:
669 st_mode = os.lstat(name).st_mode & 0777
716 st_mode = os.lstat(name).st_mode & 0777
670 except OSError, inst:
717 except OSError, inst:
671 if inst.errno != errno.ENOENT:
718 if inst.errno != errno.ENOENT:
672 raise
719 raise
673 st_mode = createmode
720 st_mode = createmode
674 if st_mode is None:
721 if st_mode is None:
675 st_mode = ~umask
722 st_mode = ~umask
676 st_mode &= 0666
723 st_mode &= 0666
677 os.chmod(temp, st_mode)
724 os.chmod(temp, st_mode)
678 if emptyok:
725 if emptyok:
679 return temp
726 return temp
680 try:
727 try:
681 try:
728 try:
682 ifp = posixfile(name, "rb")
729 ifp = posixfile(name, "rb")
683 except IOError, inst:
730 except IOError, inst:
684 if inst.errno == errno.ENOENT:
731 if inst.errno == errno.ENOENT:
685 return temp
732 return temp
686 if not getattr(inst, 'filename', None):
733 if not getattr(inst, 'filename', None):
687 inst.filename = name
734 inst.filename = name
688 raise
735 raise
689 ofp = posixfile(temp, "wb")
736 ofp = posixfile(temp, "wb")
690 for chunk in filechunkiter(ifp):
737 for chunk in filechunkiter(ifp):
691 ofp.write(chunk)
738 ofp.write(chunk)
692 ifp.close()
739 ifp.close()
693 ofp.close()
740 ofp.close()
694 except:
741 except:
695 try: os.unlink(temp)
742 try: os.unlink(temp)
696 except: pass
743 except: pass
697 raise
744 raise
698 return temp
745 return temp
699
746
700 class atomictempfile(object):
747 class atomictempfile(object):
701 '''writeable file object that atomically updates a file
748 '''writeable file object that atomically updates a file
702
749
703 All writes will go to a temporary copy of the original file. Call
750 All writes will go to a temporary copy of the original file. Call
704 rename() when you are done writing, and atomictempfile will rename
751 rename() when you are done writing, and atomictempfile will rename
705 the temporary copy to the original name, making the changes visible.
752 the temporary copy to the original name, making the changes visible.
706
753
707 Unlike other file-like objects, close() discards your writes by
754 Unlike other file-like objects, close() discards your writes by
708 simply deleting the temporary file.
755 simply deleting the temporary file.
709 '''
756 '''
710 def __init__(self, name, mode='w+b', createmode=None):
757 def __init__(self, name, mode='w+b', createmode=None):
711 self.__name = name # permanent name
758 self.__name = name # permanent name
712 self._tempname = mktempcopy(name, emptyok=('w' in mode),
759 self._tempname = mktempcopy(name, emptyok=('w' in mode),
713 createmode=createmode)
760 createmode=createmode)
714 self._fp = posixfile(self._tempname, mode)
761 self._fp = posixfile(self._tempname, mode)
715
762
716 # delegated methods
763 # delegated methods
717 self.write = self._fp.write
764 self.write = self._fp.write
718 self.fileno = self._fp.fileno
765 self.fileno = self._fp.fileno
719
766
720 def rename(self):
767 def rename(self):
721 if not self._fp.closed:
768 if not self._fp.closed:
722 self._fp.close()
769 self._fp.close()
723 rename(self._tempname, localpath(self.__name))
770 rename(self._tempname, localpath(self.__name))
724
771
725 def close(self):
772 def close(self):
726 if not self._fp.closed:
773 if not self._fp.closed:
727 try:
774 try:
728 os.unlink(self._tempname)
775 os.unlink(self._tempname)
729 except OSError:
776 except OSError:
730 pass
777 pass
731 self._fp.close()
778 self._fp.close()
732
779
733 def __del__(self):
780 def __del__(self):
734 if hasattr(self, '_fp'): # constructor actually did something
781 if hasattr(self, '_fp'): # constructor actually did something
735 self.close()
782 self.close()
736
783
737 def makedirs(name, mode=None):
784 def makedirs(name, mode=None):
738 """recursive directory creation with parent mode inheritance"""
785 """recursive directory creation with parent mode inheritance"""
739 parent = os.path.abspath(os.path.dirname(name))
786 parent = os.path.abspath(os.path.dirname(name))
740 try:
787 try:
741 os.mkdir(name)
788 os.mkdir(name)
742 if mode is not None:
789 if mode is not None:
743 os.chmod(name, mode)
790 os.chmod(name, mode)
744 return
791 return
745 except OSError, err:
792 except OSError, err:
746 if err.errno == errno.EEXIST:
793 if err.errno == errno.EEXIST:
747 return
794 return
748 if not name or parent == name or err.errno != errno.ENOENT:
795 if not name or parent == name or err.errno != errno.ENOENT:
749 raise
796 raise
750 makedirs(parent, mode)
797 makedirs(parent, mode)
751 makedirs(name, mode)
798 makedirs(name, mode)
752
799
753 def readfile(path):
800 def readfile(path):
754 fp = open(path, 'rb')
801 fp = open(path, 'rb')
755 try:
802 try:
756 return fp.read()
803 return fp.read()
757 finally:
804 finally:
758 fp.close()
805 fp.close()
759
806
760 def writefile(path, text):
807 def writefile(path, text):
761 fp = open(path, 'wb')
808 fp = open(path, 'wb')
762 try:
809 try:
763 fp.write(text)
810 fp.write(text)
764 finally:
811 finally:
765 fp.close()
812 fp.close()
766
813
767 def appendfile(path, text):
814 def appendfile(path, text):
768 fp = open(path, 'ab')
815 fp = open(path, 'ab')
769 try:
816 try:
770 fp.write(text)
817 fp.write(text)
771 finally:
818 finally:
772 fp.close()
819 fp.close()
773
820
774 class chunkbuffer(object):
821 class chunkbuffer(object):
775 """Allow arbitrary sized chunks of data to be efficiently read from an
822 """Allow arbitrary sized chunks of data to be efficiently read from an
776 iterator over chunks of arbitrary size."""
823 iterator over chunks of arbitrary size."""
777
824
778 def __init__(self, in_iter):
825 def __init__(self, in_iter):
779 """in_iter is the iterator that's iterating over the input chunks.
826 """in_iter is the iterator that's iterating over the input chunks.
780 targetsize is how big a buffer to try to maintain."""
827 targetsize is how big a buffer to try to maintain."""
781 def splitbig(chunks):
828 def splitbig(chunks):
782 for chunk in chunks:
829 for chunk in chunks:
783 if len(chunk) > 2**20:
830 if len(chunk) > 2**20:
784 pos = 0
831 pos = 0
785 while pos < len(chunk):
832 while pos < len(chunk):
786 end = pos + 2 ** 18
833 end = pos + 2 ** 18
787 yield chunk[pos:end]
834 yield chunk[pos:end]
788 pos = end
835 pos = end
789 else:
836 else:
790 yield chunk
837 yield chunk
791 self.iter = splitbig(in_iter)
838 self.iter = splitbig(in_iter)
792 self._queue = []
839 self._queue = []
793
840
794 def read(self, l):
841 def read(self, l):
795 """Read L bytes of data from the iterator of chunks of data.
842 """Read L bytes of data from the iterator of chunks of data.
796 Returns less than L bytes if the iterator runs dry."""
843 Returns less than L bytes if the iterator runs dry."""
797 left = l
844 left = l
798 buf = ''
845 buf = ''
799 queue = self._queue
846 queue = self._queue
800 while left > 0:
847 while left > 0:
801 # refill the queue
848 # refill the queue
802 if not queue:
849 if not queue:
803 target = 2**18
850 target = 2**18
804 for chunk in self.iter:
851 for chunk in self.iter:
805 queue.append(chunk)
852 queue.append(chunk)
806 target -= len(chunk)
853 target -= len(chunk)
807 if target <= 0:
854 if target <= 0:
808 break
855 break
809 if not queue:
856 if not queue:
810 break
857 break
811
858
812 chunk = queue.pop(0)
859 chunk = queue.pop(0)
813 left -= len(chunk)
860 left -= len(chunk)
814 if left < 0:
861 if left < 0:
815 queue.insert(0, chunk[left:])
862 queue.insert(0, chunk[left:])
816 buf += chunk[:left]
863 buf += chunk[:left]
817 else:
864 else:
818 buf += chunk
865 buf += chunk
819
866
820 return buf
867 return buf
821
868
822 def filechunkiter(f, size=65536, limit=None):
869 def filechunkiter(f, size=65536, limit=None):
823 """Create a generator that produces the data in the file size
870 """Create a generator that produces the data in the file size
824 (default 65536) bytes at a time, up to optional limit (default is
871 (default 65536) bytes at a time, up to optional limit (default is
825 to read all data). Chunks may be less than size bytes if the
872 to read all data). Chunks may be less than size bytes if the
826 chunk is the last chunk in the file, or the file is a socket or
873 chunk is the last chunk in the file, or the file is a socket or
827 some other type of file that sometimes reads less data than is
874 some other type of file that sometimes reads less data than is
828 requested."""
875 requested."""
829 assert size >= 0
876 assert size >= 0
830 assert limit is None or limit >= 0
877 assert limit is None or limit >= 0
831 while True:
878 while True:
832 if limit is None:
879 if limit is None:
833 nbytes = size
880 nbytes = size
834 else:
881 else:
835 nbytes = min(limit, size)
882 nbytes = min(limit, size)
836 s = nbytes and f.read(nbytes)
883 s = nbytes and f.read(nbytes)
837 if not s:
884 if not s:
838 break
885 break
839 if limit:
886 if limit:
840 limit -= len(s)
887 limit -= len(s)
841 yield s
888 yield s
842
889
843 def makedate():
890 def makedate():
844 lt = time.localtime()
891 lt = time.localtime()
845 if lt[8] == 1 and time.daylight:
892 if lt[8] == 1 and time.daylight:
846 tz = time.altzone
893 tz = time.altzone
847 else:
894 else:
848 tz = time.timezone
895 tz = time.timezone
849 t = time.mktime(lt)
896 t = time.mktime(lt)
850 if t < 0:
897 if t < 0:
851 hint = _("check your clock")
898 hint = _("check your clock")
852 raise Abort(_("negative timestamp: %d") % t, hint=hint)
899 raise Abort(_("negative timestamp: %d") % t, hint=hint)
853 return t, tz
900 return t, tz
854
901
855 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
902 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
856 """represent a (unixtime, offset) tuple as a localized time.
903 """represent a (unixtime, offset) tuple as a localized time.
857 unixtime is seconds since the epoch, and offset is the time zone's
904 unixtime is seconds since the epoch, and offset is the time zone's
858 number of seconds away from UTC. if timezone is false, do not
905 number of seconds away from UTC. if timezone is false, do not
859 append time zone to string."""
906 append time zone to string."""
860 t, tz = date or makedate()
907 t, tz = date or makedate()
861 if t < 0:
908 if t < 0:
862 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
909 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
863 tz = 0
910 tz = 0
864 if "%1" in format or "%2" in format:
911 if "%1" in format or "%2" in format:
865 sign = (tz > 0) and "-" or "+"
912 sign = (tz > 0) and "-" or "+"
866 minutes = abs(tz) // 60
913 minutes = abs(tz) // 60
867 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
914 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
868 format = format.replace("%2", "%02d" % (minutes % 60))
915 format = format.replace("%2", "%02d" % (minutes % 60))
869 s = time.strftime(format, time.gmtime(float(t) - tz))
916 s = time.strftime(format, time.gmtime(float(t) - tz))
870 return s
917 return s
871
918
872 def shortdate(date=None):
919 def shortdate(date=None):
873 """turn (timestamp, tzoff) tuple into iso 8631 date."""
920 """turn (timestamp, tzoff) tuple into iso 8631 date."""
874 return datestr(date, format='%Y-%m-%d')
921 return datestr(date, format='%Y-%m-%d')
875
922
876 def strdate(string, format, defaults=[]):
923 def strdate(string, format, defaults=[]):
877 """parse a localized time string and return a (unixtime, offset) tuple.
924 """parse a localized time string and return a (unixtime, offset) tuple.
878 if the string cannot be parsed, ValueError is raised."""
925 if the string cannot be parsed, ValueError is raised."""
879 def timezone(string):
926 def timezone(string):
880 tz = string.split()[-1]
927 tz = string.split()[-1]
881 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
928 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
882 sign = (tz[0] == "+") and 1 or -1
929 sign = (tz[0] == "+") and 1 or -1
883 hours = int(tz[1:3])
930 hours = int(tz[1:3])
884 minutes = int(tz[3:5])
931 minutes = int(tz[3:5])
885 return -sign * (hours * 60 + minutes) * 60
932 return -sign * (hours * 60 + minutes) * 60
886 if tz == "GMT" or tz == "UTC":
933 if tz == "GMT" or tz == "UTC":
887 return 0
934 return 0
888 return None
935 return None
889
936
890 # NOTE: unixtime = localunixtime + offset
937 # NOTE: unixtime = localunixtime + offset
891 offset, date = timezone(string), string
938 offset, date = timezone(string), string
892 if offset is not None:
939 if offset is not None:
893 date = " ".join(string.split()[:-1])
940 date = " ".join(string.split()[:-1])
894
941
895 # add missing elements from defaults
942 # add missing elements from defaults
896 usenow = False # default to using biased defaults
943 usenow = False # default to using biased defaults
897 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
944 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
898 found = [True for p in part if ("%"+p) in format]
945 found = [True for p in part if ("%"+p) in format]
899 if not found:
946 if not found:
900 date += "@" + defaults[part][usenow]
947 date += "@" + defaults[part][usenow]
901 format += "@%" + part[0]
948 format += "@%" + part[0]
902 else:
949 else:
903 # We've found a specific time element, less specific time
950 # We've found a specific time element, less specific time
904 # elements are relative to today
951 # elements are relative to today
905 usenow = True
952 usenow = True
906
953
907 timetuple = time.strptime(date, format)
954 timetuple = time.strptime(date, format)
908 localunixtime = int(calendar.timegm(timetuple))
955 localunixtime = int(calendar.timegm(timetuple))
909 if offset is None:
956 if offset is None:
910 # local timezone
957 # local timezone
911 unixtime = int(time.mktime(timetuple))
958 unixtime = int(time.mktime(timetuple))
912 offset = unixtime - localunixtime
959 offset = unixtime - localunixtime
913 else:
960 else:
914 unixtime = localunixtime + offset
961 unixtime = localunixtime + offset
915 return unixtime, offset
962 return unixtime, offset
916
963
917 def parsedate(date, formats=None, bias={}):
964 def parsedate(date, formats=None, bias={}):
918 """parse a localized date/time and return a (unixtime, offset) tuple.
965 """parse a localized date/time and return a (unixtime, offset) tuple.
919
966
920 The date may be a "unixtime offset" string or in one of the specified
967 The date may be a "unixtime offset" string or in one of the specified
921 formats. If the date already is a (unixtime, offset) tuple, it is returned.
968 formats. If the date already is a (unixtime, offset) tuple, it is returned.
922 """
969 """
923 if not date:
970 if not date:
924 return 0, 0
971 return 0, 0
925 if isinstance(date, tuple) and len(date) == 2:
972 if isinstance(date, tuple) and len(date) == 2:
926 return date
973 return date
927 if not formats:
974 if not formats:
928 formats = defaultdateformats
975 formats = defaultdateformats
929 date = date.strip()
976 date = date.strip()
930 try:
977 try:
931 when, offset = map(int, date.split(' '))
978 when, offset = map(int, date.split(' '))
932 except ValueError:
979 except ValueError:
933 # fill out defaults
980 # fill out defaults
934 now = makedate()
981 now = makedate()
935 defaults = {}
982 defaults = {}
936 for part in ("d", "mb", "yY", "HI", "M", "S"):
983 for part in ("d", "mb", "yY", "HI", "M", "S"):
937 # this piece is for rounding the specific end of unknowns
984 # this piece is for rounding the specific end of unknowns
938 b = bias.get(part)
985 b = bias.get(part)
939 if b is None:
986 if b is None:
940 if part[0] in "HMS":
987 if part[0] in "HMS":
941 b = "00"
988 b = "00"
942 else:
989 else:
943 b = "0"
990 b = "0"
944
991
945 # this piece is for matching the generic end to today's date
992 # this piece is for matching the generic end to today's date
946 n = datestr(now, "%" + part[0])
993 n = datestr(now, "%" + part[0])
947
994
948 defaults[part] = (b, n)
995 defaults[part] = (b, n)
949
996
950 for format in formats:
997 for format in formats:
951 try:
998 try:
952 when, offset = strdate(date, format, defaults)
999 when, offset = strdate(date, format, defaults)
953 except (ValueError, OverflowError):
1000 except (ValueError, OverflowError):
954 pass
1001 pass
955 else:
1002 else:
956 break
1003 break
957 else:
1004 else:
958 raise Abort(_('invalid date: %r') % date)
1005 raise Abort(_('invalid date: %r') % date)
959 # validate explicit (probably user-specified) date and
1006 # validate explicit (probably user-specified) date and
960 # time zone offset. values must fit in signed 32 bits for
1007 # time zone offset. values must fit in signed 32 bits for
961 # current 32-bit linux runtimes. timezones go from UTC-12
1008 # current 32-bit linux runtimes. timezones go from UTC-12
962 # to UTC+14
1009 # to UTC+14
963 if abs(when) > 0x7fffffff:
1010 if abs(when) > 0x7fffffff:
964 raise Abort(_('date exceeds 32 bits: %d') % when)
1011 raise Abort(_('date exceeds 32 bits: %d') % when)
965 if when < 0:
1012 if when < 0:
966 raise Abort(_('negative date value: %d') % when)
1013 raise Abort(_('negative date value: %d') % when)
967 if offset < -50400 or offset > 43200:
1014 if offset < -50400 or offset > 43200:
968 raise Abort(_('impossible time zone offset: %d') % offset)
1015 raise Abort(_('impossible time zone offset: %d') % offset)
969 return when, offset
1016 return when, offset
970
1017
971 def matchdate(date):
1018 def matchdate(date):
972 """Return a function that matches a given date match specifier
1019 """Return a function that matches a given date match specifier
973
1020
974 Formats include:
1021 Formats include:
975
1022
976 '{date}' match a given date to the accuracy provided
1023 '{date}' match a given date to the accuracy provided
977
1024
978 '<{date}' on or before a given date
1025 '<{date}' on or before a given date
979
1026
980 '>{date}' on or after a given date
1027 '>{date}' on or after a given date
981
1028
982 >>> p1 = parsedate("10:29:59")
1029 >>> p1 = parsedate("10:29:59")
983 >>> p2 = parsedate("10:30:00")
1030 >>> p2 = parsedate("10:30:00")
984 >>> p3 = parsedate("10:30:59")
1031 >>> p3 = parsedate("10:30:59")
985 >>> p4 = parsedate("10:31:00")
1032 >>> p4 = parsedate("10:31:00")
986 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1033 >>> p5 = parsedate("Sep 15 10:30:00 1999")
987 >>> f = matchdate("10:30")
1034 >>> f = matchdate("10:30")
988 >>> f(p1[0])
1035 >>> f(p1[0])
989 False
1036 False
990 >>> f(p2[0])
1037 >>> f(p2[0])
991 True
1038 True
992 >>> f(p3[0])
1039 >>> f(p3[0])
993 True
1040 True
994 >>> f(p4[0])
1041 >>> f(p4[0])
995 False
1042 False
996 >>> f(p5[0])
1043 >>> f(p5[0])
997 False
1044 False
998 """
1045 """
999
1046
1000 def lower(date):
1047 def lower(date):
1001 d = dict(mb="1", d="1")
1048 d = dict(mb="1", d="1")
1002 return parsedate(date, extendeddateformats, d)[0]
1049 return parsedate(date, extendeddateformats, d)[0]
1003
1050
1004 def upper(date):
1051 def upper(date):
1005 d = dict(mb="12", HI="23", M="59", S="59")
1052 d = dict(mb="12", HI="23", M="59", S="59")
1006 for days in ("31", "30", "29"):
1053 for days in ("31", "30", "29"):
1007 try:
1054 try:
1008 d["d"] = days
1055 d["d"] = days
1009 return parsedate(date, extendeddateformats, d)[0]
1056 return parsedate(date, extendeddateformats, d)[0]
1010 except:
1057 except:
1011 pass
1058 pass
1012 d["d"] = "28"
1059 d["d"] = "28"
1013 return parsedate(date, extendeddateformats, d)[0]
1060 return parsedate(date, extendeddateformats, d)[0]
1014
1061
1015 date = date.strip()
1062 date = date.strip()
1016
1063
1017 if not date:
1064 if not date:
1018 raise Abort(_("dates cannot consist entirely of whitespace"))
1065 raise Abort(_("dates cannot consist entirely of whitespace"))
1019 elif date[0] == "<":
1066 elif date[0] == "<":
1020 if not date[1:]:
1067 if not date[1:]:
1021 raise Abort(_("invalid day spec, use '<DATE'"))
1068 raise Abort(_("invalid day spec, use '<DATE'"))
1022 when = upper(date[1:])
1069 when = upper(date[1:])
1023 return lambda x: x <= when
1070 return lambda x: x <= when
1024 elif date[0] == ">":
1071 elif date[0] == ">":
1025 if not date[1:]:
1072 if not date[1:]:
1026 raise Abort(_("invalid day spec, use '>DATE'"))
1073 raise Abort(_("invalid day spec, use '>DATE'"))
1027 when = lower(date[1:])
1074 when = lower(date[1:])
1028 return lambda x: x >= when
1075 return lambda x: x >= when
1029 elif date[0] == "-":
1076 elif date[0] == "-":
1030 try:
1077 try:
1031 days = int(date[1:])
1078 days = int(date[1:])
1032 except ValueError:
1079 except ValueError:
1033 raise Abort(_("invalid day spec: %s") % date[1:])
1080 raise Abort(_("invalid day spec: %s") % date[1:])
1034 if days < 0:
1081 if days < 0:
1035 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
1082 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
1036 % date[1:])
1083 % date[1:])
1037 when = makedate()[0] - days * 3600 * 24
1084 when = makedate()[0] - days * 3600 * 24
1038 return lambda x: x >= when
1085 return lambda x: x >= when
1039 elif " to " in date:
1086 elif " to " in date:
1040 a, b = date.split(" to ")
1087 a, b = date.split(" to ")
1041 start, stop = lower(a), upper(b)
1088 start, stop = lower(a), upper(b)
1042 return lambda x: x >= start and x <= stop
1089 return lambda x: x >= start and x <= stop
1043 else:
1090 else:
1044 start, stop = lower(date), upper(date)
1091 start, stop = lower(date), upper(date)
1045 return lambda x: x >= start and x <= stop
1092 return lambda x: x >= start and x <= stop
1046
1093
1047 def shortuser(user):
1094 def shortuser(user):
1048 """Return a short representation of a user name or email address."""
1095 """Return a short representation of a user name or email address."""
1049 f = user.find('@')
1096 f = user.find('@')
1050 if f >= 0:
1097 if f >= 0:
1051 user = user[:f]
1098 user = user[:f]
1052 f = user.find('<')
1099 f = user.find('<')
1053 if f >= 0:
1100 if f >= 0:
1054 user = user[f + 1:]
1101 user = user[f + 1:]
1055 f = user.find(' ')
1102 f = user.find(' ')
1056 if f >= 0:
1103 if f >= 0:
1057 user = user[:f]
1104 user = user[:f]
1058 f = user.find('.')
1105 f = user.find('.')
1059 if f >= 0:
1106 if f >= 0:
1060 user = user[:f]
1107 user = user[:f]
1061 return user
1108 return user
1062
1109
1063 def email(author):
1110 def email(author):
1064 '''get email of author.'''
1111 '''get email of author.'''
1065 r = author.find('>')
1112 r = author.find('>')
1066 if r == -1:
1113 if r == -1:
1067 r = None
1114 r = None
1068 return author[author.find('<') + 1:r]
1115 return author[author.find('<') + 1:r]
1069
1116
1070 def _ellipsis(text, maxlength):
1117 def _ellipsis(text, maxlength):
1071 if len(text) <= maxlength:
1118 if len(text) <= maxlength:
1072 return text, False
1119 return text, False
1073 else:
1120 else:
1074 return "%s..." % (text[:maxlength - 3]), True
1121 return "%s..." % (text[:maxlength - 3]), True
1075
1122
1076 def ellipsis(text, maxlength=400):
1123 def ellipsis(text, maxlength=400):
1077 """Trim string to at most maxlength (default: 400) characters."""
1124 """Trim string to at most maxlength (default: 400) characters."""
1078 try:
1125 try:
1079 # use unicode not to split at intermediate multi-byte sequence
1126 # use unicode not to split at intermediate multi-byte sequence
1080 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1127 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1081 maxlength)
1128 maxlength)
1082 if not truncated:
1129 if not truncated:
1083 return text
1130 return text
1084 return utext.encode(encoding.encoding)
1131 return utext.encode(encoding.encoding)
1085 except (UnicodeDecodeError, UnicodeEncodeError):
1132 except (UnicodeDecodeError, UnicodeEncodeError):
1086 return _ellipsis(text, maxlength)[0]
1133 return _ellipsis(text, maxlength)[0]
1087
1134
1088 def bytecount(nbytes):
1135 def bytecount(nbytes):
1089 '''return byte count formatted as readable string, with units'''
1136 '''return byte count formatted as readable string, with units'''
1090
1137
1091 units = (
1138 units = (
1092 (100, 1 << 30, _('%.0f GB')),
1139 (100, 1 << 30, _('%.0f GB')),
1093 (10, 1 << 30, _('%.1f GB')),
1140 (10, 1 << 30, _('%.1f GB')),
1094 (1, 1 << 30, _('%.2f GB')),
1141 (1, 1 << 30, _('%.2f GB')),
1095 (100, 1 << 20, _('%.0f MB')),
1142 (100, 1 << 20, _('%.0f MB')),
1096 (10, 1 << 20, _('%.1f MB')),
1143 (10, 1 << 20, _('%.1f MB')),
1097 (1, 1 << 20, _('%.2f MB')),
1144 (1, 1 << 20, _('%.2f MB')),
1098 (100, 1 << 10, _('%.0f KB')),
1145 (100, 1 << 10, _('%.0f KB')),
1099 (10, 1 << 10, _('%.1f KB')),
1146 (10, 1 << 10, _('%.1f KB')),
1100 (1, 1 << 10, _('%.2f KB')),
1147 (1, 1 << 10, _('%.2f KB')),
1101 (1, 1, _('%.0f bytes')),
1148 (1, 1, _('%.0f bytes')),
1102 )
1149 )
1103
1150
1104 for multiplier, divisor, format in units:
1151 for multiplier, divisor, format in units:
1105 if nbytes >= divisor * multiplier:
1152 if nbytes >= divisor * multiplier:
1106 return format % (nbytes / float(divisor))
1153 return format % (nbytes / float(divisor))
1107 return units[-1][2] % nbytes
1154 return units[-1][2] % nbytes
1108
1155
1109 def uirepr(s):
1156 def uirepr(s):
1110 # Avoid double backslash in Windows path repr()
1157 # Avoid double backslash in Windows path repr()
1111 return repr(s).replace('\\\\', '\\')
1158 return repr(s).replace('\\\\', '\\')
1112
1159
1113 # delay import of textwrap
1160 # delay import of textwrap
1114 def MBTextWrapper(**kwargs):
1161 def MBTextWrapper(**kwargs):
1115 class tw(textwrap.TextWrapper):
1162 class tw(textwrap.TextWrapper):
1116 """
1163 """
1117 Extend TextWrapper for double-width characters.
1164 Extend TextWrapper for double-width characters.
1118
1165
1119 Some Asian characters use two terminal columns instead of one.
1166 Some Asian characters use two terminal columns instead of one.
1120 A good example of this behavior can be seen with u'\u65e5\u672c',
1167 A good example of this behavior can be seen with u'\u65e5\u672c',
1121 the two Japanese characters for "Japan":
1168 the two Japanese characters for "Japan":
1122 len() returns 2, but when printed to a terminal, they eat 4 columns.
1169 len() returns 2, but when printed to a terminal, they eat 4 columns.
1123
1170
1124 (Note that this has nothing to do whatsoever with unicode
1171 (Note that this has nothing to do whatsoever with unicode
1125 representation, or encoding of the underlying string)
1172 representation, or encoding of the underlying string)
1126 """
1173 """
1127 def __init__(self, **kwargs):
1174 def __init__(self, **kwargs):
1128 textwrap.TextWrapper.__init__(self, **kwargs)
1175 textwrap.TextWrapper.__init__(self, **kwargs)
1129
1176
1130 def _cutdown(self, str, space_left):
1177 def _cutdown(self, str, space_left):
1131 l = 0
1178 l = 0
1132 ucstr = unicode(str, encoding.encoding)
1179 ucstr = unicode(str, encoding.encoding)
1133 colwidth = unicodedata.east_asian_width
1180 colwidth = unicodedata.east_asian_width
1134 for i in xrange(len(ucstr)):
1181 for i in xrange(len(ucstr)):
1135 l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
1182 l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
1136 if space_left < l:
1183 if space_left < l:
1137 return (ucstr[:i].encode(encoding.encoding),
1184 return (ucstr[:i].encode(encoding.encoding),
1138 ucstr[i:].encode(encoding.encoding))
1185 ucstr[i:].encode(encoding.encoding))
1139 return str, ''
1186 return str, ''
1140
1187
1141 # overriding of base class
1188 # overriding of base class
1142 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1189 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1143 space_left = max(width - cur_len, 1)
1190 space_left = max(width - cur_len, 1)
1144
1191
1145 if self.break_long_words:
1192 if self.break_long_words:
1146 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1193 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1147 cur_line.append(cut)
1194 cur_line.append(cut)
1148 reversed_chunks[-1] = res
1195 reversed_chunks[-1] = res
1149 elif not cur_line:
1196 elif not cur_line:
1150 cur_line.append(reversed_chunks.pop())
1197 cur_line.append(reversed_chunks.pop())
1151
1198
1152 global MBTextWrapper
1199 global MBTextWrapper
1153 MBTextWrapper = tw
1200 MBTextWrapper = tw
1154 return tw(**kwargs)
1201 return tw(**kwargs)
1155
1202
1156 def wrap(line, width, initindent='', hangindent=''):
1203 def wrap(line, width, initindent='', hangindent=''):
1157 maxindent = max(len(hangindent), len(initindent))
1204 maxindent = max(len(hangindent), len(initindent))
1158 if width <= maxindent:
1205 if width <= maxindent:
1159 # adjust for weird terminal size
1206 # adjust for weird terminal size
1160 width = max(78, maxindent + 1)
1207 width = max(78, maxindent + 1)
1161 wrapper = MBTextWrapper(width=width,
1208 wrapper = MBTextWrapper(width=width,
1162 initial_indent=initindent,
1209 initial_indent=initindent,
1163 subsequent_indent=hangindent)
1210 subsequent_indent=hangindent)
1164 return wrapper.fill(line)
1211 return wrapper.fill(line)
1165
1212
1166 def iterlines(iterator):
1213 def iterlines(iterator):
1167 for chunk in iterator:
1214 for chunk in iterator:
1168 for line in chunk.splitlines():
1215 for line in chunk.splitlines():
1169 yield line
1216 yield line
1170
1217
1171 def expandpath(path):
1218 def expandpath(path):
1172 return os.path.expanduser(os.path.expandvars(path))
1219 return os.path.expanduser(os.path.expandvars(path))
1173
1220
1174 def hgcmd():
1221 def hgcmd():
1175 """Return the command used to execute current hg
1222 """Return the command used to execute current hg
1176
1223
1177 This is different from hgexecutable() because on Windows we want
1224 This is different from hgexecutable() because on Windows we want
1178 to avoid things opening new shell windows like batch files, so we
1225 to avoid things opening new shell windows like batch files, so we
1179 get either the python call or current executable.
1226 get either the python call or current executable.
1180 """
1227 """
1181 if mainfrozen():
1228 if mainfrozen():
1182 return [sys.executable]
1229 return [sys.executable]
1183 return gethgcmd()
1230 return gethgcmd()
1184
1231
1185 def rundetached(args, condfn):
1232 def rundetached(args, condfn):
1186 """Execute the argument list in a detached process.
1233 """Execute the argument list in a detached process.
1187
1234
1188 condfn is a callable which is called repeatedly and should return
1235 condfn is a callable which is called repeatedly and should return
1189 True once the child process is known to have started successfully.
1236 True once the child process is known to have started successfully.
1190 At this point, the child process PID is returned. If the child
1237 At this point, the child process PID is returned. If the child
1191 process fails to start or finishes before condfn() evaluates to
1238 process fails to start or finishes before condfn() evaluates to
1192 True, return -1.
1239 True, return -1.
1193 """
1240 """
1194 # Windows case is easier because the child process is either
1241 # Windows case is easier because the child process is either
1195 # successfully starting and validating the condition or exiting
1242 # successfully starting and validating the condition or exiting
1196 # on failure. We just poll on its PID. On Unix, if the child
1243 # on failure. We just poll on its PID. On Unix, if the child
1197 # process fails to start, it will be left in a zombie state until
1244 # process fails to start, it will be left in a zombie state until
1198 # the parent wait on it, which we cannot do since we expect a long
1245 # the parent wait on it, which we cannot do since we expect a long
1199 # running process on success. Instead we listen for SIGCHLD telling
1246 # running process on success. Instead we listen for SIGCHLD telling
1200 # us our child process terminated.
1247 # us our child process terminated.
1201 terminated = set()
1248 terminated = set()
1202 def handler(signum, frame):
1249 def handler(signum, frame):
1203 terminated.add(os.wait())
1250 terminated.add(os.wait())
1204 prevhandler = None
1251 prevhandler = None
1205 if hasattr(signal, 'SIGCHLD'):
1252 if hasattr(signal, 'SIGCHLD'):
1206 prevhandler = signal.signal(signal.SIGCHLD, handler)
1253 prevhandler = signal.signal(signal.SIGCHLD, handler)
1207 try:
1254 try:
1208 pid = spawndetached(args)
1255 pid = spawndetached(args)
1209 while not condfn():
1256 while not condfn():
1210 if ((pid in terminated or not testpid(pid))
1257 if ((pid in terminated or not testpid(pid))
1211 and not condfn()):
1258 and not condfn()):
1212 return -1
1259 return -1
1213 time.sleep(0.1)
1260 time.sleep(0.1)
1214 return pid
1261 return pid
1215 finally:
1262 finally:
1216 if prevhandler is not None:
1263 if prevhandler is not None:
1217 signal.signal(signal.SIGCHLD, prevhandler)
1264 signal.signal(signal.SIGCHLD, prevhandler)
1218
1265
1219 try:
1266 try:
1220 any, all = any, all
1267 any, all = any, all
1221 except NameError:
1268 except NameError:
1222 def any(iterable):
1269 def any(iterable):
1223 for i in iterable:
1270 for i in iterable:
1224 if i:
1271 if i:
1225 return True
1272 return True
1226 return False
1273 return False
1227
1274
1228 def all(iterable):
1275 def all(iterable):
1229 for i in iterable:
1276 for i in iterable:
1230 if not i:
1277 if not i:
1231 return False
1278 return False
1232 return True
1279 return True
1233
1280
1234 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1281 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1235 """Return the result of interpolating items in the mapping into string s.
1282 """Return the result of interpolating items in the mapping into string s.
1236
1283
1237 prefix is a single character string, or a two character string with
1284 prefix is a single character string, or a two character string with
1238 a backslash as the first character if the prefix needs to be escaped in
1285 a backslash as the first character if the prefix needs to be escaped in
1239 a regular expression.
1286 a regular expression.
1240
1287
1241 fn is an optional function that will be applied to the replacement text
1288 fn is an optional function that will be applied to the replacement text
1242 just before replacement.
1289 just before replacement.
1243
1290
1244 escape_prefix is an optional flag that allows using doubled prefix for
1291 escape_prefix is an optional flag that allows using doubled prefix for
1245 its escaping.
1292 its escaping.
1246 """
1293 """
1247 fn = fn or (lambda s: s)
1294 fn = fn or (lambda s: s)
1248 patterns = '|'.join(mapping.keys())
1295 patterns = '|'.join(mapping.keys())
1249 if escape_prefix:
1296 if escape_prefix:
1250 patterns += '|' + prefix
1297 patterns += '|' + prefix
1251 if len(prefix) > 1:
1298 if len(prefix) > 1:
1252 prefix_char = prefix[1:]
1299 prefix_char = prefix[1:]
1253 else:
1300 else:
1254 prefix_char = prefix
1301 prefix_char = prefix
1255 mapping[prefix_char] = prefix_char
1302 mapping[prefix_char] = prefix_char
1256 r = re.compile(r'%s(%s)' % (prefix, patterns))
1303 r = re.compile(r'%s(%s)' % (prefix, patterns))
1257 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1304 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1258
1305
1259 def getport(port):
1306 def getport(port):
1260 """Return the port for a given network service.
1307 """Return the port for a given network service.
1261
1308
1262 If port is an integer, it's returned as is. If it's a string, it's
1309 If port is an integer, it's returned as is. If it's a string, it's
1263 looked up using socket.getservbyname(). If there's no matching
1310 looked up using socket.getservbyname(). If there's no matching
1264 service, util.Abort is raised.
1311 service, util.Abort is raised.
1265 """
1312 """
1266 try:
1313 try:
1267 return int(port)
1314 return int(port)
1268 except ValueError:
1315 except ValueError:
1269 pass
1316 pass
1270
1317
1271 try:
1318 try:
1272 return socket.getservbyname(port)
1319 return socket.getservbyname(port)
1273 except socket.error:
1320 except socket.error:
1274 raise Abort(_("no port number associated with service '%s'") % port)
1321 raise Abort(_("no port number associated with service '%s'") % port)
1275
1322
1276 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1323 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1277 '0': False, 'no': False, 'false': False, 'off': False,
1324 '0': False, 'no': False, 'false': False, 'off': False,
1278 'never': False}
1325 'never': False}
1279
1326
1280 def parsebool(s):
1327 def parsebool(s):
1281 """Parse s into a boolean.
1328 """Parse s into a boolean.
1282
1329
1283 If s is not a valid boolean, returns None.
1330 If s is not a valid boolean, returns None.
1284 """
1331 """
1285 return _booleans.get(s.lower(), None)
1332 return _booleans.get(s.lower(), None)
1286
1333
1287 _hexdig = '0123456789ABCDEFabcdef'
1334 _hexdig = '0123456789ABCDEFabcdef'
1288 _hextochr = dict((a + b, chr(int(a + b, 16)))
1335 _hextochr = dict((a + b, chr(int(a + b, 16)))
1289 for a in _hexdig for b in _hexdig)
1336 for a in _hexdig for b in _hexdig)
1290
1337
1291 def _urlunquote(s):
1338 def _urlunquote(s):
1292 """unquote('abc%20def') -> 'abc def'."""
1339 """unquote('abc%20def') -> 'abc def'."""
1293 res = s.split('%')
1340 res = s.split('%')
1294 # fastpath
1341 # fastpath
1295 if len(res) == 1:
1342 if len(res) == 1:
1296 return s
1343 return s
1297 s = res[0]
1344 s = res[0]
1298 for item in res[1:]:
1345 for item in res[1:]:
1299 try:
1346 try:
1300 s += _hextochr[item[:2]] + item[2:]
1347 s += _hextochr[item[:2]] + item[2:]
1301 except KeyError:
1348 except KeyError:
1302 s += '%' + item
1349 s += '%' + item
1303 except UnicodeDecodeError:
1350 except UnicodeDecodeError:
1304 s += unichr(int(item[:2], 16)) + item[2:]
1351 s += unichr(int(item[:2], 16)) + item[2:]
1305 return s
1352 return s
1306
1353
1307 class url(object):
1354 class url(object):
1308 r"""Reliable URL parser.
1355 r"""Reliable URL parser.
1309
1356
1310 This parses URLs and provides attributes for the following
1357 This parses URLs and provides attributes for the following
1311 components:
1358 components:
1312
1359
1313 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
1360 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
1314
1361
1315 Missing components are set to None. The only exception is
1362 Missing components are set to None. The only exception is
1316 fragment, which is set to '' if present but empty.
1363 fragment, which is set to '' if present but empty.
1317
1364
1318 If parsefragment is False, fragment is included in query. If
1365 If parsefragment is False, fragment is included in query. If
1319 parsequery is False, query is included in path. If both are
1366 parsequery is False, query is included in path. If both are
1320 False, both fragment and query are included in path.
1367 False, both fragment and query are included in path.
1321
1368
1322 See http://www.ietf.org/rfc/rfc2396.txt for more information.
1369 See http://www.ietf.org/rfc/rfc2396.txt for more information.
1323
1370
1324 Note that for backward compatibility reasons, bundle URLs do not
1371 Note that for backward compatibility reasons, bundle URLs do not
1325 take host names. That means 'bundle://../' has a path of '../'.
1372 take host names. That means 'bundle://../' has a path of '../'.
1326
1373
1327 Examples:
1374 Examples:
1328
1375
1329 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
1376 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
1330 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
1377 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
1331 >>> url('ssh://[::1]:2200//home/joe/repo')
1378 >>> url('ssh://[::1]:2200//home/joe/repo')
1332 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
1379 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
1333 >>> url('file:///home/joe/repo')
1380 >>> url('file:///home/joe/repo')
1334 <url scheme: 'file', path: '/home/joe/repo'>
1381 <url scheme: 'file', path: '/home/joe/repo'>
1335 >>> url('file:///c:/temp/foo/')
1382 >>> url('file:///c:/temp/foo/')
1336 <url scheme: 'file', path: 'c:/temp/foo/'>
1383 <url scheme: 'file', path: 'c:/temp/foo/'>
1337 >>> url('bundle:foo')
1384 >>> url('bundle:foo')
1338 <url scheme: 'bundle', path: 'foo'>
1385 <url scheme: 'bundle', path: 'foo'>
1339 >>> url('bundle://../foo')
1386 >>> url('bundle://../foo')
1340 <url scheme: 'bundle', path: '../foo'>
1387 <url scheme: 'bundle', path: '../foo'>
1341 >>> url(r'c:\foo\bar')
1388 >>> url(r'c:\foo\bar')
1342 <url path: 'c:\\foo\\bar'>
1389 <url path: 'c:\\foo\\bar'>
1343 >>> url(r'\\blah\blah\blah')
1390 >>> url(r'\\blah\blah\blah')
1344 <url path: '\\\\blah\\blah\\blah'>
1391 <url path: '\\\\blah\\blah\\blah'>
1345
1392
1346 Authentication credentials:
1393 Authentication credentials:
1347
1394
1348 >>> url('ssh://joe:xyz@x/repo')
1395 >>> url('ssh://joe:xyz@x/repo')
1349 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
1396 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
1350 >>> url('ssh://joe@x/repo')
1397 >>> url('ssh://joe@x/repo')
1351 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
1398 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
1352
1399
1353 Query strings and fragments:
1400 Query strings and fragments:
1354
1401
1355 >>> url('http://host/a?b#c')
1402 >>> url('http://host/a?b#c')
1356 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
1403 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
1357 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
1404 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
1358 <url scheme: 'http', host: 'host', path: 'a?b#c'>
1405 <url scheme: 'http', host: 'host', path: 'a?b#c'>
1359 """
1406 """
1360
1407
1361 _safechars = "!~*'()+"
1408 _safechars = "!~*'()+"
1362 _safepchars = "/!~*'()+"
1409 _safepchars = "/!~*'()+"
1363 _matchscheme = re.compile(r'^[a-zA-Z0-9+.\-]+:').match
1410 _matchscheme = re.compile(r'^[a-zA-Z0-9+.\-]+:').match
1364
1411
1365 def __init__(self, path, parsequery=True, parsefragment=True):
1412 def __init__(self, path, parsequery=True, parsefragment=True):
1366 # We slowly chomp away at path until we have only the path left
1413 # We slowly chomp away at path until we have only the path left
1367 self.scheme = self.user = self.passwd = self.host = None
1414 self.scheme = self.user = self.passwd = self.host = None
1368 self.port = self.path = self.query = self.fragment = None
1415 self.port = self.path = self.query = self.fragment = None
1369 self._localpath = True
1416 self._localpath = True
1370 self._hostport = ''
1417 self._hostport = ''
1371 self._origpath = path
1418 self._origpath = path
1372
1419
1373 # special case for Windows drive letters and UNC paths
1420 # special case for Windows drive letters and UNC paths
1374 if hasdriveletter(path) or path.startswith(r'\\'):
1421 if hasdriveletter(path) or path.startswith(r'\\'):
1375 self.path = path
1422 self.path = path
1376 return
1423 return
1377
1424
1378 # For compatibility reasons, we can't handle bundle paths as
1425 # For compatibility reasons, we can't handle bundle paths as
1379 # normal URLS
1426 # normal URLS
1380 if path.startswith('bundle:'):
1427 if path.startswith('bundle:'):
1381 self.scheme = 'bundle'
1428 self.scheme = 'bundle'
1382 path = path[7:]
1429 path = path[7:]
1383 if path.startswith('//'):
1430 if path.startswith('//'):
1384 path = path[2:]
1431 path = path[2:]
1385 self.path = path
1432 self.path = path
1386 return
1433 return
1387
1434
1388 if self._matchscheme(path):
1435 if self._matchscheme(path):
1389 parts = path.split(':', 1)
1436 parts = path.split(':', 1)
1390 if parts[0]:
1437 if parts[0]:
1391 self.scheme, path = parts
1438 self.scheme, path = parts
1392 self._localpath = False
1439 self._localpath = False
1393
1440
1394 if not path:
1441 if not path:
1395 path = None
1442 path = None
1396 if self._localpath:
1443 if self._localpath:
1397 self.path = ''
1444 self.path = ''
1398 return
1445 return
1399 else:
1446 else:
1400 if parsefragment and '#' in path:
1447 if parsefragment and '#' in path:
1401 path, self.fragment = path.split('#', 1)
1448 path, self.fragment = path.split('#', 1)
1402 if not path:
1449 if not path:
1403 path = None
1450 path = None
1404 if self._localpath:
1451 if self._localpath:
1405 self.path = path
1452 self.path = path
1406 return
1453 return
1407
1454
1408 if parsequery and '?' in path:
1455 if parsequery and '?' in path:
1409 path, self.query = path.split('?', 1)
1456 path, self.query = path.split('?', 1)
1410 if not path:
1457 if not path:
1411 path = None
1458 path = None
1412 if not self.query:
1459 if not self.query:
1413 self.query = None
1460 self.query = None
1414
1461
1415 # // is required to specify a host/authority
1462 # // is required to specify a host/authority
1416 if path and path.startswith('//'):
1463 if path and path.startswith('//'):
1417 parts = path[2:].split('/', 1)
1464 parts = path[2:].split('/', 1)
1418 if len(parts) > 1:
1465 if len(parts) > 1:
1419 self.host, path = parts
1466 self.host, path = parts
1420 path = path
1467 path = path
1421 else:
1468 else:
1422 self.host = parts[0]
1469 self.host = parts[0]
1423 path = None
1470 path = None
1424 if not self.host:
1471 if not self.host:
1425 self.host = None
1472 self.host = None
1426 if path and not hasdriveletter(path):
1473 if path and not hasdriveletter(path):
1427 path = '/' + path
1474 path = '/' + path
1428
1475
1429 if self.host and '@' in self.host:
1476 if self.host and '@' in self.host:
1430 self.user, self.host = self.host.rsplit('@', 1)
1477 self.user, self.host = self.host.rsplit('@', 1)
1431 if ':' in self.user:
1478 if ':' in self.user:
1432 self.user, self.passwd = self.user.split(':', 1)
1479 self.user, self.passwd = self.user.split(':', 1)
1433 if not self.host:
1480 if not self.host:
1434 self.host = None
1481 self.host = None
1435
1482
1436 # Don't split on colons in IPv6 addresses without ports
1483 # Don't split on colons in IPv6 addresses without ports
1437 if (self.host and ':' in self.host and
1484 if (self.host and ':' in self.host and
1438 not (self.host.startswith('[') and self.host.endswith(']'))):
1485 not (self.host.startswith('[') and self.host.endswith(']'))):
1439 self._hostport = self.host
1486 self._hostport = self.host
1440 self.host, self.port = self.host.rsplit(':', 1)
1487 self.host, self.port = self.host.rsplit(':', 1)
1441 if not self.host:
1488 if not self.host:
1442 self.host = None
1489 self.host = None
1443
1490
1444 if (self.host and self.scheme == 'file' and
1491 if (self.host and self.scheme == 'file' and
1445 self.host not in ('localhost', '127.0.0.1', '[::1]')):
1492 self.host not in ('localhost', '127.0.0.1', '[::1]')):
1446 raise Abort(_('file:// URLs can only refer to localhost'))
1493 raise Abort(_('file:// URLs can only refer to localhost'))
1447
1494
1448 self.path = path
1495 self.path = path
1449
1496
1450 for a in ('user', 'passwd', 'host', 'port',
1497 for a in ('user', 'passwd', 'host', 'port',
1451 'path', 'query', 'fragment'):
1498 'path', 'query', 'fragment'):
1452 v = getattr(self, a)
1499 v = getattr(self, a)
1453 if v is not None:
1500 if v is not None:
1454 setattr(self, a, _urlunquote(v))
1501 setattr(self, a, _urlunquote(v))
1455
1502
1456 def __repr__(self):
1503 def __repr__(self):
1457 attrs = []
1504 attrs = []
1458 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
1505 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
1459 'query', 'fragment'):
1506 'query', 'fragment'):
1460 v = getattr(self, a)
1507 v = getattr(self, a)
1461 if v is not None:
1508 if v is not None:
1462 attrs.append('%s: %r' % (a, v))
1509 attrs.append('%s: %r' % (a, v))
1463 return '<url %s>' % ', '.join(attrs)
1510 return '<url %s>' % ', '.join(attrs)
1464
1511
1465 def __str__(self):
1512 def __str__(self):
1466 r"""Join the URL's components back into a URL string.
1513 r"""Join the URL's components back into a URL string.
1467
1514
1468 Examples:
1515 Examples:
1469
1516
1470 >>> str(url('http://user:pw@host:80/?foo#bar'))
1517 >>> str(url('http://user:pw@host:80/?foo#bar'))
1471 'http://user:pw@host:80/?foo#bar'
1518 'http://user:pw@host:80/?foo#bar'
1472 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
1519 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
1473 'ssh://user:pw@[::1]:2200//home/joe#'
1520 'ssh://user:pw@[::1]:2200//home/joe#'
1474 >>> str(url('http://localhost:80//'))
1521 >>> str(url('http://localhost:80//'))
1475 'http://localhost:80//'
1522 'http://localhost:80//'
1476 >>> str(url('http://localhost:80/'))
1523 >>> str(url('http://localhost:80/'))
1477 'http://localhost:80/'
1524 'http://localhost:80/'
1478 >>> str(url('http://localhost:80'))
1525 >>> str(url('http://localhost:80'))
1479 'http://localhost:80/'
1526 'http://localhost:80/'
1480 >>> str(url('bundle:foo'))
1527 >>> str(url('bundle:foo'))
1481 'bundle:foo'
1528 'bundle:foo'
1482 >>> str(url('bundle://../foo'))
1529 >>> str(url('bundle://../foo'))
1483 'bundle:../foo'
1530 'bundle:../foo'
1484 >>> str(url('path'))
1531 >>> str(url('path'))
1485 'path'
1532 'path'
1486 >>> str(url('file:///tmp/foo/bar'))
1533 >>> str(url('file:///tmp/foo/bar'))
1487 'file:///tmp/foo/bar'
1534 'file:///tmp/foo/bar'
1488 >>> print url(r'bundle:foo\bar')
1535 >>> print url(r'bundle:foo\bar')
1489 bundle:foo\bar
1536 bundle:foo\bar
1490 """
1537 """
1491 if self._localpath:
1538 if self._localpath:
1492 s = self.path
1539 s = self.path
1493 if self.scheme == 'bundle':
1540 if self.scheme == 'bundle':
1494 s = 'bundle:' + s
1541 s = 'bundle:' + s
1495 if self.fragment:
1542 if self.fragment:
1496 s += '#' + self.fragment
1543 s += '#' + self.fragment
1497 return s
1544 return s
1498
1545
1499 s = self.scheme + ':'
1546 s = self.scheme + ':'
1500 if self.user or self.passwd or self.host:
1547 if self.user or self.passwd or self.host:
1501 s += '//'
1548 s += '//'
1502 elif self.scheme and (not self.path or self.path.startswith('/')):
1549 elif self.scheme and (not self.path or self.path.startswith('/')):
1503 s += '//'
1550 s += '//'
1504 if self.user:
1551 if self.user:
1505 s += urllib.quote(self.user, safe=self._safechars)
1552 s += urllib.quote(self.user, safe=self._safechars)
1506 if self.passwd:
1553 if self.passwd:
1507 s += ':' + urllib.quote(self.passwd, safe=self._safechars)
1554 s += ':' + urllib.quote(self.passwd, safe=self._safechars)
1508 if self.user or self.passwd:
1555 if self.user or self.passwd:
1509 s += '@'
1556 s += '@'
1510 if self.host:
1557 if self.host:
1511 if not (self.host.startswith('[') and self.host.endswith(']')):
1558 if not (self.host.startswith('[') and self.host.endswith(']')):
1512 s += urllib.quote(self.host)
1559 s += urllib.quote(self.host)
1513 else:
1560 else:
1514 s += self.host
1561 s += self.host
1515 if self.port:
1562 if self.port:
1516 s += ':' + urllib.quote(self.port)
1563 s += ':' + urllib.quote(self.port)
1517 if self.host:
1564 if self.host:
1518 s += '/'
1565 s += '/'
1519 if self.path:
1566 if self.path:
1520 s += urllib.quote(self.path, safe=self._safepchars)
1567 s += urllib.quote(self.path, safe=self._safepchars)
1521 if self.query:
1568 if self.query:
1522 s += '?' + urllib.quote(self.query, safe=self._safepchars)
1569 s += '?' + urllib.quote(self.query, safe=self._safepchars)
1523 if self.fragment is not None:
1570 if self.fragment is not None:
1524 s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
1571 s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
1525 return s
1572 return s
1526
1573
1527 def authinfo(self):
1574 def authinfo(self):
1528 user, passwd = self.user, self.passwd
1575 user, passwd = self.user, self.passwd
1529 try:
1576 try:
1530 self.user, self.passwd = None, None
1577 self.user, self.passwd = None, None
1531 s = str(self)
1578 s = str(self)
1532 finally:
1579 finally:
1533 self.user, self.passwd = user, passwd
1580 self.user, self.passwd = user, passwd
1534 if not self.user:
1581 if not self.user:
1535 return (s, None)
1582 return (s, None)
1536 return (s, (None, (str(self), self.host),
1583 return (s, (None, (str(self), self.host),
1537 self.user, self.passwd or ''))
1584 self.user, self.passwd or ''))
1538
1585
1539 def isabs(self):
1586 def isabs(self):
1540 if self.scheme and self.scheme != 'file':
1587 if self.scheme and self.scheme != 'file':
1541 return True # remote URL
1588 return True # remote URL
1542 if hasdriveletter(self.path):
1589 if hasdriveletter(self.path):
1543 return True # absolute for our purposes - can't be joined()
1590 return True # absolute for our purposes - can't be joined()
1544 if self.path.startswith(r'\\'):
1591 if self.path.startswith(r'\\'):
1545 return True # Windows UNC path
1592 return True # Windows UNC path
1546 if self.path.startswith('/'):
1593 if self.path.startswith('/'):
1547 return True # POSIX-style
1594 return True # POSIX-style
1548 return False
1595 return False
1549
1596
1550 def localpath(self):
1597 def localpath(self):
1551 if self.scheme == 'file' or self.scheme == 'bundle':
1598 if self.scheme == 'file' or self.scheme == 'bundle':
1552 path = self.path or '/'
1599 path = self.path or '/'
1553 # For Windows, we need to promote hosts containing drive
1600 # For Windows, we need to promote hosts containing drive
1554 # letters to paths with drive letters.
1601 # letters to paths with drive letters.
1555 if hasdriveletter(self._hostport):
1602 if hasdriveletter(self._hostport):
1556 path = self._hostport + '/' + self.path
1603 path = self._hostport + '/' + self.path
1557 elif self.host is not None and self.path:
1604 elif self.host is not None and self.path:
1558 path = '/' + path
1605 path = '/' + path
1559 # We also need to handle the case of file:///C:/, which
1606 # We also need to handle the case of file:///C:/, which
1560 # should return C:/, not /C:/.
1607 # should return C:/, not /C:/.
1561 elif hasdriveletter(path):
1608 elif hasdriveletter(path):
1562 # Strip leading slash from paths with drive names
1609 # Strip leading slash from paths with drive names
1563 return path[1:]
1610 return path[1:]
1564 return path
1611 return path
1565 return self._origpath
1612 return self._origpath
1566
1613
1567 def hasscheme(path):
1614 def hasscheme(path):
1568 return bool(url(path).scheme)
1615 return bool(url(path).scheme)
1569
1616
1570 def hasdriveletter(path):
1617 def hasdriveletter(path):
1571 return path[1:2] == ':' and path[0:1].isalpha()
1618 return path[1:2] == ':' and path[0:1].isalpha()
1572
1619
1573 def urllocalpath(path):
1620 def urllocalpath(path):
1574 return url(path, parsequery=False, parsefragment=False).localpath()
1621 return url(path, parsequery=False, parsefragment=False).localpath()
1575
1622
1576 def hidepassword(u):
1623 def hidepassword(u):
1577 '''hide user credential in a url string'''
1624 '''hide user credential in a url string'''
1578 u = url(u)
1625 u = url(u)
1579 if u.passwd:
1626 if u.passwd:
1580 u.passwd = '***'
1627 u.passwd = '***'
1581 return str(u)
1628 return str(u)
1582
1629
1583 def removeauth(u):
1630 def removeauth(u):
1584 '''remove all authentication information from a url string'''
1631 '''remove all authentication information from a url string'''
1585 u = url(u)
1632 u = url(u)
1586 u.user = u.passwd = None
1633 u.user = u.passwd = None
1587 return str(u)
1634 return str(u)
1588
1635
1589 def isatty(fd):
1636 def isatty(fd):
1590 try:
1637 try:
1591 return fd.isatty()
1638 return fd.isatty()
1592 except AttributeError:
1639 except AttributeError:
1593 return False
1640 return False
@@ -1,286 +1,289
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
9 import osutil
10 import errno, msvcrt, os, re, sys
10 import errno, msvcrt, os, re, sys
11
11
12 nulldev = 'NUL:'
12 nulldev = 'NUL:'
13 umask = 002
13 umask = 002
14
14
15 # wrap osutil.posixfile to provide friendlier exceptions
15 # wrap osutil.posixfile to provide friendlier exceptions
16 def posixfile(name, mode='r', buffering=-1):
16 def posixfile(name, mode='r', buffering=-1):
17 try:
17 try:
18 return osutil.posixfile(name, mode, buffering)
18 return osutil.posixfile(name, mode, buffering)
19 except WindowsError, err:
19 except WindowsError, err:
20 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
20 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
21 posixfile.__doc__ = osutil.posixfile.__doc__
21 posixfile.__doc__ = osutil.posixfile.__doc__
22
22
23 class winstdout(object):
23 class winstdout(object):
24 '''stdout on windows misbehaves if sent through a pipe'''
24 '''stdout on windows misbehaves if sent through a pipe'''
25
25
26 def __init__(self, fp):
26 def __init__(self, fp):
27 self.fp = fp
27 self.fp = fp
28
28
29 def __getattr__(self, key):
29 def __getattr__(self, key):
30 return getattr(self.fp, key)
30 return getattr(self.fp, key)
31
31
32 def close(self):
32 def close(self):
33 try:
33 try:
34 self.fp.close()
34 self.fp.close()
35 except IOError:
35 except IOError:
36 pass
36 pass
37
37
38 def write(self, s):
38 def write(self, s):
39 try:
39 try:
40 # This is workaround for "Not enough space" error on
40 # This is workaround for "Not enough space" error on
41 # writing large size of data to console.
41 # writing large size of data to console.
42 limit = 16000
42 limit = 16000
43 l = len(s)
43 l = len(s)
44 start = 0
44 start = 0
45 self.softspace = 0
45 self.softspace = 0
46 while start < l:
46 while start < l:
47 end = start + limit
47 end = start + limit
48 self.fp.write(s[start:end])
48 self.fp.write(s[start:end])
49 start = end
49 start = end
50 except IOError, inst:
50 except IOError, inst:
51 if inst.errno != 0:
51 if inst.errno != 0:
52 raise
52 raise
53 self.close()
53 self.close()
54 raise IOError(errno.EPIPE, 'Broken pipe')
54 raise IOError(errno.EPIPE, 'Broken pipe')
55
55
56 def flush(self):
56 def flush(self):
57 try:
57 try:
58 return self.fp.flush()
58 return self.fp.flush()
59 except IOError, inst:
59 except IOError, inst:
60 if inst.errno != errno.EINVAL:
60 if inst.errno != errno.EINVAL:
61 raise
61 raise
62 self.close()
62 self.close()
63 raise IOError(errno.EPIPE, 'Broken pipe')
63 raise IOError(errno.EPIPE, 'Broken pipe')
64
64
65 sys.__stdout__ = sys.stdout = winstdout(sys.stdout)
65 sys.__stdout__ = sys.stdout = winstdout(sys.stdout)
66
66
67 def _is_win_9x():
67 def _is_win_9x():
68 '''return true if run on windows 95, 98 or me.'''
68 '''return true if run on windows 95, 98 or me.'''
69 try:
69 try:
70 return sys.getwindowsversion()[3] == 1
70 return sys.getwindowsversion()[3] == 1
71 except AttributeError:
71 except AttributeError:
72 return 'command' in os.environ.get('comspec', '')
72 return 'command' in os.environ.get('comspec', '')
73
73
74 def openhardlinks():
74 def openhardlinks():
75 return not _is_win_9x()
75 return not _is_win_9x()
76
76
77 def parsepatchoutput(output_line):
77 def parsepatchoutput(output_line):
78 """parses the output produced by patch and returns the filename"""
78 """parses the output produced by patch and returns the filename"""
79 pf = output_line[14:]
79 pf = output_line[14:]
80 if pf[0] == '`':
80 if pf[0] == '`':
81 pf = pf[1:-1] # Remove the quotes
81 pf = pf[1:-1] # Remove the quotes
82 return pf
82 return pf
83
83
84 def sshargs(sshcmd, host, user, port):
84 def sshargs(sshcmd, host, user, port):
85 '''Build argument list for ssh or Plink'''
85 '''Build argument list for ssh or Plink'''
86 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
86 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
87 args = user and ("%s@%s" % (user, host)) or host
87 args = user and ("%s@%s" % (user, host)) or host
88 return port and ("%s %s %s" % (args, pflag, port)) or args
88 return port and ("%s %s %s" % (args, pflag, port)) or args
89
89
90 def setflags(f, l, x):
90 def setflags(f, l, x):
91 pass
91 pass
92
92
93 def checkexec(path):
93 def checkexec(path):
94 return False
94 return False
95
95
96 def checklink(path):
96 def checklink(path):
97 return False
97 return False
98
98
99 def setbinary(fd):
99 def setbinary(fd):
100 # When run without console, pipes may expose invalid
100 # When run without console, pipes may expose invalid
101 # fileno(), usually set to -1.
101 # fileno(), usually set to -1.
102 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
102 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
103 msvcrt.setmode(fd.fileno(), os.O_BINARY)
103 msvcrt.setmode(fd.fileno(), os.O_BINARY)
104
104
105 def pconvert(path):
105 def pconvert(path):
106 return '/'.join(path.split(os.sep))
106 return '/'.join(path.split(os.sep))
107
107
108 def localpath(path):
108 def localpath(path):
109 return path.replace('/', '\\')
109 return path.replace('/', '\\')
110
110
111 def normpath(path):
111 def normpath(path):
112 return pconvert(os.path.normpath(path))
112 return pconvert(os.path.normpath(path))
113
113
114 def realpath(path):
114 def realpath(path):
115 '''
115 '''
116 Returns the true, canonical file system path equivalent to the given
116 Returns the true, canonical file system path equivalent to the given
117 path.
117 path.
118 '''
118 '''
119 # TODO: There may be a more clever way to do this that also handles other,
119 # TODO: There may be a more clever way to do this that also handles other,
120 # less common file systems.
120 # less common file systems.
121 return os.path.normpath(os.path.normcase(os.path.realpath(path)))
121 return os.path.normpath(os.path.normcase(os.path.realpath(path)))
122
122
123 def samestat(s1, s2):
123 def samestat(s1, s2):
124 return False
124 return False
125
125
126 # A sequence of backslashes is special iff it precedes a double quote:
126 # A sequence of backslashes is special iff it precedes a double quote:
127 # - if there's an even number of backslashes, the double quote is not
127 # - if there's an even number of backslashes, the double quote is not
128 # quoted (i.e. it ends the quoted region)
128 # quoted (i.e. it ends the quoted region)
129 # - if there's an odd number of backslashes, the double quote is quoted
129 # - if there's an odd number of backslashes, the double quote is quoted
130 # - in both cases, every pair of backslashes is unquoted into a single
130 # - in both cases, every pair of backslashes is unquoted into a single
131 # backslash
131 # backslash
132 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
132 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
133 # So, to quote a string, we must surround it in double quotes, double
133 # So, to quote a string, we must surround it in double quotes, double
134 # the number of backslashes that preceed double quotes and add another
134 # the number of backslashes that preceed double quotes and add another
135 # backslash before every double quote (being careful with the double
135 # backslash before every double quote (being careful with the double
136 # quote we've appended to the end)
136 # quote we've appended to the end)
137 _quotere = None
137 _quotere = None
138 def shellquote(s):
138 def shellquote(s):
139 global _quotere
139 global _quotere
140 if _quotere is None:
140 if _quotere is None:
141 _quotere = re.compile(r'(\\*)("|\\$)')
141 _quotere = re.compile(r'(\\*)("|\\$)')
142 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
142 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
143
143
144 def quotecommand(cmd):
144 def quotecommand(cmd):
145 """Build a command string suitable for os.popen* calls."""
145 """Build a command string suitable for os.popen* calls."""
146 if sys.version_info < (2, 7, 1):
146 if sys.version_info < (2, 7, 1):
147 # Python versions since 2.7.1 do this extra quoting themselves
147 # Python versions since 2.7.1 do this extra quoting themselves
148 return '"' + cmd + '"'
148 return '"' + cmd + '"'
149 return cmd
149 return cmd
150
150
151 def popen(command, mode='r'):
151 def popen(command, mode='r'):
152 # Work around "popen spawned process may not write to stdout
152 # Work around "popen spawned process may not write to stdout
153 # under windows"
153 # under windows"
154 # http://bugs.python.org/issue1366
154 # http://bugs.python.org/issue1366
155 command += " 2> %s" % nulldev
155 command += " 2> %s" % nulldev
156 return os.popen(quotecommand(command), mode)
156 return os.popen(quotecommand(command), mode)
157
157
158 def explainexit(code):
158 def explainexit(code):
159 return _("exited with status %d") % code, code
159 return _("exited with status %d") % code, code
160
160
161 # if you change this stub into a real check, please try to implement the
161 # if you change this stub into a real check, please try to implement the
162 # username and groupname functions above, too.
162 # username and groupname functions above, too.
163 def isowner(st):
163 def isowner(st):
164 return True
164 return True
165
165
166 def findexe(command):
166 def findexe(command):
167 '''Find executable for command searching like cmd.exe does.
167 '''Find executable for command searching like cmd.exe does.
168 If command is a basename then PATH is searched for command.
168 If command is a basename then PATH is searched for command.
169 PATH isn't searched if command is an absolute or relative path.
169 PATH isn't searched if command is an absolute or relative path.
170 An extension from PATHEXT is found and added if not present.
170 An extension from PATHEXT is found and added if not present.
171 If command isn't found None is returned.'''
171 If command isn't found None is returned.'''
172 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
172 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
173 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
173 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
174 if os.path.splitext(command)[1].lower() in pathexts:
174 if os.path.splitext(command)[1].lower() in pathexts:
175 pathexts = ['']
175 pathexts = ['']
176
176
177 def findexisting(pathcommand):
177 def findexisting(pathcommand):
178 'Will append extension (if needed) and return existing file'
178 'Will append extension (if needed) and return existing file'
179 for ext in pathexts:
179 for ext in pathexts:
180 executable = pathcommand + ext
180 executable = pathcommand + ext
181 if os.path.exists(executable):
181 if os.path.exists(executable):
182 return executable
182 return executable
183 return None
183 return None
184
184
185 if os.sep in command:
185 if os.sep in command:
186 return findexisting(command)
186 return findexisting(command)
187
187
188 for path in os.environ.get('PATH', '').split(os.pathsep):
188 for path in os.environ.get('PATH', '').split(os.pathsep):
189 executable = findexisting(os.path.join(path, command))
189 executable = findexisting(os.path.join(path, command))
190 if executable is not None:
190 if executable is not None:
191 return executable
191 return executable
192 return findexisting(os.path.expanduser(os.path.expandvars(command)))
192 return findexisting(os.path.expanduser(os.path.expandvars(command)))
193
193
194 def statfiles(files):
194 def statfiles(files):
195 '''Stat each file in files and yield stat or None if file does not exist.
195 '''Stat each file in files and yield stat or None if file does not exist.
196 Cluster and cache stat per directory to minimize number of OS stat calls.'''
196 Cluster and cache stat per directory to minimize number of OS stat calls.'''
197 ncase = os.path.normcase
197 ncase = os.path.normcase
198 dircache = {} # dirname -> filename -> status | None if file does not exist
198 dircache = {} # dirname -> filename -> status | None if file does not exist
199 for nf in files:
199 for nf in files:
200 nf = ncase(nf)
200 nf = ncase(nf)
201 dir, base = os.path.split(nf)
201 dir, base = os.path.split(nf)
202 if not dir:
202 if not dir:
203 dir = '.'
203 dir = '.'
204 cache = dircache.get(dir, None)
204 cache = dircache.get(dir, None)
205 if cache is None:
205 if cache is None:
206 try:
206 try:
207 dmap = dict([(ncase(n), s)
207 dmap = dict([(ncase(n), s)
208 for n, k, s in osutil.listdir(dir, True)])
208 for n, k, s in osutil.listdir(dir, True)])
209 except OSError, err:
209 except OSError, err:
210 # handle directory not found in Python version prior to 2.5
210 # handle directory not found in Python version prior to 2.5
211 # Python <= 2.4 returns native Windows code 3 in errno
211 # Python <= 2.4 returns native Windows code 3 in errno
212 # Python >= 2.5 returns ENOENT and adds winerror field
212 # Python >= 2.5 returns ENOENT and adds winerror field
213 # EINVAL is raised if dir is not a directory.
213 # EINVAL is raised if dir is not a directory.
214 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
214 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
215 errno.ENOTDIR):
215 errno.ENOTDIR):
216 raise
216 raise
217 dmap = {}
217 dmap = {}
218 cache = dircache.setdefault(dir, dmap)
218 cache = dircache.setdefault(dir, dmap)
219 yield cache.get(base, None)
219 yield cache.get(base, None)
220
220
221 def username(uid=None):
221 def username(uid=None):
222 """Return the name of the user with the given uid.
222 """Return the name of the user with the given uid.
223
223
224 If uid is None, return the name of the current user."""
224 If uid is None, return the name of the current user."""
225 return None
225 return None
226
226
227 def groupname(gid=None):
227 def groupname(gid=None):
228 """Return the name of the group with the given gid.
228 """Return the name of the group with the given gid.
229
229
230 If gid is None, return the name of the current group."""
230 If gid is None, return the name of the current group."""
231 return None
231 return None
232
232
233 def _removedirs(name):
233 def _removedirs(name):
234 """special version of os.removedirs that does not remove symlinked
234 """special version of os.removedirs that does not remove symlinked
235 directories or junction points if they actually contain files"""
235 directories or junction points if they actually contain files"""
236 if osutil.listdir(name):
236 if osutil.listdir(name):
237 return
237 return
238 os.rmdir(name)
238 os.rmdir(name)
239 head, tail = os.path.split(name)
239 head, tail = os.path.split(name)
240 if not tail:
240 if not tail:
241 head, tail = os.path.split(head)
241 head, tail = os.path.split(head)
242 while head and tail:
242 while head and tail:
243 try:
243 try:
244 if osutil.listdir(head):
244 if osutil.listdir(head):
245 return
245 return
246 os.rmdir(head)
246 os.rmdir(head)
247 except (ValueError, OSError):
247 except (ValueError, OSError):
248 break
248 break
249 head, tail = os.path.split(head)
249 head, tail = os.path.split(head)
250
250
251 def unlinkpath(f):
251 def unlinkpath(f):
252 """unlink and remove the directory if it is empty"""
252 """unlink and remove the directory if it is empty"""
253 unlink(f)
253 unlink(f)
254 # try removing directories that might now be empty
254 # try removing directories that might now be empty
255 try:
255 try:
256 _removedirs(os.path.dirname(f))
256 _removedirs(os.path.dirname(f))
257 except OSError:
257 except OSError:
258 pass
258 pass
259
259
260 def rename(src, dst):
260 def rename(src, dst):
261 '''atomically rename file src to dst, replacing dst if it exists'''
261 '''atomically rename file src to dst, replacing dst if it exists'''
262 try:
262 try:
263 os.rename(src, dst)
263 os.rename(src, dst)
264 except OSError, e:
264 except OSError, e:
265 if e.errno != errno.EEXIST:
265 if e.errno != errno.EEXIST:
266 raise
266 raise
267 unlink(dst)
267 unlink(dst)
268 os.rename(src, dst)
268 os.rename(src, dst)
269
269
270 def gethgcmd():
270 def gethgcmd():
271 return [sys.executable] + sys.argv[:1]
271 return [sys.executable] + sys.argv[:1]
272
272
273 def termwidth():
273 def termwidth():
274 # cmd.exe does not handle CR like a unix console, the CR is
274 # cmd.exe does not handle CR like a unix console, the CR is
275 # counted in the line length. On 80 columns consoles, if 80
275 # counted in the line length. On 80 columns consoles, if 80
276 # characters are written, the following CR won't apply on the
276 # characters are written, the following CR won't apply on the
277 # current line but on the new one. Keep room for it.
277 # current line but on the new one. Keep room for it.
278 return 79
278 return 79
279
279
280 def groupmembers(name):
280 def groupmembers(name):
281 # Don't support groups on Windows for now
281 # Don't support groups on Windows for now
282 raise KeyError()
282 raise KeyError()
283
283
284 def isexec(f):
285 return False
286
284 from win32 import *
287 from win32 import *
285
288
286 expandglobs = True
289 expandglobs = True
@@ -1,14 +1,12
1 $ "$TESTDIR/hghave" pyflakes || exit 80
1 $ "$TESTDIR/hghave" pyflakes || exit 80
2 $ cd $(dirname $TESTDIR)
2 $ cd $(dirname $TESTDIR)
3 $ pyflakes mercurial hgext 2>&1 | $TESTDIR/filterpyflakes.py
3 $ pyflakes mercurial hgext 2>&1 | $TESTDIR/filterpyflakes.py
4 mercurial/hgweb/server.py:*: 'activeCount' imported but unused (glob)
4 mercurial/hgweb/server.py:*: 'activeCount' imported but unused (glob)
5 mercurial/commands.py:*: 'base85' imported but unused (glob)
5 mercurial/commands.py:*: 'base85' imported but unused (glob)
6 mercurial/commands.py:*: 'bdiff' imported but unused (glob)
6 mercurial/commands.py:*: 'bdiff' imported but unused (glob)
7 mercurial/commands.py:*: 'mpatch' imported but unused (glob)
7 mercurial/commands.py:*: 'mpatch' imported but unused (glob)
8 mercurial/commands.py:*: 'osutil' imported but unused (glob)
8 mercurial/commands.py:*: 'osutil' imported but unused (glob)
9 hgext/inotify/linux/__init__.py:*: 'from _inotify import *' used; unable to detect undefined names (glob)
9 hgext/inotify/linux/__init__.py:*: 'from _inotify import *' used; unable to detect undefined names (glob)
10 mercurial/util.py:*: 'from posix import *' used; unable to detect undefined names (glob)
11 mercurial/windows.py:*: 'from win32 import *' used; unable to detect undefined names (glob)
10 mercurial/windows.py:*: 'from win32 import *' used; unable to detect undefined names (glob)
12 mercurial/util.py:*: 'from windows import *' used; unable to detect undefined names (glob)
13
11
14
12
General Comments 0
You need to be logged in to leave comments. Login now