##// END OF EJS Templates
merge with stable
Martin Geisler -
r17223:c315842c merge default
parent child Browse files
Show More
@@ -1,336 +1,329
1 # windows.py - Windows utility function implementations for Mercurial
1 # windows.py - Windows utility function implementations for Mercurial
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import osutil, encoding
9 import osutil, encoding
10 import errno, msvcrt, os, re, sys, _winreg
10 import errno, msvcrt, os, re, sys, _winreg
11
11
12 import win32
12 import win32
13 executablepath = win32.executablepath
13 executablepath = win32.executablepath
14 getuser = win32.getuser
14 getuser = win32.getuser
15 hidewindow = win32.hidewindow
15 hidewindow = win32.hidewindow
16 makedir = win32.makedir
16 makedir = win32.makedir
17 nlinks = win32.nlinks
17 nlinks = win32.nlinks
18 oslink = win32.oslink
18 oslink = win32.oslink
19 samedevice = win32.samedevice
19 samedevice = win32.samedevice
20 samefile = win32.samefile
20 samefile = win32.samefile
21 setsignalhandler = win32.setsignalhandler
21 setsignalhandler = win32.setsignalhandler
22 spawndetached = win32.spawndetached
22 spawndetached = win32.spawndetached
23 termwidth = win32.termwidth
23 termwidth = win32.termwidth
24 testpid = win32.testpid
24 testpid = win32.testpid
25 unlink = win32.unlink
25 unlink = win32.unlink
26
26
27 nulldev = 'NUL:'
27 nulldev = 'NUL:'
28 umask = 0022
28 umask = 0022
29
29
30 # wrap osutil.posixfile to provide friendlier exceptions
30 # wrap osutil.posixfile to provide friendlier exceptions
31 def posixfile(name, mode='r', buffering=-1):
31 def posixfile(name, mode='r', buffering=-1):
32 try:
32 try:
33 return osutil.posixfile(name, mode, buffering)
33 return osutil.posixfile(name, mode, buffering)
34 except WindowsError, err:
34 except WindowsError, err:
35 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
35 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
36 posixfile.__doc__ = osutil.posixfile.__doc__
36 posixfile.__doc__ = osutil.posixfile.__doc__
37
37
38 class winstdout(object):
38 class winstdout(object):
39 '''stdout on windows misbehaves if sent through a pipe'''
39 '''stdout on windows misbehaves if sent through a pipe'''
40
40
41 def __init__(self, fp):
41 def __init__(self, fp):
42 self.fp = fp
42 self.fp = fp
43
43
44 def __getattr__(self, key):
44 def __getattr__(self, key):
45 return getattr(self.fp, key)
45 return getattr(self.fp, key)
46
46
47 def close(self):
47 def close(self):
48 try:
48 try:
49 self.fp.close()
49 self.fp.close()
50 except IOError:
50 except IOError:
51 pass
51 pass
52
52
53 def write(self, s):
53 def write(self, s):
54 try:
54 try:
55 # This is workaround for "Not enough space" error on
55 # This is workaround for "Not enough space" error on
56 # writing large size of data to console.
56 # writing large size of data to console.
57 limit = 16000
57 limit = 16000
58 l = len(s)
58 l = len(s)
59 start = 0
59 start = 0
60 self.softspace = 0
60 self.softspace = 0
61 while start < l:
61 while start < l:
62 end = start + limit
62 end = start + limit
63 self.fp.write(s[start:end])
63 self.fp.write(s[start:end])
64 start = end
64 start = end
65 except IOError, inst:
65 except IOError, inst:
66 if inst.errno != 0:
66 if inst.errno != 0:
67 raise
67 raise
68 self.close()
68 self.close()
69 raise IOError(errno.EPIPE, 'Broken pipe')
69 raise IOError(errno.EPIPE, 'Broken pipe')
70
70
71 def flush(self):
71 def flush(self):
72 try:
72 try:
73 return self.fp.flush()
73 return self.fp.flush()
74 except IOError, inst:
74 except IOError, inst:
75 if inst.errno != errno.EINVAL:
75 if inst.errno != errno.EINVAL:
76 raise
76 raise
77 self.close()
77 self.close()
78 raise IOError(errno.EPIPE, 'Broken pipe')
78 raise IOError(errno.EPIPE, 'Broken pipe')
79
79
80 sys.__stdout__ = sys.stdout = winstdout(sys.stdout)
80 sys.__stdout__ = sys.stdout = winstdout(sys.stdout)
81
81
82 def _is_win_9x():
82 def _is_win_9x():
83 '''return true if run on windows 95, 98 or me.'''
83 '''return true if run on windows 95, 98 or me.'''
84 try:
84 try:
85 return sys.getwindowsversion()[3] == 1
85 return sys.getwindowsversion()[3] == 1
86 except AttributeError:
86 except AttributeError:
87 return 'command' in os.environ.get('comspec', '')
87 return 'command' in os.environ.get('comspec', '')
88
88
89 def openhardlinks():
89 def openhardlinks():
90 return not _is_win_9x()
90 return not _is_win_9x()
91
91
92 def parsepatchoutput(output_line):
92 def parsepatchoutput(output_line):
93 """parses the output produced by patch and returns the filename"""
93 """parses the output produced by patch and returns the filename"""
94 pf = output_line[14:]
94 pf = output_line[14:]
95 if pf[0] == '`':
95 if pf[0] == '`':
96 pf = pf[1:-1] # Remove the quotes
96 pf = pf[1:-1] # Remove the quotes
97 return pf
97 return pf
98
98
99 def sshargs(sshcmd, host, user, port):
99 def sshargs(sshcmd, host, user, port):
100 '''Build argument list for ssh or Plink'''
100 '''Build argument list for ssh or Plink'''
101 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
101 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
102 args = user and ("%s@%s" % (user, host)) or host
102 args = user and ("%s@%s" % (user, host)) or host
103 return port and ("%s %s %s" % (args, pflag, port)) or args
103 return port and ("%s %s %s" % (args, pflag, port)) or args
104
104
105 def setflags(f, l, x):
105 def setflags(f, l, x):
106 pass
106 pass
107
107
108 def copymode(src, dst, mode=None):
108 def copymode(src, dst, mode=None):
109 pass
109 pass
110
110
111 def checkexec(path):
111 def checkexec(path):
112 return False
112 return False
113
113
114 def checklink(path):
114 def checklink(path):
115 return False
115 return False
116
116
117 def setbinary(fd):
117 def setbinary(fd):
118 # When run without console, pipes may expose invalid
118 # When run without console, pipes may expose invalid
119 # fileno(), usually set to -1.
119 # fileno(), usually set to -1.
120 fno = getattr(fd, 'fileno', None)
120 fno = getattr(fd, 'fileno', None)
121 if fno is not None and fno() >= 0:
121 if fno is not None and fno() >= 0:
122 msvcrt.setmode(fno(), os.O_BINARY)
122 msvcrt.setmode(fno(), os.O_BINARY)
123
123
124 def pconvert(path):
124 def pconvert(path):
125 return path.replace(os.sep, '/')
125 return path.replace(os.sep, '/')
126
126
127 def localpath(path):
127 def localpath(path):
128 return path.replace('/', '\\')
128 return path.replace('/', '\\')
129
129
130 def normpath(path):
130 def normpath(path):
131 return pconvert(os.path.normpath(path))
131 return pconvert(os.path.normpath(path))
132
132
133 def normcase(path):
133 def normcase(path):
134 return encoding.upper(path)
134 return encoding.upper(path)
135
135
136 def realpath(path):
136 def realpath(path):
137 '''
137 '''
138 Returns the true, canonical file system path equivalent to the given
138 Returns the true, canonical file system path equivalent to the given
139 path.
139 path.
140 '''
140 '''
141 # TODO: There may be a more clever way to do this that also handles other,
141 # TODO: There may be a more clever way to do this that also handles other,
142 # less common file systems.
142 # less common file systems.
143 return os.path.normpath(normcase(os.path.realpath(path)))
143 return os.path.normpath(normcase(os.path.realpath(path)))
144
144
145 def samestat(s1, s2):
145 def samestat(s1, s2):
146 return False
146 return False
147
147
148 # A sequence of backslashes is special iff it precedes a double quote:
148 # A sequence of backslashes is special iff it precedes a double quote:
149 # - if there's an even number of backslashes, the double quote is not
149 # - if there's an even number of backslashes, the double quote is not
150 # quoted (i.e. it ends the quoted region)
150 # quoted (i.e. it ends the quoted region)
151 # - if there's an odd number of backslashes, the double quote is quoted
151 # - if there's an odd number of backslashes, the double quote is quoted
152 # - in both cases, every pair of backslashes is unquoted into a single
152 # - in both cases, every pair of backslashes is unquoted into a single
153 # backslash
153 # backslash
154 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
154 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
155 # So, to quote a string, we must surround it in double quotes, double
155 # So, to quote a string, we must surround it in double quotes, double
156 # the number of backslashes that preceed double quotes and add another
156 # the number of backslashes that preceed double quotes and add another
157 # backslash before every double quote (being careful with the double
157 # backslash before every double quote (being careful with the double
158 # quote we've appended to the end)
158 # quote we've appended to the end)
159 _quotere = None
159 _quotere = None
160 def shellquote(s):
160 def shellquote(s):
161 global _quotere
161 global _quotere
162 if _quotere is None:
162 if _quotere is None:
163 _quotere = re.compile(r'(\\*)("|\\$)')
163 _quotere = re.compile(r'(\\*)("|\\$)')
164 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
164 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
165
165
166 def quotecommand(cmd):
166 def quotecommand(cmd):
167 """Build a command string suitable for os.popen* calls."""
167 """Build a command string suitable for os.popen* calls."""
168 if sys.version_info < (2, 7, 1):
168 if sys.version_info < (2, 7, 1):
169 # Python versions since 2.7.1 do this extra quoting themselves
169 # Python versions since 2.7.1 do this extra quoting themselves
170 return '"' + cmd + '"'
170 return '"' + cmd + '"'
171 return cmd
171 return cmd
172
172
173 def popen(command, mode='r'):
173 def popen(command, mode='r'):
174 # Work around "popen spawned process may not write to stdout
174 # Work around "popen spawned process may not write to stdout
175 # under windows"
175 # under windows"
176 # http://bugs.python.org/issue1366
176 # http://bugs.python.org/issue1366
177 command += " 2> %s" % nulldev
177 command += " 2> %s" % nulldev
178 return os.popen(quotecommand(command), mode)
178 return os.popen(quotecommand(command), mode)
179
179
180 def explainexit(code):
180 def explainexit(code):
181 return _("exited with status %d") % code, code
181 return _("exited with status %d") % code, code
182
182
183 # if you change this stub into a real check, please try to implement the
183 # if you change this stub into a real check, please try to implement the
184 # username and groupname functions above, too.
184 # username and groupname functions above, too.
185 def isowner(st):
185 def isowner(st):
186 return True
186 return True
187
187
188 def findexe(command):
188 def findexe(command):
189 '''Find executable for command searching like cmd.exe does.
189 '''Find executable for command searching like cmd.exe does.
190 If command is a basename then PATH is searched for command.
190 If command is a basename then PATH is searched for command.
191 PATH isn't searched if command is an absolute or relative path.
191 PATH isn't searched if command is an absolute or relative path.
192 An extension from PATHEXT is found and added if not present.
192 An extension from PATHEXT is found and added if not present.
193 If command isn't found None is returned.'''
193 If command isn't found None is returned.'''
194 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
194 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
195 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
195 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
196 if os.path.splitext(command)[1].lower() in pathexts:
196 if os.path.splitext(command)[1].lower() in pathexts:
197 pathexts = ['']
197 pathexts = ['']
198
198
199 def findexisting(pathcommand):
199 def findexisting(pathcommand):
200 'Will append extension (if needed) and return existing file'
200 'Will append extension (if needed) and return existing file'
201 for ext in pathexts:
201 for ext in pathexts:
202 executable = pathcommand + ext
202 executable = pathcommand + ext
203 if os.path.exists(executable):
203 if os.path.exists(executable):
204 return executable
204 return executable
205 return None
205 return None
206
206
207 if os.sep in command:
207 if os.sep in command:
208 return findexisting(command)
208 return findexisting(command)
209
209
210 for path in os.environ.get('PATH', '').split(os.pathsep):
210 for path in os.environ.get('PATH', '').split(os.pathsep):
211 executable = findexisting(os.path.join(path, command))
211 executable = findexisting(os.path.join(path, command))
212 if executable is not None:
212 if executable is not None:
213 return executable
213 return executable
214 return findexisting(os.path.expanduser(os.path.expandvars(command)))
214 return findexisting(os.path.expanduser(os.path.expandvars(command)))
215
215
216 def statfiles(files):
216 def statfiles(files):
217 '''Stat each file in files and yield stat or None if file does not exist.
217 '''Stat each file in files and yield stat or None if file does not exist.
218 Cluster and cache stat per directory to minimize number of OS stat calls.'''
218 Cluster and cache stat per directory to minimize number of OS stat calls.'''
219 dircache = {} # dirname -> filename -> status | None if file does not exist
219 dircache = {} # dirname -> filename -> status | None if file does not exist
220 for nf in files:
220 for nf in files:
221 nf = normcase(nf)
221 nf = normcase(nf)
222 dir, base = os.path.split(nf)
222 dir, base = os.path.split(nf)
223 if not dir:
223 if not dir:
224 dir = '.'
224 dir = '.'
225 cache = dircache.get(dir, None)
225 cache = dircache.get(dir, None)
226 if cache is None:
226 if cache is None:
227 try:
227 try:
228 dmap = dict([(normcase(n), s)
228 dmap = dict([(normcase(n), s)
229 for n, k, s in osutil.listdir(dir, True)])
229 for n, k, s in osutil.listdir(dir, True)])
230 except OSError, err:
230 except OSError, err:
231 # handle directory not found in Python version prior to 2.5
231 # handle directory not found in Python version prior to 2.5
232 # Python <= 2.4 returns native Windows code 3 in errno
232 # Python <= 2.4 returns native Windows code 3 in errno
233 # Python >= 2.5 returns ENOENT and adds winerror field
233 # Python >= 2.5 returns ENOENT and adds winerror field
234 # EINVAL is raised if dir is not a directory.
234 # EINVAL is raised if dir is not a directory.
235 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
235 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
236 errno.ENOTDIR):
236 errno.ENOTDIR):
237 raise
237 raise
238 dmap = {}
238 dmap = {}
239 cache = dircache.setdefault(dir, dmap)
239 cache = dircache.setdefault(dir, dmap)
240 yield cache.get(base, None)
240 yield cache.get(base, None)
241
241
242 def username(uid=None):
242 def username(uid=None):
243 """Return the name of the user with the given uid.
243 """Return the name of the user with the given uid.
244
244
245 If uid is None, return the name of the current user."""
245 If uid is None, return the name of the current user."""
246 return None
246 return None
247
247
248 def groupname(gid=None):
248 def groupname(gid=None):
249 """Return the name of the group with the given gid.
249 """Return the name of the group with the given gid.
250
250
251 If gid is None, return the name of the current group."""
251 If gid is None, return the name of the current group."""
252 return None
252 return None
253
253
254 def _removedirs(name):
254 def _removedirs(name):
255 """special version of os.removedirs that does not remove symlinked
255 """special version of os.removedirs that does not remove symlinked
256 directories or junction points if they actually contain files"""
256 directories or junction points if they actually contain files"""
257 if osutil.listdir(name):
257 if osutil.listdir(name):
258 return
258 return
259 os.rmdir(name)
259 os.rmdir(name)
260 head, tail = os.path.split(name)
260 head, tail = os.path.split(name)
261 if not tail:
261 if not tail:
262 head, tail = os.path.split(head)
262 head, tail = os.path.split(head)
263 while head and tail:
263 while head and tail:
264 try:
264 try:
265 if osutil.listdir(head):
265 if osutil.listdir(head):
266 return
266 return
267 os.rmdir(head)
267 os.rmdir(head)
268 except (ValueError, OSError):
268 except (ValueError, OSError):
269 break
269 break
270 head, tail = os.path.split(head)
270 head, tail = os.path.split(head)
271
271
272 def unlinkpath(f):
272 def unlinkpath(f):
273 """unlink and remove the directory if it is empty"""
273 """unlink and remove the directory if it is empty"""
274 unlink(f)
274 unlink(f)
275 # try removing directories that might now be empty
275 # try removing directories that might now be empty
276 try:
276 try:
277 _removedirs(os.path.dirname(f))
277 _removedirs(os.path.dirname(f))
278 except OSError:
278 except OSError:
279 pass
279 pass
280
280
281 def rename(src, dst):
281 def rename(src, dst):
282 '''atomically rename file src to dst, replacing dst if it exists'''
282 '''atomically rename file src to dst, replacing dst if it exists'''
283 try:
283 try:
284 os.rename(src, dst)
284 os.rename(src, dst)
285 except OSError, e:
285 except OSError, e:
286 if e.errno != errno.EEXIST:
286 if e.errno != errno.EEXIST:
287 raise
287 raise
288 unlink(dst)
288 unlink(dst)
289 os.rename(src, dst)
289 os.rename(src, dst)
290
290
291 def gethgcmd():
291 def gethgcmd():
292 return [sys.executable] + sys.argv[:1]
292 return [sys.executable] + sys.argv[:1]
293
293
294 def termwidth():
295 # cmd.exe does not handle CR like a unix console, the CR is
296 # counted in the line length. On 80 columns consoles, if 80
297 # characters are written, the following CR won't apply on the
298 # current line but on the new one. Keep room for it.
299 return 79
300
301 def groupmembers(name):
294 def groupmembers(name):
302 # Don't support groups on Windows for now
295 # Don't support groups on Windows for now
303 raise KeyError
296 raise KeyError
304
297
305 def isexec(f):
298 def isexec(f):
306 return False
299 return False
307
300
308 class cachestat(object):
301 class cachestat(object):
309 def __init__(self, path):
302 def __init__(self, path):
310 pass
303 pass
311
304
312 def cacheable(self):
305 def cacheable(self):
313 return False
306 return False
314
307
315 def lookupreg(key, valname=None, scope=None):
308 def lookupreg(key, valname=None, scope=None):
316 ''' Look up a key/value name in the Windows registry.
309 ''' Look up a key/value name in the Windows registry.
317
310
318 valname: value name. If unspecified, the default value for the key
311 valname: value name. If unspecified, the default value for the key
319 is used.
312 is used.
320 scope: optionally specify scope for registry lookup, this can be
313 scope: optionally specify scope for registry lookup, this can be
321 a sequence of scopes to look up in order. Default (CURRENT_USER,
314 a sequence of scopes to look up in order. Default (CURRENT_USER,
322 LOCAL_MACHINE).
315 LOCAL_MACHINE).
323 '''
316 '''
324 if scope is None:
317 if scope is None:
325 scope = (_winreg.HKEY_CURRENT_USER, _winreg.HKEY_LOCAL_MACHINE)
318 scope = (_winreg.HKEY_CURRENT_USER, _winreg.HKEY_LOCAL_MACHINE)
326 elif not isinstance(scope, (list, tuple)):
319 elif not isinstance(scope, (list, tuple)):
327 scope = (scope,)
320 scope = (scope,)
328 for s in scope:
321 for s in scope:
329 try:
322 try:
330 val = _winreg.QueryValueEx(_winreg.OpenKey(s, key), valname)[0]
323 val = _winreg.QueryValueEx(_winreg.OpenKey(s, key), valname)[0]
331 # never let a Unicode string escape into the wild
324 # never let a Unicode string escape into the wild
332 return encoding.tolocal(val.encode('UTF-8'))
325 return encoding.tolocal(val.encode('UTF-8'))
333 except EnvironmentError:
326 except EnvironmentError:
334 pass
327 pass
335
328
336 expandglobs = True
329 expandglobs = True
General Comments 0
You need to be logged in to leave comments. Login now