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