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