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