/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Andrew Bennetts
  • Date: 2008-10-27 06:14:45 UTC
  • mfrom: (3793 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3795.
  • Revision ID: andrew.bennetts@canonical.com-20081027061445-eqt9lz6uw1mbvq4g
Merge from bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
from cStringIO import StringIO
18
17
import os
19
18
import re
20
19
import stat
35
34
                    splitdrive as _nt_splitdrive,
36
35
                    )
37
36
import posixpath
38
 
import sha
39
37
import shutil
40
38
from shutil import (
41
39
    rmtree,
53
51
    )
54
52
""")
55
53
 
 
54
# sha and md5 modules are deprecated in python2.6 but hashlib is available as
 
55
# of 2.5
 
56
if sys.version_info < (2, 5):
 
57
    import md5 as _mod_md5
 
58
    md5 = _mod_md5.new
 
59
    import sha as _mod_sha
 
60
    sha = _mod_sha.new
 
61
else:
 
62
    from hashlib import (
 
63
        md5,
 
64
        sha1 as sha,
 
65
        )
 
66
 
56
67
 
57
68
import bzrlib
58
69
from bzrlib import symbol_versioning
59
 
from bzrlib.symbol_versioning import (
60
 
    deprecated_function,
61
 
    )
62
 
from bzrlib.trace import mutter
63
70
 
64
71
 
65
72
# On win32, O_BINARY is used to indicate the file should
123
130
 
124
131
_directory_kind = 'directory'
125
132
 
126
 
_formats = {
127
 
    stat.S_IFDIR:_directory_kind,
128
 
    stat.S_IFCHR:'chardev',
129
 
    stat.S_IFBLK:'block',
130
 
    stat.S_IFREG:'file',
131
 
    stat.S_IFIFO:'fifo',
132
 
    stat.S_IFLNK:'symlink',
133
 
    stat.S_IFSOCK:'socket',
134
 
}
135
 
 
136
 
 
137
 
def file_kind_from_stat_mode(stat_mode, _formats=_formats, _unknown='unknown'):
138
 
    """Generate a file kind from a stat mode. This is used in walkdirs.
139
 
 
140
 
    Its performance is critical: Do not mutate without careful benchmarking.
141
 
    """
142
 
    try:
143
 
        return _formats[stat_mode & 0170000]
144
 
    except KeyError:
145
 
        return _unknown
146
 
 
147
 
 
148
 
def file_kind(f, _lstat=os.lstat, _mapper=file_kind_from_stat_mode):
149
 
    try:
150
 
        return _mapper(_lstat(f).st_mode)
151
 
    except OSError, e:
152
 
        if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
153
 
            raise errors.NoSuchFile(f)
154
 
        raise
155
 
 
156
 
 
157
133
def get_umask():
158
134
    """Return the current umask"""
159
135
    # Assume that people aren't messing with the umask while running
203
179
    """
204
180
 
205
181
    # sftp rename doesn't allow overwriting, so play tricks:
206
 
    import random
207
182
    base = os.path.basename(new)
208
183
    dirname = os.path.dirname(new)
209
184
    tmp_name = u'tmp.%s.%.9f.%d.%s' % (base, time.time(), os.getpid(), rand_chars(10))
316
291
        path = cwd + '\\' + path
317
292
    return _win32_fixdrive(_nt_normpath(path).replace('\\', '/'))
318
293
 
319
 
if win32utils.winver == 'Windows 98':
320
 
    _win32_abspath = _win98_abspath
321
 
 
322
294
 
323
295
def _win32_realpath(path):
324
296
    # Real _nt_realpath doesn't have a problem with a unicode cwd
383
355
 
384
356
 
385
357
if sys.platform == 'win32':
386
 
    abspath = _win32_abspath
 
358
    if win32utils.winver == 'Windows 98':
 
359
        abspath = _win98_abspath
 
360
    else:
 
361
        abspath = _win32_abspath
387
362
    realpath = _win32_realpath
388
363
    pathjoin = _win32_pathjoin
389
364
    normpath = _win32_normpath
418
393
 
419
394
    This attempts to check both sys.stdout and sys.stdin to see
420
395
    what encoding they are in, and if that fails it falls back to
421
 
    bzrlib.user_encoding.
 
396
    osutils.get_user_encoding().
422
397
    The problem is that on Windows, locale.getpreferredencoding()
423
398
    is not the same encoding as that used by the console:
424
399
    http://mail.python.org/pipermail/python-list/2003-May/162357.html
426
401
    On my standard US Windows XP, the preferred encoding is
