##// END OF EJS Templates
posix: disable cygwin's symlink emulation
Matt Mackall -
r16241:60cc3a0d default
parent child Browse files
Show More
@@ -1,460 +1,466 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, unicodedata
9 import os, sys, errno, stat, getpass, pwd, grp, tempfile, unicodedata
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 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 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 parsepatchoutput(output_line):
31 def parsepatchoutput(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 isexec(f):
47 def isexec(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 setflags(f, l, x):
51 def setflags(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 copymode(src, dst, mode=None):
87 def copymode(src, dst, mode=None):
88 '''Copy the file mode from the file at path src to dst.
88 '''Copy the file mode from the file at path src to dst.
89 If src doesn't exist, we're using mode instead. If mode is None, we're
89 If src doesn't exist, we're using mode instead. If mode is None, we're
90 using umask.'''
90 using umask.'''
91 try:
91 try:
92 st_mode = os.lstat(src).st_mode & 0777
92 st_mode = os.lstat(src).st_mode & 0777
93 except OSError, inst:
93 except OSError, inst:
94 if inst.errno != errno.ENOENT:
94 if inst.errno != errno.ENOENT:
95 raise
95 raise
96 st_mode = mode
96 st_mode = mode
97 if st_mode is None:
97 if st_mode is None:
98 st_mode = ~umask
98 st_mode = ~umask
99 st_mode &= 0666
99 st_mode &= 0666
100 os.chmod(dst, st_mode)
100 os.chmod(dst, st_mode)
101
101
102 def checkexec(path):
102 def checkexec(path):
103 """
103 """
104 Check whether the given path is on a filesystem with UNIX-like exec flags
104 Check whether the given path is on a filesystem with UNIX-like exec flags
105
105
106 Requires a directory (like /foo/.hg)
106 Requires a directory (like /foo/.hg)
107 """
107 """
108
108
109 # VFAT on some Linux versions can flip mode but it doesn't persist
109 # VFAT on some Linux versions can flip mode but it doesn't persist
110 # a FS remount. Frequently we can detect it if files are created
110 # a FS remount. Frequently we can detect it if files are created
111 # with exec bit on.
111 # with exec bit on.
112
112
113 try:
113 try:
114 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
114 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
115 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
115 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
116 try:
116 try:
117 os.close(fh)
117 os.close(fh)
118 m = os.stat(fn).st_mode & 0777
118 m = os.stat(fn).st_mode & 0777
119 new_file_has_exec = m & EXECFLAGS
119 new_file_has_exec = m & EXECFLAGS
120 os.chmod(fn, m ^ EXECFLAGS)
120 os.chmod(fn, m ^ EXECFLAGS)
121 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
121 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
122 finally:
122 finally:
123 os.unlink(fn)
123 os.unlink(fn)
124 except (IOError, OSError):
124 except (IOError, OSError):
125 # we don't care, the user probably won't be able to commit anyway
125 # we don't care, the user probably won't be able to commit anyway
126 return False
126 return False
127 return not (new_file_has_exec or exec_flags_cannot_flip)
127 return not (new_file_has_exec or exec_flags_cannot_flip)
128
128
129 def checklink(path):
129 def checklink(path):
130 """check whether the given path is on a symlink-capable filesystem"""
130 """check whether the given path is on a symlink-capable filesystem"""
131 # mktemp is not racy because symlink creation will fail if the
131 # mktemp is not racy because symlink creation will fail if the
132 # file already exists
132 # file already exists
133 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
133 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
134 try:
134 try:
135 os.symlink(".", name)
135 os.symlink(".", name)
136 os.unlink(name)
136 os.unlink(name)
137 return True
137 return True
138 except (OSError, AttributeError):
138 except (OSError, AttributeError):
139 return False
139 return False
140
140
141 def checkosfilename(path):
141 def checkosfilename(path):
142 '''Check that the base-relative path is a valid filename on this platform.
142 '''Check that the base-relative path is a valid filename on this platform.
143 Returns None if the path is ok, or a UI string describing the problem.'''
143 Returns None if the path is ok, or a UI string describing the problem.'''
144 pass # on posix platforms, every path is ok
144 pass # on posix platforms, every path is ok
145
145
146 def setbinary(fd):
146 def setbinary(fd):
147 pass
147 pass
148
148
149 def pconvert(path):
149 def pconvert(path):
150 return path
150 return path
151
151
152 def localpath(path):
152 def localpath(path):
153 return path
153 return path
154
154
155 def samefile(fpath1, fpath2):
155 def samefile(fpath1, fpath2):
156 """Returns whether path1 and path2 refer to the same file. This is only
156 """Returns whether path1 and path2 refer to the same file. This is only
157 guaranteed to work for files, not directories."""
157 guaranteed to work for files, not directories."""
158 return os.path.samefile(fpath1, fpath2)
158 return os.path.samefile(fpath1, fpath2)
159
159
160 def samedevice(fpath1, fpath2):
160 def samedevice(fpath1, fpath2):
161 """Returns whether fpath1 and fpath2 are on the same device. This is only
161 """Returns whether fpath1 and fpath2 are on the same device. This is only
162 guaranteed to work for files, not directories."""
162 guaranteed to work for files, not directories."""
163 st1 = os.lstat(fpath1)
163 st1 = os.lstat(fpath1)
164 st2 = os.lstat(fpath2)
164 st2 = os.lstat(fpath2)
165 return st1.st_dev == st2.st_dev
165 return st1.st_dev == st2.st_dev
166
166
167 encodinglower = None
167 encodinglower = None
168 encodingupper = None
168 encodingupper = None
169
169
170 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
170 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
171 def normcase(path):
171 def normcase(path):
172 return path.lower()
172 return path.lower()
173
173
174 if sys.platform == 'darwin':
174 if sys.platform == 'darwin':
175 import fcntl # only needed on darwin, missing on jython
175 import fcntl # only needed on darwin, missing on jython
176
176
177 def normcase(path):
177 def normcase(path):
178 try:
178 try:
179 u = path.decode('utf-8')
179 u = path.decode('utf-8')
180 except UnicodeDecodeError:
180 except UnicodeDecodeError:
181 # percent-encode any characters that don't round-trip
181 # percent-encode any characters that don't round-trip
182 p2 = path.decode('utf-8', 'ignore').encode('utf-8')
182 p2 = path.decode('utf-8', 'ignore').encode('utf-8')
183 s = ""
183 s = ""
184 pos = 0
184 pos = 0
185 for c in path:
185 for c in path:
186 if p2[pos:pos + 1] == c:
186 if p2[pos:pos + 1] == c:
187 s += c
187 s += c
188 pos += 1
188 pos += 1
189 else:
189 else:
190 s += "%%%02X" % ord(c)
190 s += "%%%02X" % ord(c)
191 u = s.decode('utf-8')
191 u = s.decode('utf-8')
192
192
193 # Decompose then lowercase (HFS+ technote specifies lower)
193 # Decompose then lowercase (HFS+ technote specifies lower)
194 return unicodedata.normalize('NFD', u).lower().encode('utf-8')
194 return unicodedata.normalize('NFD', u).lower().encode('utf-8')
195
195
196 def realpath(path):
196 def realpath(path):
197 '''
197 '''
198 Returns the true, canonical file system path equivalent to the given
198 Returns the true, canonical file system path equivalent to the given
199 path.
199 path.
200
200
201 Equivalent means, in this case, resulting in the same, unique
201 Equivalent means, in this case, resulting in the same, unique
202 file system link to the path. Every file system entry, whether a file,
202 file system link to the path. Every file system entry, whether a file,
203 directory, hard link or symbolic link or special, will have a single
203 directory, hard link or symbolic link or special, will have a single
204 path preferred by the system, but may allow multiple, differing path
204 path preferred by the system, but may allow multiple, differing path
205 lookups to point to it.
205 lookups to point to it.
206
206
207 Most regular UNIX file systems only allow a file system entry to be
207 Most regular UNIX file systems only allow a file system entry to be
208 looked up by its distinct path. Obviously, this does not apply to case
208 looked up by its distinct path. Obviously, this does not apply to case
209 insensitive file systems, whether case preserving or not. The most
209 insensitive file systems, whether case preserving or not. The most
210 complex issue to deal with is file systems transparently reencoding the
210 complex issue to deal with is file systems transparently reencoding the
211 path, such as the non-standard Unicode normalisation required for HFS+
211 path, such as the non-standard Unicode normalisation required for HFS+
212 and HFSX.
212 and HFSX.
213 '''
213 '''
214 # Constants copied from /usr/include/sys/fcntl.h
214 # Constants copied from /usr/include/sys/fcntl.h
215 F_GETPATH = 50
215 F_GETPATH = 50
216 O_SYMLINK = 0x200000
216 O_SYMLINK = 0x200000
217
217
218 try:
218 try:
219 fd = os.open(path, O_SYMLINK)
219 fd = os.open(path, O_SYMLINK)
220 except OSError, err:
220 except OSError, err:
221 if err.errno == errno.ENOENT:
221 if err.errno == errno.ENOENT:
222 return path
222 return path
223 raise
223 raise
224
224
225 try:
225 try:
226 return fcntl.fcntl(fd, F_GETPATH, '\0' * 1024).rstrip('\0')
226 return fcntl.fcntl(fd, F_GETPATH, '\0' * 1024).rstrip('\0')
227 finally:
227 finally:
228 os.close(fd)
228 os.close(fd)
229 elif sys.version_info < (2, 4, 2, 'final'):
229 elif sys.version_info < (2, 4, 2, 'final'):
230 # Workaround for http://bugs.python.org/issue1213894 (os.path.realpath
230 # Workaround for http://bugs.python.org/issue1213894 (os.path.realpath
231 # didn't resolve symlinks that were the first component of the path.)
231 # didn't resolve symlinks that were the first component of the path.)
232 def realpath(path):
232 def realpath(path):
233 if os.path.isabs(path):
233 if os.path.isabs(path):
234 return os.path.realpath(path)
234 return os.path.realpath(path)
235 else:
235 else:
236 return os.path.realpath('./' + path)
236 return os.path.realpath('./' + path)
237 else:
237 else:
238 # Fallback to the likely inadequate Python builtin function.
238 # Fallback to the likely inadequate Python builtin function.
239 realpath = os.path.realpath
239 realpath = os.path.realpath
240
240
241 if sys.platform == 'cygwin':
241 if sys.platform == 'cygwin':
242 # workaround for cygwin, in which mount point part of path is
242 # workaround for cygwin, in which mount point part of path is
243 # treated as case sensitive, even though underlying NTFS is case
243 # treated as case sensitive, even though underlying NTFS is case
244 # insensitive.
244 # insensitive.
245
245
246 # default mount points
246 # default mount points
247 cygwinmountpoints = sorted([
247 cygwinmountpoints = sorted([
248 "/usr/bin",
248 "/usr/bin",
249 "/usr/lib",
249 "/usr/lib",
250 "/cygdrive",
250 "/cygdrive",
251 ], reverse=True)
251 ], reverse=True)
252
252
253 # use upper-ing as normcase as same as NTFS workaround
253 # use upper-ing as normcase as same as NTFS workaround
254 def normcase(path):
254 def normcase(path):
255 pathlen = len(path)
255 pathlen = len(path)
256 if (pathlen == 0) or (path[0] != os.sep):
256 if (pathlen == 0) or (path[0] != os.sep):
257 # treat as relative
257 # treat as relative
258 return encodingupper(path)
258 return encodingupper(path)
259
259
260 # to preserve case of mountpoint part
260 # to preserve case of mountpoint part
261 for mp in cygwinmountpoints:
261 for mp in cygwinmountpoints:
262 if not path.startswith(mp):
262 if not path.startswith(mp):
263 continue
263 continue
264
264
265 mplen = len(mp)
265 mplen = len(mp)
266 if mplen == pathlen: # mount point itself
266 if mplen == pathlen: # mount point itself
267 return mp
267 return mp
268 if path[mplen] == os.sep:
268 if path[mplen] == os.sep:
269 return mp + encodingupper(path[mplen:])
269 return mp + encodingupper(path[mplen:])
270
270
271 return encodingupper(path)
271 return encodingupper(path)
272
272
273 # Cygwin translates native ACLs to POSIX permissions,
273 # Cygwin translates native ACLs to POSIX permissions,
274 # but these translations are not supported by native
274 # but these translations are not supported by native
275 # tools, so the exec bit tends to be set erroneously.
275 # tools, so the exec bit tends to be set erroneously.
276 # Therefore, disable executable bit access on Cygwin.
276 # Therefore, disable executable bit access on Cygwin.
277 def checkexec(path):
277 def checkexec(path):
278 return False
278 return False
279
279
280 # Similarly, Cygwin's symlink emulation is likely to create
281 # problems when Mercurial is used from both Cygwin and native
282 # Windows, with other native tools, or on shared volumes
283 def checklink(path):
284 return False
285
280 def shellquote(s):
286 def shellquote(s):
281 if os.sys.platform == 'OpenVMS':
287 if os.sys.platform == 'OpenVMS':
282 return '"%s"' % s
288 return '"%s"' % s
283 else:
289 else:
284 return "'%s'" % s.replace("'", "'\\''")
290 return "'%s'" % s.replace("'", "'\\''")
285
291
286 def quotecommand(cmd):
292 def quotecommand(cmd):
287 return cmd
293 return cmd
288
294
289 def popen(command, mode='r'):
295 def popen(command, mode='r'):
290 return os.popen(command, mode)
296 return os.popen(command, mode)
291
297
292 def testpid(pid):
298 def testpid(pid):
293 '''return False if pid dead, True if running or not sure'''
299 '''return False if pid dead, True if running or not sure'''
294 if os.sys.platform == 'OpenVMS':
300 if os.sys.platform == 'OpenVMS':
295 return True
301 return True
296 try:
302 try:
297 os.kill(pid, 0)
303 os.kill(pid, 0)
298 return True
304 return True
299 except OSError, inst:
305 except OSError, inst:
300 return inst.errno != errno.ESRCH
306 return inst.errno != errno.ESRCH
301
307
302 def explainexit(code):
308 def explainexit(code):
303 """return a 2-tuple (desc, code) describing a subprocess status
309 """return a 2-tuple (desc, code) describing a subprocess status
304 (codes from kill are negative - not os.system/wait encoding)"""
310 (codes from kill are negative - not os.system/wait encoding)"""
305 if code >= 0:
311 if code >= 0:
306 return _("exited with status %d") % code, code
312 return _("exited with status %d") % code, code
307 return _("killed by signal %d") % -code, -code
313 return _("killed by signal %d") % -code, -code
308
314
309 def isowner(st):
315 def isowner(st):
310 """Return True if the stat object st is from the current user."""
316 """Return True if the stat object st is from the current user."""
311 return st.st_uid == os.getuid()
317 return st.st_uid == os.getuid()
312
318
313 def findexe(command):
319 def findexe(command):
314 '''Find executable for command searching like which does.
320 '''Find executable for command searching like which does.
315 If command is a basename then PATH is searched for command.
321 If command is a basename then PATH is searched for command.
316 PATH isn't searched if command is an absolute or relative path.
322 PATH isn't searched if command is an absolute or relative path.
317 If command isn't found None is returned.'''
323 If command isn't found None is returned.'''
318 if sys.platform == 'OpenVMS':
324 if sys.platform == 'OpenVMS':
319 return command
325 return command
320
326
321 def findexisting(executable):
327 def findexisting(executable):
322 'Will return executable if existing file'
328 'Will return executable if existing file'
323 if os.path.isfile(executable) and os.access(executable, os.X_OK):
329 if os.path.isfile(executable) and os.access(executable, os.X_OK):
324 return executable
330 return executable
325 return None
331 return None
326
332
327 if os.sep in command:
333 if os.sep in command:
328 return findexisting(command)
334 return findexisting(command)
329
335
330 for path in os.environ.get('PATH', '').split(os.pathsep):
336 for path in os.environ.get('PATH', '').split(os.pathsep):
331 executable = findexisting(os.path.join(path, command))
337 executable = findexisting(os.path.join(path, command))
332 if executable is not None:
338 if executable is not None:
333 return executable
339 return executable
334 return None
340 return None
335
341
336 def setsignalhandler():
342 def setsignalhandler():
337 pass
343 pass
338
344
339 def statfiles(files):
345 def statfiles(files):
340 'Stat each file in files and yield stat or None if file does not exist.'
346 'Stat each file in files and yield stat or None if file does not exist.'
341 lstat = os.lstat
347 lstat = os.lstat
342 for nf in files:
348 for nf in files:
343 try:
349 try:
344 st = lstat(nf)
350 st = lstat(nf)
345 except OSError, err:
351 except OSError, err:
346 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
352 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
347 raise
353 raise
348 st = None
354 st = None
349 yield st
355 yield st
350
356
351 def getuser():
357 def getuser():
352 '''return name of current user'''
358 '''return name of current user'''
353 return getpass.getuser()
359 return getpass.getuser()
354
360
355 def username(uid=None):
361 def username(uid=None):
356 """Return the name of the user with the given uid.
362 """Return the name of the user with the given uid.
357
363
358 If uid is None, return the name of the current user."""
364 If uid is None, return the name of the current user."""
359
365
360 if uid is None:
366 if uid is None:
361 uid = os.getuid()
367 uid = os.getuid()
362 try:
368 try:
363 return pwd.getpwuid(uid)[0]
369 return pwd.getpwuid(uid)[0]
364 except KeyError:
370 except KeyError:
365 return str(uid)
371 return str(uid)
366
372
367 def groupname(gid=None):
373 def groupname(gid=None):
368 """Return the name of the group with the given gid.
374 """Return the name of the group with the given gid.
369
375
370 If gid is None, return the name of the current group."""
376 If gid is None, return the name of the current group."""
371
377
372 if gid is None:
378 if gid is None:
373 gid = os.getgid()
379 gid = os.getgid()
374 try:
380 try:
375 return grp.getgrgid(gid)[0]
381 return grp.getgrgid(gid)[0]
376 except KeyError:
382 except KeyError:
377 return str(gid)
383 return str(gid)
378
384
379 def groupmembers(name):
385 def groupmembers(name):
380 """Return the list of members of the group with the given
386 """Return the list of members of the group with the given
381 name, KeyError if the group does not exist.
387 name, KeyError if the group does not exist.
382 """
388 """
383 return list(grp.getgrnam(name).gr_mem)
389 return list(grp.getgrnam(name).gr_mem)
384
390
385 def spawndetached(args):
391 def spawndetached(args):
386 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
392 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
387 args[0], args)
393 args[0], args)
388
394
389 def gethgcmd():
395 def gethgcmd():
390 return sys.argv[:1]
396 return sys.argv[:1]
391
397
392 def termwidth():
398 def termwidth():
393 try:
399 try:
394 import termios, array, fcntl
400 import termios, array, fcntl
395 for dev in (sys.stderr, sys.stdout, sys.stdin):
401 for dev in (sys.stderr, sys.stdout, sys.stdin):
396 try:
402 try:
397 try:
403 try:
398 fd = dev.fileno()
404 fd = dev.fileno()
399 except AttributeError:
405 except AttributeError:
400 continue
406 continue
401 if not os.isatty(fd):
407 if not os.isatty(fd):
402 continue
408 continue
403 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
409 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
404 width = array.array('h', arri)[1]
410 width = array.array('h', arri)[1]
405 if width > 0:
411 if width > 0:
406 return width
412 return width
407 except ValueError:
413 except ValueError:
408 pass
414 pass
409 except IOError, e:
415 except IOError, e:
410 if e[0] == errno.EINVAL:
416 if e[0] == errno.EINVAL:
411 pass
417 pass
412 else:
418 else:
413 raise
419 raise
414 except ImportError:
420 except ImportError:
415 pass
421 pass
416 return 80
422 return 80
417
423
418 def makedir(path, notindexed):
424 def makedir(path, notindexed):
419 os.mkdir(path)
425 os.mkdir(path)
420
426
421 def unlinkpath(f):
427 def unlinkpath(f):
422 """unlink and remove the directory if it is empty"""
428 """unlink and remove the directory if it is empty"""
423 os.unlink(f)
429 os.unlink(f)
424 # try removing directories that might now be empty
430 # try removing directories that might now be empty
425 try:
431 try:
426 os.removedirs(os.path.dirname(f))
432 os.removedirs(os.path.dirname(f))
427 except OSError:
433 except OSError:
428 pass
434 pass
429
435
430 def lookupreg(key, name=None, scope=None):
436 def lookupreg(key, name=None, scope=None):
431 return None
437 return None
432
438
433 def hidewindow():
439 def hidewindow():
434 """Hide current shell window.
440 """Hide current shell window.
435
441
436 Used to hide the window opened when starting asynchronous
442 Used to hide the window opened when starting asynchronous
437 child process under Windows, unneeded on other systems.
443 child process under Windows, unneeded on other systems.
438 """
444 """
439 pass
445 pass
440
446
441 class cachestat(object):
447 class cachestat(object):
442 def __init__(self, path):
448 def __init__(self, path):
443 self.stat = os.stat(path)
449 self.stat = os.stat(path)
444
450
445 def cacheable(self):
451 def cacheable(self):
446 return bool(self.stat.st_ino)
452 return bool(self.stat.st_ino)
447
453
448 __hash__ = object.__hash__
454 __hash__ = object.__hash__
449
455
450 def __eq__(self, other):
456 def __eq__(self, other):
451 try:
457 try:
452 return self.stat == other.stat
458 return self.stat == other.stat
453 except AttributeError:
459 except AttributeError:
454 return False
460 return False
455
461
456 def __ne__(self, other):
462 def __ne__(self, other):
457 return not self == other
463 return not self == other
458
464
459 def executablepath():
465 def executablepath():
460 return None # available on Windows only
466 return None # available on Windows only
General Comments 0
You need to be logged in to leave comments. Login now