##// END OF EJS Templates
ssh: ban any username@host or host that starts with - (SEC)...
Augie Fackler -
r33724:739cc0f9 stable
parent child Browse files
Show More
@@ -1,668 +1,672 b''
1 # posix.py - Posix utility function implementations for Mercurial
1 # posix.py - Posix 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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import fcntl
11 import fcntl
12 import getpass
12 import getpass
13 import grp
13 import grp
14 import os
14 import os
15 import pwd
15 import pwd
16 import re
16 import re
17 import select
17 import select
18 import stat
18 import stat
19 import sys
19 import sys
20 import tempfile
20 import tempfile
21 import unicodedata
21 import unicodedata
22
22
23 from .i18n import _
23 from .i18n import _
24 from . import (
24 from . import (
25 encoding,
25 encoding,
26 error,
26 pycompat,
27 pycompat,
27 )
28 )
28
29
29 posixfile = open
30 posixfile = open
30 normpath = os.path.normpath
31 normpath = os.path.normpath
31 samestat = os.path.samestat
32 samestat = os.path.samestat
32 try:
33 try:
33 oslink = os.link
34 oslink = os.link
34 except AttributeError:
35 except AttributeError:
35 # Some platforms build Python without os.link on systems that are
36 # Some platforms build Python without os.link on systems that are
36 # vaguely unix-like but don't have hardlink support. For those
37 # vaguely unix-like but don't have hardlink support. For those
37 # poor souls, just say we tried and that it failed so we fall back
38 # poor souls, just say we tried and that it failed so we fall back
38 # to copies.
39 # to copies.
39 def oslink(src, dst):
40 def oslink(src, dst):
40 raise OSError(errno.EINVAL,
41 raise OSError(errno.EINVAL,
41 'hardlinks not supported: %s to %s' % (src, dst))
42 'hardlinks not supported: %s to %s' % (src, dst))
42 unlink = os.unlink
43 unlink = os.unlink
43 rename = os.rename
44 rename = os.rename
44 removedirs = os.removedirs
45 removedirs = os.removedirs
45 expandglobs = False
46 expandglobs = False
46
47
47 umask = os.umask(0)
48 umask = os.umask(0)
48 os.umask(umask)
49 os.umask(umask)
49
50
50 def split(p):
51 def split(p):
51 '''Same as posixpath.split, but faster
52 '''Same as posixpath.split, but faster
52
53
53 >>> import posixpath
54 >>> import posixpath
54 >>> for f in ['/absolute/path/to/file',
55 >>> for f in ['/absolute/path/to/file',
55 ... 'relative/path/to/file',
56 ... 'relative/path/to/file',
56 ... 'file_alone',
57 ... 'file_alone',
57 ... 'path/to/directory/',
58 ... 'path/to/directory/',
58 ... '/multiple/path//separators',
59 ... '/multiple/path//separators',
59 ... '/file_at_root',
60 ... '/file_at_root',
60 ... '///multiple_leading_separators_at_root',
61 ... '///multiple_leading_separators_at_root',
61 ... '']:
62 ... '']:
62 ... assert split(f) == posixpath.split(f), f
63 ... assert split(f) == posixpath.split(f), f
63 '''
64 '''
64 ht = p.rsplit('/', 1)
65 ht = p.rsplit('/', 1)
65 if len(ht) == 1:
66 if len(ht) == 1:
66 return '', p
67 return '', p
67 nh = ht[0].rstrip('/')
68 nh = ht[0].rstrip('/')
68 if nh:
69 if nh:
69 return nh, ht[1]
70 return nh, ht[1]
70 return ht[0] + '/', ht[1]
71 return ht[0] + '/', ht[1]
71
72
72 def openhardlinks():
73 def openhardlinks():
73 '''return true if it is safe to hold open file handles to hardlinks'''
74 '''return true if it is safe to hold open file handles to hardlinks'''
74 return True
75 return True
75
76
76 def nlinks(name):
77 def nlinks(name):
77 '''return number of hardlinks for the given file'''
78 '''return number of hardlinks for the given file'''
78 return os.lstat(name).st_nlink
79 return os.lstat(name).st_nlink
79
80
80 def parsepatchoutput(output_line):
81 def parsepatchoutput(output_line):
81 """parses the output produced by patch and returns the filename"""
82 """parses the output produced by patch and returns the filename"""
82 pf = output_line[14:]
83 pf = output_line[14:]
83 if pycompat.sysplatform == 'OpenVMS':
84 if pycompat.sysplatform == 'OpenVMS':
84 if pf[0] == '`':
85 if pf[0] == '`':
85 pf = pf[1:-1] # Remove the quotes
86 pf = pf[1:-1] # Remove the quotes
86 else:
87 else:
87 if pf.startswith("'") and pf.endswith("'") and " " in pf:
88 if pf.startswith("'") and pf.endswith("'") and " " in pf:
88 pf = pf[1:-1] # Remove the quotes
89 pf = pf[1:-1] # Remove the quotes
89 return pf
90 return pf
90
91
91 def sshargs(sshcmd, host, user, port):
92 def sshargs(sshcmd, host, user, port):
92 '''Build argument list for ssh'''
93 '''Build argument list for ssh'''
93 args = user and ("%s@%s" % (user, host)) or host
94 args = user and ("%s@%s" % (user, host)) or host
95 if '-' in args[:2]:
96 raise error.Abort(
97 _('illegal ssh hostname or username starting with -: %s') % args)
94 return port and ("%s -p %s" % (args, port)) or args
98 return port and ("%s -p %s" % (args, port)) or args
95
99
96 def isexec(f):
100 def isexec(f):
97 """check whether a file is executable"""
101 """check whether a file is executable"""
98 return (os.lstat(f).st_mode & 0o100 != 0)
102 return (os.lstat(f).st_mode & 0o100 != 0)
99
103
100 def setflags(f, l, x):
104 def setflags(f, l, x):
101 st = os.lstat(f)
105 st = os.lstat(f)
102 s = st.st_mode
106 s = st.st_mode
103 if l:
107 if l:
104 if not stat.S_ISLNK(s):
108 if not stat.S_ISLNK(s):
105 # switch file to link
109 # switch file to link
106 fp = open(f)
110 fp = open(f)
107 data = fp.read()
111 data = fp.read()
108 fp.close()
112 fp.close()
109 unlink(f)
113 unlink(f)
110 try:
114 try:
111 os.symlink(data, f)
115 os.symlink(data, f)
112 except OSError:
116 except OSError:
113 # failed to make a link, rewrite file
117 # failed to make a link, rewrite file
114 fp = open(f, "w")
118 fp = open(f, "w")
115 fp.write(data)
119 fp.write(data)
116 fp.close()
120 fp.close()
117 # no chmod needed at this point
121 # no chmod needed at this point
118 return
122 return
119 if stat.S_ISLNK(s):
123 if stat.S_ISLNK(s):
120 # switch link to file
124 # switch link to file
121 data = os.readlink(f)
125 data = os.readlink(f)
122 unlink(f)
126 unlink(f)
123 fp = open(f, "w")
127 fp = open(f, "w")
124 fp.write(data)
128 fp.write(data)
125 fp.close()
129 fp.close()
126 s = 0o666 & ~umask # avoid restatting for chmod
130 s = 0o666 & ~umask # avoid restatting for chmod
127
131
128 sx = s & 0o100
132 sx = s & 0o100
129 if st.st_nlink > 1 and bool(x) != bool(sx):
133 if st.st_nlink > 1 and bool(x) != bool(sx):
130 # the file is a hardlink, break it
134 # the file is a hardlink, break it
131 with open(f, "rb") as fp:
135 with open(f, "rb") as fp:
132 data = fp.read()
136 data = fp.read()
133 unlink(f)
137 unlink(f)
134 with open(f, "wb") as fp:
138 with open(f, "wb") as fp:
135 fp.write(data)
139 fp.write(data)
136
140
137 if x and not sx:
141 if x and not sx:
138 # Turn on +x for every +r bit when making a file executable
142 # Turn on +x for every +r bit when making a file executable
139 # and obey umask.
143 # and obey umask.
140 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
144 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
141 elif not x and sx:
145 elif not x and sx:
142 # Turn off all +x bits
146 # Turn off all +x bits
143 os.chmod(f, s & 0o666)
147 os.chmod(f, s & 0o666)
144
148
145 def copymode(src, dst, mode=None):
149 def copymode(src, dst, mode=None):
146 '''Copy the file mode from the file at path src to dst.
150 '''Copy the file mode from the file at path src to dst.
147 If src doesn't exist, we're using mode instead. If mode is None, we're
151 If src doesn't exist, we're using mode instead. If mode is None, we're
148 using umask.'''
152 using umask.'''
149 try:
153 try:
150 st_mode = os.lstat(src).st_mode & 0o777
154 st_mode = os.lstat(src).st_mode & 0o777
151 except OSError as inst:
155 except OSError as inst:
152 if inst.errno != errno.ENOENT:
156 if inst.errno != errno.ENOENT:
153 raise
157 raise
154 st_mode = mode
158 st_mode = mode
155 if st_mode is None:
159 if st_mode is None:
156 st_mode = ~umask
160 st_mode = ~umask
157 st_mode &= 0o666
161 st_mode &= 0o666
158 os.chmod(dst, st_mode)
162 os.chmod(dst, st_mode)
159
163
160 def checkexec(path):
164 def checkexec(path):
161 """
165 """
162 Check whether the given path is on a filesystem with UNIX-like exec flags
166 Check whether the given path is on a filesystem with UNIX-like exec flags
163
167
164 Requires a directory (like /foo/.hg)
168 Requires a directory (like /foo/.hg)
165 """
169 """
166
170
167 # VFAT on some Linux versions can flip mode but it doesn't persist
171 # VFAT on some Linux versions can flip mode but it doesn't persist
168 # a FS remount. Frequently we can detect it if files are created
172 # a FS remount. Frequently we can detect it if files are created
169 # with exec bit on.
173 # with exec bit on.
170
174
171 try:
175 try:
172 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
176 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
173 cachedir = os.path.join(path, '.hg', 'cache')
177 cachedir = os.path.join(path, '.hg', 'cache')
174 if os.path.isdir(cachedir):
178 if os.path.isdir(cachedir):
175 checkisexec = os.path.join(cachedir, 'checkisexec')
179 checkisexec = os.path.join(cachedir, 'checkisexec')
176 checknoexec = os.path.join(cachedir, 'checknoexec')
180 checknoexec = os.path.join(cachedir, 'checknoexec')
177
181
178 try:
182 try:
179 m = os.stat(checkisexec).st_mode
183 m = os.stat(checkisexec).st_mode
180 except OSError as e:
184 except OSError as e:
181 if e.errno != errno.ENOENT:
185 if e.errno != errno.ENOENT:
182 raise
186 raise
183 # checkisexec does not exist - fall through ...
187 # checkisexec does not exist - fall through ...
184 else:
188 else:
185 # checkisexec exists, check if it actually is exec
189 # checkisexec exists, check if it actually is exec
186 if m & EXECFLAGS != 0:
190 if m & EXECFLAGS != 0:
187 # ensure checkisexec exists, check it isn't exec
191 # ensure checkisexec exists, check it isn't exec
188 try:
192 try:
189 m = os.stat(checknoexec).st_mode
193 m = os.stat(checknoexec).st_mode
190 except OSError as e:
194 except OSError as e:
191 if e.errno != errno.ENOENT:
195 if e.errno != errno.ENOENT:
192 raise
196 raise
193 open(checknoexec, 'w').close() # might fail
197 open(checknoexec, 'w').close() # might fail
194 m = os.stat(checknoexec).st_mode
198 m = os.stat(checknoexec).st_mode
195 if m & EXECFLAGS == 0:
199 if m & EXECFLAGS == 0:
196 # check-exec is exec and check-no-exec is not exec
200 # check-exec is exec and check-no-exec is not exec
197 return True
201 return True
198 # checknoexec exists but is exec - delete it
202 # checknoexec exists but is exec - delete it
199 unlink(checknoexec)
203 unlink(checknoexec)
200 # checkisexec exists but is not exec - delete it
204 # checkisexec exists but is not exec - delete it
201 unlink(checkisexec)
205 unlink(checkisexec)
202
206
203 # check using one file, leave it as checkisexec
207 # check using one file, leave it as checkisexec
204 checkdir = cachedir
208 checkdir = cachedir
205 else:
209 else:
206 # check directly in path and don't leave checkisexec behind
210 # check directly in path and don't leave checkisexec behind
207 checkdir = path
211 checkdir = path
208 checkisexec = None
212 checkisexec = None
209 fh, fn = tempfile.mkstemp(dir=checkdir, prefix='hg-checkexec-')
213 fh, fn = tempfile.mkstemp(dir=checkdir, prefix='hg-checkexec-')
210 try:
214 try:
211 os.close(fh)
215 os.close(fh)
212 m = os.stat(fn).st_mode
216 m = os.stat(fn).st_mode
213 if m & EXECFLAGS == 0:
217 if m & EXECFLAGS == 0:
214 os.chmod(fn, m & 0o777 | EXECFLAGS)
218 os.chmod(fn, m & 0o777 | EXECFLAGS)
215 if os.stat(fn).st_mode & EXECFLAGS != 0:
219 if os.stat(fn).st_mode & EXECFLAGS != 0:
216 if checkisexec is not None:
220 if checkisexec is not None:
217 os.rename(fn, checkisexec)
221 os.rename(fn, checkisexec)
218 fn = None
222 fn = None
219 return True
223 return True
220 finally:
224 finally:
221 if fn is not None:
225 if fn is not None:
222 unlink(fn)
226 unlink(fn)
223 except (IOError, OSError):
227 except (IOError, OSError):
224 # we don't care, the user probably won't be able to commit anyway
228 # we don't care, the user probably won't be able to commit anyway
225 return False
229 return False
226
230
227 def checklink(path):
231 def checklink(path):
228 """check whether the given path is on a symlink-capable filesystem"""
232 """check whether the given path is on a symlink-capable filesystem"""
229 # mktemp is not racy because symlink creation will fail if the
233 # mktemp is not racy because symlink creation will fail if the
230 # file already exists
234 # file already exists
231 while True:
235 while True:
232 cachedir = os.path.join(path, '.hg', 'cache')
236 cachedir = os.path.join(path, '.hg', 'cache')
233 checklink = os.path.join(cachedir, 'checklink')
237 checklink = os.path.join(cachedir, 'checklink')
234 # try fast path, read only
238 # try fast path, read only
235 if os.path.islink(checklink):
239 if os.path.islink(checklink):
236 return True
240 return True
237 if os.path.isdir(cachedir):
241 if os.path.isdir(cachedir):
238 checkdir = cachedir
242 checkdir = cachedir
239 else:
243 else:
240 checkdir = path
244 checkdir = path
241 cachedir = None
245 cachedir = None
242 fscheckdir = pycompat.fsdecode(checkdir)
246 fscheckdir = pycompat.fsdecode(checkdir)
243 name = tempfile.mktemp(dir=fscheckdir,
247 name = tempfile.mktemp(dir=fscheckdir,
244 prefix=r'checklink-')
248 prefix=r'checklink-')
245 name = pycompat.fsencode(name)
249 name = pycompat.fsencode(name)
246 try:
250 try:
247 fd = None
251 fd = None
248 if cachedir is None:
252 if cachedir is None:
249 fd = tempfile.NamedTemporaryFile(dir=fscheckdir,
253 fd = tempfile.NamedTemporaryFile(dir=fscheckdir,
250 prefix=r'hg-checklink-')
254 prefix=r'hg-checklink-')
251 target = pycompat.fsencode(os.path.basename(fd.name))
255 target = pycompat.fsencode(os.path.basename(fd.name))
252 else:
256 else:
253 # create a fixed file to link to; doesn't matter if it
257 # create a fixed file to link to; doesn't matter if it
254 # already exists.
258 # already exists.
255 target = 'checklink-target'
259 target = 'checklink-target'
256 try:
260 try:
257 open(os.path.join(cachedir, target), 'w').close()
261 open(os.path.join(cachedir, target), 'w').close()
258 except IOError as inst:
262 except IOError as inst:
259 if inst[0] == errno.EACCES:
263 if inst[0] == errno.EACCES:
260 # If we can't write to cachedir, just pretend
264 # If we can't write to cachedir, just pretend
261 # that the fs is readonly and by association
265 # that the fs is readonly and by association
262 # that the fs won't support symlinks. This
266 # that the fs won't support symlinks. This
263 # seems like the least dangerous way to avoid
267 # seems like the least dangerous way to avoid
264 # data loss.
268 # data loss.
265 return False
269 return False
266 raise
270 raise
267 try:
271 try:
268 os.symlink(target, name)
272 os.symlink(target, name)
269 if cachedir is None:
273 if cachedir is None:
270 unlink(name)
274 unlink(name)
271 else:
275 else:
272 try:
276 try:
273 os.rename(name, checklink)
277 os.rename(name, checklink)
274 except OSError:
278 except OSError:
275 unlink(name)
279 unlink(name)
276 return True
280 return True
277 except OSError as inst:
281 except OSError as inst:
278 # link creation might race, try again
282 # link creation might race, try again
279 if inst[0] == errno.EEXIST:
283 if inst[0] == errno.EEXIST:
280 continue
284 continue
281 raise
285 raise
282 finally:
286 finally:
283 if fd is not None:
287 if fd is not None:
284 fd.close()
288 fd.close()
285 except AttributeError:
289 except AttributeError:
286 return False
290 return False
287 except OSError as inst:
291 except OSError as inst:
288 # sshfs might report failure while successfully creating the link
292 # sshfs might report failure while successfully creating the link
289 if inst[0] == errno.EIO and os.path.exists(name):
293 if inst[0] == errno.EIO and os.path.exists(name):
290 unlink(name)
294 unlink(name)
291 return False
295 return False
292
296
293 def checkosfilename(path):
297 def checkosfilename(path):
294 '''Check that the base-relative path is a valid filename on this platform.
298 '''Check that the base-relative path is a valid filename on this platform.
295 Returns None if the path is ok, or a UI string describing the problem.'''
299 Returns None if the path is ok, or a UI string describing the problem.'''
296 pass # on posix platforms, every path is ok
300 pass # on posix platforms, every path is ok
297
301
298 def setbinary(fd):
302 def setbinary(fd):
299 pass
303 pass
300
304
301 def pconvert(path):
305 def pconvert(path):
302 return path
306 return path
303
307
304 def localpath(path):
308 def localpath(path):
305 return path
309 return path
306
310
307 def samefile(fpath1, fpath2):
311 def samefile(fpath1, fpath2):
308 """Returns whether path1 and path2 refer to the same file. This is only
312 """Returns whether path1 and path2 refer to the same file. This is only
309 guaranteed to work for files, not directories."""
313 guaranteed to work for files, not directories."""
310 return os.path.samefile(fpath1, fpath2)
314 return os.path.samefile(fpath1, fpath2)
311
315
312 def samedevice(fpath1, fpath2):
316 def samedevice(fpath1, fpath2):
313 """Returns whether fpath1 and fpath2 are on the same device. This is only
317 """Returns whether fpath1 and fpath2 are on the same device. This is only
314 guaranteed to work for files, not directories."""
318 guaranteed to work for files, not directories."""
315 st1 = os.lstat(fpath1)
319 st1 = os.lstat(fpath1)
316 st2 = os.lstat(fpath2)
320 st2 = os.lstat(fpath2)
317 return st1.st_dev == st2.st_dev
321 return st1.st_dev == st2.st_dev
318
322
319 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
323 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
320 def normcase(path):
324 def normcase(path):
321 return path.lower()
325 return path.lower()
322
326
323 # what normcase does to ASCII strings
327 # what normcase does to ASCII strings
324 normcasespec = encoding.normcasespecs.lower
328 normcasespec = encoding.normcasespecs.lower
325 # fallback normcase function for non-ASCII strings
329 # fallback normcase function for non-ASCII strings
326 normcasefallback = normcase
330 normcasefallback = normcase
327
331
328 if pycompat.sysplatform == 'darwin':
332 if pycompat.sysplatform == 'darwin':
329
333
330 def normcase(path):
334 def normcase(path):
331 '''
335 '''
332 Normalize a filename for OS X-compatible comparison:
336 Normalize a filename for OS X-compatible comparison:
333 - escape-encode invalid characters
337 - escape-encode invalid characters
334 - decompose to NFD
338 - decompose to NFD
335 - lowercase
339 - lowercase
336 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
340 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
337
341
338 >>> normcase('UPPER')
342 >>> normcase('UPPER')
339 'upper'
343 'upper'
340 >>> normcase('Caf\xc3\xa9')
344 >>> normcase('Caf\xc3\xa9')
341 'cafe\\xcc\\x81'
345 'cafe\\xcc\\x81'
342 >>> normcase('\xc3\x89')
346 >>> normcase('\xc3\x89')
343 'e\\xcc\\x81'
347 'e\\xcc\\x81'
344 >>> normcase('\xb8\xca\xc3\xca\xbe\xc8.JPG') # issue3918
348 >>> normcase('\xb8\xca\xc3\xca\xbe\xc8.JPG') # issue3918
345 '%b8%ca%c3\\xca\\xbe%c8.jpg'
349 '%b8%ca%c3\\xca\\xbe%c8.jpg'
346 '''
350 '''
347
351
348 try:
352 try:
349 return encoding.asciilower(path) # exception for non-ASCII
353 return encoding.asciilower(path) # exception for non-ASCII
350 except UnicodeDecodeError:
354 except UnicodeDecodeError:
351 return normcasefallback(path)
355 return normcasefallback(path)
352
356
353 normcasespec = encoding.normcasespecs.lower
357 normcasespec = encoding.normcasespecs.lower
354
358
355 def normcasefallback(path):
359 def normcasefallback(path):
356 try:
360 try:
357 u = path.decode('utf-8')
361 u = path.decode('utf-8')
358 except UnicodeDecodeError:
362 except UnicodeDecodeError:
359 # OS X percent-encodes any bytes that aren't valid utf-8
363 # OS X percent-encodes any bytes that aren't valid utf-8
360 s = ''
364 s = ''
361 pos = 0
365 pos = 0
362 l = len(path)
366 l = len(path)
363 while pos < l:
367 while pos < l:
364 try:
368 try:
365 c = encoding.getutf8char(path, pos)
369 c = encoding.getutf8char(path, pos)
366 pos += len(c)
370 pos += len(c)
367 except ValueError:
371 except ValueError:
368 c = '%%%02X' % ord(path[pos])
372 c = '%%%02X' % ord(path[pos])
369 pos += 1
373 pos += 1
370 s += c
374 s += c
371
375
372 u = s.decode('utf-8')
376 u = s.decode('utf-8')
373
377
374 # Decompose then lowercase (HFS+ technote specifies lower)
378 # Decompose then lowercase (HFS+ technote specifies lower)
375 enc = unicodedata.normalize('NFD', u).lower().encode('utf-8')
379 enc = unicodedata.normalize('NFD', u).lower().encode('utf-8')
376 # drop HFS+ ignored characters
380 # drop HFS+ ignored characters
377 return encoding.hfsignoreclean(enc)
381 return encoding.hfsignoreclean(enc)
378
382
379 if pycompat.sysplatform == 'cygwin':
383 if pycompat.sysplatform == 'cygwin':
380 # workaround for cygwin, in which mount point part of path is
384 # workaround for cygwin, in which mount point part of path is
381 # treated as case sensitive, even though underlying NTFS is case
385 # treated as case sensitive, even though underlying NTFS is case
382 # insensitive.
386 # insensitive.
383
387
384 # default mount points
388 # default mount points
385 cygwinmountpoints = sorted([
389 cygwinmountpoints = sorted([
386 "/usr/bin",
390 "/usr/bin",
387 "/usr/lib",
391 "/usr/lib",
388 "/cygdrive",
392 "/cygdrive",
389 ], reverse=True)
393 ], reverse=True)
390
394
391 # use upper-ing as normcase as same as NTFS workaround
395 # use upper-ing as normcase as same as NTFS workaround
392 def normcase(path):
396 def normcase(path):
393 pathlen = len(path)
397 pathlen = len(path)
394 if (pathlen == 0) or (path[0] != pycompat.ossep):
398 if (pathlen == 0) or (path[0] != pycompat.ossep):
395 # treat as relative
399 # treat as relative
396 return encoding.upper(path)
400 return encoding.upper(path)
397
401
398 # to preserve case of mountpoint part
402 # to preserve case of mountpoint part
399 for mp in cygwinmountpoints:
403 for mp in cygwinmountpoints:
400 if not path.startswith(mp):
404 if not path.startswith(mp):
401 continue
405 continue
402
406
403 mplen = len(mp)
407 mplen = len(mp)
404 if mplen == pathlen: # mount point itself
408 if mplen == pathlen: # mount point itself
405 return mp
409 return mp
406 if path[mplen] == pycompat.ossep:
410 if path[mplen] == pycompat.ossep:
407 return mp + encoding.upper(path[mplen:])
411 return mp + encoding.upper(path[mplen:])
408
412
409 return encoding.upper(path)
413 return encoding.upper(path)
410
414
411 normcasespec = encoding.normcasespecs.other
415 normcasespec = encoding.normcasespecs.other
412 normcasefallback = normcase
416 normcasefallback = normcase
413
417
414 # Cygwin translates native ACLs to POSIX permissions,
418 # Cygwin translates native ACLs to POSIX permissions,
415 # but these translations are not supported by native
419 # but these translations are not supported by native
416 # tools, so the exec bit tends to be set erroneously.
420 # tools, so the exec bit tends to be set erroneously.
417 # Therefore, disable executable bit access on Cygwin.
421 # Therefore, disable executable bit access on Cygwin.
418 def checkexec(path):
422 def checkexec(path):
419 return False
423 return False
420
424
421 # Similarly, Cygwin's symlink emulation is likely to create
425 # Similarly, Cygwin's symlink emulation is likely to create
422 # problems when Mercurial is used from both Cygwin and native
426 # problems when Mercurial is used from both Cygwin and native
423 # Windows, with other native tools, or on shared volumes
427 # Windows, with other native tools, or on shared volumes
424 def checklink(path):
428 def checklink(path):
425 return False
429 return False
426
430
427 _needsshellquote = None
431 _needsshellquote = None
428 def shellquote(s):
432 def shellquote(s):
429 if pycompat.sysplatform == 'OpenVMS':
433 if pycompat.sysplatform == 'OpenVMS':
430 return '"%s"' % s
434 return '"%s"' % s
431 global _needsshellquote
435 global _needsshellquote
432 if _needsshellquote is None:
436 if _needsshellquote is None:
433 _needsshellquote = re.compile(br'[^a-zA-Z0-9._/+-]').search
437 _needsshellquote = re.compile(br'[^a-zA-Z0-9._/+-]').search
434 if s and not _needsshellquote(s):
438 if s and not _needsshellquote(s):
435 # "s" shouldn't have to be quoted
439 # "s" shouldn't have to be quoted
436 return s
440 return s
437 else:
441 else:
438 return "'%s'" % s.replace("'", "'\\''")
442 return "'%s'" % s.replace("'", "'\\''")
439
443
440 def quotecommand(cmd):
444 def quotecommand(cmd):
441 return cmd
445 return cmd
442
446
443 def popen(command, mode='r'):
447 def popen(command, mode='r'):
444 return os.popen(command, mode)
448 return os.popen(command, mode)
445
449
446 def testpid(pid):
450 def testpid(pid):
447 '''return False if pid dead, True if running or not sure'''
451 '''return False if pid dead, True if running or not sure'''
448 if pycompat.sysplatform == 'OpenVMS':
452 if pycompat.sysplatform == 'OpenVMS':
449 return True
453 return True
450 try:
454 try:
451 os.kill(pid, 0)
455 os.kill(pid, 0)
452 return True
456 return True
453 except OSError as inst:
457 except OSError as inst:
454 return inst.errno != errno.ESRCH
458 return inst.errno != errno.ESRCH
455
459
456 def explainexit(code):
460 def explainexit(code):
457 """return a 2-tuple (desc, code) describing a subprocess status
461 """return a 2-tuple (desc, code) describing a subprocess status
458 (codes from kill are negative - not os.system/wait encoding)"""
462 (codes from kill are negative - not os.system/wait encoding)"""
459 if code >= 0:
463 if code >= 0:
460 return _("exited with status %d") % code, code
464 return _("exited with status %d") % code, code
461 return _("killed by signal %d") % -code, -code
465 return _("killed by signal %d") % -code, -code
462
466
463 def isowner(st):
467 def isowner(st):
464 """Return True if the stat object st is from the current user."""
468 """Return True if the stat object st is from the current user."""
465 return st.st_uid == os.getuid()
469 return st.st_uid == os.getuid()
466
470
467 def findexe(command):
471 def findexe(command):
468 '''Find executable for command searching like which does.
472 '''Find executable for command searching like which does.
469 If command is a basename then PATH is searched for command.
473 If command is a basename then PATH is searched for command.
470 PATH isn't searched if command is an absolute or relative path.
474 PATH isn't searched if command is an absolute or relative path.
471 If command isn't found None is returned.'''
475 If command isn't found None is returned.'''
472 if pycompat.sysplatform == 'OpenVMS':
476 if pycompat.sysplatform == 'OpenVMS':
473 return command
477 return command
474
478
475 def findexisting(executable):
479 def findexisting(executable):
476 'Will return executable if existing file'
480 'Will return executable if existing file'
477 if os.path.isfile(executable) and os.access(executable, os.X_OK):
481 if os.path.isfile(executable) and os.access(executable, os.X_OK):
478 return executable
482 return executable
479 return None
483 return None
480
484
481 if pycompat.ossep in command:
485 if pycompat.ossep in command:
482 return findexisting(command)
486 return findexisting(command)
483
487
484 if pycompat.sysplatform == 'plan9':
488 if pycompat.sysplatform == 'plan9':
485 return findexisting(os.path.join('/bin', command))
489 return findexisting(os.path.join('/bin', command))
486
490
487 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
491 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
488 executable = findexisting(os.path.join(path, command))
492 executable = findexisting(os.path.join(path, command))
489 if executable is not None:
493 if executable is not None:
490 return executable
494 return executable
491 return None
495 return None
492
496
493 def setsignalhandler():
497 def setsignalhandler():
494 pass
498 pass
495
499
496 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
500 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
497
501
498 def statfiles(files):
502 def statfiles(files):
499 '''Stat each file in files. Yield each stat, or None if a file does not
503 '''Stat each file in files. Yield each stat, or None if a file does not
500 exist or has a type we don't care about.'''
504 exist or has a type we don't care about.'''
501 lstat = os.lstat
505 lstat = os.lstat
502 getkind = stat.S_IFMT
506 getkind = stat.S_IFMT
503 for nf in files:
507 for nf in files:
504 try:
508 try:
505 st = lstat(nf)
509 st = lstat(nf)
506 if getkind(st.st_mode) not in _wantedkinds:
510 if getkind(st.st_mode) not in _wantedkinds:
507 st = None
511 st = None
508 except OSError as err:
512 except OSError as err:
509 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
513 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
510 raise
514 raise
511 st = None
515 st = None
512 yield st
516 yield st
513
517
514 def getuser():
518 def getuser():
515 '''return name of current user'''
519 '''return name of current user'''
516 return pycompat.fsencode(getpass.getuser())
520 return pycompat.fsencode(getpass.getuser())
517
521
518 def username(uid=None):
522 def username(uid=None):
519 """Return the name of the user with the given uid.
523 """Return the name of the user with the given uid.
520
524
521 If uid is None, return the name of the current user."""
525 If uid is None, return the name of the current user."""
522
526
523 if uid is None:
527 if uid is None:
524 uid = os.getuid()
528 uid = os.getuid()
525 try:
529 try:
526 return pwd.getpwuid(uid)[0]
530 return pwd.getpwuid(uid)[0]
527 except KeyError:
531 except KeyError:
528 return str(uid)
532 return str(uid)
529
533
530 def groupname(gid=None):
534 def groupname(gid=None):
531 """Return the name of the group with the given gid.
535 """Return the name of the group with the given gid.
532
536
533 If gid is None, return the name of the current group."""
537 If gid is None, return the name of the current group."""
534
538
535 if gid is None:
539 if gid is None:
536 gid = os.getgid()
540 gid = os.getgid()
537 try:
541 try:
538 return grp.getgrgid(gid)[0]
542 return grp.getgrgid(gid)[0]
539 except KeyError:
543 except KeyError:
540 return str(gid)
544 return str(gid)
541
545
542 def groupmembers(name):
546 def groupmembers(name):
543 """Return the list of members of the group with the given
547 """Return the list of members of the group with the given
544 name, KeyError if the group does not exist.
548 name, KeyError if the group does not exist.
545 """
549 """
546 return list(grp.getgrnam(name).gr_mem)
550 return list(grp.getgrnam(name).gr_mem)
547
551
548 def spawndetached(args):
552 def spawndetached(args):
549 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
553 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
550 args[0], args)
554 args[0], args)
551
555
552 def gethgcmd():
556 def gethgcmd():
553 return sys.argv[:1]
557 return sys.argv[:1]
554
558
555 def makedir(path, notindexed):
559 def makedir(path, notindexed):
556 os.mkdir(path)
560 os.mkdir(path)
557
561
558 def lookupreg(key, name=None, scope=None):
562 def lookupreg(key, name=None, scope=None):
559 return None
563 return None
560
564
561 def hidewindow():
565 def hidewindow():
562 """Hide current shell window.
566 """Hide current shell window.
563
567
564 Used to hide the window opened when starting asynchronous
568 Used to hide the window opened when starting asynchronous
565 child process under Windows, unneeded on other systems.
569 child process under Windows, unneeded on other systems.
566 """
570 """
567 pass
571 pass
568
572
569 class cachestat(object):
573 class cachestat(object):
570 def __init__(self, path):
574 def __init__(self, path):
571 self.stat = os.stat(path)
575 self.stat = os.stat(path)
572
576
573 def cacheable(self):
577 def cacheable(self):
574 return bool(self.stat.st_ino)
578 return bool(self.stat.st_ino)
575
579
576 __hash__ = object.__hash__
580 __hash__ = object.__hash__
577
581
578 def __eq__(self, other):
582 def __eq__(self, other):
579 try:
583 try:
580 # Only dev, ino, size, mtime and atime are likely to change. Out
584 # Only dev, ino, size, mtime and atime are likely to change. Out
581 # of these, we shouldn't compare atime but should compare the
585 # of these, we shouldn't compare atime but should compare the
582 # rest. However, one of the other fields changing indicates
586 # rest. However, one of the other fields changing indicates
583 # something fishy going on, so return False if anything but atime
587 # something fishy going on, so return False if anything but atime
584 # changes.
588 # changes.
585 return (self.stat.st_mode == other.stat.st_mode and
589 return (self.stat.st_mode == other.stat.st_mode and
586 self.stat.st_ino == other.stat.st_ino and
590 self.stat.st_ino == other.stat.st_ino and
587 self.stat.st_dev == other.stat.st_dev and
591 self.stat.st_dev == other.stat.st_dev and
588 self.stat.st_nlink == other.stat.st_nlink and
592 self.stat.st_nlink == other.stat.st_nlink and
589 self.stat.st_uid == other.stat.st_uid and
593 self.stat.st_uid == other.stat.st_uid and
590 self.stat.st_gid == other.stat.st_gid and
594 self.stat.st_gid == other.stat.st_gid and
591 self.stat.st_size == other.stat.st_size and
595 self.stat.st_size == other.stat.st_size and
592 self.stat.st_mtime == other.stat.st_mtime and
596 self.stat.st_mtime == other.stat.st_mtime and
593 self.stat.st_ctime == other.stat.st_ctime)
597 self.stat.st_ctime == other.stat.st_ctime)
594 except AttributeError:
598 except AttributeError:
595 return False
599 return False
596
600
597 def __ne__(self, other):
601 def __ne__(self, other):
598 return not self == other
602 return not self == other
599
603
600 def executablepath():
604 def executablepath():
601 return None # available on Windows only
605 return None # available on Windows only
602
606
603 def statislink(st):
607 def statislink(st):
604 '''check whether a stat result is a symlink'''
608 '''check whether a stat result is a symlink'''
605 return st and stat.S_ISLNK(st.st_mode)
609 return st and stat.S_ISLNK(st.st_mode)
606
610
607 def statisexec(st):
611 def statisexec(st):
608 '''check whether a stat result is an executable file'''
612 '''check whether a stat result is an executable file'''
609 return st and (st.st_mode & 0o100 != 0)
613 return st and (st.st_mode & 0o100 != 0)
610
614
611 def poll(fds):
615 def poll(fds):
612 """block until something happens on any file descriptor
616 """block until something happens on any file descriptor
613
617
614 This is a generic helper that will check for any activity
618 This is a generic helper that will check for any activity
615 (read, write. exception) and return the list of touched files.
619 (read, write. exception) and return the list of touched files.
616
620
617 In unsupported cases, it will raise a NotImplementedError"""
621 In unsupported cases, it will raise a NotImplementedError"""
618 try:
622 try:
619 while True:
623 while True:
620 try:
624 try:
621 res = select.select(fds, fds, fds)
625 res = select.select(fds, fds, fds)
622 break
626 break
623 except select.error as inst:
627 except select.error as inst:
624 if inst.args[0] == errno.EINTR:
628 if inst.args[0] == errno.EINTR:
625 continue
629 continue
626 raise
630 raise
627 except ValueError: # out of range file descriptor
631 except ValueError: # out of range file descriptor
628 raise NotImplementedError()
632 raise NotImplementedError()
629 return sorted(list(set(sum(res, []))))
633 return sorted(list(set(sum(res, []))))
630
634
631 def readpipe(pipe):
635 def readpipe(pipe):
632 """Read all available data from a pipe."""
636 """Read all available data from a pipe."""
633 # We can't fstat() a pipe because Linux will always report 0.
637 # We can't fstat() a pipe because Linux will always report 0.
634 # So, we set the pipe to non-blocking mode and read everything
638 # So, we set the pipe to non-blocking mode and read everything
635 # that's available.
639 # that's available.
636 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
640 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
637 flags |= os.O_NONBLOCK
641 flags |= os.O_NONBLOCK
638 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
642 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
639
643
640 try:
644 try:
641 chunks = []
645 chunks = []
642 while True:
646 while True:
643 try:
647 try:
644 s = pipe.read()
648 s = pipe.read()
645 if not s:
649 if not s:
646 break
650 break
647 chunks.append(s)
651 chunks.append(s)
648 except IOError:
652 except IOError:
649 break
653 break
650
654
651 return ''.join(chunks)
655 return ''.join(chunks)
652 finally:
656 finally:
653 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
657 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
654
658
655 def bindunixsocket(sock, path):
659 def bindunixsocket(sock, path):
656 """Bind the UNIX domain socket to the specified path"""
660 """Bind the UNIX domain socket to the specified path"""
657 # use relative path instead of full path at bind() if possible, since
661 # use relative path instead of full path at bind() if possible, since
658 # AF_UNIX path has very small length limit (107 chars) on common
662 # AF_UNIX path has very small length limit (107 chars) on common
659 # platforms (see sys/un.h)
663 # platforms (see sys/un.h)
660 dirname, basename = os.path.split(path)
664 dirname, basename = os.path.split(path)
661 bakwdfd = None
665 bakwdfd = None
662 if dirname:
666 if dirname:
663 bakwdfd = os.open('.', os.O_DIRECTORY)
667 bakwdfd = os.open('.', os.O_DIRECTORY)
664 os.chdir(dirname)
668 os.chdir(dirname)
665 sock.bind(basename)
669 sock.bind(basename)
666 if bakwdfd:
670 if bakwdfd:
667 os.fchdir(bakwdfd)
671 os.fchdir(bakwdfd)
668 os.close(bakwdfd)
672 os.close(bakwdfd)
@@ -1,476 +1,481 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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import msvcrt
11 import msvcrt
12 import os
12 import os
13 import re
13 import re
14 import stat
14 import stat
15 import sys
15 import sys
16
16
17 from .i18n import _
17 from .i18n import _
18 from . import (
18 from . import (
19 encoding,
19 encoding,
20 error,
20 policy,
21 policy,
21 pycompat,
22 pycompat,
22 win32,
23 win32,
23 )
24 )
24
25
25 try:
26 try:
26 import _winreg as winreg
27 import _winreg as winreg
27 winreg.CloseKey
28 winreg.CloseKey
28 except ImportError:
29 except ImportError:
29 import winreg
30 import winreg
30
31
31 osutil = policy.importmod(r'osutil')
32 osutil = policy.importmod(r'osutil')
32
33
33 executablepath = win32.executablepath
34 executablepath = win32.executablepath
34 getuser = win32.getuser
35 getuser = win32.getuser
35 hidewindow = win32.hidewindow
36 hidewindow = win32.hidewindow
36 makedir = win32.makedir
37 makedir = win32.makedir
37 nlinks = win32.nlinks
38 nlinks = win32.nlinks
38 oslink = win32.oslink
39 oslink = win32.oslink
39 samedevice = win32.samedevice
40 samedevice = win32.samedevice
40 samefile = win32.samefile
41 samefile = win32.samefile
41 setsignalhandler = win32.setsignalhandler
42 setsignalhandler = win32.setsignalhandler
42 spawndetached = win32.spawndetached
43 spawndetached = win32.spawndetached
43 split = os.path.split
44 split = os.path.split
44 testpid = win32.testpid
45 testpid = win32.testpid
45 unlink = win32.unlink
46 unlink = win32.unlink
46
47
47 umask = 0o022
48 umask = 0o022
48
49
49 class mixedfilemodewrapper(object):
50 class mixedfilemodewrapper(object):
50 """Wraps a file handle when it is opened in read/write mode.
51 """Wraps a file handle when it is opened in read/write mode.
51
52
52 fopen() and fdopen() on Windows have a specific-to-Windows requirement
53 fopen() and fdopen() on Windows have a specific-to-Windows requirement
53 that files opened with mode r+, w+, or a+ make a call to a file positioning
54 that files opened with mode r+, w+, or a+ make a call to a file positioning
54 function when switching between reads and writes. Without this extra call,
55 function when switching between reads and writes. Without this extra call,
55 Python will raise a not very intuitive "IOError: [Errno 0] Error."
56 Python will raise a not very intuitive "IOError: [Errno 0] Error."
56
57
57 This class wraps posixfile instances when the file is opened in read/write
58 This class wraps posixfile instances when the file is opened in read/write
58 mode and automatically adds checks or inserts appropriate file positioning
59 mode and automatically adds checks or inserts appropriate file positioning
59 calls when necessary.
60 calls when necessary.
60 """
61 """
61 OPNONE = 0
62 OPNONE = 0
62 OPREAD = 1
63 OPREAD = 1
63 OPWRITE = 2
64 OPWRITE = 2
64
65
65 def __init__(self, fp):
66 def __init__(self, fp):
66 object.__setattr__(self, r'_fp', fp)
67 object.__setattr__(self, r'_fp', fp)
67 object.__setattr__(self, r'_lastop', 0)
68 object.__setattr__(self, r'_lastop', 0)
68
69
69 def __enter__(self):
70 def __enter__(self):
70 return self._fp.__enter__()
71 return self._fp.__enter__()
71
72
72 def __exit__(self, exc_type, exc_val, exc_tb):
73 def __exit__(self, exc_type, exc_val, exc_tb):
73 self._fp.__exit__(exc_type, exc_val, exc_tb)
74 self._fp.__exit__(exc_type, exc_val, exc_tb)
74
75
75 def __getattr__(self, name):
76 def __getattr__(self, name):
76 return getattr(self._fp, name)
77 return getattr(self._fp, name)
77
78
78 def __setattr__(self, name, value):
79 def __setattr__(self, name, value):
79 return self._fp.__setattr__(name, value)
80 return self._fp.__setattr__(name, value)
80
81
81 def _noopseek(self):
82 def _noopseek(self):
82 self._fp.seek(0, os.SEEK_CUR)
83 self._fp.seek(0, os.SEEK_CUR)
83
84
84 def seek(self, *args, **kwargs):
85 def seek(self, *args, **kwargs):
85 object.__setattr__(self, r'_lastop', self.OPNONE)
86 object.__setattr__(self, r'_lastop', self.OPNONE)
86 return self._fp.seek(*args, **kwargs)
87 return self._fp.seek(*args, **kwargs)
87
88
88 def write(self, d):
89 def write(self, d):
89 if self._lastop == self.OPREAD:
90 if self._lastop == self.OPREAD:
90 self._noopseek()
91 self._noopseek()
91
92
92 object.__setattr__(self, r'_lastop', self.OPWRITE)
93 object.__setattr__(self, r'_lastop', self.OPWRITE)
93 return self._fp.write(d)
94 return self._fp.write(d)
94
95
95 def writelines(self, *args, **kwargs):
96 def writelines(self, *args, **kwargs):
96 if self._lastop == self.OPREAD:
97 if self._lastop == self.OPREAD:
97 self._noopeseek()
98 self._noopeseek()
98
99
99 object.__setattr__(self, r'_lastop', self.OPWRITE)
100 object.__setattr__(self, r'_lastop', self.OPWRITE)
100 return self._fp.writelines(*args, **kwargs)
101 return self._fp.writelines(*args, **kwargs)
101
102
102 def read(self, *args, **kwargs):
103 def read(self, *args, **kwargs):
103 if self._lastop == self.OPWRITE:
104 if self._lastop == self.OPWRITE:
104 self._noopseek()
105 self._noopseek()
105
106
106 object.__setattr__(self, r'_lastop', self.OPREAD)
107 object.__setattr__(self, r'_lastop', self.OPREAD)
107 return self._fp.read(*args, **kwargs)
108 return self._fp.read(*args, **kwargs)
108
109
109 def readline(self, *args, **kwargs):
110 def readline(self, *args, **kwargs):
110 if self._lastop == self.OPWRITE:
111 if self._lastop == self.OPWRITE:
111 self._noopseek()
112 self._noopseek()
112
113
113 object.__setattr__(self, r'_lastop', self.OPREAD)
114 object.__setattr__(self, r'_lastop', self.OPREAD)
114 return self._fp.readline(*args, **kwargs)
115 return self._fp.readline(*args, **kwargs)
115
116
116 def readlines(self, *args, **kwargs):
117 def readlines(self, *args, **kwargs):
117 if self._lastop == self.OPWRITE:
118 if self._lastop == self.OPWRITE:
118 self._noopseek()
119 self._noopseek()
119
120
120 object.__setattr__(self, r'_lastop', self.OPREAD)
121 object.__setattr__(self, r'_lastop', self.OPREAD)
121 return self._fp.readlines(*args, **kwargs)
122 return self._fp.readlines(*args, **kwargs)
122
123
123 def posixfile(name, mode='r', buffering=-1):
124 def posixfile(name, mode='r', buffering=-1):
124 '''Open a file with even more POSIX-like semantics'''
125 '''Open a file with even more POSIX-like semantics'''
125 try:
126 try:
126 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
127 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
127
128
128 # The position when opening in append mode is implementation defined, so
129 # The position when opening in append mode is implementation defined, so
129 # make it consistent with other platforms, which position at EOF.
130 # make it consistent with other platforms, which position at EOF.
130 if 'a' in mode:
131 if 'a' in mode:
131 fp.seek(0, os.SEEK_END)
132 fp.seek(0, os.SEEK_END)
132
133
133 if '+' in mode:
134 if '+' in mode:
134 return mixedfilemodewrapper(fp)
135 return mixedfilemodewrapper(fp)
135
136
136 return fp
137 return fp
137 except WindowsError as err:
138 except WindowsError as err:
138 # convert to a friendlier exception
139 # convert to a friendlier exception
139 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
140 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
140
141
141 # may be wrapped by win32mbcs extension
142 # may be wrapped by win32mbcs extension
142 listdir = osutil.listdir
143 listdir = osutil.listdir
143
144
144 class winstdout(object):
145 class winstdout(object):
145 '''stdout on windows misbehaves if sent through a pipe'''
146 '''stdout on windows misbehaves if sent through a pipe'''
146
147
147 def __init__(self, fp):
148 def __init__(self, fp):
148 self.fp = fp
149 self.fp = fp
149
150
150 def __getattr__(self, key):
151 def __getattr__(self, key):
151 return getattr(self.fp, key)
152 return getattr(self.fp, key)
152
153
153 def close(self):
154 def close(self):
154 try:
155 try:
155 self.fp.close()
156 self.fp.close()
156 except IOError:
157 except IOError:
157 pass
158 pass
158
159
159 def write(self, s):
160 def write(self, s):
160 try:
161 try:
161 # This is workaround for "Not enough space" error on
162 # This is workaround for "Not enough space" error on
162 # writing large size of data to console.
163 # writing large size of data to console.
163 limit = 16000
164 limit = 16000
164 l = len(s)
165 l = len(s)
165 start = 0
166 start = 0
166 self.softspace = 0
167 self.softspace = 0
167 while start < l:
168 while start < l:
168 end = start + limit
169 end = start + limit
169 self.fp.write(s[start:end])
170 self.fp.write(s[start:end])
170 start = end
171 start = end
171 except IOError as inst:
172 except IOError as inst:
172 if inst.errno != 0:
173 if inst.errno != 0:
173 raise
174 raise
174 self.close()
175 self.close()
175 raise IOError(errno.EPIPE, 'Broken pipe')
176 raise IOError(errno.EPIPE, 'Broken pipe')
176
177
177 def flush(self):
178 def flush(self):
178 try:
179 try:
179 return self.fp.flush()
180 return self.fp.flush()
180 except IOError as inst:
181 except IOError as inst:
181 if inst.errno != errno.EINVAL:
182 if inst.errno != errno.EINVAL:
182 raise
183 raise
183 raise IOError(errno.EPIPE, 'Broken pipe')
184 raise IOError(errno.EPIPE, 'Broken pipe')
184
185
185 def _is_win_9x():
186 def _is_win_9x():
186 '''return true if run on windows 95, 98 or me.'''
187 '''return true if run on windows 95, 98 or me.'''
187 try:
188 try:
188 return sys.getwindowsversion()[3] == 1
189 return sys.getwindowsversion()[3] == 1
189 except AttributeError:
190 except AttributeError:
190 return 'command' in encoding.environ.get('comspec', '')
191 return 'command' in encoding.environ.get('comspec', '')
191
192
192 def openhardlinks():
193 def openhardlinks():
193 return not _is_win_9x()
194 return not _is_win_9x()
194
195
195 def parsepatchoutput(output_line):
196 def parsepatchoutput(output_line):
196 """parses the output produced by patch and returns the filename"""
197 """parses the output produced by patch and returns the filename"""
197 pf = output_line[14:]
198 pf = output_line[14:]
198 if pf[0] == '`':
199 if pf[0] == '`':
199 pf = pf[1:-1] # Remove the quotes
200 pf = pf[1:-1] # Remove the quotes
200 return pf
201 return pf
201
202
202 def sshargs(sshcmd, host, user, port):
203 def sshargs(sshcmd, host, user, port):
203 '''Build argument list for ssh or Plink'''
204 '''Build argument list for ssh or Plink'''
204 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
205 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
205 args = user and ("%s@%s" % (user, host)) or host
206 args = user and ("%s@%s" % (user, host)) or host
207 if args.startswith('-') or args.startswith('/'):
208 raise error.Abort(
209 _('illegal ssh hostname or username starting with - or /: %s') %
210 args)
206 return port and ("%s %s %s" % (args, pflag, port)) or args
211 return port and ("%s %s %s" % (args, pflag, port)) or args
207
212
208 def setflags(f, l, x):
213 def setflags(f, l, x):
209 pass
214 pass
210
215
211 def copymode(src, dst, mode=None):
216 def copymode(src, dst, mode=None):
212 pass
217 pass
213
218
214 def checkexec(path):
219 def checkexec(path):
215 return False
220 return False
216
221
217 def checklink(path):
222 def checklink(path):
218 return False
223 return False
219
224
220 def setbinary(fd):
225 def setbinary(fd):
221 # When run without console, pipes may expose invalid
226 # When run without console, pipes may expose invalid
222 # fileno(), usually set to -1.
227 # fileno(), usually set to -1.
223 fno = getattr(fd, 'fileno', None)
228 fno = getattr(fd, 'fileno', None)
224 if fno is not None and fno() >= 0:
229 if fno is not None and fno() >= 0:
225 msvcrt.setmode(fno(), os.O_BINARY)
230 msvcrt.setmode(fno(), os.O_BINARY)
226
231
227 def pconvert(path):
232 def pconvert(path):
228 return path.replace(pycompat.ossep, '/')
233 return path.replace(pycompat.ossep, '/')
229
234
230 def localpath(path):
235 def localpath(path):
231 return path.replace('/', '\\')
236 return path.replace('/', '\\')
232
237
233 def normpath(path):
238 def normpath(path):
234 return pconvert(os.path.normpath(path))
239 return pconvert(os.path.normpath(path))
235
240
236 def normcase(path):
241 def normcase(path):
237 return encoding.upper(path) # NTFS compares via upper()
242 return encoding.upper(path) # NTFS compares via upper()
238
243
239 # see posix.py for definitions
244 # see posix.py for definitions
240 normcasespec = encoding.normcasespecs.upper
245 normcasespec = encoding.normcasespecs.upper
241 normcasefallback = encoding.upperfallback
246 normcasefallback = encoding.upperfallback
242
247
243 def samestat(s1, s2):
248 def samestat(s1, s2):
244 return False
249 return False
245
250
246 # A sequence of backslashes is special iff it precedes a double quote:
251 # A sequence of backslashes is special iff it precedes a double quote:
247 # - if there's an even number of backslashes, the double quote is not
252 # - if there's an even number of backslashes, the double quote is not
248 # quoted (i.e. it ends the quoted region)
253 # quoted (i.e. it ends the quoted region)
249 # - if there's an odd number of backslashes, the double quote is quoted
254 # - if there's an odd number of backslashes, the double quote is quoted
250 # - in both cases, every pair of backslashes is unquoted into a single
255 # - in both cases, every pair of backslashes is unquoted into a single
251 # backslash
256 # backslash
252 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
257 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
253 # So, to quote a string, we must surround it in double quotes, double
258 # So, to quote a string, we must surround it in double quotes, double
254 # the number of backslashes that precede double quotes and add another
259 # the number of backslashes that precede double quotes and add another
255 # backslash before every double quote (being careful with the double
260 # backslash before every double quote (being careful with the double
256 # quote we've appended to the end)
261 # quote we've appended to the end)
257 _quotere = None
262 _quotere = None
258 _needsshellquote = None
263 _needsshellquote = None
259 def shellquote(s):
264 def shellquote(s):
260 r"""
265 r"""
261 >>> shellquote(r'C:\Users\xyz')
266 >>> shellquote(r'C:\Users\xyz')
262 '"C:\\Users\\xyz"'
267 '"C:\\Users\\xyz"'
263 >>> shellquote(r'C:\Users\xyz/mixed')
268 >>> shellquote(r'C:\Users\xyz/mixed')
264 '"C:\\Users\\xyz/mixed"'
269 '"C:\\Users\\xyz/mixed"'
265 >>> # Would be safe not to quote too, since it is all double backslashes
270 >>> # Would be safe not to quote too, since it is all double backslashes
266 >>> shellquote(r'C:\\Users\\xyz')
271 >>> shellquote(r'C:\\Users\\xyz')
267 '"C:\\\\Users\\\\xyz"'
272 '"C:\\\\Users\\\\xyz"'
268 >>> # But this must be quoted
273 >>> # But this must be quoted
269 >>> shellquote(r'C:\\Users\\xyz/abc')
274 >>> shellquote(r'C:\\Users\\xyz/abc')
270 '"C:\\\\Users\\\\xyz/abc"'
275 '"C:\\\\Users\\\\xyz/abc"'
271 """
276 """
272 global _quotere
277 global _quotere
273 if _quotere is None:
278 if _quotere is None:
274 _quotere = re.compile(r'(\\*)("|\\$)')
279 _quotere = re.compile(r'(\\*)("|\\$)')
275 global _needsshellquote
280 global _needsshellquote
276 if _needsshellquote is None:
281 if _needsshellquote is None:
277 # ":" is also treated as "safe character", because it is used as a part
282 # ":" is also treated as "safe character", because it is used as a part
278 # of path name on Windows. "\" is also part of a path name, but isn't
283 # of path name on Windows. "\" is also part of a path name, but isn't
279 # safe because shlex.split() (kind of) treats it as an escape char and
284 # safe because shlex.split() (kind of) treats it as an escape char and
280 # drops it. It will leave the next character, even if it is another
285 # drops it. It will leave the next character, even if it is another
281 # "\".
286 # "\".
282 _needsshellquote = re.compile(r'[^a-zA-Z0-9._:/-]').search
287 _needsshellquote = re.compile(r'[^a-zA-Z0-9._:/-]').search
283 if s and not _needsshellquote(s) and not _quotere.search(s):
288 if s and not _needsshellquote(s) and not _quotere.search(s):
284 # "s" shouldn't have to be quoted
289 # "s" shouldn't have to be quoted
285 return s
290 return s
286 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
291 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
287
292
288 def quotecommand(cmd):
293 def quotecommand(cmd):
289 """Build a command string suitable for os.popen* calls."""
294 """Build a command string suitable for os.popen* calls."""
290 if sys.version_info < (2, 7, 1):
295 if sys.version_info < (2, 7, 1):
291 # Python versions since 2.7.1 do this extra quoting themselves
296 # Python versions since 2.7.1 do this extra quoting themselves
292 return '"' + cmd + '"'
297 return '"' + cmd + '"'
293 return cmd
298 return cmd
294
299
295 def popen(command, mode='r'):
300 def popen(command, mode='r'):
296 # Work around "popen spawned process may not write to stdout
301 # Work around "popen spawned process may not write to stdout
297 # under windows"
302 # under windows"
298 # http://bugs.python.org/issue1366
303 # http://bugs.python.org/issue1366
299 command += " 2> %s" % os.devnull
304 command += " 2> %s" % os.devnull
300 return os.popen(quotecommand(command), mode)
305 return os.popen(quotecommand(command), mode)
301
306
302 def explainexit(code):
307 def explainexit(code):
303 return _("exited with status %d") % code, code
308 return _("exited with status %d") % code, code
304
309
305 # if you change this stub into a real check, please try to implement the
310 # if you change this stub into a real check, please try to implement the
306 # username and groupname functions above, too.
311 # username and groupname functions above, too.
307 def isowner(st):
312 def isowner(st):
308 return True
313 return True
309
314
310 def findexe(command):
315 def findexe(command):
311 '''Find executable for command searching like cmd.exe does.
316 '''Find executable for command searching like cmd.exe does.
312 If command is a basename then PATH is searched for command.
317 If command is a basename then PATH is searched for command.
313 PATH isn't searched if command is an absolute or relative path.
318 PATH isn't searched if command is an absolute or relative path.
314 An extension from PATHEXT is found and added if not present.
319 An extension from PATHEXT is found and added if not present.
315 If command isn't found None is returned.'''
320 If command isn't found None is returned.'''
316 pathext = encoding.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
321 pathext = encoding.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
317 pathexts = [ext for ext in pathext.lower().split(pycompat.ospathsep)]
322 pathexts = [ext for ext in pathext.lower().split(pycompat.ospathsep)]
318 if os.path.splitext(command)[1].lower() in pathexts:
323 if os.path.splitext(command)[1].lower() in pathexts:
319 pathexts = ['']
324 pathexts = ['']
320
325
321 def findexisting(pathcommand):
326 def findexisting(pathcommand):
322 'Will append extension (if needed) and return existing file'
327 'Will append extension (if needed) and return existing file'
323 for ext in pathexts:
328 for ext in pathexts:
324 executable = pathcommand + ext
329 executable = pathcommand + ext
325 if os.path.exists(executable):
330 if os.path.exists(executable):
326 return executable
331 return executable
327 return None
332 return None
328
333
329 if pycompat.ossep in command:
334 if pycompat.ossep in command:
330 return findexisting(command)
335 return findexisting(command)
331
336
332 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
337 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
333 executable = findexisting(os.path.join(path, command))
338 executable = findexisting(os.path.join(path, command))
334 if executable is not None:
339 if executable is not None:
335 return executable
340 return executable
336 return findexisting(os.path.expanduser(os.path.expandvars(command)))
341 return findexisting(os.path.expanduser(os.path.expandvars(command)))
337
342
338 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
343 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
339
344
340 def statfiles(files):
345 def statfiles(files):
341 '''Stat each file in files. Yield each stat, or None if a file
346 '''Stat each file in files. Yield each stat, or None if a file
342 does not exist or has a type we don't care about.
347 does not exist or has a type we don't care about.
343
348
344 Cluster and cache stat per directory to minimize number of OS stat calls.'''
349 Cluster and cache stat per directory to minimize number of OS stat calls.'''
345 dircache = {} # dirname -> filename -> status | None if file does not exist
350 dircache = {} # dirname -> filename -> status | None if file does not exist
346 getkind = stat.S_IFMT
351 getkind = stat.S_IFMT
347 for nf in files:
352 for nf in files:
348 nf = normcase(nf)
353 nf = normcase(nf)
349 dir, base = os.path.split(nf)
354 dir, base = os.path.split(nf)
350 if not dir:
355 if not dir:
351 dir = '.'
356 dir = '.'
352 cache = dircache.get(dir, None)
357 cache = dircache.get(dir, None)
353 if cache is None:
358 if cache is None:
354 try:
359 try:
355 dmap = dict([(normcase(n), s)
360 dmap = dict([(normcase(n), s)
356 for n, k, s in listdir(dir, True)
361 for n, k, s in listdir(dir, True)
357 if getkind(s.st_mode) in _wantedkinds])
362 if getkind(s.st_mode) in _wantedkinds])
358 except OSError as err:
363 except OSError as err:
359 # Python >= 2.5 returns ENOENT and adds winerror field
364 # Python >= 2.5 returns ENOENT and adds winerror field
360 # EINVAL is raised if dir is not a directory.
365 # EINVAL is raised if dir is not a directory.
361 if err.errno not in (errno.ENOENT, errno.EINVAL,
366 if err.errno not in (errno.ENOENT, errno.EINVAL,
362 errno.ENOTDIR):
367 errno.ENOTDIR):
363 raise
368 raise
364 dmap = {}
369 dmap = {}
365 cache = dircache.setdefault(dir, dmap)
370 cache = dircache.setdefault(dir, dmap)
366 yield cache.get(base, None)
371 yield cache.get(base, None)
367
372
368 def username(uid=None):
373 def username(uid=None):
369 """Return the name of the user with the given uid.
374 """Return the name of the user with the given uid.
370
375
371 If uid is None, return the name of the current user."""
376 If uid is None, return the name of the current user."""
372 return None
377 return None
373
378
374 def groupname(gid=None):
379 def groupname(gid=None):
375 """Return the name of the group with the given gid.
380 """Return the name of the group with the given gid.
376
381
377 If gid is None, return the name of the current group."""
382 If gid is None, return the name of the current group."""
378 return None
383 return None
379
384
380 def removedirs(name):
385 def removedirs(name):
381 """special version of os.removedirs that does not remove symlinked
386 """special version of os.removedirs that does not remove symlinked
382 directories or junction points if they actually contain files"""
387 directories or junction points if they actually contain files"""
383 if listdir(name):
388 if listdir(name):
384 return
389 return
385 os.rmdir(name)
390 os.rmdir(name)
386 head, tail = os.path.split(name)
391 head, tail = os.path.split(name)
387 if not tail:
392 if not tail:
388 head, tail = os.path.split(head)
393 head, tail = os.path.split(head)
389 while head and tail:
394 while head and tail:
390 try:
395 try:
391 if listdir(head):
396 if listdir(head):
392 return
397 return
393 os.rmdir(head)
398 os.rmdir(head)
394 except (ValueError, OSError):
399 except (ValueError, OSError):
395 break
400 break
396 head, tail = os.path.split(head)
401 head, tail = os.path.split(head)
397
402
398 def rename(src, dst):
403 def rename(src, dst):
399 '''atomically rename file src to dst, replacing dst if it exists'''
404 '''atomically rename file src to dst, replacing dst if it exists'''
400 try:
405 try:
401 os.rename(src, dst)
406 os.rename(src, dst)
402 except OSError as e:
407 except OSError as e:
403 if e.errno != errno.EEXIST:
408 if e.errno != errno.EEXIST:
404 raise
409 raise
405 unlink(dst)
410 unlink(dst)
406 os.rename(src, dst)
411 os.rename(src, dst)
407
412
408 def gethgcmd():
413 def gethgcmd():
409 return [sys.executable] + sys.argv[:1]
414 return [sys.executable] + sys.argv[:1]
410
415
411 def groupmembers(name):
416 def groupmembers(name):
412 # Don't support groups on Windows for now
417 # Don't support groups on Windows for now
413 raise KeyError
418 raise KeyError
414
419
415 def isexec(f):
420 def isexec(f):
416 return False
421 return False
417
422
418 class cachestat(object):
423 class cachestat(object):
419 def __init__(self, path):
424 def __init__(self, path):
420 pass
425 pass
421
426
422 def cacheable(self):
427 def cacheable(self):
423 return False
428 return False
424
429
425 def lookupreg(key, valname=None, scope=None):
430 def lookupreg(key, valname=None, scope=None):
426 ''' Look up a key/value name in the Windows registry.
431 ''' Look up a key/value name in the Windows registry.
427
432
428 valname: value name. If unspecified, the default value for the key
433 valname: value name. If unspecified, the default value for the key
429 is used.
434 is used.
430 scope: optionally specify scope for registry lookup, this can be
435 scope: optionally specify scope for registry lookup, this can be
431 a sequence of scopes to look up in order. Default (CURRENT_USER,
436 a sequence of scopes to look up in order. Default (CURRENT_USER,
432 LOCAL_MACHINE).
437 LOCAL_MACHINE).
433 '''
438 '''
434 if scope is None:
439 if scope is None:
435 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
440 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
436 elif not isinstance(scope, (list, tuple)):
441 elif not isinstance(scope, (list, tuple)):
437 scope = (scope,)
442 scope = (scope,)
438 for s in scope:
443 for s in scope:
439 try:
444 try:
440 val = winreg.QueryValueEx(winreg.OpenKey(s, key), valname)[0]
445 val = winreg.QueryValueEx(winreg.OpenKey(s, key), valname)[0]
441 # never let a Unicode string escape into the wild
446 # never let a Unicode string escape into the wild
442 return encoding.unitolocal(val)
447 return encoding.unitolocal(val)
443 except EnvironmentError:
448 except EnvironmentError:
444 pass
449 pass
445
450
446 expandglobs = True
451 expandglobs = True
447
452
448 def statislink(st):
453 def statislink(st):
449 '''check whether a stat result is a symlink'''
454 '''check whether a stat result is a symlink'''
450 return False
455 return False
451
456
452 def statisexec(st):
457 def statisexec(st):
453 '''check whether a stat result is an executable file'''
458 '''check whether a stat result is an executable file'''
454 return False
459 return False
455
460
456 def poll(fds):
461 def poll(fds):
457 # see posix.py for description
462 # see posix.py for description
458 raise NotImplementedError()
463 raise NotImplementedError()
459
464
460 def readpipe(pipe):
465 def readpipe(pipe):
461 """Read all available data from a pipe."""
466 """Read all available data from a pipe."""
462 chunks = []
467 chunks = []
463 while True:
468 while True:
464 size = win32.peekpipe(pipe)
469 size = win32.peekpipe(pipe)
465 if not size:
470 if not size:
466 break
471 break
467
472
468 s = pipe.read(size)
473 s = pipe.read(size)
469 if not s:
474 if not s:
470 break
475 break
471 chunks.append(s)
476 chunks.append(s)
472
477
473 return ''.join(chunks)
478 return ''.join(chunks)
474
479
475 def bindunixsocket(sock, path):
480 def bindunixsocket(sock, path):
476 raise NotImplementedError('unsupported platform')
481 raise NotImplementedError('unsupported platform')
General Comments 0
You need to be logged in to leave comments. Login now