427
402
    cp1252, but the console is cp437
428
403
    """
 
404
    from bzrlib.trace import mutter
429
405
    output_encoding = getattr(sys.stdout, 'encoding', None)
430
406
    if not output_encoding:
431
407
        input_encoding = getattr(sys.stdin, 'encoding', None)
432
408
        if not input_encoding:
433
 
            output_encoding = bzrlib.user_encoding
434
 
            mutter('encoding stdout as bzrlib.user_encoding %r', output_encoding)
 
409
            output_encoding = get_user_encoding()
 
410
            mutter('encoding stdout as osutils.get_user_encoding() %r',
 
411
                   output_encoding)
435
412
        else:
436
413
            output_encoding = input_encoding
437
414
            mutter('encoding stdout as sys.stdin encoding %r', output_encoding)
439
416
        mutter('encoding stdout as sys.stdout encoding %r', output_encoding)
440
417
    if output_encoding == 'cp0':
441
418
        # invalid encoding (cp0 means 'no codepage' on Windows)
442
 
        output_encoding = bzrlib.user_encoding
 
419
        output_encoding = get_user_encoding()
443
420
        mutter('cp0 is invalid encoding.'
444
 
               ' encoding stdout as bzrlib.user_encoding %r', output_encoding)
 
421
               ' encoding stdout as osutils.get_user_encoding() %r',
 
422
               output_encoding)
445
423
    # check encoding
446
424
    try:
447
425
        codecs.lookup(output_encoding)
449
427
        sys.stderr.write('bzr: warning:'
450
428
                         ' unknown terminal encoding %s.\n'
451
429
                         '  Using encoding %s instead.\n'
452
 
                         % (output_encoding, bzrlib.user_encoding)
 
430
                         % (output_encoding, get_user_encoding())
453
431
                        )
454
 
        output_encoding = bzrlib.user_encoding
 
432
        output_encoding = get_user_encoding()
455
433
 
456
434
    return output_encoding
457
435
 
600
578
 
601
579
    The file cursor should be already at the start.
602
580
    """
603
 
    s = sha.new()
 
581
    s = sha()
604
582
    BUFSIZE = 128<<10
605
583
    while True:
606
584
        b = f.read(BUFSIZE)
612
590
 
613
591
def sha_file_by_name(fname):
614
592
    """Calculate the SHA1 of a file by reading the full text"""
615
 
    s = sha.new()
 
593
    s = sha()
616
594
    f = os.open(fname, os.O_RDONLY | O_BINARY)
617
595
    try:
618
596
        while True:
624
602
        os.close(f)
625
603
 
626
604
 
627
 
def sha_strings(strings, _factory=sha.new):
 
605
def sha_strings(strings, _factory=sha):
628
606
    """Return the sha-1 of concatenation of strings"""
629
607
    s = _factory()
630
608
    map(s.update, strings)
631
609
    return s.hexdigest()
632
610
 
633
611
 
634
 
def sha_string(f, _factory=sha.new):
 
612
def sha_string(f, _factory=sha):
635
613
    return _factory(f).hexdigest()
636
614
 
637
615
 
638
616
def fingerprint_file(f):
639
617
    b = f.read()
640
618
    return {'size': len(b),
641
 
            'sha1': sha.new(b).hexdigest()}
 
619
            'sha1': sha(b).hexdigest()}
642
620
 
643
621
 
644
622
def compare_files(a, b):
671
649
    :param timezone: How to display the time: 'utc', 'original' for the
672
650
         timezone specified by offset, or 'local' for the process's current
673
651
         timezone.
674
 
    :param show_offset: Whether to append the timezone.
675
 
    :param date_fmt: strftime format.
676
 
    """
 
652
    :param date_fmt: strftime format.
 
653
    :param show_offset: Whether to append the timezone.
 
654
    """
 
655
    (date_fmt, tt, offset_str) = \
 
656
               _format_date(t, offset, timezone, date_fmt, show_offset)
 
657
    date_fmt = date_fmt.replace('%a', weekdays[tt[6]])
 
658
    date_str = time.strftime(date_fmt, tt)
 
659
    return date_str + offset_str
 
660
 
 
661
def format_local_date(t, offset=0, timezone='original', date_fmt=None,
 
662
                      show_offset=True):
 
663
    """Return an unicode date string formatted according to the current locale.
 
664
 
 
665
    :param t: Seconds since the epoch.
 
666
    :param offset: Timezone offset in seconds east of utc.
 
667
    :param timezone: How to display the time: 'utc', 'original' for the
 
668
         timezone specified by offset, or 'local' for the process's current
 
669
         timezone.
 
670
    :param date_fmt: strftime format.
 
671
    :param show_offset: Whether to append the timezone.
 
672
    """
 
