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