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