673
    (date_fmt, tt, offset_str) = \
 
674
               _format_date(t, offset, timezone, date_fmt, show_offset)
 
675
    date_str = time.strftime(date_fmt, tt)
 
676
    if not isinstance(date_str, unicode):
 
677
        date_str = date_str.decode(bzrlib.user_encoding, 'replace')
 
678
    return date_str + offset_str
 
679
 
 
680
def _format_date(t, offset, timezone, date_fmt, show_offset):
677
681
    if timezone == 'utc':
678
682
        tt = time.gmtime(t)
679
683
        offset = 0
692
696
        offset_str = ' %+03d%02d' % (offset / 3600, (offset / 60) % 60)
693
697
    else:
694
698
        offset_str = ''
695
 
    # day of week depends on locale, so we do this ourself
696
 
    date_fmt = date_fmt.replace('%a', weekdays[tt[6]])
697
 
    return (time.strftime(date_fmt, tt) +  offset_str)
 
699
    return (date_fmt, tt, offset_str)
698
700
 
699
701
 
700
702
def compact_date(when):
1118
1120
            del os.environ[env_variable]
1119
1121
    else:
1120
1122
        if isinstance(value, unicode):
1121
 
            value = value.encode(bzrlib.user_encoding)
 
1123
            value = value.encode(get_user_encoding())
1122
1124
        os.environ[env_variable] = value
1123
1125
    return orig_val
1124
1126
 
1201
1203
    _lstat = os.lstat
1202
1204
    _directory = _directory_kind
1203
1205
    _listdir = os.listdir
1204
 
    _kind_from_mode = _formats.get
 
1206
    _kind_from_mode = file_kind_from_stat_mode
1205
1207
    pending = [(safe_unicode(prefix), "", _directory, None, safe_unicode(top))]
1206
1208
    while pending:
1207
1209
        # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1223
1225
            for name in names:
1224
1226
                abspath = top_slash + name
1225
1227
                statvalue = _lstat(abspath)
1226
 
                kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1228
                kind = _kind_from_mode(statvalue.st_mode)
1227
1229
                append((relprefix + name, name, kind, statvalue, abspath))
1228
1230
        yield (relroot, top), dirblock
1229
1231
 
1231
1233
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1232
1234
 
1233
1235
 
1234
 
_real_walkdirs_utf8 = None
 
1236
class DirReader(object):
 
1237
    """An interface for reading directories."""
 
1238
 
 
1239
    def top_prefix_to_starting_dir(self, top, prefix=""):
 
1240
        """Converts top and prefix to a starting dir entry
 
1241
 
 
1242
        :param top: A utf8 path
 
1243
        :param prefix: An optional utf8 path to prefix output relative paths
 
1244
            with.
 
1245
        :return: A tuple starting with prefix, and ending with the native
 
1246
            encoding of top.
 
1247
        """
 
1248
        raise NotImplementedError(self.top_prefix_to_starting_dir)
 
1249
 
 
1250
    def read_dir(self, prefix, top):
 
1251
        """Read a specific dir.
 
1252
 
 
1253
        :param prefix: A utf8 prefix to be preprended to the path basenames.
 
1254
        :param top: A natively encoded path to read.
 
1255
        :return: A list of the directories contents. Each item contains:
 
1256
            (utf8_relpath, utf8_name, kind, lstatvalue, native_abspath)
 
1257
        """
 
1258
        raise NotImplementedError(self.read_dir)
 
1259
 
 
1260
 
 
1261
_selected_dir_reader = None
 
1262
 
1235
1263
 
1236
1264
def _walkdirs_utf8(top, prefix=""):
1237
1265
    """Yield data about all the directories in a tree.
1247
1275
        path-from-top might be unicode or utf8, but it is the correct path to
1248
1276
        pass to os functions to affect the file in question. (such as os.lstat)
1249
1277
    """
1250
 
    global _real_walkdirs_utf8
1251
 
    if _real_walkdirs_utf8 is None:
 
1278
    global _selected_dir_reader
 
1279
    if _selected_dir_reader is None:
1252
1280
        fs_encoding = _fs_enc.upper()
1253
 
        if win32utils.winver == 'Windows NT':
 
1281
        if sys.platform == "win32" and win32utils.winver == 'Windows NT':
