~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-12-20 16:16:34 UTC
  • mfrom: (3123.5.18 hardlinks)
  • Revision ID: pqm@pqm.ubuntu.com-20071220161634-2kcjb650o21ydko4
Accelerate build_tree using similar workingtrees (abentley)

Show diffs side-by-side

added added

removed removed

Lines of Context:
53
53
    )
54
54
""")
55
55
 
56
 
 
57
56
import bzrlib
58
57
from bzrlib import symbol_versioning
59
58
from bzrlib.symbol_versioning import (
60
59
    deprecated_function,
 
60
    one_zero,
61
61
    )
62
62
from bzrlib.trace import mutter
63
63
 
149
149
    try:
150
150
        return _mapper(_lstat(f).st_mode)
151
151
    except OSError, e:
152
 
        if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
 
152
        if getattr(e, 'errno', None) == errno.ENOENT:
153
153
            raise errors.NoSuchFile(f)
154
154
        raise
155
155
 
360
360
 
361
361
 
362
362
def _mac_getcwd():
363
 
    return unicodedata.normalize('NFC', os.getcwdu())
 
363
    return unicodedata.normalize('NFKC', os.getcwdu())
364
364
 
365
365
 
366
366
# Default is to just use the python builtins, but these can be rebound on
468
468
        return pathjoin(F(p), e)
469
469
 
470
470
 
 
471
@deprecated_function(one_zero)
 
472
def backup_file(fn):
 
473
    """Copy a file to a backup.
 
474
 
 
475
    Backups are named in GNU-style, with a ~ suffix.
 
476
 
 
477
    If the file is already a backup, it's not copied.
 
478
    """
 
479
    if fn[-1] == '~':
 
480
        return
 
481
    bfn = fn + '~'
 
482
 
 
483
    if has_symlinks() and os.path.islink(fn):
 
484
        target = os.readlink(fn)
 
485
        os.symlink(target, bfn)
 
486
        return
 
487
    inf = file(fn, 'rb')
 
488
    try:
 
489
        content = inf.read()
 
490
    finally:
 
491
        inf.close()
 
492
    
 
493
    outf = file(bfn, 'wb')
 
494
    try:
 
495
        outf.write(content)
 
496
    finally:
 
497
        outf.close()
 
498
 
 
499
 
471
500
def isdir(f):
472
501
    """True if f is an accessible directory."""
473
502
    try:
530
559
    return False
531
560
 
532
561
 
533
 
def pumpfile(from_file, to_file, read_length=-1, buff_size=32768):
 
562
def pumpfile(fromfile, tofile):
534
563
    """Copy contents of one file to another.
535
 
 
536
 
    The read_length can either be -1 to read to end-of-file (EOF) or
537
 
    it can specify the maximum number of bytes to read.
538
 
 
539
 
    The buff_size represents the maximum size for each read operation
540
 
    performed on from_file.
541
 
 
 
564
    
542
565
    :return: The number of bytes copied.
543
566
    """
 
567
    BUFSIZE = 32768
544
568
    length = 0
545
 
    if read_length >= 0:
546
 
        # read specified number of bytes
547
 
 
548
 
        while read_length > 0:
549
 
            num_bytes_to_read = min(read_length, buff_size)
550
 
 
551
 
            block = from_file.read(num_bytes_to_read)
552
 
            if not block:
553
 
                # EOF reached
554
 
                break
555
 
            to_file.write(block)
556
 
 
557
 
            actual_bytes_read = len(block)
558
 
            read_length -= actual_bytes_read
559
 
            length += actual_bytes_read
560
 
    else:
561
 
        # read to EOF
562
 
        while True:
563
 
            block = from_file.read(buff_size)
564
 
            if not block:
565
 
                # EOF reached
566
 
                break
567
 
            to_file.write(block)
568
 
            length += len(block)
 
569
    while True:
 
570
        b = fromfile.read(BUFSIZE)
 
571
        if not b:
 
572
            break
 
573
        tofile.write(b)
 
574
        length += len(b)
569
575
    return length
570
576
 
571
577
 
578
584
 
579
585
 
580
586
def sha_file(f):
581
 
    """Calculate the hexdigest of an open file.
582
 
 
583
 
    The file cursor should be already at the start.
