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