1254
1282
            # Win98 doesn't have unicode apis like FindFirstFileW
1255
1283
            # TODO: We possibly could support Win98 by falling back to the
1256
1284
            #       original FindFirstFile, and using TCHAR instead of WCHAR,
1257
1285
            #       but that gets a bit tricky, and requires custom compiling
1258
1286
            #       for win98 anyway.
1259
1287
            try:
1260
 
                from bzrlib._walkdirs_win32 import _walkdirs_utf8_win32_find_file
 
1288
                from bzrlib._walkdirs_win32 import Win32ReadDir
1261
1289
            except ImportError:
1262
 
                _real_walkdirs_utf8 = _walkdirs_unicode_to_utf8
 
1290
                _selected_dir_reader = UnicodeDirReader()
1263
1291
            else:
1264
 
                _real_walkdirs_utf8 = _walkdirs_utf8_win32_find_file
 
1292
                _selected_dir_reader = Win32ReadDir()
1265
1293
        elif fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968'):
1266
1294
            # ANSI_X3.4-1968 is a form of ASCII
1267
 
            _real_walkdirs_utf8 = _walkdirs_unicode_to_utf8
 
1295
            _selected_dir_reader = UnicodeDirReader()
1268
1296
        else:
1269
 
            _real_walkdirs_utf8 = _walkdirs_fs_utf8
1270
 
    return _real_walkdirs_utf8(top, prefix=prefix)
1271
 
 
1272
 
 
1273
 
def _walkdirs_fs_utf8(top, prefix=""):
1274
 
    """See _walkdirs_utf8.
1275
 
 
1276
 
    This sub-function is called when we know the filesystem is already in utf8
1277
 
    encoding. So we don't need to transcode filenames.
1278
 
    """
1279
 
    _lstat = os.lstat
1280
 
    _directory = _directory_kind
1281
 
    # Use C accelerated directory listing.
1282
 
    _listdir = _read_dir
1283
 
    _kind_from_mode = _formats.get
1284
 
 
 
1297
            try:
 
1298
                from bzrlib._readdir_pyx import UTF8DirReader
 
1299
            except ImportError:
 
1300
                # No optimised code path
 
1301
                _selected_dir_reader = UnicodeDirReader()
 
1302
            else:
 
1303
                _selected_dir_reader = UTF8DirReader()
1285
1304
    # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1286
1305
    # But we don't actually uses 1-3 in pending, so set them to None
1287
 
    pending = [(safe_utf8(prefix), None, None, None, safe_utf8(top))]
 
1306
    pending = [[_selected_dir_reader.top_prefix_to_starting_dir(top, prefix)]]
 
1307
    read_dir = _selected_dir_reader.read_dir
 
1308
    _directory = _directory_kind
1288
1309
    while pending:
1289
 
        relroot, _, _, _, top = pending.pop()
1290
 
        if relroot:
1291
 
            relprefix = relroot + '/'
1292
 
        else:
1293
 
            relprefix = ''
1294
 
        top_slash = top + '/'
1295
 
 
1296
 
        dirblock = []
1297
 
        append = dirblock.append
1298
 
        # read_dir supplies in should-stat order.
1299
 
        for _, name in sorted(_listdir(top)):
1300
 
            abspath = top_slash + name
1301
 
            statvalue = _lstat(abspath)
1302
 
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1303
 
            append((relprefix + name, name, kind, statvalue, abspath))
1304
 
        dirblock.sort()
 
1310
        relroot, _, _, _, top = pending[-1].pop()
 
1311
        if not pending[-1]:
 
1312
            pending.pop()
 
1313
        dirblock = sorted(read_dir(relroot, top))
1305
1314
        yield (relroot, top), dirblock
1306
 
 
1307
1315
        # push the user specified dirs from dirblock
1308
 
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1309
 
 
1310
 
 
1311
 
def _walkdirs_unicode_to_utf8(top, prefix=""):
1312
 
    """See _walkdirs_utf8
1313
 
 
1314
 
    Because Win32 has a Unicode api, all of the 'path-from-top' entries will be
1315
 
    Unicode paths.
1316
 
    This is currently the fallback code path when the filesystem encoding is
1317
 
    not UTF-8. It may be better to implement an alternative so that we can
1318
 
    safely handle paths that are not properly decodable in the current
1319
 
    encoding.
1320
 
    """
1321
 
    _utf8_encode = codecs.getencoder('utf8')
1322
 
    _lstat = os.lstat
1323
 
    _directory = _directory_kind
1324
 
    _listdir = os.listdir
