74 # https://github.com/python/cpython/blob/v3.7.3/Modules/_io/fileio.c#L474 |
74 # https://github.com/python/cpython/blob/v3.7.3/Modules/_io/fileio.c#L474 |
75 posixfile = open |
75 posixfile = open |
76 |
76 |
77 |
77 |
78 def split(p): |
78 def split(p): |
79 '''Same as posixpath.split, but faster |
79 """Same as posixpath.split, but faster |
80 |
80 |
81 >>> import posixpath |
81 >>> import posixpath |
82 >>> for f in [b'/absolute/path/to/file', |
82 >>> for f in [b'/absolute/path/to/file', |
83 ... b'relative/path/to/file', |
83 ... b'relative/path/to/file', |
84 ... b'file_alone', |
84 ... b'file_alone', |
86 ... b'/multiple/path//separators', |
86 ... b'/multiple/path//separators', |
87 ... b'/file_at_root', |
87 ... b'/file_at_root', |
88 ... b'///multiple_leading_separators_at_root', |
88 ... b'///multiple_leading_separators_at_root', |
89 ... b'']: |
89 ... b'']: |
90 ... assert split(f) == posixpath.split(f), f |
90 ... assert split(f) == posixpath.split(f), f |
91 ''' |
91 """ |
92 ht = p.rsplit(b'/', 1) |
92 ht = p.rsplit(b'/', 1) |
93 if len(ht) == 1: |
93 if len(ht) == 1: |
94 return b'', p |
94 return b'', p |
95 nh = ht[0].rstrip(b'/') |
95 nh = ht[0].rstrip(b'/') |
96 if nh: |
96 if nh: |
181 # Turn off all +x bits |
181 # Turn off all +x bits |
182 os.chmod(f, s & 0o666) |
182 os.chmod(f, s & 0o666) |
183 |
183 |
184 |
184 |
185 def copymode(src, dst, mode=None, enforcewritable=False): |
185 def copymode(src, dst, mode=None, enforcewritable=False): |
186 '''Copy the file mode from the file at path src to dst. |
186 """Copy the file mode from the file at path src to dst. |
187 If src doesn't exist, we're using mode instead. If mode is None, we're |
187 If src doesn't exist, we're using mode instead. If mode is None, we're |
188 using umask.''' |
188 using umask.""" |
189 try: |
189 try: |
190 st_mode = os.lstat(src).st_mode & 0o777 |
190 st_mode = os.lstat(src).st_mode & 0o777 |
191 except OSError as inst: |
191 except OSError as inst: |
192 if inst.errno != errno.ENOENT: |
192 if inst.errno != errno.ENOENT: |
193 raise |
193 raise |
357 unlink(name) |
357 unlink(name) |
358 return False |
358 return False |
359 |
359 |
360 |
360 |
361 def checkosfilename(path): |
361 def checkosfilename(path): |
362 '''Check that the base-relative path is a valid filename on this platform. |
362 """Check that the base-relative path is a valid filename on this platform. |
363 Returns None if the path is ok, or a UI string describing the problem.''' |
363 Returns None if the path is ok, or a UI string describing the problem.""" |
364 return None # on posix platforms, every path is ok |
364 return None # on posix platforms, every path is ok |
365 |
365 |
366 |
366 |
367 def getfsmountpoint(dirpath): |
367 def getfsmountpoint(dirpath): |
368 '''Get the filesystem mount point from a directory (best-effort) |
368 """Get the filesystem mount point from a directory (best-effort) |
369 |
369 |
370 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc. |
370 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc. |
371 ''' |
371 """ |
372 return getattr(osutil, 'getfsmountpoint', lambda x: None)(dirpath) |
372 return getattr(osutil, 'getfsmountpoint', lambda x: None)(dirpath) |
373 |
373 |
374 |
374 |
375 def getfstype(dirpath): |
375 def getfstype(dirpath): |
376 '''Get the filesystem type name from a directory (best-effort) |
376 """Get the filesystem type name from a directory (best-effort) |
377 |
377 |
378 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc. |
378 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc. |
379 ''' |
379 """ |
380 return getattr(osutil, 'getfstype', lambda x: None)(dirpath) |
380 return getattr(osutil, 'getfstype', lambda x: None)(dirpath) |
381 |
381 |
382 |
382 |
383 def setbinary(fd): |
383 def setbinary(fd): |
384 pass |
384 pass |
417 normcasefallback = normcase |
417 normcasefallback = normcase |
418 |
418 |
419 if pycompat.isdarwin: |
419 if pycompat.isdarwin: |
420 |
420 |
421 def normcase(path): |
421 def normcase(path): |
422 ''' |
422 """ |
423 Normalize a filename for OS X-compatible comparison: |
423 Normalize a filename for OS X-compatible comparison: |
424 - escape-encode invalid characters |
424 - escape-encode invalid characters |
425 - decompose to NFD |
425 - decompose to NFD |
426 - lowercase |
426 - lowercase |
427 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff] |
427 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff] |
432 'cafe\\xcc\\x81' |
432 'cafe\\xcc\\x81' |
433 >>> normcase(b'\\xc3\\x89') |
433 >>> normcase(b'\\xc3\\x89') |
434 'e\\xcc\\x81' |
434 'e\\xcc\\x81' |
435 >>> normcase(b'\\xb8\\xca\\xc3\\xca\\xbe\\xc8.JPG') # issue3918 |
435 >>> normcase(b'\\xb8\\xca\\xc3\\xca\\xbe\\xc8.JPG') # issue3918 |
436 '%b8%ca%c3\\xca\\xbe%c8.jpg' |
436 '%b8%ca%c3\\xca\\xbe%c8.jpg' |
437 ''' |
437 """ |
438 |
438 |
439 try: |
439 try: |
440 return encoding.asciilower(path) # exception for non-ASCII |
440 return encoding.asciilower(path) # exception for non-ASCII |
441 except UnicodeDecodeError: |
441 except UnicodeDecodeError: |
442 return normcasefallback(path) |
442 return normcasefallback(path) |
473 # treated as case sensitive, even though underlying NTFS is case |
473 # treated as case sensitive, even though underlying NTFS is case |
474 # insensitive. |
474 # insensitive. |
475 |
475 |
476 # default mount points |
476 # default mount points |
477 cygwinmountpoints = sorted( |
477 cygwinmountpoints = sorted( |
478 [b"/usr/bin", b"/usr/lib", b"/cygdrive",], reverse=True |
478 [ |
|
479 b"/usr/bin", |
|
480 b"/usr/lib", |
|
481 b"/cygdrive", |
|
482 ], |
|
483 reverse=True, |
479 ) |
484 ) |
480 |
485 |
481 # use upper-ing as normcase as same as NTFS workaround |
486 # use upper-ing as normcase as same as NTFS workaround |
482 def normcase(path): |
487 def normcase(path): |
483 pathlen = len(path) |
488 pathlen = len(path) |
551 """Return True if the stat object st is from the current user.""" |
556 """Return True if the stat object st is from the current user.""" |
552 return st.st_uid == os.getuid() |
557 return st.st_uid == os.getuid() |
553 |
558 |
554 |
559 |
555 def findexe(command): |
560 def findexe(command): |
556 '''Find executable for command searching like which does. |
561 """Find executable for command searching like which does. |
557 If command is a basename then PATH is searched for command. |
562 If command is a basename then PATH is searched for command. |
558 PATH isn't searched if command is an absolute or relative path. |
563 PATH isn't searched if command is an absolute or relative path. |
559 If command isn't found None is returned.''' |
564 If command isn't found None is returned.""" |
560 if pycompat.sysplatform == b'OpenVMS': |
565 if pycompat.sysplatform == b'OpenVMS': |
561 return command |
566 return command |
562 |
567 |
563 def findexisting(executable): |
568 def findexisting(executable): |
564 b'Will return executable if existing file' |
569 b'Will return executable if existing file' |
585 |
590 |
586 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK} |
591 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK} |
587 |
592 |
588 |
593 |
589 def statfiles(files): |
594 def statfiles(files): |
590 '''Stat each file in files. Yield each stat, or None if a file does not |
595 """Stat each file in files. Yield each stat, or None if a file does not |
591 exist or has a type we don't care about.''' |
596 exist or has a type we don't care about.""" |
592 lstat = os.lstat |
597 lstat = os.lstat |
593 getkind = stat.S_IFMT |
598 getkind = stat.S_IFMT |
594 for nf in files: |
599 for nf in files: |
595 try: |
600 try: |
596 st = lstat(nf) |
601 st = lstat(nf) |