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