##// END OF EJS Templates
port win32.py to using the Python ctypes library...
Adrian Buehlmann -
r13375:f1fa8f48 default
parent child Browse files
Show More
@@ -1,40 +1,31 b''
1 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
3 3
4 4 <?include guids.wxi ?>
5 5 <?include defines.wxi ?>
6 6
7 7 <Fragment>
8 8 <DirectoryRef Id="INSTALLDIR" FileSource="$(var.SourceDir)">
9 9 <Component Id="distOutput" Guid="$(var.dist.guid)" Win64='$(var.IsX64)'>
10 10 <File Name="library.zip" KeyPath="yes" />
11 11 <File Name="mercurial.base85.pyd" />
12 12 <File Name="mercurial.bdiff.pyd" />
13 13 <File Name="mercurial.diffhelpers.pyd" />
14 14 <File Name="mercurial.mpatch.pyd" />
15 15 <File Name="mercurial.osutil.pyd" />
16 16 <File Name="mercurial.parsers.pyd" />
17 17 <File Name="pyexpat.pyd" />
18 18 <File Name="python26.dll" />
19 <File Name="pythoncom26.dll" />
20 <File Name="pywintypes26.dll" />
21 19 <File Name="bz2.pyd" />
22 20 <File Name="select.pyd" />
23 21 <File Name="unicodedata.pyd" />
24 <File Name="win32api.pyd" />
25 <File Name="win32com.shell.shell.pyd" />
26 <File Name="win32console.pyd" />
27 <File Name="win32file.pyd" />
28 <File Name="win32gui.pyd" />
29 <File Name="win32pipe.pyd" />
30 <File Name="win32process.pyd" />
22 <File Name="_ctypes.pyd" />
31 23 <File Name="_elementtree.pyd" />
32 24 <File Name="_hashlib.pyd" />
33 25 <File Name="_socket.pyd" />
34 26 <File Name="_ssl.pyd" />
35 <File Name="_win32sysloader.pyd" />
36 27 </Component>
37 28 </DirectoryRef>
38 29 </Fragment>
39 30
40 31 </Wix>
@@ -1,52 +1,52 b''
1 1 <Include>
2 2 <!-- These are component GUIDs used for Mercurial installers.
3 3 YOU MUST CHANGE ALL GUIDs below when copying this file
4 4 and replace 'Mercurial' in this notice with the name of
5 5 your project. Component GUIDs have global namespace! -->
6 6
7 7 <!-- contrib.wxs -->
8 8 <?define contrib.guid = {F17D27B7-4A6B-4cd2-AE72-FED3CFAA585E} ?>
9 9 <?define contrib.vim.guid = {BB04903A-652D-4C4F-9590-2BD07A2304F2} ?>
10 10
11 11 <!-- dist.wxs -->
12 <?define dist.guid = {0F63D160-0740-4BAF-BF25-0C6930310F51} ?>
12 <?define dist.guid = {C3B634A4-1B05-4A40-94A9-38EE853CF693} ?>
13 13
14 14 <!-- doc.wxs -->
15 15 <?define doc.hg.1.html.guid = {AAAA3FDA-EDC5-4220-B59D-D342722358A2} ?>
16 16 <?define doc.hgignore.5.html.guid = {AA9118C4-F3A0-4429-A5F4-5A1906B2D67F} ?>
17 17 <?define doc.hgrc.5.html = {E0CEA1EB-FA01-408c-844B-EE5965165BAE} ?>
18 18 <?define doc.style.css = {172F8262-98E0-4711-BD39-4DAE0D77EF05} ?>
19 19
20 20 <!-- help.wxs -->
21 21 <?define helpFolder.guid = {21FE9CF9-933E-4C2E-B2EC-413A569FB996} ?>
22 22
23 23 <!-- i18n.wxs -->
24 24 <?define i18nFolder.guid = {EADFA693-A0B5-4f31-87C9-3997CFAC1B42} ?>
25 25
26 26 <!-- templates.wxs -->
27 27 <?define templates.root.guid = {111509CB-4C96-4035-80BC-F66A99CD5ACB} ?>
28 28 <?define templates.atom.guid = {45FCDF84-DE27-44f4-AF6C-C41F5994AE0D} ?>
29 29 <?define templates.coal.guid = {B63CCAAB-4EAF-43b4-901E-4BD13F5B78FC} ?>
30 30 <?define templates.gitweb.guid = {D8BFE3ED-06DD-4C4D-A00D-6D825955F922} ?>
31 31 <?define templates.monoblue.guid = {A394B4D5-2AF7-4AAC-AEA8-E92176E5501E} ?>
32 32 <?define templates.paper.guid = {7C94B80D-FD0D-44E7-8489-F30A9E20A47F} ?>
33 33 <?define templates.raw.guid = {04DE03A2-FBFD-4c5f-8DEA-5436DDF4689D} ?>
34 34 <?define templates.rss.guid = {A7D608DE-0CF6-44f4-AF1E-EE30CC237FDA} ?>
35 35 <?define templates.spartan.guid = {80222625-FA8F-44b1-86CE-1781EF375D09} ?>
36 36 <?define templates.static.guid = {68C9F843-DE7E-480f-9DA2-D220B19D02C3} ?>
37 37
38 38 <!-- mercurial.wxs -->
39 39 <?define ProductUpgradeCode = {A1CC6134-E945-4399-BE36-EB0017FDF7CF} ?>
40 40
41 41 <?define ComponentMainExecutableGUID = {D102B8FA-059B-4ACC-9FA3-8C78C3B58EEF} ?>
42 42
43 43 <?define ReadMe.guid = {56A8E372-991D-4DCA-B91D-93D775974CF5} ?>
44 44 <?define COPYING.guid = {B7801DBA-1C49-4BF4-91AD-33C65F5C7895} ?>
45 45 <?define mercurial.rc.guid = {1D5FAEEE-7E6E-43B1-9F7F-802714316B15} ?>
46 46 <?define mergetools.rc.guid = {E8A1DC29-FF40-4B5F-BD12-80B9F7BF0CCD} ?>
47 47 <?define paths.rc.guid = {F9ADF21D-5F0B-4934-8CD9-14BE63664721} ?>
48 48 <?define cacert.pem.guid = {EC1B2630-FE21-46E6-915B-A6545AF703D4} ?>
49 49 <?define ProgramMenuDir.guid = {D5A63320-1238-489B-B68B-CF053E9577CA} ?>
50 50 <?define hgcmd.guid = {65CCC756-E72E-4C5F-901E-D575EDC80DB3} ?>
51 51
52 52 </Include>
@@ -1,298 +1,303 b''
1 1 # posix.py - Posix utility function implementations for Mercurial
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import osutil
10 10 import os, sys, errno, stat, getpass, pwd, grp
11 11
12 12 posixfile = open
13 13 nulldev = '/dev/null'
14 14 normpath = os.path.normpath
15 15 samestat = os.path.samestat
16 os_link = os.link
16 17 unlink = os.unlink
17 18 rename = os.rename
18 19 expandglobs = False
19 20
20 21 umask = os.umask(0)
21 22 os.umask(umask)
22 23
23 24 def openhardlinks():
24 25 '''return true if it is safe to hold open file handles to hardlinks'''
25 26 return True
26 27
28 def nlinks(name):
29 '''return number of hardlinks for the given file'''
30 return os.lstat(name).st_nlink
31
27 32 def rcfiles(path):
28 33 rcs = [os.path.join(path, 'hgrc')]
29 34 rcdir = os.path.join(path, 'hgrc.d')
30 35 try:
31 36 rcs.extend([os.path.join(rcdir, f)
32 37 for f, kind in osutil.listdir(rcdir)
33 38 if f.endswith(".rc")])
34 39 except OSError:
35 40 pass
36 41 return rcs
37 42
38 43 def system_rcpath():
39 44 path = []
40 45 # old mod_python does not set sys.argv
41 46 if len(getattr(sys, 'argv', [])) > 0:
42 47 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
43 48 '/../etc/mercurial'))
44 49 path.extend(rcfiles('/etc/mercurial'))
45 50 return path
46 51
47 52 def user_rcpath():
48 53 return [os.path.expanduser('~/.hgrc')]
49 54
50 55 def parse_patch_output(output_line):
51 56 """parses the output produced by patch and returns the filename"""
52 57 pf = output_line[14:]
53 58 if os.sys.platform == 'OpenVMS':
54 59 if pf[0] == '`':
55 60 pf = pf[1:-1] # Remove the quotes
56 61 else:
57 62 if pf.startswith("'") and pf.endswith("'") and " " in pf:
58 63 pf = pf[1:-1] # Remove the quotes
59 64 return pf
60 65
61 66 def sshargs(sshcmd, host, user, port):
62 67 '''Build argument list for ssh'''
63 68 args = user and ("%s@%s" % (user, host)) or host
64 69 return port and ("%s -p %s" % (args, port)) or args
65 70
66 71 def is_exec(f):
67 72 """check whether a file is executable"""
68 73 return (os.lstat(f).st_mode & 0100 != 0)
69 74
70 75 def set_flags(f, l, x):
71 76 s = os.lstat(f).st_mode
72 77 if l:
73 78 if not stat.S_ISLNK(s):
74 79 # switch file to link
75 80 data = open(f).read()
76 81 os.unlink(f)
77 82 try:
78 83 os.symlink(data, f)
79 84 except:
80 85 # failed to make a link, rewrite file
81 86 open(f, "w").write(data)
82 87 # no chmod needed at this point
83 88 return
84 89 if stat.S_ISLNK(s):
85 90 # switch link to file
86 91 data = os.readlink(f)
87 92 os.unlink(f)
88 93 open(f, "w").write(data)
89 94 s = 0666 & ~umask # avoid restatting for chmod
90 95
91 96 sx = s & 0100
92 97 if x and not sx:
93 98 # Turn on +x for every +r bit when making a file executable
94 99 # and obey umask.
95 100 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
96 101 elif not x and sx:
97 102 # Turn off all +x bits
98 103 os.chmod(f, s & 0666)
99 104
100 105 def set_binary(fd):
101 106 pass
102 107
103 108 def pconvert(path):
104 109 return path
105 110
106 111 def localpath(path):
107 112 return path
108 113
109 114 def samefile(fpath1, fpath2):
110 115 """Returns whether path1 and path2 refer to the same file. This is only
111 116 guaranteed to work for files, not directories."""
112 117 return os.path.samefile(fpath1, fpath2)
113 118
114 119 def samedevice(fpath1, fpath2):
115 120 """Returns whether fpath1 and fpath2 are on the same device. This is only
116 121 guaranteed to work for files, not directories."""
117 122 st1 = os.lstat(fpath1)
118 123 st2 = os.lstat(fpath2)
119 124 return st1.st_dev == st2.st_dev
120 125
121 126 if sys.platform == 'darwin':
122 127 import fcntl # only needed on darwin, missing on jython
123 128 def realpath(path):
124 129 '''
125 130 Returns the true, canonical file system path equivalent to the given
126 131 path.
127 132
128 133 Equivalent means, in this case, resulting in the same, unique
129 134 file system link to the path. Every file system entry, whether a file,
130 135 directory, hard link or symbolic link or special, will have a single
131 136 path preferred by the system, but may allow multiple, differing path
132 137 lookups to point to it.
133 138
134 139 Most regular UNIX file systems only allow a file system entry to be
135 140 looked up by its distinct path. Obviously, this does not apply to case
136 141 insensitive file systems, whether case preserving or not. The most
137 142 complex issue to deal with is file systems transparently reencoding the
138 143 path, such as the non-standard Unicode normalisation required for HFS+
139 144 and HFSX.
140 145 '''
141 146 # Constants copied from /usr/include/sys/fcntl.h
142 147 F_GETPATH = 50
143 148 O_SYMLINK = 0x200000
144 149
145 150 try:
146 151 fd = os.open(path, O_SYMLINK)
147 152 except OSError, err:
148 153 if err.errno == errno.ENOENT:
149 154 return path
150 155 raise
151 156
152 157 try:
153 158 return fcntl.fcntl(fd, F_GETPATH, '\0' * 1024).rstrip('\0')
154 159 finally:
155 160 os.close(fd)
156 161 else:
157 162 # Fallback to the likely inadequate Python builtin function.
158 163 realpath = os.path.realpath
159 164
160 165 def shellquote(s):
161 166 if os.sys.platform == 'OpenVMS':
162 167 return '"%s"' % s
163 168 else:
164 169 return "'%s'" % s.replace("'", "'\\''")
165 170
166 171 def quotecommand(cmd):
167 172 return cmd
168 173
169 174 def popen(command, mode='r'):
170 175 return os.popen(command, mode)
171 176
172 177 def testpid(pid):
173 178 '''return False if pid dead, True if running or not sure'''
174 179 if os.sys.platform == 'OpenVMS':
175 180 return True
176 181 try:
177 182 os.kill(pid, 0)
178 183 return True
179 184 except OSError, inst:
180 185 return inst.errno != errno.ESRCH
181 186
182 187 def explain_exit(code):
183 188 """return a 2-tuple (desc, code) describing a subprocess status
184 189 (codes from kill are negative - not os.system/wait encoding)"""
185 190 if code >= 0:
186 191 return _("exited with status %d") % code, code
187 192 return _("killed by signal %d") % -code, -code
188 193
189 194 def isowner(st):
190 195 """Return True if the stat object st is from the current user."""
191 196 return st.st_uid == os.getuid()
192 197
193 198 def find_exe(command):
194 199 '''Find executable for command searching like which does.
195 200 If command is a basename then PATH is searched for command.
196 201 PATH isn't searched if command is an absolute or relative path.
197 202 If command isn't found None is returned.'''
198 203 if sys.platform == 'OpenVMS':
199 204 return command
200 205
201 206 def findexisting(executable):
202 207 'Will return executable if existing file'
203 208 if os.path.exists(executable):
204 209 return executable
205 210 return None
206 211
207 212 if os.sep in command:
208 213 return findexisting(command)
209 214
210 215 for path in os.environ.get('PATH', '').split(os.pathsep):
211 216 executable = findexisting(os.path.join(path, command))
212 217 if executable is not None:
213 218 return executable
214 219 return None
215 220
216 221 def set_signal_handler():
217 222 pass
218 223
219 224 def statfiles(files):
220 225 'Stat each file in files and yield stat or None if file does not exist.'
221 226 lstat = os.lstat
222 227 for nf in files:
223 228 try:
224 229 st = lstat(nf)
225 230 except OSError, err:
226 231 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
227 232 raise
228 233 st = None
229 234 yield st
230 235
231 236 def getuser():
232 237 '''return name of current user'''
233 238 return getpass.getuser()
234 239
235 240 def expand_glob(pats):
236 241 '''On Windows, expand the implicit globs in a list of patterns'''
237 242 return list(pats)
238 243
239 244 def username(uid=None):
240 245 """Return the name of the user with the given uid.
241 246
242 247 If uid is None, return the name of the current user."""
243 248
244 249 if uid is None:
245 250 uid = os.getuid()
246 251 try:
247 252 return pwd.getpwuid(uid)[0]
248 253 except KeyError:
249 254 return str(uid)
250 255
251 256 def groupname(gid=None):
252 257 """Return the name of the group with the given gid.
253 258
254 259 If gid is None, return the name of the current group."""
255 260
256 261 if gid is None:
257 262 gid = os.getgid()
258 263 try:
259 264 return grp.getgrgid(gid)[0]
260 265 except KeyError:
261 266 return str(gid)
262 267
263 268 def groupmembers(name):
264 269 """Return the list of members of the group with the given
265 270 name, KeyError if the group does not exist.
266 271 """
267 272 return list(grp.getgrnam(name).gr_mem)
268 273
269 274 def spawndetached(args):
270 275 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
271 276 args[0], args)
272 277
273 278 def gethgcmd():
274 279 return sys.argv[:1]
275 280
276 281 def termwidth():
277 282 try:
278 283 import termios, array, fcntl
279 284 for dev in (sys.stderr, sys.stdout, sys.stdin):
280 285 try:
281 286 try:
282 287 fd = dev.fileno()
283 288 except AttributeError:
284 289 continue
285 290 if not os.isatty(fd):
286 291 continue
287 292 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
288 293 return array.array('h', arri)[1]
289 294 except ValueError:
290 295 pass
291 296 except IOError, e:
292 297 if e[0] == errno.EINVAL:
293 298 pass
294 299 else:
295 300 raise
296 301 except ImportError:
297 302 pass
298 303 return 80
@@ -1,1555 +1,1545 b''
1 1 # util.py - Mercurial utility functions and platform specfic implementations
2 2 #
3 3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 """Mercurial utility functions and platform specfic implementations.
11 11
12 12 This contains helper routines that are independent of the SCM core and
13 13 hide platform-specific details from the core.
14 14 """
15 15
16 16 from i18n import _
17 17 import error, osutil, encoding
18 18 import errno, re, shutil, sys, tempfile, traceback
19 19 import os, stat, time, calendar, textwrap, unicodedata, signal
20 20 import imp, socket
21 21
22 22 # Python compatibility
23 23
24 24 def sha1(s):
25 25 return _fastsha1(s)
26 26
27 27 def _fastsha1(s):
28 28 # This function will import sha1 from hashlib or sha (whichever is
29 29 # available) and overwrite itself with it on the first call.
30 30 # Subsequent calls will go directly to the imported function.
31 31 if sys.version_info >= (2, 5):
32 32 from hashlib import sha1 as _sha1
33 33 else:
34 34 from sha import sha as _sha1
35 35 global _fastsha1, sha1
36 36 _fastsha1 = sha1 = _sha1
37 37 return _sha1(s)
38 38
39 39 import __builtin__
40 40
41 41 if sys.version_info[0] < 3:
42 42 def fakebuffer(sliceable, offset=0):
43 43 return sliceable[offset:]
44 44 else:
45 45 def fakebuffer(sliceable, offset=0):
46 46 return memoryview(sliceable)[offset:]
47 47 try:
48 48 buffer
49 49 except NameError:
50 50 __builtin__.buffer = fakebuffer
51 51
52 52 import subprocess
53 53 closefds = os.name == 'posix'
54 54
55 55 def popen2(cmd, env=None, newlines=False):
56 56 # Setting bufsize to -1 lets the system decide the buffer size.
57 57 # The default for bufsize is 0, meaning unbuffered. This leads to
58 58 # poor performance on Mac OS X: http://bugs.python.org/issue4194
59 59 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
60 60 close_fds=closefds,
61 61 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
62 62 universal_newlines=newlines,
63 63 env=env)
64 64 return p.stdin, p.stdout
65 65
66 66 def popen3(cmd, env=None, newlines=False):
67 67 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
68 68 close_fds=closefds,
69 69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
70 70 stderr=subprocess.PIPE,
71 71 universal_newlines=newlines,
72 72 env=env)
73 73 return p.stdin, p.stdout, p.stderr
74 74
75 75 def version():
76 76 """Return version information if available."""
77 77 try:
78 78 import __version__
79 79 return __version__.version
80 80 except ImportError:
81 81 return 'unknown'
82 82
83 83 # used by parsedate
84 84 defaultdateformats = (
85 85 '%Y-%m-%d %H:%M:%S',
86 86 '%Y-%m-%d %I:%M:%S%p',
87 87 '%Y-%m-%d %H:%M',
88 88 '%Y-%m-%d %I:%M%p',
89 89 '%Y-%m-%d',
90 90 '%m-%d',
91 91 '%m/%d',
92 92 '%m/%d/%y',
93 93 '%m/%d/%Y',
94 94 '%a %b %d %H:%M:%S %Y',
95 95 '%a %b %d %I:%M:%S%p %Y',
96 96 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
97 97 '%b %d %H:%M:%S %Y',
98 98 '%b %d %I:%M:%S%p %Y',
99 99 '%b %d %H:%M:%S',
100 100 '%b %d %I:%M:%S%p',
101 101 '%b %d %H:%M',
102 102 '%b %d %I:%M%p',
103 103 '%b %d %Y',
104 104 '%b %d',
105 105 '%H:%M:%S',
106 106 '%I:%M:%S%p',
107 107 '%H:%M',
108 108 '%I:%M%p',
109 109 )
110 110
111 111 extendeddateformats = defaultdateformats + (
112 112 "%Y",
113 113 "%Y-%m",
114 114 "%b",
115 115 "%b %Y",
116 116 )
117 117
118 118 def cachefunc(func):
119 119 '''cache the result of function calls'''
120 120 # XXX doesn't handle keywords args
121 121 cache = {}
122 122 if func.func_code.co_argcount == 1:
123 123 # we gain a small amount of time because
124 124 # we don't need to pack/unpack the list
125 125 def f(arg):
126 126 if arg not in cache:
127 127 cache[arg] = func(arg)
128 128 return cache[arg]
129 129 else:
130 130 def f(*args):
131 131 if args not in cache:
132 132 cache[args] = func(*args)
133 133 return cache[args]
134 134
135 135 return f
136 136
137 137 def lrucachefunc(func):
138 138 '''cache most recent results of function calls'''
139 139 cache = {}
140 140 order = []
141 141 if func.func_code.co_argcount == 1:
142 142 def f(arg):
143 143 if arg not in cache:
144 144 if len(cache) > 20:
145 145 del cache[order.pop(0)]
146 146 cache[arg] = func(arg)
147 147 else:
148 148 order.remove(arg)
149 149 order.append(arg)
150 150 return cache[arg]
151 151 else:
152 152 def f(*args):
153 153 if args not in cache:
154 154 if len(cache) > 20:
155 155 del cache[order.pop(0)]
156 156 cache[args] = func(*args)
157 157 else:
158 158 order.remove(args)
159 159 order.append(args)
160 160 return cache[args]
161 161
162 162 return f
163 163
164 164 class propertycache(object):
165 165 def __init__(self, func):
166 166 self.func = func
167 167 self.name = func.__name__
168 168 def __get__(self, obj, type=None):
169 169 result = self.func(obj)
170 170 setattr(obj, self.name, result)
171 171 return result
172 172
173 173 def pipefilter(s, cmd):
174 174 '''filter string S through command CMD, returning its output'''
175 175 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
176 176 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
177 177 pout, perr = p.communicate(s)
178 178 return pout
179 179
180 180 def tempfilter(s, cmd):
181 181 '''filter string S through a pair of temporary files with CMD.
182 182 CMD is used as a template to create the real command to be run,
183 183 with the strings INFILE and OUTFILE replaced by the real names of
184 184 the temporary files generated.'''
185 185 inname, outname = None, None
186 186 try:
187 187 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
188 188 fp = os.fdopen(infd, 'wb')
189 189 fp.write(s)
190 190 fp.close()
191 191 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
192 192 os.close(outfd)
193 193 cmd = cmd.replace('INFILE', inname)
194 194 cmd = cmd.replace('OUTFILE', outname)
195 195 code = os.system(cmd)
196 196 if sys.platform == 'OpenVMS' and code & 1:
197 197 code = 0
198 198 if code:
199 199 raise Abort(_("command '%s' failed: %s") %
200 200 (cmd, explain_exit(code)))
201 201 return open(outname, 'rb').read()
202 202 finally:
203 203 try:
204 204 if inname:
205 205 os.unlink(inname)
206 206 except:
207 207 pass
208 208 try:
209 209 if outname:
210 210 os.unlink(outname)
211 211 except:
212 212 pass
213 213
214 214 filtertable = {
215 215 'tempfile:': tempfilter,
216 216 'pipe:': pipefilter,
217 217 }
218 218
219 219 def filter(s, cmd):
220 220 "filter a string through a command that transforms its input to its output"
221 221 for name, fn in filtertable.iteritems():
222 222 if cmd.startswith(name):
223 223 return fn(s, cmd[len(name):].lstrip())
224 224 return pipefilter(s, cmd)
225 225
226 226 def binary(s):
227 227 """return true if a string is binary data"""
228 228 return bool(s and '\0' in s)
229 229
230 230 def increasingchunks(source, min=1024, max=65536):
231 231 '''return no less than min bytes per chunk while data remains,
232 232 doubling min after each chunk until it reaches max'''
233 233 def log2(x):
234 234 if not x:
235 235 return 0
236 236 i = 0
237 237 while x:
238 238 x >>= 1
239 239 i += 1
240 240 return i - 1
241 241
242 242 buf = []
243 243 blen = 0
244 244 for chunk in source:
245 245 buf.append(chunk)
246 246 blen += len(chunk)
247 247 if blen >= min:
248 248 if min < max:
249 249 min = min << 1
250 250 nmin = 1 << log2(blen)
251 251 if nmin > min:
252 252 min = nmin
253 253 if min > max:
254 254 min = max
255 255 yield ''.join(buf)
256 256 blen = 0
257 257 buf = []
258 258 if buf:
259 259 yield ''.join(buf)
260 260
261 261 Abort = error.Abort
262 262
263 263 def always(fn):
264 264 return True
265 265
266 266 def never(fn):
267 267 return False
268 268
269 269 def pathto(root, n1, n2):
270 270 '''return the relative path from one place to another.
271 271 root should use os.sep to separate directories
272 272 n1 should use os.sep to separate directories
273 273 n2 should use "/" to separate directories
274 274 returns an os.sep-separated path.
275 275
276 276 If n1 is a relative path, it's assumed it's
277 277 relative to root.
278 278 n2 should always be relative to root.
279 279 '''
280 280 if not n1:
281 281 return localpath(n2)
282 282 if os.path.isabs(n1):
283 283 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
284 284 return os.path.join(root, localpath(n2))
285 285 n2 = '/'.join((pconvert(root), n2))
286 286 a, b = splitpath(n1), n2.split('/')
287 287 a.reverse()
288 288 b.reverse()
289 289 while a and b and a[-1] == b[-1]:
290 290 a.pop()
291 291 b.pop()
292 292 b.reverse()
293 293 return os.sep.join((['..'] * len(a)) + b) or '.'
294 294
295 295 def canonpath(root, cwd, myname, auditor=None):
296 296 """return the canonical path of myname, given cwd and root"""
297 297 if endswithsep(root):
298 298 rootsep = root
299 299 else:
300 300 rootsep = root + os.sep
301 301 name = myname
302 302 if not os.path.isabs(name):
303 303 name = os.path.join(root, cwd, name)
304 304 name = os.path.normpath(name)
305 305 if auditor is None:
306 306 auditor = path_auditor(root)
307 307 if name != rootsep and name.startswith(rootsep):
308 308 name = name[len(rootsep):]
309 309 auditor(name)
310 310 return pconvert(name)
311 311 elif name == root:
312 312 return ''
313 313 else:
314 314 # Determine whether `name' is in the hierarchy at or beneath `root',
315 315 # by iterating name=dirname(name) until that causes no change (can't
316 316 # check name == '/', because that doesn't work on windows). For each
317 317 # `name', compare dev/inode numbers. If they match, the list `rel'
318 318 # holds the reversed list of components making up the relative file
319 319 # name we want.
320 320 root_st = os.stat(root)
321 321 rel = []
322 322 while True:
323 323 try:
324 324 name_st = os.stat(name)
325 325 except OSError:
326 326 break
327 327 if samestat(name_st, root_st):
328 328 if not rel:
329 329 # name was actually the same as root (maybe a symlink)
330 330 return ''
331 331 rel.reverse()
332 332 name = os.path.join(*rel)
333 333 auditor(name)
334 334 return pconvert(name)
335 335 dirname, basename = os.path.split(name)
336 336 rel.append(basename)
337 337 if dirname == name:
338 338 break
339 339 name = dirname
340 340
341 341 raise Abort('%s not under root' % myname)
342 342
343 343 _hgexecutable = None
344 344
345 345 def main_is_frozen():
346 346 """return True if we are a frozen executable.
347 347
348 348 The code supports py2exe (most common, Windows only) and tools/freeze
349 349 (portable, not much used).
350 350 """
351 351 return (hasattr(sys, "frozen") or # new py2exe
352 352 hasattr(sys, "importers") or # old py2exe
353 353 imp.is_frozen("__main__")) # tools/freeze
354 354
355 355 def hgexecutable():
356 356 """return location of the 'hg' executable.
357 357
358 358 Defaults to $HG or 'hg' in the search path.
359 359 """
360 360 if _hgexecutable is None:
361 361 hg = os.environ.get('HG')
362 362 if hg:
363 363 set_hgexecutable(hg)
364 364 elif main_is_frozen():
365 365 set_hgexecutable(sys.executable)
366 366 else:
367 367 exe = find_exe('hg') or os.path.basename(sys.argv[0])
368 368 set_hgexecutable(exe)
369 369 return _hgexecutable
370 370
371 371 def set_hgexecutable(path):
372 372 """set location of the 'hg' executable"""
373 373 global _hgexecutable
374 374 _hgexecutable = path
375 375
376 376 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
377 377 '''enhanced shell command execution.
378 378 run with environment maybe modified, maybe in different dir.
379 379
380 380 if command fails and onerr is None, return status. if ui object,
381 381 print error message and return status, else raise onerr object as
382 382 exception.
383 383
384 384 if out is specified, it is assumed to be a file-like object that has a
385 385 write() method. stdout and stderr will be redirected to out.'''
386 386 def py2shell(val):
387 387 'convert python object into string that is useful to shell'
388 388 if val is None or val is False:
389 389 return '0'
390 390 if val is True:
391 391 return '1'
392 392 return str(val)
393 393 origcmd = cmd
394 394 cmd = quotecommand(cmd)
395 395 env = dict(os.environ)
396 396 env.update((k, py2shell(v)) for k, v in environ.iteritems())
397 397 env['HG'] = hgexecutable()
398 398 if out is None:
399 399 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
400 400 env=env, cwd=cwd)
401 401 else:
402 402 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
403 403 env=env, cwd=cwd, stdout=subprocess.PIPE,
404 404 stderr=subprocess.STDOUT)
405 405 for line in proc.stdout:
406 406 out.write(line)
407 407 proc.wait()
408 408 rc = proc.returncode
409 409 if sys.platform == 'OpenVMS' and rc & 1:
410 410 rc = 0
411 411 if rc and onerr:
412 412 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
413 413 explain_exit(rc)[0])
414 414 if errprefix:
415 415 errmsg = '%s: %s' % (errprefix, errmsg)
416 416 try:
417 417 onerr.warn(errmsg + '\n')
418 418 except AttributeError:
419 419 raise onerr(errmsg)
420 420 return rc
421 421
422 422 def checksignature(func):
423 423 '''wrap a function with code to check for calling errors'''
424 424 def check(*args, **kwargs):
425 425 try:
426 426 return func(*args, **kwargs)
427 427 except TypeError:
428 428 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
429 429 raise error.SignatureError
430 430 raise
431 431
432 432 return check
433 433
434 434 def unlinkpath(f):
435 435 """unlink and remove the directory if it is empty"""
436 436 os.unlink(f)
437 437 # try removing directories that might now be empty
438 438 try:
439 439 os.removedirs(os.path.dirname(f))
440 440 except OSError:
441 441 pass
442 442
443 443 def copyfile(src, dest):
444 444 "copy a file, preserving mode and atime/mtime"
445 445 if os.path.islink(src):
446 446 try:
447 447 os.unlink(dest)
448 448 except:
449 449 pass
450 450 os.symlink(os.readlink(src), dest)
451 451 else:
452 452 try:
453 453 shutil.copyfile(src, dest)
454 454 shutil.copymode(src, dest)
455 455 except shutil.Error, inst:
456 456 raise Abort(str(inst))
457 457
458 458 def copyfiles(src, dst, hardlink=None):
459 459 """Copy a directory tree using hardlinks if possible"""
460 460
461 461 if hardlink is None:
462 462 hardlink = (os.stat(src).st_dev ==
463 463 os.stat(os.path.dirname(dst)).st_dev)
464 464
465 465 num = 0
466 466 if os.path.isdir(src):
467 467 os.mkdir(dst)
468 468 for name, kind in osutil.listdir(src):
469 469 srcname = os.path.join(src, name)
470 470 dstname = os.path.join(dst, name)
471 471 hardlink, n = copyfiles(srcname, dstname, hardlink)
472 472 num += n
473 473 else:
474 474 if hardlink:
475 475 try:
476 476 os_link(src, dst)
477 477 except (IOError, OSError):
478 478 hardlink = False
479 479 shutil.copy(src, dst)
480 480 else:
481 481 shutil.copy(src, dst)
482 482 num += 1
483 483
484 484 return hardlink, num
485 485
486 486 class path_auditor(object):
487 487 '''ensure that a filesystem path contains no banned components.
488 488 the following properties of a path are checked:
489 489
490 490 - ends with a directory separator
491 491 - under top-level .hg
492 492 - starts at the root of a windows drive
493 493 - contains ".."
494 494 - traverses a symlink (e.g. a/symlink_here/b)
495 495 - inside a nested repository (a callback can be used to approve
496 496 some nested repositories, e.g., subrepositories)
497 497 '''
498 498
499 499 def __init__(self, root, callback=None):
500 500 self.audited = set()
501 501 self.auditeddir = set()
502 502 self.root = root
503 503 self.callback = callback
504 504
505 505 def __call__(self, path):
506 506 if path in self.audited:
507 507 return
508 508 # AIX ignores "/" at end of path, others raise EISDIR.
509 509 if endswithsep(path):
510 510 raise Abort(_("path ends in directory separator: %s") % path)
511 511 normpath = os.path.normcase(path)
512 512 parts = splitpath(normpath)
513 513 if (os.path.splitdrive(path)[0]
514 514 or parts[0].lower() in ('.hg', '.hg.', '')
515 515 or os.pardir in parts):
516 516 raise Abort(_("path contains illegal component: %s") % path)
517 517 if '.hg' in path.lower():
518 518 lparts = [p.lower() for p in parts]
519 519 for p in '.hg', '.hg.':
520 520 if p in lparts[1:]:
521 521 pos = lparts.index(p)
522 522 base = os.path.join(*parts[:pos])
523 523 raise Abort(_('path %r is inside repo %r') % (path, base))
524 524 def check(prefix):
525 525 curpath = os.path.join(self.root, prefix)
526 526 try:
527 527 st = os.lstat(curpath)
528 528 except OSError, err:
529 529 # EINVAL can be raised as invalid path syntax under win32.
530 530 # They must be ignored for patterns can be checked too.
531 531 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
532 532 raise
533 533 else:
534 534 if stat.S_ISLNK(st.st_mode):
535 535 raise Abort(_('path %r traverses symbolic link %r') %
536 536 (path, prefix))
537 537 elif (stat.S_ISDIR(st.st_mode) and
538 538 os.path.isdir(os.path.join(curpath, '.hg'))):
539 539 if not self.callback or not self.callback(curpath):
540 540 raise Abort(_('path %r is inside repo %r') %
541 541 (path, prefix))
542 542 parts.pop()
543 543 prefixes = []
544 544 while parts:
545 545 prefix = os.sep.join(parts)
546 546 if prefix in self.auditeddir:
547 547 break
548 548 check(prefix)
549 549 prefixes.append(prefix)
550 550 parts.pop()
551 551
552 552 self.audited.add(path)
553 553 # only add prefixes to the cache after checking everything: we don't
554 554 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
555 555 self.auditeddir.update(prefixes)
556 556
557 def nlinks(pathname):
558 """Return number of hardlinks for the given file."""
559 return os.lstat(pathname).st_nlink
560
561 if hasattr(os, 'link'):
562 os_link = os.link
563 else:
564 def os_link(src, dst):
565 raise OSError(0, _("Hardlinks not supported"))
566
567 557 def lookup_reg(key, name=None, scope=None):
568 558 return None
569 559
570 560 def hidewindow():
571 561 """Hide current shell window.
572 562
573 563 Used to hide the window opened when starting asynchronous
574 564 child process under Windows, unneeded on other systems.
575 565 """
576 566 pass
577 567
578 568 if os.name == 'nt':
579 569 from windows import *
580 570 else:
581 571 from posix import *
582 572
583 573 def makelock(info, pathname):
584 574 try:
585 575 return os.symlink(info, pathname)
586 576 except OSError, why:
587 577 if why.errno == errno.EEXIST:
588 578 raise
589 579 except AttributeError: # no symlink in os
590 580 pass
591 581
592 582 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
593 583 os.write(ld, info)
594 584 os.close(ld)
595 585
596 586 def readlock(pathname):
597 587 try:
598 588 return os.readlink(pathname)
599 589 except OSError, why:
600 590 if why.errno not in (errno.EINVAL, errno.ENOSYS):
601 591 raise
602 592 except AttributeError: # no symlink in os
603 593 pass
604 594 return posixfile(pathname).read()
605 595
606 596 def fstat(fp):
607 597 '''stat file object that may not have fileno method.'''
608 598 try:
609 599 return os.fstat(fp.fileno())
610 600 except AttributeError:
611 601 return os.stat(fp.name)
612 602
613 603 # File system features
614 604
615 605 def checkcase(path):
616 606 """
617 607 Check whether the given path is on a case-sensitive filesystem
618 608
619 609 Requires a path (like /foo/.hg) ending with a foldable final
620 610 directory component.
621 611 """
622 612 s1 = os.stat(path)
623 613 d, b = os.path.split(path)
624 614 p2 = os.path.join(d, b.upper())
625 615 if path == p2:
626 616 p2 = os.path.join(d, b.lower())
627 617 try:
628 618 s2 = os.stat(p2)
629 619 if s2 == s1:
630 620 return False
631 621 return True
632 622 except:
633 623 return True
634 624
635 625 _fspathcache = {}
636 626 def fspath(name, root):
637 627 '''Get name in the case stored in the filesystem
638 628
639 629 The name is either relative to root, or it is an absolute path starting
640 630 with root. Note that this function is unnecessary, and should not be
641 631 called, for case-sensitive filesystems (simply because it's expensive).
642 632 '''
643 633 # If name is absolute, make it relative
644 634 if name.lower().startswith(root.lower()):
645 635 l = len(root)
646 636 if name[l] == os.sep or name[l] == os.altsep:
647 637 l = l + 1
648 638 name = name[l:]
649 639
650 640 if not os.path.lexists(os.path.join(root, name)):
651 641 return None
652 642
653 643 seps = os.sep
654 644 if os.altsep:
655 645 seps = seps + os.altsep
656 646 # Protect backslashes. This gets silly very quickly.
657 647 seps.replace('\\','\\\\')
658 648 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
659 649 dir = os.path.normcase(os.path.normpath(root))
660 650 result = []
661 651 for part, sep in pattern.findall(name):
662 652 if sep:
663 653 result.append(sep)
664 654 continue
665 655
666 656 if dir not in _fspathcache:
667 657 _fspathcache[dir] = os.listdir(dir)
668 658 contents = _fspathcache[dir]
669 659
670 660 lpart = part.lower()
671 661 lenp = len(part)
672 662 for n in contents:
673 663 if lenp == len(n) and n.lower() == lpart:
674 664 result.append(n)
675 665 break
676 666 else:
677 667 # Cannot happen, as the file exists!
678 668 result.append(part)
679 669 dir = os.path.join(dir, lpart)
680 670
681 671 return ''.join(result)
682 672
683 673 def checkexec(path):
684 674 """
685 675 Check whether the given path is on a filesystem with UNIX-like exec flags
686 676
687 677 Requires a directory (like /foo/.hg)
688 678 """
689 679
690 680 # VFAT on some Linux versions can flip mode but it doesn't persist
691 681 # a FS remount. Frequently we can detect it if files are created
692 682 # with exec bit on.
693 683
694 684 try:
695 685 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
696 686 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
697 687 try:
698 688 os.close(fh)
699 689 m = os.stat(fn).st_mode & 0777
700 690 new_file_has_exec = m & EXECFLAGS
701 691 os.chmod(fn, m ^ EXECFLAGS)
702 692 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
703 693 finally:
704 694 os.unlink(fn)
705 695 except (IOError, OSError):
706 696 # we don't care, the user probably won't be able to commit anyway
707 697 return False
708 698 return not (new_file_has_exec or exec_flags_cannot_flip)
709 699
710 700 def checklink(path):
711 701 """check whether the given path is on a symlink-capable filesystem"""
712 702 # mktemp is not racy because symlink creation will fail if the
713 703 # file already exists
714 704 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
715 705 try:
716 706 os.symlink(".", name)
717 707 os.unlink(name)
718 708 return True
719 709 except (OSError, AttributeError):
720 710 return False
721 711
722 712 def checknlink(testfile):
723 713 '''check whether hardlink count reporting works properly'''
724 714
725 715 # testfile may be open, so we need a separate file for checking to
726 716 # work around issue2543 (or testfile may get lost on Samba shares)
727 717 f1 = testfile + ".hgtmp1"
728 718 if os.path.lexists(f1):
729 719 return False
730 720 try:
731 721 posixfile(f1, 'w').close()
732 722 except IOError:
733 723 return False
734 724
735 725 f2 = testfile + ".hgtmp2"
736 726 fd = None
737 727 try:
738 728 try:
739 729 os_link(f1, f2)
740 730 except OSError:
741 731 return False
742 732
743 733 # nlinks() may behave differently for files on Windows shares if
744 734 # the file is open.
745 735 fd = posixfile(f2)
746 736 return nlinks(f2) > 1
747 737 finally:
748 738 if fd is not None:
749 739 fd.close()
750 740 for f in (f1, f2):
751 741 try:
752 742 os.unlink(f)
753 743 except OSError:
754 744 pass
755 745
756 746 return False
757 747
758 748 def endswithsep(path):
759 749 '''Check path ends with os.sep or os.altsep.'''
760 750 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
761 751
762 752 def splitpath(path):
763 753 '''Split path by os.sep.
764 754 Note that this function does not use os.altsep because this is
765 755 an alternative of simple "xxx.split(os.sep)".
766 756 It is recommended to use os.path.normpath() before using this
767 757 function if need.'''
768 758 return path.split(os.sep)
769 759
770 760 def gui():
771 761 '''Are we running in a GUI?'''
772 762 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
773 763
774 764 def mktempcopy(name, emptyok=False, createmode=None):
775 765 """Create a temporary file with the same contents from name
776 766
777 767 The permission bits are copied from the original file.
778 768
779 769 If the temporary file is going to be truncated immediately, you
780 770 can use emptyok=True as an optimization.
781 771
782 772 Returns the name of the temporary file.
783 773 """
784 774 d, fn = os.path.split(name)
785 775 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
786 776 os.close(fd)
787 777 # Temporary files are created with mode 0600, which is usually not
788 778 # what we want. If the original file already exists, just copy
789 779 # its mode. Otherwise, manually obey umask.
790 780 try:
791 781 st_mode = os.lstat(name).st_mode & 0777
792 782 except OSError, inst:
793 783 if inst.errno != errno.ENOENT:
794 784 raise
795 785 st_mode = createmode
796 786 if st_mode is None:
797 787 st_mode = ~umask
798 788 st_mode &= 0666
799 789 os.chmod(temp, st_mode)
800 790 if emptyok:
801 791 return temp
802 792 try:
803 793 try:
804 794 ifp = posixfile(name, "rb")
805 795 except IOError, inst:
806 796 if inst.errno == errno.ENOENT:
807 797 return temp
808 798 if not getattr(inst, 'filename', None):
809 799 inst.filename = name
810 800 raise
811 801 ofp = posixfile(temp, "wb")
812 802 for chunk in filechunkiter(ifp):
813 803 ofp.write(chunk)
814 804 ifp.close()
815 805 ofp.close()
816 806 except:
817 807 try: os.unlink(temp)
818 808 except: pass
819 809 raise
820 810 return temp
821 811
822 812 class atomictempfile(object):
823 813 """file-like object that atomically updates a file
824 814
825 815 All writes will be redirected to a temporary copy of the original
826 816 file. When rename is called, the copy is renamed to the original
827 817 name, making the changes visible.
828 818 """
829 819 def __init__(self, name, mode='w+b', createmode=None):
830 820 self.__name = name
831 821 self._fp = None
832 822 self.temp = mktempcopy(name, emptyok=('w' in mode),
833 823 createmode=createmode)
834 824 self._fp = posixfile(self.temp, mode)
835 825
836 826 def __getattr__(self, name):
837 827 return getattr(self._fp, name)
838 828
839 829 def rename(self):
840 830 if not self._fp.closed:
841 831 self._fp.close()
842 832 rename(self.temp, localpath(self.__name))
843 833
844 834 def close(self):
845 835 if not self._fp:
846 836 return
847 837 if not self._fp.closed:
848 838 try:
849 839 os.unlink(self.temp)
850 840 except: pass
851 841 self._fp.close()
852 842
853 843 def __del__(self):
854 844 self.close()
855 845
856 846 def makedirs(name, mode=None):
857 847 """recursive directory creation with parent mode inheritance"""
858 848 parent = os.path.abspath(os.path.dirname(name))
859 849 try:
860 850 os.mkdir(name)
861 851 if mode is not None:
862 852 os.chmod(name, mode)
863 853 return
864 854 except OSError, err:
865 855 if err.errno == errno.EEXIST:
866 856 return
867 857 if not name or parent == name or err.errno != errno.ENOENT:
868 858 raise
869 859 makedirs(parent, mode)
870 860 makedirs(name, mode)
871 861
872 862 class opener(object):
873 863 """Open files relative to a base directory
874 864
875 865 This class is used to hide the details of COW semantics and
876 866 remote file access from higher level code.
877 867 """
878 868 def __init__(self, base, audit=True):
879 869 self.base = base
880 870 if audit:
881 871 self.auditor = path_auditor(base)
882 872 else:
883 873 self.auditor = always
884 874 self.createmode = None
885 875 self._trustnlink = None
886 876
887 877 @propertycache
888 878 def _can_symlink(self):
889 879 return checklink(self.base)
890 880
891 881 def _fixfilemode(self, name):
892 882 if self.createmode is None:
893 883 return
894 884 os.chmod(name, self.createmode & 0666)
895 885
896 886 def __call__(self, path, mode="r", text=False, atomictemp=False):
897 887 self.auditor(path)
898 888 f = os.path.join(self.base, path)
899 889
900 890 if not text and "b" not in mode:
901 891 mode += "b" # for that other OS
902 892
903 893 nlink = -1
904 894 dirname, basename = os.path.split(f)
905 895 # If basename is empty, then the path is malformed because it points
906 896 # to a directory. Let the posixfile() call below raise IOError.
907 897 if basename and mode not in ('r', 'rb'):
908 898 if atomictemp:
909 899 if not os.path.isdir(dirname):
910 900 makedirs(dirname, self.createmode)
911 901 return atomictempfile(f, mode, self.createmode)
912 902 try:
913 903 if 'w' in mode:
914 904 unlink(f)
915 905 nlink = 0
916 906 else:
917 907 # nlinks() may behave differently for files on Windows
918 908 # shares if the file is open.
919 909 fd = posixfile(f)
920 910 nlink = nlinks(f)
921 911 if nlink < 1:
922 912 nlink = 2 # force mktempcopy (issue1922)
923 913 fd.close()
924 914 except (OSError, IOError), e:
925 915 if e.errno != errno.ENOENT:
926 916 raise
927 917 nlink = 0
928 918 if not os.path.isdir(dirname):
929 919 makedirs(dirname, self.createmode)
930 920 if nlink > 0:
931 921 if self._trustnlink is None:
932 922 self._trustnlink = nlink > 1 or checknlink(f)
933 923 if nlink > 1 or not self._trustnlink:
934 924 rename(mktempcopy(f), f)
935 925 fp = posixfile(f, mode)
936 926 if nlink == 0:
937 927 self._fixfilemode(f)
938 928 return fp
939 929
940 930 def symlink(self, src, dst):
941 931 self.auditor(dst)
942 932 linkname = os.path.join(self.base, dst)
943 933 try:
944 934 os.unlink(linkname)
945 935 except OSError:
946 936 pass
947 937
948 938 dirname = os.path.dirname(linkname)
949 939 if not os.path.exists(dirname):
950 940 makedirs(dirname, self.createmode)
951 941
952 942 if self._can_symlink:
953 943 try:
954 944 os.symlink(src, linkname)
955 945 except OSError, err:
956 946 raise OSError(err.errno, _('could not symlink to %r: %s') %
957 947 (src, err.strerror), linkname)
958 948 else:
959 949 f = self(dst, "w")
960 950 f.write(src)
961 951 f.close()
962 952 self._fixfilemode(dst)
963 953
964 954 class chunkbuffer(object):
965 955 """Allow arbitrary sized chunks of data to be efficiently read from an
966 956 iterator over chunks of arbitrary size."""
967 957
968 958 def __init__(self, in_iter):
969 959 """in_iter is the iterator that's iterating over the input chunks.
970 960 targetsize is how big a buffer to try to maintain."""
971 961 def splitbig(chunks):
972 962 for chunk in chunks:
973 963 if len(chunk) > 2**20:
974 964 pos = 0
975 965 while pos < len(chunk):
976 966 end = pos + 2 ** 18
977 967 yield chunk[pos:end]
978 968 pos = end
979 969 else:
980 970 yield chunk
981 971 self.iter = splitbig(in_iter)
982 972 self._queue = []
983 973
984 974 def read(self, l):
985 975 """Read L bytes of data from the iterator of chunks of data.
986 976 Returns less than L bytes if the iterator runs dry."""
987 977 left = l
988 978 buf = ''
989 979 queue = self._queue
990 980 while left > 0:
991 981 # refill the queue
992 982 if not queue:
993 983 target = 2**18
994 984 for chunk in self.iter:
995 985 queue.append(chunk)
996 986 target -= len(chunk)
997 987 if target <= 0:
998 988 break
999 989 if not queue:
1000 990 break
1001 991
1002 992 chunk = queue.pop(0)
1003 993 left -= len(chunk)
1004 994 if left < 0:
1005 995 queue.insert(0, chunk[left:])
1006 996 buf += chunk[:left]
1007 997 else:
1008 998 buf += chunk
1009 999
1010 1000 return buf
1011 1001
1012 1002 def filechunkiter(f, size=65536, limit=None):
1013 1003 """Create a generator that produces the data in the file size
1014 1004 (default 65536) bytes at a time, up to optional limit (default is
1015 1005 to read all data). Chunks may be less than size bytes if the
1016 1006 chunk is the last chunk in the file, or the file is a socket or
1017 1007 some other type of file that sometimes reads less data than is
1018 1008 requested."""
1019 1009 assert size >= 0
1020 1010 assert limit is None or limit >= 0
1021 1011 while True:
1022 1012 if limit is None:
1023 1013 nbytes = size
1024 1014 else:
1025 1015 nbytes = min(limit, size)
1026 1016 s = nbytes and f.read(nbytes)
1027 1017 if not s:
1028 1018 break
1029 1019 if limit:
1030 1020 limit -= len(s)
1031 1021 yield s
1032 1022
1033 1023 def makedate():
1034 1024 lt = time.localtime()
1035 1025 if lt[8] == 1 and time.daylight:
1036 1026 tz = time.altzone
1037 1027 else:
1038 1028 tz = time.timezone
1039 1029 t = time.mktime(lt)
1040 1030 if t < 0:
1041 1031 hint = _("check your clock")
1042 1032 raise Abort(_("negative timestamp: %d") % t, hint=hint)
1043 1033 return t, tz
1044 1034
1045 1035 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1046 1036 """represent a (unixtime, offset) tuple as a localized time.
1047 1037 unixtime is seconds since the epoch, and offset is the time zone's
1048 1038 number of seconds away from UTC. if timezone is false, do not
1049 1039 append time zone to string."""
1050 1040 t, tz = date or makedate()
1051 1041 if t < 0:
1052 1042 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1053 1043 tz = 0
1054 1044 if "%1" in format or "%2" in format:
1055 1045 sign = (tz > 0) and "-" or "+"
1056 1046 minutes = abs(tz) // 60
1057 1047 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1058 1048 format = format.replace("%2", "%02d" % (minutes % 60))
1059 1049 s = time.strftime(format, time.gmtime(float(t) - tz))
1060 1050 return s
1061 1051
1062 1052 def shortdate(date=None):
1063 1053 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1064 1054 return datestr(date, format='%Y-%m-%d')
1065 1055
1066 1056 def strdate(string, format, defaults=[]):
1067 1057 """parse a localized time string and return a (unixtime, offset) tuple.
1068 1058 if the string cannot be parsed, ValueError is raised."""
1069 1059 def timezone(string):
1070 1060 tz = string.split()[-1]
1071 1061 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1072 1062 sign = (tz[0] == "+") and 1 or -1
1073 1063 hours = int(tz[1:3])
1074 1064 minutes = int(tz[3:5])
1075 1065 return -sign * (hours * 60 + minutes) * 60
1076 1066 if tz == "GMT" or tz == "UTC":
1077 1067 return 0
1078 1068 return None
1079 1069
1080 1070 # NOTE: unixtime = localunixtime + offset
1081 1071 offset, date = timezone(string), string
1082 1072 if offset is not None:
1083 1073 date = " ".join(string.split()[:-1])
1084 1074
1085 1075 # add missing elements from defaults
1086 1076 usenow = False # default to using biased defaults
1087 1077 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1088 1078 found = [True for p in part if ("%"+p) in format]
1089 1079 if not found:
1090 1080 date += "@" + defaults[part][usenow]
1091 1081 format += "@%" + part[0]
1092 1082 else:
1093 1083 # We've found a specific time element, less specific time
1094 1084 # elements are relative to today
1095 1085 usenow = True
1096 1086
1097 1087 timetuple = time.strptime(date, format)
1098 1088 localunixtime = int(calendar.timegm(timetuple))
1099 1089 if offset is None:
1100 1090 # local timezone
1101 1091 unixtime = int(time.mktime(timetuple))
1102 1092 offset = unixtime - localunixtime
1103 1093 else:
1104 1094 unixtime = localunixtime + offset
1105 1095 return unixtime, offset
1106 1096
1107 1097 def parsedate(date, formats=None, bias={}):
1108 1098 """parse a localized date/time and return a (unixtime, offset) tuple.
1109 1099
1110 1100 The date may be a "unixtime offset" string or in one of the specified
1111 1101 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1112 1102 """
1113 1103 if not date:
1114 1104 return 0, 0
1115 1105 if isinstance(date, tuple) and len(date) == 2:
1116 1106 return date
1117 1107 if not formats:
1118 1108 formats = defaultdateformats
1119 1109 date = date.strip()
1120 1110 try:
1121 1111 when, offset = map(int, date.split(' '))
1122 1112 except ValueError:
1123 1113 # fill out defaults
1124 1114 now = makedate()
1125 1115 defaults = {}
1126 1116 nowmap = {}
1127 1117 for part in ("d", "mb", "yY", "HI", "M", "S"):
1128 1118 # this piece is for rounding the specific end of unknowns
1129 1119 b = bias.get(part)
1130 1120 if b is None:
1131 1121 if part[0] in "HMS":
1132 1122 b = "00"
1133 1123 else:
1134 1124 b = "0"
1135 1125
1136 1126 # this piece is for matching the generic end to today's date
1137 1127 n = datestr(now, "%" + part[0])
1138 1128
1139 1129 defaults[part] = (b, n)
1140 1130
1141 1131 for format in formats:
1142 1132 try:
1143 1133 when, offset = strdate(date, format, defaults)
1144 1134 except (ValueError, OverflowError):
1145 1135 pass
1146 1136 else:
1147 1137 break
1148 1138 else:
1149 1139 raise Abort(_('invalid date: %r') % date)
1150 1140 # validate explicit (probably user-specified) date and
1151 1141 # time zone offset. values must fit in signed 32 bits for
1152 1142 # current 32-bit linux runtimes. timezones go from UTC-12
1153 1143 # to UTC+14
1154 1144 if abs(when) > 0x7fffffff:
1155 1145 raise Abort(_('date exceeds 32 bits: %d') % when)
1156 1146 if when < 0:
1157 1147 raise Abort(_('negative date value: %d') % when)
1158 1148 if offset < -50400 or offset > 43200:
1159 1149 raise Abort(_('impossible time zone offset: %d') % offset)
1160 1150 return when, offset
1161 1151
1162 1152 def matchdate(date):
1163 1153 """Return a function that matches a given date match specifier
1164 1154
1165 1155 Formats include:
1166 1156
1167 1157 '{date}' match a given date to the accuracy provided
1168 1158
1169 1159 '<{date}' on or before a given date
1170 1160
1171 1161 '>{date}' on or after a given date
1172 1162
1173 1163 >>> p1 = parsedate("10:29:59")
1174 1164 >>> p2 = parsedate("10:30:00")
1175 1165 >>> p3 = parsedate("10:30:59")
1176 1166 >>> p4 = parsedate("10:31:00")
1177 1167 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1178 1168 >>> f = matchdate("10:30")
1179 1169 >>> f(p1[0])
1180 1170 False
1181 1171 >>> f(p2[0])
1182 1172 True
1183 1173 >>> f(p3[0])
1184 1174 True
1185 1175 >>> f(p4[0])
1186 1176 False
1187 1177 >>> f(p5[0])
1188 1178 False
1189 1179 """
1190 1180
1191 1181 def lower(date):
1192 1182 d = dict(mb="1", d="1")
1193 1183 return parsedate(date, extendeddateformats, d)[0]
1194 1184
1195 1185 def upper(date):
1196 1186 d = dict(mb="12", HI="23", M="59", S="59")
1197 1187 for days in ("31", "30", "29"):
1198 1188 try:
1199 1189 d["d"] = days
1200 1190 return parsedate(date, extendeddateformats, d)[0]
1201 1191 except:
1202 1192 pass
1203 1193 d["d"] = "28"
1204 1194 return parsedate(date, extendeddateformats, d)[0]
1205 1195
1206 1196 date = date.strip()
1207 1197 if date[0] == "<":
1208 1198 when = upper(date[1:])
1209 1199 return lambda x: x <= when
1210 1200 elif date[0] == ">":
1211 1201 when = lower(date[1:])
1212 1202 return lambda x: x >= when
1213 1203 elif date[0] == "-":
1214 1204 try:
1215 1205 days = int(date[1:])
1216 1206 except ValueError:
1217 1207 raise Abort(_("invalid day spec: %s") % date[1:])
1218 1208 when = makedate()[0] - days * 3600 * 24
1219 1209 return lambda x: x >= when
1220 1210 elif " to " in date:
1221 1211 a, b = date.split(" to ")
1222 1212 start, stop = lower(a), upper(b)
1223 1213 return lambda x: x >= start and x <= stop
1224 1214 else:
1225 1215 start, stop = lower(date), upper(date)
1226 1216 return lambda x: x >= start and x <= stop
1227 1217
1228 1218 def shortuser(user):
1229 1219 """Return a short representation of a user name or email address."""
1230 1220 f = user.find('@')
1231 1221 if f >= 0:
1232 1222 user = user[:f]
1233 1223 f = user.find('<')
1234 1224 if f >= 0:
1235 1225 user = user[f + 1:]
1236 1226 f = user.find(' ')
1237 1227 if f >= 0:
1238 1228 user = user[:f]
1239 1229 f = user.find('.')
1240 1230 if f >= 0:
1241 1231 user = user[:f]
1242 1232 return user
1243 1233
1244 1234 def email(author):
1245 1235 '''get email of author.'''
1246 1236 r = author.find('>')
1247 1237 if r == -1:
1248 1238 r = None
1249 1239 return author[author.find('<') + 1:r]
1250 1240
1251 1241 def _ellipsis(text, maxlength):
1252 1242 if len(text) <= maxlength:
1253 1243 return text, False
1254 1244 else:
1255 1245 return "%s..." % (text[:maxlength - 3]), True
1256 1246
1257 1247 def ellipsis(text, maxlength=400):
1258 1248 """Trim string to at most maxlength (default: 400) characters."""
1259 1249 try:
1260 1250 # use unicode not to split at intermediate multi-byte sequence
1261 1251 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1262 1252 maxlength)
1263 1253 if not truncated:
1264 1254 return text
1265 1255 return utext.encode(encoding.encoding)
1266 1256 except (UnicodeDecodeError, UnicodeEncodeError):
1267 1257 return _ellipsis(text, maxlength)[0]
1268 1258
1269 1259 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1270 1260 '''yield every hg repository under path, recursively.'''
1271 1261 def errhandler(err):
1272 1262 if err.filename == path:
1273 1263 raise err
1274 1264 if followsym and hasattr(os.path, 'samestat'):
1275 1265 def _add_dir_if_not_there(dirlst, dirname):
1276 1266 match = False
1277 1267 samestat = os.path.samestat
1278 1268 dirstat = os.stat(dirname)
1279 1269 for lstdirstat in dirlst:
1280 1270 if samestat(dirstat, lstdirstat):
1281 1271 match = True
1282 1272 break
1283 1273 if not match:
1284 1274 dirlst.append(dirstat)
1285 1275 return not match
1286 1276 else:
1287 1277 followsym = False
1288 1278
1289 1279 if (seen_dirs is None) and followsym:
1290 1280 seen_dirs = []
1291 1281 _add_dir_if_not_there(seen_dirs, path)
1292 1282 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1293 1283 dirs.sort()
1294 1284 if '.hg' in dirs:
1295 1285 yield root # found a repository
1296 1286 qroot = os.path.join(root, '.hg', 'patches')
1297 1287 if os.path.isdir(os.path.join(qroot, '.hg')):
1298 1288 yield qroot # we have a patch queue repo here
1299 1289 if recurse:
1300 1290 # avoid recursing inside the .hg directory
1301 1291 dirs.remove('.hg')
1302 1292 else:
1303 1293 dirs[:] = [] # don't descend further
1304 1294 elif followsym:
1305 1295 newdirs = []
1306 1296 for d in dirs:
1307 1297 fname = os.path.join(root, d)
1308 1298 if _add_dir_if_not_there(seen_dirs, fname):
1309 1299 if os.path.islink(fname):
1310 1300 for hgname in walkrepos(fname, True, seen_dirs):
1311 1301 yield hgname
1312 1302 else:
1313 1303 newdirs.append(d)
1314 1304 dirs[:] = newdirs
1315 1305
1316 1306 _rcpath = None
1317 1307
1318 1308 def os_rcpath():
1319 1309 '''return default os-specific hgrc search path'''
1320 1310 path = system_rcpath()
1321 1311 path.extend(user_rcpath())
1322 1312 path = [os.path.normpath(f) for f in path]
1323 1313 return path
1324 1314
1325 1315 def rcpath():
1326 1316 '''return hgrc search path. if env var HGRCPATH is set, use it.
1327 1317 for each item in path, if directory, use files ending in .rc,
1328 1318 else use item.
1329 1319 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1330 1320 if no HGRCPATH, use default os-specific path.'''
1331 1321 global _rcpath
1332 1322 if _rcpath is None:
1333 1323 if 'HGRCPATH' in os.environ:
1334 1324 _rcpath = []
1335 1325 for p in os.environ['HGRCPATH'].split(os.pathsep):
1336 1326 if not p:
1337 1327 continue
1338 1328 p = expandpath(p)
1339 1329 if os.path.isdir(p):
1340 1330 for f, kind in osutil.listdir(p):
1341 1331 if f.endswith('.rc'):
1342 1332 _rcpath.append(os.path.join(p, f))
1343 1333 else:
1344 1334 _rcpath.append(p)
1345 1335 else:
1346 1336 _rcpath = os_rcpath()
1347 1337 return _rcpath
1348 1338
1349 1339 def bytecount(nbytes):
1350 1340 '''return byte count formatted as readable string, with units'''
1351 1341
1352 1342 units = (
1353 1343 (100, 1 << 30, _('%.0f GB')),
1354 1344 (10, 1 << 30, _('%.1f GB')),
1355 1345 (1, 1 << 30, _('%.2f GB')),
1356 1346 (100, 1 << 20, _('%.0f MB')),
1357 1347 (10, 1 << 20, _('%.1f MB')),
1358 1348 (1, 1 << 20, _('%.2f MB')),
1359 1349 (100, 1 << 10, _('%.0f KB')),
1360 1350 (10, 1 << 10, _('%.1f KB')),
1361 1351 (1, 1 << 10, _('%.2f KB')),
1362 1352 (1, 1, _('%.0f bytes')),
1363 1353 )
1364 1354
1365 1355 for multiplier, divisor, format in units:
1366 1356 if nbytes >= divisor * multiplier:
1367 1357 return format % (nbytes / float(divisor))
1368 1358 return units[-1][2] % nbytes
1369 1359
1370 1360 def drop_scheme(scheme, path):
1371 1361 sc = scheme + ':'
1372 1362 if path.startswith(sc):
1373 1363 path = path[len(sc):]
1374 1364 if path.startswith('//'):
1375 1365 if scheme == 'file':
1376 1366 i = path.find('/', 2)
1377 1367 if i == -1:
1378 1368 return ''
1379 1369 # On Windows, absolute paths are rooted at the current drive
1380 1370 # root. On POSIX they are rooted at the file system root.
1381 1371 if os.name == 'nt':
1382 1372 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1383 1373 path = os.path.join(droot, path[i + 1:])
1384 1374 else:
1385 1375 path = path[i:]
1386 1376 else:
1387 1377 path = path[2:]
1388 1378 return path
1389 1379
1390 1380 def uirepr(s):
1391 1381 # Avoid double backslash in Windows path repr()
1392 1382 return repr(s).replace('\\\\', '\\')
1393 1383
1394 1384 # delay import of textwrap
1395 1385 def MBTextWrapper(**kwargs):
1396 1386 class tw(textwrap.TextWrapper):
1397 1387 """
1398 1388 Extend TextWrapper for double-width characters.
1399 1389
1400 1390 Some Asian characters use two terminal columns instead of one.
1401 1391 A good example of this behavior can be seen with u'\u65e5\u672c',
1402 1392 the two Japanese characters for "Japan":
1403 1393 len() returns 2, but when printed to a terminal, they eat 4 columns.
1404 1394
1405 1395 (Note that this has nothing to do whatsoever with unicode
1406 1396 representation, or encoding of the underlying string)
1407 1397 """
1408 1398 def __init__(self, **kwargs):
1409 1399 textwrap.TextWrapper.__init__(self, **kwargs)
1410 1400
1411 1401 def _cutdown(self, str, space_left):
1412 1402 l = 0
1413 1403 ucstr = unicode(str, encoding.encoding)
1414 1404 colwidth = unicodedata.east_asian_width
1415 1405 for i in xrange(len(ucstr)):
1416 1406 l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
1417 1407 if space_left < l:
1418 1408 return (ucstr[:i].encode(encoding.encoding),
1419 1409 ucstr[i:].encode(encoding.encoding))
1420 1410 return str, ''
1421 1411
1422 1412 # overriding of base class
1423 1413 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1424 1414 space_left = max(width - cur_len, 1)
1425 1415
1426 1416 if self.break_long_words:
1427 1417 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1428 1418 cur_line.append(cut)
1429 1419 reversed_chunks[-1] = res
1430 1420 elif not cur_line:
1431 1421 cur_line.append(reversed_chunks.pop())
1432 1422
1433 1423 global MBTextWrapper
1434 1424 MBTextWrapper = tw
1435 1425 return tw(**kwargs)
1436 1426
1437 1427 def wrap(line, width, initindent='', hangindent=''):
1438 1428 maxindent = max(len(hangindent), len(initindent))
1439 1429 if width <= maxindent:
1440 1430 # adjust for weird terminal size
1441 1431 width = max(78, maxindent + 1)
1442 1432 wrapper = MBTextWrapper(width=width,
1443 1433 initial_indent=initindent,
1444 1434 subsequent_indent=hangindent)
1445 1435 return wrapper.fill(line)
1446 1436
1447 1437 def iterlines(iterator):
1448 1438 for chunk in iterator:
1449 1439 for line in chunk.splitlines():
1450 1440 yield line
1451 1441
1452 1442 def expandpath(path):
1453 1443 return os.path.expanduser(os.path.expandvars(path))
1454 1444
1455 1445 def hgcmd():
1456 1446 """Return the command used to execute current hg
1457 1447
1458 1448 This is different from hgexecutable() because on Windows we want
1459 1449 to avoid things opening new shell windows like batch files, so we
1460 1450 get either the python call or current executable.
1461 1451 """
1462 1452 if main_is_frozen():
1463 1453 return [sys.executable]
1464 1454 return gethgcmd()
1465 1455
1466 1456 def rundetached(args, condfn):
1467 1457 """Execute the argument list in a detached process.
1468 1458
1469 1459 condfn is a callable which is called repeatedly and should return
1470 1460 True once the child process is known to have started successfully.
1471 1461 At this point, the child process PID is returned. If the child
1472 1462 process fails to start or finishes before condfn() evaluates to
1473 1463 True, return -1.
1474 1464 """
1475 1465 # Windows case is easier because the child process is either
1476 1466 # successfully starting and validating the condition or exiting
1477 1467 # on failure. We just poll on its PID. On Unix, if the child
1478 1468 # process fails to start, it will be left in a zombie state until
1479 1469 # the parent wait on it, which we cannot do since we expect a long
1480 1470 # running process on success. Instead we listen for SIGCHLD telling
1481 1471 # us our child process terminated.
1482 1472 terminated = set()
1483 1473 def handler(signum, frame):
1484 1474 terminated.add(os.wait())
1485 1475 prevhandler = None
1486 1476 if hasattr(signal, 'SIGCHLD'):
1487 1477 prevhandler = signal.signal(signal.SIGCHLD, handler)
1488 1478 try:
1489 1479 pid = spawndetached(args)
1490 1480 while not condfn():
1491 1481 if ((pid in terminated or not testpid(pid))
1492 1482 and not condfn()):
1493 1483 return -1
1494 1484 time.sleep(0.1)
1495 1485 return pid
1496 1486 finally:
1497 1487 if prevhandler is not None:
1498 1488 signal.signal(signal.SIGCHLD, prevhandler)
1499 1489
1500 1490 try:
1501 1491 any, all = any, all
1502 1492 except NameError:
1503 1493 def any(iterable):
1504 1494 for i in iterable:
1505 1495 if i:
1506 1496 return True
1507 1497 return False
1508 1498
1509 1499 def all(iterable):
1510 1500 for i in iterable:
1511 1501 if not i:
1512 1502 return False
1513 1503 return True
1514 1504
1515 1505 def interpolate(prefix, mapping, s, fn=None):
1516 1506 """Return the result of interpolating items in the mapping into string s.
1517 1507
1518 1508 prefix is a single character string, or a two character string with
1519 1509 a backslash as the first character if the prefix needs to be escaped in
1520 1510 a regular expression.
1521 1511
1522 1512 fn is an optional function that will be applied to the replacement text
1523 1513 just before replacement.
1524 1514 """
1525 1515 fn = fn or (lambda s: s)
1526 1516 r = re.compile(r'%s(%s)' % (prefix, '|'.join(mapping.keys())))
1527 1517 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1528 1518
1529 1519 def getport(port):
1530 1520 """Return the port for a given network service.
1531 1521
1532 1522 If port is an integer, it's returned as is. If it's a string, it's
1533 1523 looked up using socket.getservbyname(). If there's no matching
1534 1524 service, util.Abort is raised.
1535 1525 """
1536 1526 try:
1537 1527 return int(port)
1538 1528 except ValueError:
1539 1529 pass
1540 1530
1541 1531 try:
1542 1532 return socket.getservbyname(port)
1543 1533 except socket.error:
1544 1534 raise Abort(_("no port number associated with service '%s'") % port)
1545 1535
1546 1536 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1547 1537 '0': False, 'no': False, 'false': False, 'off': False,
1548 1538 'never': False}
1549 1539
1550 1540 def parsebool(s):
1551 1541 """Parse s into a boolean.
1552 1542
1553 1543 If s is not a valid boolean, returns None.
1554 1544 """
1555 1545 return _booleans.get(s.lower(), None)
@@ -1,183 +1,350 b''
1 1 # win32.py - utility functions that use win32 API
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 """Utility functions that use win32 API.
8 import osutil, encoding
9 import ctypes, errno, os, struct, subprocess
10
11 _kernel32 = ctypes.windll.kernel32
12
13 _BOOL = ctypes.c_long
14 _WORD = ctypes.c_ushort
15 _DWORD = ctypes.c_ulong
16 _LPCSTR = _LPSTR = ctypes.c_char_p
17 _HANDLE = ctypes.c_void_p
18 _HWND = _HANDLE
19
20 _INVALID_HANDLE_VALUE = -1
21
22 # GetLastError
23 _ERROR_SUCCESS = 0
24 _ERROR_INVALID_PARAMETER = 87
25 _ERROR_INSUFFICIENT_BUFFER = 122
26
27 # WPARAM is defined as UINT_PTR (unsigned type)
28 # LPARAM is defined as LONG_PTR (signed type)
29 if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
30 _WPARAM = ctypes.c_ulong
31 _LPARAM = ctypes.c_long
32 elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
33 _WPARAM = ctypes.c_ulonglong
34 _LPARAM = ctypes.c_longlong
35
36 class _FILETIME(ctypes.Structure):
37 _fields_ = [('dwLowDateTime', _DWORD),
38 ('dwHighDateTime', _DWORD)]
39
40 class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
41 _fields_ = [('dwFileAttributes', _DWORD),
42 ('ftCreationTime', _FILETIME),
43 ('ftLastAccessTime', _FILETIME),
44 ('ftLastWriteTime', _FILETIME),
45 ('dwVolumeSerialNumber', _DWORD),
46 ('nFileSizeHigh', _DWORD),
47 ('nFileSizeLow', _DWORD),
48 ('nNumberOfLinks', _DWORD),
49 ('nFileIndexHigh', _DWORD),
50 ('nFileIndexLow', _DWORD)]
51
52 # CreateFile
53 _FILE_SHARE_READ = 0x00000001
54 _FILE_SHARE_WRITE = 0x00000002
55 _FILE_SHARE_DELETE = 0x00000004
56
57 _OPEN_EXISTING = 3
58
59 # Process Security and Access Rights
60 _PROCESS_QUERY_INFORMATION = 0x0400
61
62 # GetExitCodeProcess
63 _STILL_ACTIVE = 259
64
65 # registry
66 _HKEY_CURRENT_USER = 0x80000001L
67 _HKEY_LOCAL_MACHINE = 0x80000002L
68 _KEY_READ = 0x20019
69 _REG_SZ = 1
70 _REG_DWORD = 4
9 71
10 Mark Hammond's win32all package allows better functionality on
11 Windows. This module overrides definitions in util.py. If not
12 available, import of this module will fail, and generic code will be
13 used.
14 """
72 class _STARTUPINFO(ctypes.Structure):
73 _fields_ = [('cb', _DWORD),
74 ('lpReserved', _LPSTR),
75 ('lpDesktop', _LPSTR),
76 ('lpTitle', _LPSTR),
77 ('dwX', _DWORD),
78 ('dwY', _DWORD),
79 ('dwXSize', _DWORD),
80 ('dwYSize', _DWORD),
81 ('dwXCountChars', _DWORD),
82 ('dwYCountChars', _DWORD),
83 ('dwFillAttribute', _DWORD),
84 ('dwFlags', _DWORD),
85 ('wShowWindow', _WORD),
86 ('cbReserved2', _WORD),
87 ('lpReserved2', ctypes.c_char_p),
88 ('hStdInput', _HANDLE),
89 ('hStdOutput', _HANDLE),
90 ('hStdError', _HANDLE)]
91
92 class _PROCESS_INFORMATION(ctypes.Structure):
93 _fields_ = [('hProcess', _HANDLE),
94 ('hThread', _HANDLE),
95 ('dwProcessId', _DWORD),
96 ('dwThreadId', _DWORD)]
97
98 _DETACHED_PROCESS = 0x00000008
99 _STARTF_USESHOWWINDOW = 0x00000001
100 _SW_HIDE = 0
15 101
16 import win32api
102 class _COORD(ctypes.Structure):
103 _fields_ = [('X', ctypes.c_short),
104 ('Y', ctypes.c_short)]
105
106 class _SMALL_RECT(ctypes.Structure):
107 _fields_ = [('Left', ctypes.c_short),
108 ('Top', ctypes.c_short),
109 ('Right', ctypes.c_short),
110 ('Bottom', ctypes.c_short)]
111
112 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
113 _fields_ = [('dwSize', _COORD),
114 ('dwCursorPosition', _COORD),
115 ('wAttributes', _WORD),
116 ('srWindow', _SMALL_RECT),
117 ('dwMaximumWindowSize', _COORD)]
17 118
18 import errno, os, sys, pywintypes, win32con, win32file, win32process
19 import winerror, win32gui, win32console
20 import osutil, encoding
21 from win32com.shell import shell, shellcon
119 _STD_ERROR_HANDLE = 0xfffffff4L # (DWORD)-12
120
121 def _raiseoserror(name):
122 err = ctypes.WinError()
123 raise OSError(err.errno, '%s: %s' % (name, err.strerror))
124
125 def _getfileinfo(name):
126 fh = _kernel32.CreateFileA(name, 0,
127 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
128 None, _OPEN_EXISTING, 0, None)
129 if fh == _INVALID_HANDLE_VALUE:
130 _raiseoserror(name)
131 try:
132 fi = _BY_HANDLE_FILE_INFORMATION()
133 if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
134 _raiseoserror(name)
135 return fi
136 finally:
137 _kernel32.CloseHandle(fh)
22 138
23 139 def os_link(src, dst):
24 try:
25 win32file.CreateHardLink(dst, src)
26 except pywintypes.error:
27 raise OSError(errno.EINVAL, 'target implements hardlinks improperly')
28 except NotImplementedError: # Another fake error win Win98
29 raise OSError(errno.EINVAL, 'Hardlinking not supported')
140 if not _kernel32.CreateHardLinkA(dst, src, None):
141 _raiseoserror(src)
30 142
31 def _getfileinfo(pathname):
32 """Return number of hardlinks for the given file."""
33 try:
34 fh = win32file.CreateFile(pathname, 0,
35 win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE |
36 win32file.FILE_SHARE_DELETE,
37 None, win32file.OPEN_EXISTING, 0, None)
38 except pywintypes.error:
39 raise OSError(errno.ENOENT, 'The system cannot find the file specified')
40 try:
41 return win32file.GetFileInformationByHandle(fh)
42 finally:
43 fh.Close()
44
45 def nlinks(pathname):
46 """Return number of hardlinks for the given file."""
47 return _getfileinfo(pathname)[7]
143 def nlinks(name):
144 '''return number of hardlinks for the given file'''
145 return _getfileinfo(name).nNumberOfLinks
48 146
49 147 def samefile(fpath1, fpath2):
50 """Returns whether fpath1 and fpath2 refer to the same file. This is only
51 guaranteed to work for files, not directories."""
148 '''Returns whether fpath1 and fpath2 refer to the same file. This is only
149 guaranteed to work for files, not directories.'''
52 150 res1 = _getfileinfo(fpath1)
53 151 res2 = _getfileinfo(fpath2)
54 # Index 4 is the volume serial number, and 8 and 9 contain the file ID
55 return res1[4] == res2[4] and res1[8] == res2[8] and res1[9] == res2[9]
152 return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
153 and res1.nFileIndexHigh == res2.nFileIndexHigh
154 and res1.nFileIndexLow == res2.nFileIndexLow)
56 155
57 156 def samedevice(fpath1, fpath2):
58 """Returns whether fpath1 and fpath2 are on the same device. This is only
59 guaranteed to work for files, not directories."""
157 '''Returns whether fpath1 and fpath2 are on the same device. This is only
158 guaranteed to work for files, not directories.'''
60 159 res1 = _getfileinfo(fpath1)
61 160 res2 = _getfileinfo(fpath2)
62 return res1[4] == res2[4]
161 return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
63 162
64 163 def testpid(pid):
65 164 '''return True if pid is still running or unable to
66 165 determine, False otherwise'''
67 try:
68 handle = win32api.OpenProcess(
69 win32con.PROCESS_QUERY_INFORMATION, False, pid)
70 if handle:
71 status = win32process.GetExitCodeProcess(handle)
72 return status == win32con.STILL_ACTIVE
73 except pywintypes.error, details:
74 return details[0] != winerror.ERROR_INVALID_PARAMETER
75 return True
166 h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
167 if h:
168 try:
169 status = _DWORD()
170 if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
171 return status.value == _STILL_ACTIVE
172 finally:
173 _kernel32.CloseHandle(h)
174 return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
76 175
77 176 def lookup_reg(key, valname=None, scope=None):
78 177 ''' Look up a key/value name in the Windows registry.
79 178
80 179 valname: value name. If unspecified, the default value for the key
81 180 is used.
82 181 scope: optionally specify scope for registry lookup, this can be
83 182 a sequence of scopes to look up in order. Default (CURRENT_USER,
84 183 LOCAL_MACHINE).
85 184 '''
86 try:
87 from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, \
88 QueryValueEx, OpenKey
89 except ImportError:
90 return None
91
185 adv = ctypes.windll.advapi32
186 byref = ctypes.byref
92 187 if scope is None:
93 scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE)
188 scope = (_HKEY_CURRENT_USER, _HKEY_LOCAL_MACHINE)
94 189 elif not isinstance(scope, (list, tuple)):
95 190 scope = (scope,)
96 191 for s in scope:
192 kh = _HANDLE()
193 res = adv.RegOpenKeyExA(s, key, 0, _KEY_READ, ctypes.byref(kh))
194 if res != _ERROR_SUCCESS:
195 continue
97 196 try:
98 val = QueryValueEx(OpenKey(s, key), valname)[0]
99 # never let a Unicode string escape into the wild
100 return encoding.tolocal(val.encode('UTF-8'))
101 except EnvironmentError:
102 pass
197 size = _DWORD(600)
198 type = _DWORD()
199 buf = ctypes.create_string_buffer(size.value + 1)
200 res = adv.RegQueryValueExA(kh.value, valname, None,
201 byref(type), buf, byref(size))
202 if res != _ERROR_SUCCESS:
203 continue
204 if type.value == _REG_SZ:
205 # never let a Unicode string escape into the wild
206 return encoding.tolocal(buf.value.encode('UTF-8'))
207 elif type.value == _REG_DWORD:
208 fmt = '<L'
209 s = ctypes.string_at(byref(buf), struct.calcsize(fmt))
210 return struct.unpack(fmt, s)[0]
211 finally:
212 adv.RegCloseKey(kh.value)
103 213
104 214 def system_rcpath_win32():
105 215 '''return default os-specific hgrc search path'''
106 filename = win32api.GetModuleFileName(0)
216 rcpath = []
217 size = 600
218 buf = ctypes.create_string_buffer(size + 1)
219 len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
220 if len == 0:
221 raise ctypes.WinError()
222 elif len == size:
223 raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
224 filename = buf.value
107 225 # Use mercurial.ini found in directory with hg.exe
108 226 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
109 227 if os.path.isfile(progrc):
110 return [progrc]
228 rcpath.append(progrc)
229 return rcpath
111 230 # Use hgrc.d found in directory with hg.exe
112 231 progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
113 232 if os.path.isdir(progrcd):
114 rcpath = []
115 233 for f, kind in osutil.listdir(progrcd):
116 234 if f.endswith('.rc'):
117 235 rcpath.append(os.path.join(progrcd, f))
118 236 return rcpath
119 237 # else look for a system rcpath in the registry
120 try:
121 value = win32api.RegQueryValue(
122 win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial')
123 rcpath = []
124 for p in value.split(os.pathsep):
125 if p.lower().endswith('mercurial.ini'):
126 rcpath.append(p)
127 elif os.path.isdir(p):
128 for f, kind in osutil.listdir(p):
129 if f.endswith('.rc'):
130 rcpath.append(os.path.join(p, f))
238 value = lookup_reg('SOFTWARE\\Mercurial', None, _HKEY_LOCAL_MACHINE)
239 if not isinstance(value, str) or not value:
131 240 return rcpath
132 except pywintypes.error:
133 return []
241 value = value.replace('/', os.sep)
242 for p in value.split(os.pathsep):
243 if p.lower().endswith('mercurial.ini'):
244 rcpath.append(p)
245 elif os.path.isdir(p):
246 for f, kind in osutil.listdir(p):
247 if f.endswith('.rc'):
248 rcpath.append(os.path.join(p, f))
249 return rcpath
134 250
135 251 def user_rcpath_win32():
136 252 '''return os-specific hgrc search path to the user dir'''
137 253 userdir = os.path.expanduser('~')
138 if sys.getwindowsversion()[3] != 2 and userdir == '~':
139 # We are on win < nt: fetch the APPDATA directory location and use
140 # the parent directory as the user home dir.
141 appdir = shell.SHGetPathFromIDList(
142 shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
143 userdir = os.path.dirname(appdir)
144 254 return [os.path.join(userdir, 'mercurial.ini'),
145 255 os.path.join(userdir, '.hgrc')]
146 256
147 257 def getuser():
148 258 '''return name of current user'''
149 return win32api.GetUserName()
259 adv = ctypes.windll.advapi32
260 size = _DWORD(300)
261 buf = ctypes.create_string_buffer(size.value + 1)
262 if not adv.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
263 raise ctypes.WinError()
264 return buf.value
150 265
151 def set_signal_handler_win32():
152 """Register a termination handler for console events including
266 _SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
267 _signal_handler = []
268
269 def set_signal_handler():
270 '''Register a termination handler for console events including
153 271 CTRL+C. python signal handlers do not work well with socket
154 272 operations.
155 """
273 '''
156 274 def handler(event):
157 win32process.ExitProcess(1)
158 win32api.SetConsoleCtrlHandler(handler)
275 _kernel32.ExitProcess(1)
276
277 if _signal_handler:
278 return # already registered
279 h = _SIGNAL_HANDLER(handler)
280 _signal_handler.append(h) # needed to prevent garbage collection
281 if not _kernel32.SetConsoleCtrlHandler(h, True):
282 raise ctypes.WinError()
283
284 _WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
159 285
160 286 def hidewindow():
161 def callback(*args, **kwargs):
162 hwnd, pid = args
163 wpid = win32process.GetWindowThreadProcessId(hwnd)[1]
164 if pid == wpid:
165 win32gui.ShowWindow(hwnd, win32con.SW_HIDE)
287 user32 = ctypes.windll.user32
166 288
167 pid = win32process.GetCurrentProcessId()
168 win32gui.EnumWindows(callback, pid)
289 def callback(hwnd, pid):
290 wpid = _DWORD()
291 user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
292 if pid == wpid.value:
293 user32.ShowWindow(hwnd, _SW_HIDE)
294 return False # stop enumerating windows
295 return True
296
297 pid = _kernel32.GetCurrentProcessId()
298 user32.EnumWindows(_WNDENUMPROC(callback), pid)
169 299
170 300 def termwidth():
171 try:
172 # Query stderr to avoid problems with redirections
173 screenbuf = win32console.GetStdHandle(win32console.STD_ERROR_HANDLE)
174 if screenbuf is None:
175 return 79
176 try:
177 window = screenbuf.GetConsoleScreenBufferInfo()['Window']
178 width = window.Right - window.Left
179 return width
180 finally:
181 screenbuf.Detach()
182 except pywintypes.error:
183 return 79
301 # cmd.exe does not handle CR like a unix console, the CR is
302 # counted in the line length. On 80 columns consoles, if 80
303 # characters are written, the following CR won't apply on the
304 # current line but on the new one. Keep room for it.
305 width = 79
306 # Query stderr to avoid problems with redirections
307 screenbuf = _kernel32.GetStdHandle(
308 _STD_ERROR_HANDLE) # don't close the handle returned
309 if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
310 return width
311 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
312 if not _kernel32.GetConsoleScreenBufferInfo(
313 screenbuf, ctypes.byref(csbi)):
314 return width
315 width = csbi.srWindow.Right - csbi.srWindow.Left
316 return width
317
318 def spawndetached(args):
319 # No standard library function really spawns a fully detached
320 # process under win32 because they allocate pipes or other objects
321 # to handle standard streams communications. Passing these objects
322 # to the child process requires handle inheritance to be enabled
323 # which makes really detached processes impossible.
324 si = _STARTUPINFO()
325 si.cb = ctypes.sizeof(_STARTUPINFO)
326 si.dwFlags = _STARTF_USESHOWWINDOW
327 si.wShowWindow = _SW_HIDE
328
329 pi = _PROCESS_INFORMATION()
330
331 env = ''
332 for k in os.environ:
333 env += "%s=%s\0" % (k, os.environ[k])
334 if not env:
335 env = '\0'
336 env += '\0'
337
338 args = subprocess.list2cmdline(args)
339 # Not running the command in shell mode makes python26 hang when
340 # writing to hgweb output socket.
341 comspec = os.environ.get("COMSPEC", "cmd.exe")
342 args = comspec + " /c " + args
343
344 res = _kernel32.CreateProcessA(
345 None, args, None, None, False, _DETACHED_PROCESS,
346 env, os.getcwd(), ctypes.byref(si), ctypes.byref(pi))
347 if not res:
348 raise ctypes.WinError()
349
350 return pi.dwProcessId
@@ -1,389 +1,339 b''
1 1 # windows.py - Windows utility function implementations for Mercurial
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import osutil, error
10 10 import errno, msvcrt, os, re, sys, random, subprocess
11 11
12 12 nulldev = 'NUL:'
13 13 umask = 002
14 14
15 15 # wrap osutil.posixfile to provide friendlier exceptions
16 16 def posixfile(name, mode='r', buffering=-1):
17 17 try:
18 18 return osutil.posixfile(name, mode, buffering)
19 19 except WindowsError, err:
20 20 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
21 21 posixfile.__doc__ = osutil.posixfile.__doc__
22 22
23 23 class winstdout(object):
24 24 '''stdout on windows misbehaves if sent through a pipe'''
25 25
26 26 def __init__(self, fp):
27 27 self.fp = fp
28 28
29 29 def __getattr__(self, key):
30 30 return getattr(self.fp, key)
31 31
32 32 def close(self):
33 33 try:
34 34 self.fp.close()
35 35 except: pass
36 36
37 37 def write(self, s):
38 38 try:
39 39 # This is workaround for "Not enough space" error on
40 40 # writing large size of data to console.
41 41 limit = 16000
42 42 l = len(s)
43 43 start = 0
44 44 self.softspace = 0
45 45 while start < l:
46 46 end = start + limit
47 47 self.fp.write(s[start:end])
48 48 start = end
49 49 except IOError, inst:
50 50 if inst.errno != 0:
51 51 raise
52 52 self.close()
53 53 raise IOError(errno.EPIPE, 'Broken pipe')
54 54
55 55 def flush(self):
56 56 try:
57 57 return self.fp.flush()
58 58 except IOError, inst:
59 59 if inst.errno != errno.EINVAL:
60 60 raise
61 61 self.close()
62 62 raise IOError(errno.EPIPE, 'Broken pipe')
63 63
64 64 sys.stdout = winstdout(sys.stdout)
65 65
66 66 def _is_win_9x():
67 67 '''return true if run on windows 95, 98 or me.'''
68 68 try:
69 69 return sys.getwindowsversion()[3] == 1
70 70 except AttributeError:
71 71 return 'command' in os.environ.get('comspec', '')
72 72
73 73 def openhardlinks():
74 return not _is_win_9x() and "win32api" in globals()
74 return not _is_win_9x()
75 75
76 76 def system_rcpath():
77 77 try:
78 78 return system_rcpath_win32()
79 79 except:
80 80 return [r'c:\mercurial\mercurial.ini']
81 81
82 82 def user_rcpath():
83 83 '''return os-specific hgrc search path to the user dir'''
84 84 try:
85 85 path = user_rcpath_win32()
86 86 except:
87 87 home = os.path.expanduser('~')
88 88 path = [os.path.join(home, 'mercurial.ini'),
89 89 os.path.join(home, '.hgrc')]
90 90 userprofile = os.environ.get('USERPROFILE')
91 91 if userprofile:
92 92 path.append(os.path.join(userprofile, 'mercurial.ini'))
93 93 path.append(os.path.join(userprofile, '.hgrc'))
94 94 return path
95 95
96 96 def parse_patch_output(output_line):
97 97 """parses the output produced by patch and returns the filename"""
98 98 pf = output_line[14:]
99 99 if pf[0] == '`':
100 100 pf = pf[1:-1] # Remove the quotes
101 101 return pf
102 102
103 103 def sshargs(sshcmd, host, user, port):
104 104 '''Build argument list for ssh or Plink'''
105 105 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
106 106 args = user and ("%s@%s" % (user, host)) or host
107 107 return port and ("%s %s %s" % (args, pflag, port)) or args
108 108
109 def testpid(pid):
110 '''return False if pid dead, True if running or not known'''
111 return True
112
113 109 def set_flags(f, l, x):
114 110 pass
115 111
116 112 def set_binary(fd):
117 113 # When run without console, pipes may expose invalid
118 114 # fileno(), usually set to -1.
119 115 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
120 116 msvcrt.setmode(fd.fileno(), os.O_BINARY)
121 117
122 118 def pconvert(path):
123 119 return '/'.join(path.split(os.sep))
124 120
125 121 def localpath(path):
126 122 return path.replace('/', '\\')
127 123
128 124 def normpath(path):
129 125 return pconvert(os.path.normpath(path))
130 126
131 127 def realpath(path):
132 128 '''
133 129 Returns the true, canonical file system path equivalent to the given
134 130 path.
135 131 '''
136 132 # TODO: There may be a more clever way to do this that also handles other,
137 133 # less common file systems.
138 134 return os.path.normpath(os.path.normcase(os.path.realpath(path)))
139 135
140 136 def samestat(s1, s2):
141 137 return False
142 138
143 139 # A sequence of backslashes is special iff it precedes a double quote:
144 140 # - if there's an even number of backslashes, the double quote is not
145 141 # quoted (i.e. it ends the quoted region)
146 142 # - if there's an odd number of backslashes, the double quote is quoted
147 143 # - in both cases, every pair of backslashes is unquoted into a single
148 144 # backslash
149 145 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
150 146 # So, to quote a string, we must surround it in double quotes, double
151 147 # the number of backslashes that preceed double quotes and add another
152 148 # backslash before every double quote (being careful with the double
153 149 # quote we've appended to the end)
154 150 _quotere = None
155 151 def shellquote(s):
156 152 global _quotere
157 153 if _quotere is None:
158 154 _quotere = re.compile(r'(\\*)("|\\$)')
159 155 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
160 156
161 157 def quotecommand(cmd):
162 158 """Build a command string suitable for os.popen* calls."""
163 159 if sys.version_info < (2, 7, 1):
164 160 # Python versions since 2.7.1 do this extra quoting themselves
165 161 return '"' + cmd + '"'
166 162 return cmd
167 163
168 164 def popen(command, mode='r'):
169 165 # Work around "popen spawned process may not write to stdout
170 166 # under windows"
171 167 # http://bugs.python.org/issue1366
172 168 command += " 2> %s" % nulldev
173 169 return os.popen(quotecommand(command), mode)
174 170
175 171 def explain_exit(code):
176 172 return _("exited with status %d") % code, code
177 173
178 174 # if you change this stub into a real check, please try to implement the
179 175 # username and groupname functions above, too.
180 176 def isowner(st):
181 177 return True
182 178
183 179 def find_exe(command):
184 180 '''Find executable for command searching like cmd.exe does.
185 181 If command is a basename then PATH is searched for command.
186 182 PATH isn't searched if command is an absolute or relative path.
187 183 An extension from PATHEXT is found and added if not present.
188 184 If command isn't found None is returned.'''
189 185 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
190 186 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
191 187 if os.path.splitext(command)[1].lower() in pathexts:
192 188 pathexts = ['']
193 189
194 190 def findexisting(pathcommand):
195 191 'Will append extension (if needed) and return existing file'
196 192 for ext in pathexts:
197 193 executable = pathcommand + ext
198 194 if os.path.exists(executable):
199 195 return executable
200 196 return None
201 197
202 198 if os.sep in command:
203 199 return findexisting(command)
204 200
205 201 for path in os.environ.get('PATH', '').split(os.pathsep):
206 202 executable = findexisting(os.path.join(path, command))
207 203 if executable is not None:
208 204 return executable
209 205 return findexisting(os.path.expanduser(os.path.expandvars(command)))
210 206
211 def set_signal_handler():
212 try:
213 set_signal_handler_win32()
214 except NameError:
215 pass
216
217 207 def statfiles(files):
218 208 '''Stat each file in files and yield stat or None if file does not exist.
219 209 Cluster and cache stat per directory to minimize number of OS stat calls.'''
220 210 ncase = os.path.normcase
221 211 dircache = {} # dirname -> filename -> status | None if file does not exist
222 212 for nf in files:
223 213 nf = ncase(nf)
224 214 dir, base = os.path.split(nf)
225 215 if not dir:
226 216 dir = '.'
227 217 cache = dircache.get(dir, None)
228 218 if cache is None:
229 219 try:
230 220 dmap = dict([(ncase(n), s)
231 221 for n, k, s in osutil.listdir(dir, True)])
232 222 except OSError, err:
233 223 # handle directory not found in Python version prior to 2.5
234 224 # Python <= 2.4 returns native Windows code 3 in errno
235 225 # Python >= 2.5 returns ENOENT and adds winerror field
236 226 # EINVAL is raised if dir is not a directory.
237 227 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
238 228 errno.ENOTDIR):
239 229 raise
240 230 dmap = {}
241 231 cache = dircache.setdefault(dir, dmap)
242 232 yield cache.get(base, None)
243 233
244 def getuser():
245 '''return name of current user'''
246 raise error.Abort(_('user name not available - set USERNAME '
247 'environment variable'))
248
249 234 def username(uid=None):
250 235 """Return the name of the user with the given uid.
251 236
252 237 If uid is None, return the name of the current user."""
253 238 return None
254 239
255 240 def groupname(gid=None):
256 241 """Return the name of the group with the given gid.
257 242
258 243 If gid is None, return the name of the current group."""
259 244 return None
260 245
261 246 def _removedirs(name):
262 247 """special version of os.removedirs that does not remove symlinked
263 248 directories or junction points if they actually contain files"""
264 249 if osutil.listdir(name):
265 250 return
266 251 os.rmdir(name)
267 252 head, tail = os.path.split(name)
268 253 if not tail:
269 254 head, tail = os.path.split(head)
270 255 while head and tail:
271 256 try:
272 257 if osutil.listdir(head):
273 258 return
274 259 os.rmdir(head)
275 260 except:
276 261 break
277 262 head, tail = os.path.split(head)
278 263
279 264 def unlinkpath(f):
280 265 """unlink and remove the directory if it is empty"""
281 266 os.unlink(f)
282 267 # try removing directories that might now be empty
283 268 try:
284 269 _removedirs(os.path.dirname(f))
285 270 except OSError:
286 271 pass
287 272
288 273 def unlink(f):
289 274 '''try to implement POSIX' unlink semantics on Windows'''
290 275
291 276 # POSIX allows to unlink and rename open files. Windows has serious
292 277 # problems with doing that:
293 278 # - Calling os.unlink (or os.rename) on a file f fails if f or any
294 279 # hardlinked copy of f has been opened with Python's open(). There is no
295 280 # way such a file can be deleted or renamed on Windows (other than
296 281 # scheduling the delete or rename for the next reboot).
297 282 # - Calling os.unlink on a file that has been opened with Mercurial's
298 283 # posixfile (or comparable methods) will delay the actual deletion of
299 284 # the file for as long as the file is held open. The filename is blocked
300 285 # during that time and cannot be used for recreating a new file under
301 286 # that same name ("zombie file"). Directories containing such zombie files
302 287 # cannot be removed or moved.
303 288 # A file that has been opened with posixfile can be renamed, so we rename
304 289 # f to a random temporary name before calling os.unlink on it. This allows
305 290 # callers to recreate f immediately while having other readers do their
306 291 # implicit zombie filename blocking on a temporary name.
307 292
308 293 for tries in xrange(10):
309 294 temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
310 295 try:
311 296 os.rename(f, temp) # raises OSError EEXIST if temp exists
312 297 break
313 298 except OSError, e:
314 299 if e.errno != errno.EEXIST:
315 300 raise
316 301 else:
317 302 raise IOError, (errno.EEXIST, "No usable temporary filename found")
318 303
319 304 try:
320 305 os.unlink(temp)
321 306 except:
322 307 # Some very rude AV-scanners on Windows may cause this unlink to fail.
323 308 # Not aborting here just leaks the temp file, whereas aborting at this
324 309 # point may leave serious inconsistencies. Ideally, we would notify
325 310 # the user in this case here.
326 311 pass
327 312
328 313 def rename(src, dst):
329 314 '''atomically rename file src to dst, replacing dst if it exists'''
330 315 try:
331 316 os.rename(src, dst)
332 317 except OSError, e:
333 318 if e.errno != errno.EEXIST:
334 319 raise
335 320 unlink(dst)
336 321 os.rename(src, dst)
337 322
338 def spawndetached(args):
339 # No standard library function really spawns a fully detached
340 # process under win32 because they allocate pipes or other objects
341 # to handle standard streams communications. Passing these objects
342 # to the child process requires handle inheritance to be enabled
343 # which makes really detached processes impossible.
344 class STARTUPINFO:
345 dwFlags = subprocess.STARTF_USESHOWWINDOW
346 hStdInput = None
347 hStdOutput = None
348 hStdError = None
349 wShowWindow = subprocess.SW_HIDE
350
351 args = subprocess.list2cmdline(args)
352 # Not running the command in shell mode makes python26 hang when
353 # writing to hgweb output socket.
354 comspec = os.environ.get("COMSPEC", "cmd.exe")
355 args = comspec + " /c " + args
356 hp, ht, pid, tid = subprocess.CreateProcess(
357 None, args,
358 # no special security
359 None, None,
360 # Do not inherit handles
361 0,
362 # DETACHED_PROCESS
363 0x00000008,
364 os.environ,
365 os.getcwd(),
366 STARTUPINFO())
367 return pid
368
369 323 def gethgcmd():
370 324 return [sys.executable] + sys.argv[:1]
371 325
372 326 def termwidth():
373 327 # cmd.exe does not handle CR like a unix console, the CR is
374 328 # counted in the line length. On 80 columns consoles, if 80
375 329 # characters are written, the following CR won't apply on the
376 330 # current line but on the new one. Keep room for it.
377 331 return 79
378 332
379 333 def groupmembers(name):
380 334 # Don't support groups on Windows for now
381 335 raise KeyError()
382 336
383 try:
384 # override functions with win32 versions if possible
385 from win32 import *
386 except ImportError:
387 pass
337 from win32 import *
388 338
389 339 expandglobs = True
General Comments 0
You need to be logged in to leave comments. Login now