584
 
    """
 
587
    if getattr(f, 'tell', None) is not None:
 
588
        assert f.tell() == 0
585
589
    s = sha.new()
586
590
    BUFSIZE = 128<<10
587
591
    while True:
642
646
    offset = datetime.fromtimestamp(t) - datetime.utcfromtimestamp(t)
643
647
    return offset.days * 86400 + offset.seconds
644
648
 
645
 
weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
646
649
    
647
650
def format_date(t, offset=0, timezone='original', date_fmt=None,
648
651
                show_offset=True):
667
670
        tt = time.localtime(t)
668
671
        offset = local_time_offset(t)
669
672
    else:
670
 
        raise errors.UnsupportedTimezoneFormat(timezone)
 
673
        raise errors.BzrError("unsupported timezone format %r" % timezone,
 
674
                              ['options are "utc", "original", "local"'])
671
675
    if date_fmt is None:
672
676
        date_fmt = "%a %Y-%m-%d %H:%M:%S"
673
677
    if show_offset:
674
678
        offset_str = ' %+03d%02d' % (offset / 3600, (offset / 60) % 60)
675
679
    else:
676
680
        offset_str = ''
677
 
    # day of week depends on locale, so we do this ourself
678
 
    date_fmt = date_fmt.replace('%a', weekdays[tt[6]])
679
681
    return (time.strftime(date_fmt, tt) +  offset_str)
680
682
 
681
683
 
778
780
 
779
781
def splitpath(p):
780
782
    """Turn string into list of parts."""
 
783
    assert isinstance(p, basestring)
 
784
 
781
785
    # split on either delimiter because people might use either on
782
786
    # Windows
783
787
    ps = re.split(r'[\\/]', p)
793
797
    return rps
794
798
 
795
799
def joinpath(p):
 
800
    assert isinstance(p, (list, tuple))
796
801
    for f in p:
797
802
        if (f == '..') or (f is None) or (f == ''):
798
803
            raise errors.BzrError("sorry, %r not allowed in path" % f)
845
850
        return False
846
851
 
847
852
 
848
 
def has_hardlinks():
849
 
    if getattr(os, 'link', None) is not None:
850
 
        return True
851
 
    else:
852
 
        return False
853
 
 
854
 
 
855
 
def host_os_dereferences_symlinks():
856
 
    return (has_symlinks()
857
 
            and sys.platform not in ('cygwin', 'win32'))
858
 
 
859
 
 
860
853
def contains_whitespace(s):
861
854
    """True if there are any whitespace characters in s."""
862
855
    # string.whitespace can include '\xa0' in certain locales, because it is
897
890
    avoids that problem.
898
891
    """
899
892
 
900
 
    if len(base) < MIN_ABS_PATHLENGTH:
901
 
        # must have space for e.g. a drive letter
902
 
        raise ValueError('%r is too short to calculate a relative path'
903
 
            % (base,))
 
893
    assert len(base) >= MIN_ABS_PATHLENGTH, ('Length of base must be equal or'
 
894
        ' exceed the platform minimum length (which is %d)' % 
 
895
        MIN_ABS_PATHLENGTH)
904
896
 
905
897
    rp = abspath(path)
906
898
 
1023
1015
    On platforms where the system does not normalize filenames 
1024
1016
    (Windows, Linux), you have to access a file by its exact path.
1025
1017
 
1026
 
    Internally, bzr only supports NFC normalization, since that is 
 
1018
    Internally, bzr only supports NFC/NFKC normalization, since that is 
1027
1019
    the standard for XML documents.
1028
1020
 
1029
1021
    So return the normalized path, and a flag indicating if the file
1030
1022
    can be accessed by that path.
1031
1023
    """
1032
1024
 
1033
 
    return unicodedata.normalize('NFC', unicode(path)), True
 
1025
    return unicodedata.normalize('NFKC', unicode(path)), True
1034
1026
 
1035
1027
 
1036
1028
def _inaccessible_normalized_filename(path):
1037
1029
    __doc__ = _accessible_normalized_filename.__doc__
1038
1030
 
1039
 
    normalized = unicodedata.normalize('NFC', unicode(path))
 
1031
    normalized = unicodedata.normalize('NFKC', unicode(path))
1040
1032
    return normalized, normalized == path
1041
1033
 
1042
1034
 
1119
1111
        raise errors.IllegalPath(path)
1120
1112
 
1121
1113
 
1122
 
_WIN32_ERROR_DIRECTORY = 267 # Similar to errno.ENOTDIR
1123
 
 
1124
 
def _is_error_enotdir(e):
1125
 
    """Check if this exception represents ENOTDIR.
1126
 
 
1127
 
    Unfortunately, python is very inconsistent about the exception
1128
 
    here. The cases are:
1129
 
      1) Linux, Mac OSX all versions seem to set errno == ENOTDIR
1130
 
      2) Windows, Python2.4, uses errno == ERROR_DIRECTORY (267)
1131
 
         which is the windows error code.
1132
 
      3) Windows, Python2.5 uses errno == EINVAL and