1325
 
    _kind_from_mode = _formats.get
1326
 
 
1327
 
    pending = [(safe_utf8(prefix), None, None, None, safe_unicode(top))]
1328
 
    while pending:
1329
 
        relroot, _, _, _, top = pending.pop()
1330
 
        if relroot:
1331
 
            relprefix = relroot + '/'
 
1316
        next = [d for d in reversed(dirblock) if d[2] == _directory]
 
1317
        if next:
 
1318
            pending.append(next)
 
1319
 
 
1320
 
 
1321
class UnicodeDirReader(DirReader):
 
1322
    """A dir reader for non-utf8 file systems, which transcodes."""
 
1323
 
 
1324
    __slots__ = ['_utf8_encode']
 
1325
 
 
1326
    def __init__(self):
 
1327
        self._utf8_encode = codecs.getencoder('utf8')
 
1328
 
 
1329
    def top_prefix_to_starting_dir(self, top, prefix=""):
 
1330
        """See DirReader.top_prefix_to_starting_dir."""
 
1331
        return (safe_utf8(prefix), None, None, None, safe_unicode(top))
 
1332
 
 
1333
    def read_dir(self, prefix, top):
 
1334
        """Read a single directory from a non-utf8 file system.
 
1335
 
 
1336
        top, and the abspath element in the output are unicode, all other paths
 
1337
        are utf8. Local disk IO is done via unicode calls to listdir etc.
 
1338
 
 
1339
        This is currently the fallback code path when the filesystem encoding is
 
1340
        not UTF-8. It may be better to implement an alternative so that we can
 
1341
        safely handle paths that are not properly decodable in the current
 
1342
        encoding.
 
1343
 
 
1344
        See DirReader.read_dir for details.
 
1345
        """
 
1346
        _utf8_encode = self._utf8_encode
 
1347
        _lstat = os.lstat
 
1348
        _listdir = os.listdir
 
1349
        _kind_from_mode = file_kind_from_stat_mode
 
1350
 
 
1351
        if prefix:
 
1352
            relprefix = prefix + '/'
1332
1353
        else:
1333
1354
            relprefix = ''
1334
1355
        top_slash = top + u'/'
1336
1357
        dirblock = []
1337
1358
        append = dirblock.append
1338
1359
        for name in sorted(_listdir(top)):
1339
 
            name_utf8 = _utf8_encode(name)[0]
 
1360
            try:
 
1361
                name_utf8 = _utf8_encode(name)[0]
 
1362
            except UnicodeDecodeError:
 
1363
                raise errors.BadFilenameEncoding(
 
1364
                    _utf8_encode(relprefix)[0] + name, _fs_enc)
1340
1365
            abspath = top_slash + name
1341
1366
            statvalue = _lstat(abspath)
1342
 
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1367
            kind = _kind_from_mode(statvalue.st_mode)
1343
1368
            append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
1344
 
        yield (relroot, top), dirblock
1345
 
 
1346
 
        # push the user specified dirs from dirblock
1347
 
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
 
1369
        return dirblock
1348
1370
 
1349
1371
 
1350
1372
def copy_tree(from_path, to_path, handlers={}):
1572
1594
    return open(filename, 'rU').read()
1573
1595
 
1574
1596
 
1575
 
try:
1576
 
    from bzrlib._readdir_pyx import read_dir as _read_dir
1577
 
except ImportError:
1578
 
    from bzrlib._readdir_py import read_dir as _read_dir
 
1597
def file_kind_from_stat_mode_thunk(mode):
 
1598
    global file_kind_from_stat_mode
 
1599
    if file_kind_from_stat_mode is file_kind_from_stat_mode_thunk:
 
1600
        try:
 
1601
            from bzrlib._readdir_pyx import UTF8DirReader
 
1602
            file_kind_from_stat_mode = UTF8DirReader().kind_from_mode
 
1603
        except ImportError:
 
1604
            from bzrlib._readdir_py import (
 
1605
                _kind_from_mode as file_kind_from_stat_mode
 
1606
                )
 
1607
    return file_kind_from_stat_mode(mode)
 
1608
file_kind_from_stat_mode = file_kind_from_stat_mode_thunk
 
1609
 
 
1610
 
 
1611
def file_kind(f, _lstat=os.lstat):
 
1612
    try:
 
1613
        return file_kind_from_stat_mode(_lstat(f).st_mode)
 
1614
    except OSError, e:
 
1615
        if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
 
1616
            raise errors.NoSuchFile(f)
 
1617
        raise
 
1618
 
 
1619