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