##// END OF EJS Templates
util.termwidth: never return 0 for terminal width...
jfh -
r14165:78bdfc75 default
parent child Browse files
Show More
@@ -1,329 +1,331 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 os, sys, errno, stat, getpass, pwd, grp, tempfile
9 import os, sys, errno, stat, getpass, pwd, grp, tempfile
10
10
11 posixfile = open
11 posixfile = open
12 nulldev = '/dev/null'
12 nulldev = '/dev/null'
13 normpath = os.path.normpath
13 normpath = os.path.normpath
14 samestat = os.path.samestat
14 samestat = os.path.samestat
15 os_link = os.link
15 os_link = 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 openhardlinks():
23 def openhardlinks():
24 '''return true if it is safe to hold open file handles to hardlinks'''
24 '''return true if it is safe to hold open file handles to hardlinks'''
25 return True
25 return True
26
26
27 def nlinks(name):
27 def nlinks(name):
28 '''return number of hardlinks for the given file'''
28 '''return number of hardlinks for the given file'''
29 return os.lstat(name).st_nlink
29 return os.lstat(name).st_nlink
30
30
31 def parse_patch_output(output_line):
31 def parse_patch_output(output_line):
32 """parses the output produced by patch and returns the filename"""
32 """parses the output produced by patch and returns the filename"""
33 pf = output_line[14:]
33 pf = output_line[14:]
34 if os.sys.platform == 'OpenVMS':
34 if os.sys.platform == 'OpenVMS':
35 if pf[0] == '`':
35 if pf[0] == '`':
36 pf = pf[1:-1] # Remove the quotes
36 pf = pf[1:-1] # Remove the quotes
37 else:
37 else:
38 if pf.startswith("'") and pf.endswith("'") and " " in pf:
38 if pf.startswith("'") and pf.endswith("'") and " " in pf:
39 pf = pf[1:-1] # Remove the quotes
39 pf = pf[1:-1] # Remove the quotes
40 return pf
40 return pf
41
41
42 def sshargs(sshcmd, host, user, port):
42 def sshargs(sshcmd, host, user, port):
43 '''Build argument list for ssh'''
43 '''Build argument list for ssh'''
44 args = user and ("%s@%s" % (user, host)) or host
44 args = user and ("%s@%s" % (user, host)) or host
45 return port and ("%s -p %s" % (args, port)) or args
45 return port and ("%s -p %s" % (args, port)) or args
46
46
47 def is_exec(f):
47 def is_exec(f):
48 """check whether a file is executable"""
48 """check whether a file is executable"""
49 return (os.lstat(f).st_mode & 0100 != 0)
49 return (os.lstat(f).st_mode & 0100 != 0)
50
50
51 def set_flags(f, l, x):
51 def set_flags(f, l, x):
52 s = os.lstat(f).st_mode
52 s = os.lstat(f).st_mode
53 if l:
53 if l:
54 if not stat.S_ISLNK(s):
54 if not stat.S_ISLNK(s):
55 # switch file to link
55 # switch file to link
56 fp = open(f)
56 fp = open(f)
57 data = fp.read()
57 data = fp.read()
58 fp.close()
58 fp.close()
59 os.unlink(f)
59 os.unlink(f)
60 try:
60 try:
61 os.symlink(data, f)
61 os.symlink(data, f)
62 except OSError:
62 except OSError:
63 # failed to make a link, rewrite file
63 # failed to make a link, rewrite file
64 fp = open(f, "w")
64 fp = open(f, "w")
65 fp.write(data)
65 fp.write(data)
66 fp.close()
66 fp.close()
67 # no chmod needed at this point
67 # no chmod needed at this point
68 return
68 return
69 if stat.S_ISLNK(s):
69 if stat.S_ISLNK(s):
70 # switch link to file
70 # switch link to file
71 data = os.readlink(f)
71 data = os.readlink(f)
72 os.unlink(f)
72 os.unlink(f)
73 fp = open(f, "w")
73 fp = open(f, "w")
74 fp.write(data)
74 fp.write(data)
75 fp.close()
75 fp.close()
76 s = 0666 & ~umask # avoid restatting for chmod
76 s = 0666 & ~umask # avoid restatting for chmod
77
77
78 sx = s & 0100
78 sx = s & 0100
79 if x and not sx:
79 if x and not sx:
80 # Turn on +x for every +r bit when making a file executable
80 # Turn on +x for every +r bit when making a file executable
81 # and obey umask.
81 # and obey umask.
82 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
82 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
83 elif not x and sx:
83 elif not x and sx:
84 # Turn off all +x bits
84 # Turn off all +x bits
85 os.chmod(f, s & 0666)
85 os.chmod(f, s & 0666)
86
86
87 def checkexec(path):
87 def checkexec(path):
88 """
88 """
89 Check whether the given path is on a filesystem with UNIX-like exec flags
89 Check whether the given path is on a filesystem with UNIX-like exec flags
90
90
91 Requires a directory (like /foo/.hg)
91 Requires a directory (like /foo/.hg)
92 """
92 """
93
93
94 # VFAT on some Linux versions can flip mode but it doesn't persist
94 # VFAT on some Linux versions can flip mode but it doesn't persist
95 # a FS remount. Frequently we can detect it if files are created
95 # a FS remount. Frequently we can detect it if files are created
96 # with exec bit on.
96 # with exec bit on.
97
97
98 try:
98 try:
99 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
99 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
100 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
100 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
101 try:
101 try:
102 os.close(fh)
102 os.close(fh)
103 m = os.stat(fn).st_mode & 0777
103 m = os.stat(fn).st_mode & 0777
104 new_file_has_exec = m & EXECFLAGS
104 new_file_has_exec = m & EXECFLAGS
105 os.chmod(fn, m ^ EXECFLAGS)
105 os.chmod(fn, m ^ EXECFLAGS)
106 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
106 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
107 finally:
107 finally:
108 os.unlink(fn)
108 os.unlink(fn)
109 except (IOError, OSError):
109 except (IOError, OSError):
110 # we don't care, the user probably won't be able to commit anyway
110 # we don't care, the user probably won't be able to commit anyway
111 return False
111 return False
112 return not (new_file_has_exec or exec_flags_cannot_flip)
112 return not (new_file_has_exec or exec_flags_cannot_flip)
113
113
114 def checklink(path):
114 def checklink(path):
115 """check whether the given path is on a symlink-capable filesystem"""
115 """check whether the given path is on a symlink-capable filesystem"""
116 # mktemp is not racy because symlink creation will fail if the
116 # mktemp is not racy because symlink creation will fail if the
117 # file already exists
117 # file already exists
118 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
118 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
119 try:
119 try:
120 os.symlink(".", name)
120 os.symlink(".", name)
121 os.unlink(name)
121 os.unlink(name)
122 return True
122 return True
123 except (OSError, AttributeError):
123 except (OSError, AttributeError):
124 return False
124 return False
125
125
126 def checkosfilename(path):
126 def checkosfilename(path):
127 '''Check that the base-relative path is a valid filename on this platform.
127 '''Check that the base-relative path is a valid filename on this platform.
128 Returns None if the path is ok, or a UI string describing the problem.'''
128 Returns None if the path is ok, or a UI string describing the problem.'''
129 pass # on posix platforms, every path is ok
129 pass # on posix platforms, every path is ok
130
130
131 def set_binary(fd):
131 def set_binary(fd):
132 pass
132 pass
133
133
134 def pconvert(path):
134 def pconvert(path):
135 return path
135 return path
136
136
137 def localpath(path):
137 def localpath(path):
138 return path
138 return path
139
139
140 def samefile(fpath1, fpath2):
140 def samefile(fpath1, fpath2):
141 """Returns whether path1 and path2 refer to the same file. This is only
141 """Returns whether path1 and path2 refer to the same file. This is only
142 guaranteed to work for files, not directories."""
142 guaranteed to work for files, not directories."""
143 return os.path.samefile(fpath1, fpath2)
143 return os.path.samefile(fpath1, fpath2)
144
144
145 def samedevice(fpath1, fpath2):
145 def samedevice(fpath1, fpath2):
146 """Returns whether fpath1 and fpath2 are on the same device. This is only
146 """Returns whether fpath1 and fpath2 are on the same device. This is only
147 guaranteed to work for files, not directories."""
147 guaranteed to work for files, not directories."""
148 st1 = os.lstat(fpath1)
148 st1 = os.lstat(fpath1)
149 st2 = os.lstat(fpath2)
149 st2 = os.lstat(fpath2)
150 return st1.st_dev == st2.st_dev
150 return st1.st_dev == st2.st_dev
151
151
152 if sys.platform == 'darwin':
152 if sys.platform == 'darwin':
153 import fcntl # only needed on darwin, missing on jython
153 import fcntl # only needed on darwin, missing on jython
154 def realpath(path):
154 def realpath(path):
155 '''
155 '''
156 Returns the true, canonical file system path equivalent to the given
156 Returns the true, canonical file system path equivalent to the given
157 path.
157 path.
158
158
159 Equivalent means, in this case, resulting in the same, unique
159 Equivalent means, in this case, resulting in the same, unique
160 file system link to the path. Every file system entry, whether a file,
160 file system link to the path. Every file system entry, whether a file,
161 directory, hard link or symbolic link or special, will have a single
161 directory, hard link or symbolic link or special, will have a single
162 path preferred by the system, but may allow multiple, differing path
162 path preferred by the system, but may allow multiple, differing path
163 lookups to point to it.
163 lookups to point to it.
164
164
165 Most regular UNIX file systems only allow a file system entry to be
165 Most regular UNIX file systems only allow a file system entry to be
166 looked up by its distinct path. Obviously, this does not apply to case
166 looked up by its distinct path. Obviously, this does not apply to case
167 insensitive file systems, whether case preserving or not. The most
167 insensitive file systems, whether case preserving or not. The most
168 complex issue to deal with is file systems transparently reencoding the
168 complex issue to deal with is file systems transparently reencoding the
169 path, such as the non-standard Unicode normalisation required for HFS+
169 path, such as the non-standard Unicode normalisation required for HFS+
170 and HFSX.
170 and HFSX.
171 '''
171 '''
172 # Constants copied from /usr/include/sys/fcntl.h
172 # Constants copied from /usr/include/sys/fcntl.h
173 F_GETPATH = 50
173 F_GETPATH = 50
174 O_SYMLINK = 0x200000
174 O_SYMLINK = 0x200000
175
175
176 try:
176 try:
177 fd = os.open(path, O_SYMLINK)
177 fd = os.open(path, O_SYMLINK)
178 except OSError, err:
178 except OSError, err:
179 if err.errno == errno.ENOENT:
179 if err.errno == errno.ENOENT:
180 return path
180 return path
181 raise
181 raise
182
182
183 try:
183 try:
184 return fcntl.fcntl(fd, F_GETPATH, '\0' * 1024).rstrip('\0')
184 return fcntl.fcntl(fd, F_GETPATH, '\0' * 1024).rstrip('\0')
185 finally:
185 finally:
186 os.close(fd)
186 os.close(fd)
187 else:
187 else:
188 # Fallback to the likely inadequate Python builtin function.
188 # Fallback to the likely inadequate Python builtin function.
189 realpath = os.path.realpath
189 realpath = os.path.realpath
190
190
191 def shellquote(s):
191 def shellquote(s):
192 if os.sys.platform == 'OpenVMS':
192 if os.sys.platform == 'OpenVMS':
193 return '"%s"' % s
193 return '"%s"' % s
194 else:
194 else:
195 return "'%s'" % s.replace("'", "'\\''")
195 return "'%s'" % s.replace("'", "'\\''")
196
196
197 def quotecommand(cmd):
197 def quotecommand(cmd):
198 return cmd
198 return cmd
199
199
200 def popen(command, mode='r'):
200 def popen(command, mode='r'):
201 return os.popen(command, mode)
201 return os.popen(command, mode)
202
202
203 def testpid(pid):
203 def testpid(pid):
204 '''return False if pid dead, True if running or not sure'''
204 '''return False if pid dead, True if running or not sure'''
205 if os.sys.platform == 'OpenVMS':
205 if os.sys.platform == 'OpenVMS':
206 return True
206 return True
207 try:
207 try:
208 os.kill(pid, 0)
208 os.kill(pid, 0)
209 return True
209 return True
210 except OSError, inst:
210 except OSError, inst:
211 return inst.errno != errno.ESRCH
211 return inst.errno != errno.ESRCH
212
212
213 def explain_exit(code):
213 def explain_exit(code):
214 """return a 2-tuple (desc, code) describing a subprocess status
214 """return a 2-tuple (desc, code) describing a subprocess status
215 (codes from kill are negative - not os.system/wait encoding)"""
215 (codes from kill are negative - not os.system/wait encoding)"""
216 if code >= 0:
216 if code >= 0:
217 return _("exited with status %d") % code, code
217 return _("exited with status %d") % code, code
218 return _("killed by signal %d") % -code, -code
218 return _("killed by signal %d") % -code, -code
219
219
220 def isowner(st):
220 def isowner(st):
221 """Return True if the stat object st is from the current user."""
221 """Return True if the stat object st is from the current user."""
222 return st.st_uid == os.getuid()
222 return st.st_uid == os.getuid()
223
223
224 def find_exe(command):
224 def find_exe(command):
225 '''Find executable for command searching like which does.
225 '''Find executable for command searching like which does.
226 If command is a basename then PATH is searched for command.
226 If command is a basename then PATH is searched for command.
227 PATH isn't searched if command is an absolute or relative path.
227 PATH isn't searched if command is an absolute or relative path.
228 If command isn't found None is returned.'''
228 If command isn't found None is returned.'''
229 if sys.platform == 'OpenVMS':
229 if sys.platform == 'OpenVMS':
230 return command
230 return command
231
231
232 def findexisting(executable):
232 def findexisting(executable):
233 'Will return executable if existing file'
233 'Will return executable if existing file'
234 if os.path.exists(executable):
234 if os.path.exists(executable):
235 return executable
235 return executable
236 return None
236 return None
237
237
238 if os.sep in command:
238 if os.sep in command:
239 return findexisting(command)
239 return findexisting(command)
240
240
241 for path in os.environ.get('PATH', '').split(os.pathsep):
241 for path in os.environ.get('PATH', '').split(os.pathsep):
242 executable = findexisting(os.path.join(path, command))
242 executable = findexisting(os.path.join(path, command))
243 if executable is not None:
243 if executable is not None:
244 return executable
244 return executable
245 return None
245 return None
246
246
247 def set_signal_handler():
247 def set_signal_handler():
248 pass
248 pass
249
249
250 def statfiles(files):
250 def statfiles(files):
251 'Stat each file in files and yield stat or None if file does not exist.'
251 'Stat each file in files and yield stat or None if file does not exist.'
252 lstat = os.lstat
252 lstat = os.lstat
253 for nf in files:
253 for nf in files:
254 try:
254 try:
255 st = lstat(nf)
255 st = lstat(nf)
256 except OSError, err:
256 except OSError, err:
257 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
257 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
258 raise
258 raise
259 st = None
259 st = None
260 yield st
260 yield st
261
261
262 def getuser():
262 def getuser():
263 '''return name of current user'''
263 '''return name of current user'''
264 return getpass.getuser()
264 return getpass.getuser()
265
265
266 def expand_glob(pats):
266 def expand_glob(pats):
267 '''On Windows, expand the implicit globs in a list of patterns'''
267 '''On Windows, expand the implicit globs in a list of patterns'''
268 return list(pats)
268 return list(pats)
269
269
270 def username(uid=None):
270 def username(uid=None):
271 """Return the name of the user with the given uid.
271 """Return the name of the user with the given uid.
272
272
273 If uid is None, return the name of the current user."""
273 If uid is None, return the name of the current user."""
274
274
275 if uid is None:
275 if uid is None:
276 uid = os.getuid()
276 uid = os.getuid()
277 try:
277 try:
278 return pwd.getpwuid(uid)[0]
278 return pwd.getpwuid(uid)[0]
279 except KeyError:
279 except KeyError:
280 return str(uid)
280 return str(uid)
281
281
282 def groupname(gid=None):
282 def groupname(gid=None):
283 """Return the name of the group with the given gid.
283 """Return the name of the group with the given gid.
284
284
285 If gid is None, return the name of the current group."""
285 If gid is None, return the name of the current group."""
286
286
287 if gid is None:
287 if gid is None:
288 gid = os.getgid()
288 gid = os.getgid()
289 try:
289 try:
290 return grp.getgrgid(gid)[0]
290 return grp.getgrgid(gid)[0]
291 except KeyError:
291 except KeyError:
292 return str(gid)
292 return str(gid)
293
293
294 def groupmembers(name):
294 def groupmembers(name):
295 """Return the list of members of the group with the given
295 """Return the list of members of the group with the given
296 name, KeyError if the group does not exist.
296 name, KeyError if the group does not exist.
297 """
297 """
298 return list(grp.getgrnam(name).gr_mem)
298 return list(grp.getgrnam(name).gr_mem)
299
299
300 def spawndetached(args):
300 def spawndetached(args):
301 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
301 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
302 args[0], args)
302 args[0], args)
303
303
304 def gethgcmd():
304 def gethgcmd():
305 return sys.argv[:1]
305 return sys.argv[:1]
306
306
307 def termwidth():
307 def termwidth():
308 try:
308 try:
309 import termios, array, fcntl
309 import termios, array, fcntl
310 for dev in (sys.stderr, sys.stdout, sys.stdin):
310 for dev in (sys.stderr, sys.stdout, sys.stdin):
311 try:
311 try:
312 try:
312 try:
313 fd = dev.fileno()
313 fd = dev.fileno()
314 except AttributeError:
314 except AttributeError:
315 continue
315 continue
316 if not os.isatty(fd):
316 if not os.isatty(fd):
317 continue
317 continue
318 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
318 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
319 return array.array('h', arri)[1]
319 width = array.array('h', arri)[1]
320 if width > 0:
321 return width
320 except ValueError:
322 except ValueError:
321 pass
323 pass
322 except IOError, e:
324 except IOError, e:
323 if e[0] == errno.EINVAL:
325 if e[0] == errno.EINVAL:
324 pass
326 pass
325 else:
327 else:
326 raise
328 raise
327 except ImportError:
329 except ImportError:
328 pass
330 pass
329 return 80
331 return 80
General Comments 0
You need to be logged in to leave comments. Login now