##// END OF EJS Templates
windows: adjust doc string and comments of posixfile()...
Adrian Buehlmann -
r24069:c6666395 default
parent child Browse files
Show More
@@ -1,371 +1,371 b''
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, stat, sys, _winreg
10 import errno, msvcrt, os, re, stat, 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 split = os.path.split
23 split = os.path.split
24 termwidth = win32.termwidth
24 termwidth = win32.termwidth
25 testpid = win32.testpid
25 testpid = win32.testpid
26 unlink = win32.unlink
26 unlink = win32.unlink
27
27
28 umask = 0022
28 umask = 0022
29 _SEEK_END = 2 # os.SEEK_END was introduced in Python 2.5
29 _SEEK_END = 2 # os.SEEK_END was introduced in Python 2.5
30
30
31 # wrap osutil.posixfile to provide friendlier exceptions
32 def posixfile(name, mode='r', buffering=-1):
31 def posixfile(name, mode='r', buffering=-1):
32 '''Open a file with even more POSIX-like semantics'''
33 try:
33 try:
34 fp = osutil.posixfile(name, mode, buffering)
34 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
35
35
36 # The position when opening in append mode is implementation defined, so
36 # The position when opening in append mode is implementation defined, so
37 # make it consistent with other platforms, which position at EOF.
37 # make it consistent with other platforms, which position at EOF.
38 if 'a' in mode:
38 if 'a' in mode:
39 fp.seek(0, _SEEK_END)
39 fp.seek(0, _SEEK_END)
40
40
41 return fp
41 return fp
42 except WindowsError, err:
42 except WindowsError, err:
43 # convert to a friendlier exception
43 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
44 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
44 posixfile.__doc__ = osutil.posixfile.__doc__
45
45
46 class winstdout(object):
46 class winstdout(object):
47 '''stdout on windows misbehaves if sent through a pipe'''
47 '''stdout on windows misbehaves if sent through a pipe'''
48
48
49 def __init__(self, fp):
49 def __init__(self, fp):
50 self.fp = fp
50 self.fp = fp
51
51
52 def __getattr__(self, key):
52 def __getattr__(self, key):
53 return getattr(self.fp, key)
53 return getattr(self.fp, key)
54
54
55 def close(self):
55 def close(self):
56 try:
56 try:
57 self.fp.close()
57 self.fp.close()
58 except IOError:
58 except IOError:
59 pass
59 pass
60
60
61 def write(self, s):
61 def write(self, s):
62 try:
62 try:
63 # This is workaround for "Not enough space" error on
63 # This is workaround for "Not enough space" error on
64 # writing large size of data to console.
64 # writing large size of data to console.
65 limit = 16000
65 limit = 16000
66 l = len(s)
66 l = len(s)
67 start = 0
67 start = 0
68 self.softspace = 0
68 self.softspace = 0
69 while start < l:
69 while start < l:
70 end = start + limit
70 end = start + limit
71 self.fp.write(s[start:end])
71 self.fp.write(s[start:end])
72 start = end
72 start = end
73 except IOError, inst:
73 except IOError, inst:
74 if inst.errno != 0:
74 if inst.errno != 0:
75 raise
75 raise
76 self.close()
76 self.close()
77 raise IOError(errno.EPIPE, 'Broken pipe')
77 raise IOError(errno.EPIPE, 'Broken pipe')
78
78
79 def flush(self):
79 def flush(self):
80 try:
80 try:
81 return self.fp.flush()
81 return self.fp.flush()
82 except IOError, inst:
82 except IOError, inst:
83 if inst.errno != errno.EINVAL:
83 if inst.errno != errno.EINVAL:
84 raise
84 raise
85 self.close()
85 self.close()
86 raise IOError(errno.EPIPE, 'Broken pipe')
86 raise IOError(errno.EPIPE, 'Broken pipe')
87
87
88 sys.__stdout__ = sys.stdout = winstdout(sys.stdout)
88 sys.__stdout__ = sys.stdout = winstdout(sys.stdout)
89
89
90 def _is_win_9x():
90 def _is_win_9x():
91 '''return true if run on windows 95, 98 or me.'''
91 '''return true if run on windows 95, 98 or me.'''
92 try:
92 try:
93 return sys.getwindowsversion()[3] == 1
93 return sys.getwindowsversion()[3] == 1
94 except AttributeError:
94 except AttributeError:
95 return 'command' in os.environ.get('comspec', '')
95 return 'command' in os.environ.get('comspec', '')
96
96
97 def openhardlinks():
97 def openhardlinks():
98 return not _is_win_9x()
98 return not _is_win_9x()
99
99
100 def parsepatchoutput(output_line):
100 def parsepatchoutput(output_line):
101 """parses the output produced by patch and returns the filename"""
101 """parses the output produced by patch and returns the filename"""
102 pf = output_line[14:]
102 pf = output_line[14:]
103 if pf[0] == '`':
103 if pf[0] == '`':
104 pf = pf[1:-1] # Remove the quotes
104 pf = pf[1:-1] # Remove the quotes
105 return pf
105 return pf
106
106
107 def sshargs(sshcmd, host, user, port):
107 def sshargs(sshcmd, host, user, port):
108 '''Build argument list for ssh or Plink'''
108 '''Build argument list for ssh or Plink'''
109 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
109 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
110 args = user and ("%s@%s" % (user, host)) or host
110 args = user and ("%s@%s" % (user, host)) or host
111 return port and ("%s %s %s" % (args, pflag, port)) or args
111 return port and ("%s %s %s" % (args, pflag, port)) or args
112
112
113 def setflags(f, l, x):
113 def setflags(f, l, x):
114 pass
114 pass
115
115
116 def copymode(src, dst, mode=None):
116 def copymode(src, dst, mode=None):
117 pass
117 pass
118
118
119 def checkexec(path):
119 def checkexec(path):
120 return False
120 return False
121
121
122 def checklink(path):
122 def checklink(path):
123 return False
123 return False
124
124
125 def setbinary(fd):
125 def setbinary(fd):
126 # When run without console, pipes may expose invalid
126 # When run without console, pipes may expose invalid
127 # fileno(), usually set to -1.
127 # fileno(), usually set to -1.
128 fno = getattr(fd, 'fileno', None)
128 fno = getattr(fd, 'fileno', None)
129 if fno is not None and fno() >= 0:
129 if fno is not None and fno() >= 0:
130 msvcrt.setmode(fno(), os.O_BINARY)
130 msvcrt.setmode(fno(), os.O_BINARY)
131
131
132 def pconvert(path):
132 def pconvert(path):
133 return path.replace(os.sep, '/')
133 return path.replace(os.sep, '/')
134
134
135 def localpath(path):
135 def localpath(path):
136 return path.replace('/', '\\')
136 return path.replace('/', '\\')
137
137
138 def normpath(path):
138 def normpath(path):
139 return pconvert(os.path.normpath(path))
139 return pconvert(os.path.normpath(path))
140
140
141 def normcase(path):
141 def normcase(path):
142 return encoding.upper(path)
142 return encoding.upper(path)
143
143
144 def samestat(s1, s2):
144 def samestat(s1, s2):
145 return False
145 return False
146
146
147 # A sequence of backslashes is special iff it precedes a double quote:
147 # A sequence of backslashes is special iff it precedes a double quote:
148 # - if there's an even number of backslashes, the double quote is not
148 # - if there's an even number of backslashes, the double quote is not
149 # quoted (i.e. it ends the quoted region)
149 # quoted (i.e. it ends the quoted region)
150 # - if there's an odd number of backslashes, the double quote is quoted
150 # - if there's an odd number of backslashes, the double quote is quoted
151 # - in both cases, every pair of backslashes is unquoted into a single
151 # - in both cases, every pair of backslashes is unquoted into a single
152 # backslash
152 # backslash
153 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
153 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
154 # So, to quote a string, we must surround it in double quotes, double
154 # So, to quote a string, we must surround it in double quotes, double
155 # the number of backslashes that precede double quotes and add another
155 # the number of backslashes that precede double quotes and add another
156 # backslash before every double quote (being careful with the double
156 # backslash before every double quote (being careful with the double
157 # quote we've appended to the end)
157 # quote we've appended to the end)
158 _quotere = None
158 _quotere = None
159 _needsshellquote = None
159 _needsshellquote = 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 global _needsshellquote
164 global _needsshellquote
165 if _needsshellquote is None:
165 if _needsshellquote is None:
166 # ":" and "\\" are also treated as "safe character", because
166 # ":" and "\\" are also treated as "safe character", because
167 # they are used as a part of path name (and the latter doesn't
167 # they are used as a part of path name (and the latter doesn't
168 # work as "escape character", like one on posix) on Windows
168 # work as "escape character", like one on posix) on Windows
169 _needsshellquote = re.compile(r'[^a-zA-Z0-9._:/\\-]').search
169 _needsshellquote = re.compile(r'[^a-zA-Z0-9._:/\\-]').search
170 if not _needsshellquote(s) and not _quotere.search(s):
170 if not _needsshellquote(s) and not _quotere.search(s):
171 # "s" shouldn't have to be quoted
171 # "s" shouldn't have to be quoted
172 return s
172 return s
173 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
173 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
174
174
175 def quotecommand(cmd):
175 def quotecommand(cmd):
176 """Build a command string suitable for os.popen* calls."""
176 """Build a command string suitable for os.popen* calls."""
177 if sys.version_info < (2, 7, 1):
177 if sys.version_info < (2, 7, 1):
178 # Python versions since 2.7.1 do this extra quoting themselves
178 # Python versions since 2.7.1 do this extra quoting themselves
179 return '"' + cmd + '"'
179 return '"' + cmd + '"'
180 return cmd
180 return cmd
181
181
182 def popen(command, mode='r'):
182 def popen(command, mode='r'):
183 # Work around "popen spawned process may not write to stdout
183 # Work around "popen spawned process may not write to stdout
184 # under windows"
184 # under windows"
185 # http://bugs.python.org/issue1366
185 # http://bugs.python.org/issue1366
186 command += " 2> %s" % os.devnull
186 command += " 2> %s" % os.devnull
187 return os.popen(quotecommand(command), mode)
187 return os.popen(quotecommand(command), mode)
188
188
189 def explainexit(code):
189 def explainexit(code):
190 return _("exited with status %d") % code, code
190 return _("exited with status %d") % code, code
191
191
192 # if you change this stub into a real check, please try to implement the
192 # if you change this stub into a real check, please try to implement the
193 # username and groupname functions above, too.
193 # username and groupname functions above, too.
194 def isowner(st):
194 def isowner(st):
195 return True
195 return True
196
196
197 def findexe(command):
197 def findexe(command):
198 '''Find executable for command searching like cmd.exe does.
198 '''Find executable for command searching like cmd.exe does.
199 If command is a basename then PATH is searched for command.
199 If command is a basename then PATH is searched for command.
200 PATH isn't searched if command is an absolute or relative path.
200 PATH isn't searched if command is an absolute or relative path.
201 An extension from PATHEXT is found and added if not present.
201 An extension from PATHEXT is found and added if not present.
202 If command isn't found None is returned.'''
202 If command isn't found None is returned.'''
203 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
203 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
204 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
204 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
205 if os.path.splitext(command)[1].lower() in pathexts:
205 if os.path.splitext(command)[1].lower() in pathexts:
206 pathexts = ['']
206 pathexts = ['']
207
207
208 def findexisting(pathcommand):
208 def findexisting(pathcommand):
209 'Will append extension (if needed) and return existing file'
209 'Will append extension (if needed) and return existing file'
210 for ext in pathexts:
210 for ext in pathexts:
211 executable = pathcommand + ext
211 executable = pathcommand + ext
212 if os.path.exists(executable):
212 if os.path.exists(executable):
213 return executable
213 return executable
214 return None
214 return None
215
215
216 if os.sep in command:
216 if os.sep in command:
217 return findexisting(command)
217 return findexisting(command)
218
218
219 for path in os.environ.get('PATH', '').split(os.pathsep):
219 for path in os.environ.get('PATH', '').split(os.pathsep):
220 executable = findexisting(os.path.join(path, command))
220 executable = findexisting(os.path.join(path, command))
221 if executable is not None:
221 if executable is not None:
222 return executable
222 return executable
223 return findexisting(os.path.expanduser(os.path.expandvars(command)))
223 return findexisting(os.path.expanduser(os.path.expandvars(command)))
224
224
225 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
225 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
226
226
227 def statfiles(files):
227 def statfiles(files):
228 '''Stat each file in files. Yield each stat, or None if a file
228 '''Stat each file in files. Yield each stat, or None if a file
229 does not exist or has a type we don't care about.
229 does not exist or has a type we don't care about.
230
230
231 Cluster and cache stat per directory to minimize number of OS stat calls.'''
231 Cluster and cache stat per directory to minimize number of OS stat calls.'''
232 dircache = {} # dirname -> filename -> status | None if file does not exist
232 dircache = {} # dirname -> filename -> status | None if file does not exist
233 getkind = stat.S_IFMT
233 getkind = stat.S_IFMT
234 for nf in files:
234 for nf in files:
235 nf = normcase(nf)
235 nf = normcase(nf)
236 dir, base = os.path.split(nf)
236 dir, base = os.path.split(nf)
237 if not dir:
237 if not dir:
238 dir = '.'
238 dir = '.'
239 cache = dircache.get(dir, None)
239 cache = dircache.get(dir, None)
240 if cache is None:
240 if cache is None:
241 try:
241 try:
242 dmap = dict([(normcase(n), s)
242 dmap = dict([(normcase(n), s)
243 for n, k, s in osutil.listdir(dir, True)
243 for n, k, s in osutil.listdir(dir, True)
244 if getkind(s.st_mode) in _wantedkinds])
244 if getkind(s.st_mode) in _wantedkinds])
245 except OSError, err:
245 except OSError, err:
246 # handle directory not found in Python version prior to 2.5
246 # handle directory not found in Python version prior to 2.5
247 # Python <= 2.4 returns native Windows code 3 in errno
247 # Python <= 2.4 returns native Windows code 3 in errno
248 # Python >= 2.5 returns ENOENT and adds winerror field
248 # Python >= 2.5 returns ENOENT and adds winerror field
249 # EINVAL is raised if dir is not a directory.
249 # EINVAL is raised if dir is not a directory.
250 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
250 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
251 errno.ENOTDIR):
251 errno.ENOTDIR):
252 raise
252 raise
253 dmap = {}
253 dmap = {}
254 cache = dircache.setdefault(dir, dmap)
254 cache = dircache.setdefault(dir, dmap)
255 yield cache.get(base, None)
255 yield cache.get(base, None)
256
256
257 def username(uid=None):
257 def username(uid=None):
258 """Return the name of the user with the given uid.
258 """Return the name of the user with the given uid.
259
259
260 If uid is None, return the name of the current user."""
260 If uid is None, return the name of the current user."""
261 return None
261 return None
262
262
263 def groupname(gid=None):
263 def groupname(gid=None):
264 """Return the name of the group with the given gid.
264 """Return the name of the group with the given gid.
265
265
266 If gid is None, return the name of the current group."""
266 If gid is None, return the name of the current group."""
267 return None
267 return None
268
268
269 def _removedirs(name):
269 def _removedirs(name):
270 """special version of os.removedirs that does not remove symlinked
270 """special version of os.removedirs that does not remove symlinked
271 directories or junction points if they actually contain files"""
271 directories or junction points if they actually contain files"""
272 if osutil.listdir(name):
272 if osutil.listdir(name):
273 return
273 return
274 os.rmdir(name)
274 os.rmdir(name)
275 head, tail = os.path.split(name)
275 head, tail = os.path.split(name)
276 if not tail:
276 if not tail:
277 head, tail = os.path.split(head)
277 head, tail = os.path.split(head)
278 while head and tail:
278 while head and tail:
279 try:
279 try:
280 if osutil.listdir(head):
280 if osutil.listdir(head):
281 return
281 return
282 os.rmdir(head)
282 os.rmdir(head)
283 except (ValueError, OSError):
283 except (ValueError, OSError):
284 break
284 break
285 head, tail = os.path.split(head)
285 head, tail = os.path.split(head)
286
286
287 def unlinkpath(f, ignoremissing=False):
287 def unlinkpath(f, ignoremissing=False):
288 """unlink and remove the directory if it is empty"""
288 """unlink and remove the directory if it is empty"""
289 try:
289 try:
290 unlink(f)
290 unlink(f)
291 except OSError, e:
291 except OSError, e:
292 if not (ignoremissing and e.errno == errno.ENOENT):
292 if not (ignoremissing and e.errno == errno.ENOENT):
293 raise
293 raise
294 # try removing directories that might now be empty
294 # try removing directories that might now be empty
295 try:
295 try:
296 _removedirs(os.path.dirname(f))
296 _removedirs(os.path.dirname(f))
297 except OSError:
297 except OSError:
298 pass
298 pass
299
299
300 def rename(src, dst):
300 def rename(src, dst):
301 '''atomically rename file src to dst, replacing dst if it exists'''
301 '''atomically rename file src to dst, replacing dst if it exists'''
302 try:
302 try:
303 os.rename(src, dst)
303 os.rename(src, dst)
304 except OSError, e:
304 except OSError, e:
305 if e.errno != errno.EEXIST:
305 if e.errno != errno.EEXIST:
306 raise
306 raise
307 unlink(dst)
307 unlink(dst)
308 os.rename(src, dst)
308 os.rename(src, dst)
309
309
310 def gethgcmd():
310 def gethgcmd():
311 return [sys.executable] + sys.argv[:1]
311 return [sys.executable] + sys.argv[:1]
312
312
313 def groupmembers(name):
313 def groupmembers(name):
314 # Don't support groups on Windows for now
314 # Don't support groups on Windows for now
315 raise KeyError
315 raise KeyError
316
316
317 def isexec(f):
317 def isexec(f):
318 return False
318 return False
319
319
320 class cachestat(object):
320 class cachestat(object):
321 def __init__(self, path):
321 def __init__(self, path):
322 pass
322 pass
323
323
324 def cacheable(self):
324 def cacheable(self):
325 return False
325 return False
326
326
327 def lookupreg(key, valname=None, scope=None):
327 def lookupreg(key, valname=None, scope=None):
328 ''' Look up a key/value name in the Windows registry.
328 ''' Look up a key/value name in the Windows registry.
329
329
330 valname: value name. If unspecified, the default value for the key
330 valname: value name. If unspecified, the default value for the key
331 is used.
331 is used.
332 scope: optionally specify scope for registry lookup, this can be
332 scope: optionally specify scope for registry lookup, this can be
333 a sequence of scopes to look up in order. Default (CURRENT_USER,
333 a sequence of scopes to look up in order. Default (CURRENT_USER,
334 LOCAL_MACHINE).
334 LOCAL_MACHINE).
335 '''
335 '''
336 if scope is None:
336 if scope is None:
337 scope = (_winreg.HKEY_CURRENT_USER, _winreg.HKEY_LOCAL_MACHINE)
337 scope = (_winreg.HKEY_CURRENT_USER, _winreg.HKEY_LOCAL_MACHINE)
338 elif not isinstance(scope, (list, tuple)):
338 elif not isinstance(scope, (list, tuple)):
339 scope = (scope,)
339 scope = (scope,)
340 for s in scope:
340 for s in scope:
341 try:
341 try:
342 val = _winreg.QueryValueEx(_winreg.OpenKey(s, key), valname)[0]
342 val = _winreg.QueryValueEx(_winreg.OpenKey(s, key), valname)[0]
343 # never let a Unicode string escape into the wild
343 # never let a Unicode string escape into the wild
344 return encoding.tolocal(val.encode('UTF-8'))
344 return encoding.tolocal(val.encode('UTF-8'))
345 except EnvironmentError:
345 except EnvironmentError:
346 pass
346 pass
347
347
348 expandglobs = True
348 expandglobs = True
349
349
350 def statislink(st):
350 def statislink(st):
351 '''check whether a stat result is a symlink'''
351 '''check whether a stat result is a symlink'''
352 return False
352 return False
353
353
354 def statisexec(st):
354 def statisexec(st):
355 '''check whether a stat result is an executable file'''
355 '''check whether a stat result is an executable file'''
356 return False
356 return False
357
357
358 def readpipe(pipe):
358 def readpipe(pipe):
359 """Read all available data from a pipe."""
359 """Read all available data from a pipe."""
360 chunks = []
360 chunks = []
361 while True:
361 while True:
362 size = os.fstat(pipe.fileno()).st_size
362 size = os.fstat(pipe.fileno()).st_size
363 if not size:
363 if not size:
364 break
364 break
365
365
366 s = pipe.read(size)
366 s = pipe.read(size)
367 if not s:
367 if not s:
368 break
368 break
369 chunks.append(s)
369 chunks.append(s)
370
370
371 return ''.join(chunks)
371 return ''.join(chunks)
General Comments 0
You need to be logged in to leave comments. Login now