1133
 
         winerror == ERROR_DIRECTORY
1134
 
 
1135
 
    :param e: An Exception object (expected to be OSError with an errno
1136
 
        attribute, but we should be able to cope with anything)
1137
 
    :return: True if this represents an ENOTDIR error. False otherwise.
1138
 
    """
1139
 
    en = getattr(e, 'errno', None)
1140
 
    if (en == errno.ENOTDIR
1141
 
        or (sys.platform == 'win32'
1142
 
            and (en == _WIN32_ERROR_DIRECTORY
1143
 
                 or (en == errno.EINVAL
1144
 
                     and getattr(e, 'winerror', None) == _WIN32_ERROR_DIRECTORY)
1145
 
        ))):
1146
 
        return True
1147
 
    return False
1148
 
 
1149
 
 
1150
1114
def walkdirs(top, prefix=""):
1151
1115
    """Yield data about all the directories in a tree.
1152
1116
    
1196
1160
 
1197
1161
        dirblock = []
1198
1162
        append = dirblock.append
1199
 
        try:
1200
 
            names = sorted(_listdir(top))
1201
 
        except OSError, e:
1202
 
            if not _is_error_enotdir(e):
1203
 
                raise
1204
 
        else:
1205
 
            for name in names:
1206
 
                abspath = top_slash + name
1207
 
                statvalue = _lstat(abspath)
1208
 
                kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1209
 
                append((relprefix + name, name, kind, statvalue, abspath))
 
1163
        for name in sorted(_listdir(top)):
 
1164
            abspath = top_slash + name
 
1165
            statvalue = _lstat(abspath)
 
1166
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1167
            append((relprefix + name, name, kind, statvalue, abspath))
1210
1168
        yield (relroot, top), dirblock
1211
1169
 
1212
1170
        # push the user specified dirs from dirblock
1213
1171
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1214
1172
 
1215
1173
 
1216
 
_real_walkdirs_utf8 = None
1217
 
 
1218
1174
def _walkdirs_utf8(top, prefix=""):
1219
1175
    """Yield data about all the directories in a tree.
1220
1176
 
1229
1185
        path-from-top might be unicode or utf8, but it is the correct path to
1230
1186
        pass to os functions to affect the file in question. (such as os.lstat)
1231
1187
    """
1232
 
    global _real_walkdirs_utf8
1233
 
    if _real_walkdirs_utf8 is None:
1234
 
        fs_encoding = _fs_enc.upper()
1235
 
        if win32utils.winver == 'Windows NT':
1236
 
            # Win98 doesn't have unicode apis like FindFirstFileW
1237
 
            # TODO: We possibly could support Win98 by falling back to the
1238
 
            #       original FindFirstFile, and using TCHAR instead of WCHAR,
1239
 
            #       but that gets a bit tricky, and requires custom compiling
1240
 
            #       for win98 anyway.
1241
 
            try:
1242
 
                from bzrlib._walkdirs_win32 import _walkdirs_utf8_win32_find_file
1243
 
            except ImportError:
1244
 
                _real_walkdirs_utf8 = _walkdirs_unicode_to_utf8
1245
 
            else:
1246
 
                _real_walkdirs_utf8 = _walkdirs_utf8_win32_find_file
1247
 
        elif fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968'):
1248
 
            # ANSI_X3.4-1968 is a form of ASCII
1249
 
            _real_walkdirs_utf8 = _walkdirs_unicode_to_utf8
1250
 
        else:
1251
 
            _real_walkdirs_utf8 = _walkdirs_fs_utf8
1252
 
    return _real_walkdirs_utf8(top, prefix=prefix)
 
1188
    fs_encoding = _fs_enc.upper()
 
1189
    if (sys.platform == 'win32' or
 
1190
        fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968')): # ascii
 
1191
        return _walkdirs_unicode_to_utf8(top, prefix=prefix)
 
1192
    else:
 
1193
        return _walkdirs_fs_utf8(top, prefix=prefix)
1253
1194
 
1254
1195
 
1255
1196
def _walkdirs_fs_utf8(top, prefix=""):
1427
1368
    # Windows returns 'cp0' to indicate there is no code page. So we'll just
1428
1369
    # treat that as ASCII, and not support printing unicode characters to the
1429
1370
    # console.
1430
 
    #
1431
 
    # For python scripts run under vim, we get '', so also treat that as ASCII
1432
 
    if user_encoding in (None, 'cp0', ''):
 
1371
    if user_encoding in (None, 'cp0'):
1433
1372
        user_encoding = 'ascii'
1434
1373
    else:
1435
1374
        # check encoding