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