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