~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: John Arbash Meinel
  • Author(s): Mark Hammond
  • Date: 2008-09-09 17:02:21 UTC
  • mto: This revision was merged to the branch mainline in revision 3697.
  • Revision ID: john@arbash-meinel.com-20080909170221-svim3jw2mrz0amp3
An updated transparent icon for bzr.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2009 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
 
17
from cStringIO import StringIO
17
18
import os
18
19
import re
19
20
import stat
34
35
                    splitdrive as _nt_splitdrive,
35
36
                    )
36
37
import posixpath
 
38
import sha
37
39
import shutil
38
40
from shutil import (
39
41
    rmtree,
51
53
    )
52
54
""")
53
55
 
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
 
 
67
56
 
68
57
import bzrlib
69
58
from bzrlib import symbol_versioning
 
59
from bzrlib.symbol_versioning import (
 
60
    deprecated_function,
 
61
    )
 
62
from bzrlib.trace import mutter
70
63
 
71
64
 
72
65
# On win32, O_BINARY is used to indicate the file should
121
114
    global _QUOTE_RE
122
115
    if _QUOTE_RE is None:
123
116
        _QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/\\_~-])')
124
 
 
 
117
        
125
118
    if _QUOTE_RE.search(f):
126
119
        return '"' + f + '"'
127
120
    else:
130
123
 
131
124
_directory_kind = 'directory'
132
125
 
 
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
 
133
157
def get_umask():
134
158
    """Return the current umask"""
135
159
    # Assume that people aren't messing with the umask while running
171
195
 
172
196
def fancy_rename(old, new, rename_func, unlink_func):
173
197
    """A fancy rename, when you don't have atomic rename.
174
 
 
 
198
    
175
199
    :param old: The old path, to rename from
176
200
    :param new: The new path, to rename to
177
201
    :param rename_func: The potentially non-atomic rename function
179
203
    """
180
204
 
181
205
    # sftp rename doesn't allow overwriting, so play tricks:
 
206
    import random
182
207
    base = os.path.basename(new)
183
208
    dirname = os.path.dirname(new)
184
209
    tmp_name = u'tmp.%s.%.9f.%d.%s' % (base, time.time(), os.getpid(), rand_chars(10))
291
316
        path = cwd + '\\' + path
292
317
    return _win32_fixdrive(_nt_normpath(path).replace('\\', '/'))
293
318
 
 
319
if win32utils.winver == 'Windows 98':
 
320
    _win32_abspath = _win98_abspath
 
321
 
294
322
 
295
323
def _win32_realpath(path):
296
324
    # Real _nt_realpath doesn't have a problem with a unicode cwd
317
345
    """We expect to be able to atomically replace 'new' with old.
318
346
 
319
347
    On win32, if new exists, it must be moved out of the way first,
320
 
    and then deleted.
 
348
    and then deleted. 
321
349
    """
322
350
    try:
323
351
        fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
324
352
    except OSError, e:
325
353
        if e.errno in (errno.EPERM, errno.EACCES, errno.EBUSY, errno.EINVAL):
326
 
            # If we try to rename a non-existant file onto cwd, we get
327
 
            # EPERM or EACCES instead of ENOENT, this will raise ENOENT
 
354
            # If we try to rename a non-existant file onto cwd, we get 
 
355
            # EPERM or EACCES instead of ENOENT, this will raise ENOENT 
328
356
            # if the old path doesn't exist, sometimes we get EACCES
329
357
            # On Linux, we seem to get EBUSY, on Mac we get EINVAL
330
358
            os.lstat(old)
355
383
 
356
384
 
357
385
if sys.platform == 'win32':
358
 
    if win32utils.winver == 'Windows 98':
359
 
        abspath = _win98_abspath
360
 
    else:
361
 
        abspath = _win32_abspath
 
386
    abspath = _win32_abspath
362
387
    realpath = _win32_realpath
363
388
    pathjoin = _win32_pathjoin
364
389
    normpath = _win32_normpath
393
418
 
394
419
    This attempts to check both sys.stdout and sys.stdin to see
395
420
    what encoding they are in, and if that fails it falls back to
396
 
    osutils.get_user_encoding().
 
421
    bzrlib.user_encoding.
397
422
    The problem is that on Windows, locale.getpreferredencoding()
398
423
    is not the same encoding as that used by the console:
399
424
    http://mail.python.org/pipermail/python-list/2003-May/162357.html
401
426
    On my standard US Windows XP, the preferred encoding is
402
427
    cp1252, but the console is cp437
403
428
    """
404
 
    from bzrlib.trace import mutter
405
429
    output_encoding = getattr(sys.stdout, 'encoding', None)
406
430
    if not output_encoding:
407
431
        input_encoding = getattr(sys.stdin, 'encoding', None)
408
432
        if not input_encoding:
409
 
            output_encoding = get_user_encoding()
410
 
            mutter('encoding stdout as osutils.get_user_encoding() %r',
411
 
                   output_encoding)
 
433
            output_encoding = bzrlib.user_encoding
 
434
            mutter('encoding stdout as bzrlib.user_encoding %r', output_encoding)
412
435
        else:
413
436
            output_encoding = input_encoding
414
437
            mutter('encoding stdout as sys.stdin encoding %r', output_encoding)
416
439
        mutter('encoding stdout as sys.stdout encoding %r', output_encoding)
417
440
    if output_encoding == 'cp0':
418
441
        # invalid encoding (cp0 means 'no codepage' on Windows)
419
 
        output_encoding = get_user_encoding()
 
442
        output_encoding = bzrlib.user_encoding
420
443
        mutter('cp0 is invalid encoding.'
421
 
               ' encoding stdout as osutils.get_user_encoding() %r',
422
 
               output_encoding)
 
444
               ' encoding stdout as bzrlib.user_encoding %r', output_encoding)
423
445
    # check encoding
424
446
    try:
425
447
        codecs.lookup(output_encoding)
427
449
        sys.stderr.write('bzr: warning:'
428
450
                         ' unknown terminal encoding %s.\n'
429
451
                         '  Using encoding %s instead.\n'
430
 
                         % (output_encoding, get_user_encoding())
 
452
                         % (output_encoding, bzrlib.user_encoding)
431
453
                        )
432
 
        output_encoding = get_user_encoding()
 
454
        output_encoding = bzrlib.user_encoding
433
455
 
434
456
    return output_encoding
435
457
 
470
492
 
471
493
def is_inside(dir, fname):
472
494
    """True if fname is inside dir.
473
 
 
 
495
    
474
496
    The parameters should typically be passed to osutils.normpath first, so
475
497
    that . and .. and repeated slashes are eliminated, and the separators
476
498
    are canonical for the platform.
477
 
 
478
 
    The empty string as a dir name is taken as top-of-tree and matches
 
499
    
 
500
    The empty string as a dir name is taken as top-of-tree and matches 
479
501
    everything.
480
502
    """
481
 
    # XXX: Most callers of this can actually do something smarter by
 
503
    # XXX: Most callers of this can actually do something smarter by 
482
504
    # looking at the inventory
483
505
    if dir == fname:
484
506
        return True
485
 
 
 
507
    
486
508
    if dir == '':
487
509
        return True
488
510
 
508
530
    return False
509
531
 
510
532
 
511
 
def pumpfile(from_file, to_file, read_length=-1, buff_size=32768,
512
 
             report_activity=None, direction='read'):
 
533
def pumpfile(from_file, to_file, read_length=-1, buff_size=32768):
513
534
    """Copy contents of one file to another.
514
535
 
515
536
    The read_length can either be -1 to read to end-of-file (EOF) or
518
539
    The buff_size represents the maximum size for each read operation
519
540
    performed on from_file.
520
541
 
521
 
    :param report_activity: Call this as bytes are read, see
522
 
        Transport._report_activity
523
 
    :param direction: Will be passed to report_activity
524
 
 
525
542
    :return: The number of bytes copied.
526
543
    """
527
544
    length = 0
535
552
            if not block:
536
553
                # EOF reached
537
554
                break
538
 
            if report_activity is not None:
539
 
                report_activity(len(block), direction)
540
555
            to_file.write(block)
541
556
 
542
557
            actual_bytes_read = len(block)
549
564
            if not block:
550
565
                # EOF reached
551
566
                break
552
 
            if report_activity is not None:
553
 
                report_activity(len(block), direction)
554
567
            to_file.write(block)
555
568
            length += len(block)
556
569
    return length
587
600
 
588
601
    The file cursor should be already at the start.
589
602
    """
590
 
    s = sha()
 
603
    s = sha.new()
591
604
    BUFSIZE = 128<<10
592
605
    while True:
593
606
        b = f.read(BUFSIZE)
597
610
    return s.hexdigest()
598
611
 
599
612
 
600
 
def size_sha_file(f):
601
 
    """Calculate the size and hexdigest of an open file.
602
 
 
603
 
    The file cursor should be already at the start and
604
 
    the caller is responsible for closing the file afterwards.
605
 
    """
606
 
    size = 0
607
 
    s = sha()
608
 
    BUFSIZE = 128<<10
609
 
    while True:
610
 
        b = f.read(BUFSIZE)
611
 
        if not b:
612
 
            break
613
 
        size += len(b)
614
 
        s.update(b)
615
 
    return size, s.hexdigest()
616
 
 
617
 
 
618
613
def sha_file_by_name(fname):
619
614
    """Calculate the SHA1 of a file by reading the full text"""
620
 
    s = sha()
 
615
    s = sha.new()
621
616
    f = os.open(fname, os.O_RDONLY | O_BINARY)
622
617
    try:
623
618
        while True:
629
624
        os.close(f)
630
625
 
631
626
 
632
 
def sha_strings(strings, _factory=sha):
 
627
def sha_strings(strings, _factory=sha.new):
633
628
    """Return the sha-1 of concatenation of strings"""
634
629
    s = _factory()
635
630
    map(s.update, strings)
636
631
    return s.hexdigest()
637
632
 
638
633
 
639
 
def sha_string(f, _factory=sha):
 
634
def sha_string(f, _factory=sha.new):
640
635
    return _factory(f).hexdigest()
641
636
 
642
637
 
643
638
def fingerprint_file(f):
644
639
    b = f.read()
645
640
    return {'size': len(b),
646
 
            'sha1': sha(b).hexdigest()}
 
641
            'sha1': sha.new(b).hexdigest()}
647
642
 
648
643
 
649
644
def compare_files(a, b):
666
661
    return offset.days * 86400 + offset.seconds
667
662
 
668
663
weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
669
 
 
 
664
    
670
665
def format_date(t, offset=0, timezone='original', date_fmt=None,
671
666
                show_offset=True):
672
667
    """Return a formatted date string.
676
671
    :param timezone: How to display the time: 'utc', 'original' for the
677
672
         timezone specified by offset, or 'local' for the process's current
678
673
         timezone.
679
 
    :param date_fmt: strftime format.
680
 
    :param show_offset: Whether to append the timezone.
681
 
    """
682
 
    (date_fmt, tt, offset_str) = \
683
 
               _format_date(t, offset, timezone, date_fmt, show_offset)
684
 
    date_fmt = date_fmt.replace('%a', weekdays[tt[6]])
685
 
    date_str = time.strftime(date_fmt, tt)
686
 
    return date_str + offset_str
687
 
 
688
 
def format_local_date(t, offset=0, timezone='original', date_fmt=None,
689
 
                      show_offset=True):
690
 
    """Return an unicode date string formatted according to the current locale.
691
 
 
692
 
    :param t: Seconds since the epoch.
693
 
    :param offset: Timezone offset in seconds east of utc.
694
 
    :param timezone: How to display the time: 'utc', 'original' for the
695
 
         timezone specified by offset, or 'local' for the process's current
696
 
         timezone.
697
 
    :param date_fmt: strftime format.
698
 
    :param show_offset: Whether to append the timezone.
699
 
    """
700
 
    (date_fmt, tt, offset_str) = \
701
 
               _format_date(t, offset, timezone, date_fmt, show_offset)
702
 
    date_str = time.strftime(date_fmt, tt)
703
 
    if not isinstance(date_str, unicode):
704
 
        date_str = date_str.decode(bzrlib.user_encoding, 'replace')
705
 
    return date_str + offset_str
706
 
 
707
 
def _format_date(t, offset, timezone, date_fmt, show_offset):
 
674
    :param show_offset: Whether to append the timezone.
 
675
    :param date_fmt: strftime format.
 
676
    """
708
677
    if timezone == 'utc':
709
678
        tt = time.gmtime(t)
710
679
        offset = 0
723
692
        offset_str = ' %+03d%02d' % (offset / 3600, (offset / 60) % 60)
724
693
    else:
725
694
        offset_str = ''
726
 
    return (date_fmt, tt, 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)
727
698
 
728
699
 
729
700
def compact_date(when):
730
701
    return time.strftime('%Y%m%d%H%M%S', time.gmtime(when))
731
 
 
 
702
    
732
703
 
733
704
def format_delta(delta):
734
705
    """Get a nice looking string for a time delta.
810
781
ALNUM = '0123456789abcdefghijklmnopqrstuvwxyz'
811
782
def rand_chars(num):
812
783
    """Return a random string of num alphanumeric characters
813
 
 
814
 
    The result only contains lowercase chars because it may be used on
 
784
    
 
785
    The result only contains lowercase chars because it may be used on 
815
786
    case-insensitive filesystems.
816
787
    """
817
788
    s = ''
839
810
            rps.append(f)
840
811
    return rps
841
812
 
842
 
 
843
813
def joinpath(p):
844
814
    for f in p:
845
815
        if (f == '..') or (f is None) or (f == ''):
847
817
    return pathjoin(*p)
848
818
 
849
819
 
850
 
try:
851
 
    from bzrlib._chunks_to_lines_pyx import chunks_to_lines
852
 
except ImportError:
853
 
    from bzrlib._chunks_to_lines_py import chunks_to_lines
854
 
 
855
 
 
856
820
def split_lines(s):
857
821
    """Split s into lines, but without removing the newline characters."""
858
 
    # Trivially convert a fulltext into a 'chunked' representation, and let
859
 
    # chunks_to_lines do the heavy lifting.
860
 
    if isinstance(s, str):
861
 
        # chunks_to_lines only supports 8-bit strings
862
 
        return chunks_to_lines([s])
863
 
    else:
864
 
        return _split_lines(s)
865
 
 
866
 
 
867
 
def _split_lines(s):
868
 
    """Split s into lines, but without removing the newline characters.
869
 
 
870
 
    This supports Unicode or plain string objects.
871
 
    """
872
822
    lines = s.split('\n')
873
823
    result = [line + '\n' for line in lines[:-1]]
874
824
    if lines[-1]:
989
939
        return ''
990
940
 
991
941
 
992
 
def _cicp_canonical_relpath(base, path):
993
 
    """Return the canonical path relative to base.
994
 
 
995
 
    Like relpath, but on case-insensitive-case-preserving file-systems, this
996
 
    will return the relpath as stored on the file-system rather than in the
997
 
    case specified in the input string, for all existing portions of the path.
998
 
 
999
 
    This will cause O(N) behaviour if called for every path in a tree; if you
1000
 
    have a number of paths to convert, you should use canonical_relpaths().
1001
 
    """
1002
 
    # TODO: it should be possible to optimize this for Windows by using the
1003
 
    # win32 API FindFiles function to look for the specified name - but using
1004
 
    # os.listdir() still gives us the correct, platform agnostic semantics in
1005
 
    # the short term.
1006
 
 
1007
 
    rel = relpath(base, path)
1008
 
    # '.' will have been turned into ''
1009
 
    if not rel:
1010
 
        return rel
1011
 
 
1012
 
    abs_base = abspath(base)
1013
 
    current = abs_base
1014
 
    _listdir = os.listdir
1015
 
 
1016
 
    # use an explicit iterator so we can easily consume the rest on early exit.
1017
 
    bit_iter = iter(rel.split('/'))
1018
 
    for bit in bit_iter:
1019
 
        lbit = bit.lower()
1020
 
        for look in _listdir(current):
1021
 
            if lbit == look.lower():
1022
 
                current = pathjoin(current, look)
1023
 
                break
1024
 
        else:
1025
 
            # got to the end, nothing matched, so we just return the
1026
 
            # non-existing bits as they were specified (the filename may be
1027
 
            # the target of a move, for example).
1028
 
            current = pathjoin(current, bit, *list(bit_iter))
1029
 
            break
1030
 
    return current[len(abs_base)+1:]
1031
 
 
1032
 
# XXX - TODO - we need better detection/integration of case-insensitive
1033
 
# file-systems; Linux often sees FAT32 devices, for example, so could
1034
 
# probably benefit from the same basic support there.  For now though, only
1035
 
# Windows gets that support, and it gets it for *all* file-systems!
1036
 
if sys.platform == "win32":
1037
 
    canonical_relpath = _cicp_canonical_relpath
1038
 
else:
1039
 
    canonical_relpath = relpath
1040
 
 
1041
 
def canonical_relpaths(base, paths):
1042
 
    """Create an iterable to canonicalize a sequence of relative paths.
1043
 
 
1044
 
    The intent is for this implementation to use a cache, vastly speeding
1045
 
    up multiple transformations in the same directory.
1046
 
    """
1047
 
    # but for now, we haven't optimized...
1048
 
    return [canonical_relpath(base, p) for p in paths]
1049
 
 
1050
942
def safe_unicode(unicode_or_utf8_string):
1051
943
    """Coerce unicode_or_utf8_string into unicode.
1052
944
 
1053
945
    If it is unicode, it is returned.
1054
946
    Otherwise it is decoded from utf-8. If a decoding error
1055
 
    occurs, it is wrapped as a If the decoding fails, the exception is wrapped
 
947
    occurs, it is wrapped as a If the decoding fails, the exception is wrapped 
1056
948
    as a BzrBadParameter exception.
1057
949
    """
1058
950
    if isinstance(unicode_or_utf8_string, unicode):
1146
1038
 
1147
1039
    On platforms where the system normalizes filenames (Mac OSX),
1148
1040
    you can access a file by any path which will normalize correctly.
1149
 
    On platforms where the system does not normalize filenames
 
1041
    On platforms where the system does not normalize filenames 
1150
1042
    (Windows, Linux), you have to access a file by its exact path.
1151
1043
 
1152
 
    Internally, bzr only supports NFC normalization, since that is
 
1044
    Internally, bzr only supports NFC normalization, since that is 
1153
1045
    the standard for XML documents.
1154
1046
 
1155
1047
    So return the normalized path, and a flag indicating if the file
1226
1118
            del os.environ[env_variable]
1227
1119
    else:
1228
1120
        if isinstance(value, unicode):
1229
 
            value = value.encode(get_user_encoding())
 
1121
            value = value.encode(bzrlib.user_encoding)
1230
1122
        os.environ[env_variable] = value
1231
1123
    return orig_val
1232
1124
 
1235
1127
 
1236
1128
 
1237
1129
def check_legal_path(path):
1238
 
    """Check whether the supplied path is legal.
 
1130
    """Check whether the supplied path is legal.  
1239
1131
    This is only required on Windows, so we don't test on other platforms
1240
1132
    right now.
1241
1133
    """
1275
1167
 
1276
1168
def walkdirs(top, prefix=""):
1277
1169
    """Yield data about all the directories in a tree.
1278
 
 
 
1170
    
1279
1171
    This yields all the data about the contents of a directory at a time.
1280
1172
    After each directory has been yielded, if the caller has mutated the list
1281
1173
    to exclude some directories, they are then not descended into.
1282
 
 
 
1174
    
1283
1175
    The data yielded is of the form:
1284
1176
    ((directory-relpath, directory-path-from-top),
1285
1177
    [(relpath, basename, kind, lstat, path-from-top), ...]),
1286
1178
     - directory-relpath is the relative path of the directory being returned
1287
1179
       with respect to top. prefix is prepended to this.
1288
 
     - directory-path-from-root is the path including top for this directory.
 
1180
     - directory-path-from-root is the path including top for this directory. 
1289
1181
       It is suitable for use with os functions.
1290
1182
     - relpath is the relative path within the subtree being walked.
1291
1183
     - basename is the basename of the path
1293
1185
       present within the tree - but it may be recorded as versioned. See
1294
1186
       versioned_kind.
1295
1187
     - lstat is the stat data *if* the file was statted.
1296
 
     - planned, not implemented:
 
1188
     - planned, not implemented: 
1297
1189
       path_from_tree_root is the path from the root of the tree.
1298
1190
 
1299
 
    :param prefix: Prefix the relpaths that are yielded with 'prefix'. This
 
1191
    :param prefix: Prefix the relpaths that are yielded with 'prefix'. This 
1300
1192
        allows one to walk a subtree but get paths that are relative to a tree
1301
1193
        rooted higher up.
1302
1194
    :return: an iterator over the dirs.
1303
1195
    """
1304
1196
    #TODO there is a bit of a smell where the results of the directory-
1305
 
    # summary in this, and the path from the root, may not agree
 
1197
    # summary in this, and the path from the root, may not agree 
1306
1198
    # depending on top and prefix - i.e. ./foo and foo as a pair leads to
1307
1199
    # potentially confusing output. We should make this more robust - but
1308
1200
    # not at a speed cost. RBC 20060731
1309
1201
    _lstat = os.lstat
1310
1202
    _directory = _directory_kind
1311
1203
    _listdir = os.listdir
1312
 
    _kind_from_mode = file_kind_from_stat_mode
 
1204
    _kind_from_mode = _formats.get
1313
1205
    pending = [(safe_unicode(prefix), "", _directory, None, safe_unicode(top))]
1314
1206
    while pending:
1315
1207
        # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1331
1223
            for name in names:
1332
1224
                abspath = top_slash + name
1333
1225
                statvalue = _lstat(abspath)
1334
 
                kind = _kind_from_mode(statvalue.st_mode)
 
1226
                kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1335
1227
                append((relprefix + name, name, kind, statvalue, abspath))
1336
1228
        yield (relroot, top), dirblock
1337
1229
 
1339
1231
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1340
1232
 
1341
1233
 
1342
 
class DirReader(object):
1343
 
    """An interface for reading directories."""
1344
 
 
1345
 
    def top_prefix_to_starting_dir(self, top, prefix=""):
1346
 
        """Converts top and prefix to a starting dir entry
1347
 
 
1348
 
        :param top: A utf8 path
1349
 
        :param prefix: An optional utf8 path to prefix output relative paths
1350
 
            with.
1351
 
        :return: A tuple starting with prefix, and ending with the native
1352
 
            encoding of top.
1353
 
        """
1354
 
        raise NotImplementedError(self.top_prefix_to_starting_dir)
1355
 
 
1356
 
    def read_dir(self, prefix, top):
1357
 
        """Read a specific dir.
1358
 
 
1359
 
        :param prefix: A utf8 prefix to be preprended to the path basenames.
1360
 
        :param top: A natively encoded path to read.
1361
 
        :return: A list of the directories contents. Each item contains:
1362
 
            (utf8_relpath, utf8_name, kind, lstatvalue, native_abspath)
1363
 
        """
1364
 
        raise NotImplementedError(self.read_dir)
1365
 
 
1366
 
 
1367
 
_selected_dir_reader = None
1368
 
 
 
1234
_real_walkdirs_utf8 = None
1369
1235
 
1370
1236
def _walkdirs_utf8(top, prefix=""):
1371
1237
    """Yield data about all the directories in a tree.
1381
1247
        path-from-top might be unicode or utf8, but it is the correct path to
1382
1248
        pass to os functions to affect the file in question. (such as os.lstat)
1383
1249
    """
1384
 
    global _selected_dir_reader
1385
 
    if _selected_dir_reader is None:
 
1250
    global _real_walkdirs_utf8
 
1251
    if _real_walkdirs_utf8 is None:
1386
1252
        fs_encoding = _fs_enc.upper()
1387
 
        if sys.platform == "win32" and win32utils.winver == 'Windows NT':
 
1253
        if win32utils.winver == 'Windows NT':
1388
1254
            # Win98 doesn't have unicode apis like FindFirstFileW
1389
1255
            # TODO: We possibly could support Win98 by falling back to the
1390
1256
            #       original FindFirstFile, and using TCHAR instead of WCHAR,
1391
1257
            #       but that gets a bit tricky, and requires custom compiling
1392
1258
            #       for win98 anyway.
1393
1259
            try:
1394
 
                from bzrlib._walkdirs_win32 import Win32ReadDir
 
1260
                from bzrlib._walkdirs_win32 import _walkdirs_utf8_win32_find_file
1395
1261
            except ImportError:
1396
 
                _selected_dir_reader = UnicodeDirReader()
 
1262
                _real_walkdirs_utf8 = _walkdirs_unicode_to_utf8
1397
1263
            else:
1398
 
                _selected_dir_reader = Win32ReadDir()
 
1264
                _real_walkdirs_utf8 = _walkdirs_utf8_win32_find_file
1399
1265
        elif fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968'):
1400
1266
            # ANSI_X3.4-1968 is a form of ASCII
1401
 
            _selected_dir_reader = UnicodeDirReader()
 
1267
            _real_walkdirs_utf8 = _walkdirs_unicode_to_utf8
1402
1268
        else:
1403
 
            try:
1404
 
                from bzrlib._readdir_pyx import UTF8DirReader
1405
 
            except ImportError:
1406
 
                # No optimised code path
1407
 
                _selected_dir_reader = UnicodeDirReader()
1408
 
            else:
1409
 
                _selected_dir_reader = UTF8DirReader()
 
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
 
1410
1285
    # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1411
1286
    # But we don't actually uses 1-3 in pending, so set them to None
1412
 
    pending = [[_selected_dir_reader.top_prefix_to_starting_dir(top, prefix)]]
1413
 
    read_dir = _selected_dir_reader.read_dir
1414
 
    _directory = _directory_kind
 
1287
    pending = [(safe_utf8(prefix), None, None, None, safe_utf8(top))]
1415
1288
    while pending:
1416
 
        relroot, _, _, _, top = pending[-1].pop()
1417
 
        if not pending[-1]:
1418
 
            pending.pop()
1419
 
        dirblock = sorted(read_dir(relroot, top))
 
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()
1420
1305
        yield (relroot, top), dirblock
 
1306
 
1421
1307
        # push the user specified dirs from dirblock
1422
 
        next = [d for d in reversed(dirblock) if d[2] == _directory]
1423
 
        if next:
1424
 
            pending.append(next)
1425
 
 
1426
 
 
1427
 
class UnicodeDirReader(DirReader):
1428
 
    """A dir reader for non-utf8 file systems, which transcodes."""
1429
 
 
1430
 
    __slots__ = ['_utf8_encode']
1431
 
 
1432
 
    def __init__(self):
1433
 
        self._utf8_encode = codecs.getencoder('utf8')
1434
 
 
1435
 
    def top_prefix_to_starting_dir(self, top, prefix=""):
1436
 
        """See DirReader.top_prefix_to_starting_dir."""
1437
 
        return (safe_utf8(prefix), None, None, None, safe_unicode(top))
1438
 
 
1439
 
    def read_dir(self, prefix, top):
1440
 
        """Read a single directory from a non-utf8 file system.
1441
 
 
1442
 
        top, and the abspath element in the output are unicode, all other paths
1443
 
        are utf8. Local disk IO is done via unicode calls to listdir etc.
1444
 
 
1445
 
        This is currently the fallback code path when the filesystem encoding is
1446
 
        not UTF-8. It may be better to implement an alternative so that we can
1447
 
        safely handle paths that are not properly decodable in the current
1448
 
        encoding.
1449
 
 
1450
 
        See DirReader.read_dir for details.
1451
 
        """
1452
 
        _utf8_encode = self._utf8_encode
1453
 
        _lstat = os.lstat
1454
 
        _listdir = os.listdir
1455
 
        _kind_from_mode = file_kind_from_stat_mode
1456
 
 
1457
 
        if prefix:
1458
 
            relprefix = prefix + '/'
 
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 + '/'
1459
1332
        else:
1460
1333
            relprefix = ''
1461
1334
        top_slash = top + u'/'
1463
1336
        dirblock = []
1464
1337
        append = dirblock.append
1465
1338
        for name in sorted(_listdir(top)):
1466
 
            try:
1467
 
                name_utf8 = _utf8_encode(name)[0]
1468
 
            except UnicodeDecodeError:
1469
 
                raise errors.BadFilenameEncoding(
1470
 
                    _utf8_encode(relprefix)[0] + name, _fs_enc)
 
1339
            name_utf8 = _utf8_encode(name)[0]
1471
1340
            abspath = top_slash + name
1472
1341
            statvalue = _lstat(abspath)
1473
 
            kind = _kind_from_mode(statvalue.st_mode)
 
1342
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1474
1343
            append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
1475
 
        return dirblock
 
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)
1476
1348
 
1477
1349
 
1478
1350
def copy_tree(from_path, to_path, handlers={}):
1479
1351
    """Copy all of the entries in from_path into to_path.
1480
1352
 
1481
 
    :param from_path: The base directory to copy.
 
1353
    :param from_path: The base directory to copy. 
1482
1354
    :param to_path: The target directory. If it does not exist, it will
1483
1355
        be created.
1484
1356
    :param handlers: A dictionary of functions, which takes a source and
1553
1425
        return _cached_user_encoding
1554
1426
 
1555
1427
    if sys.platform == 'darwin':
1556
 
        # python locale.getpreferredencoding() always return
1557
 
        # 'mac-roman' on darwin. That's a lie.
 
1428
        # work around egregious python 2.4 bug
1558
1429
        sys.platform = 'posix'
1559
1430
        try:
1560
 
            if os.environ.get('LANG', None) is None:
1561
 
                # If LANG is not set, we end up with 'ascii', which is bad
1562
 
                # ('mac-roman' is more than ascii), so we set a default which
1563
 
                # will give us UTF-8 (which appears to work in all cases on
1564
 
                # OSX). Users are still free to override LANG of course, as
1565
 
                # long as it give us something meaningful. This work-around
1566
 
                # *may* not be needed with python 3k and/or OSX 10.5, but will
1567
 
                # work with them too -- vila 20080908
1568
 
                os.environ['LANG'] = 'en_US.UTF-8'
1569
1431
            import locale
1570
1432
        finally:
1571
1433
            sys.platform = 'darwin'
1634
1496
    """
1635
1497
    b = ''
1636
1498
    while len(b) < bytes:
1637
 
        new = until_no_eintr(socket.recv, bytes - len(b))
 
1499
        new = socket.recv(bytes - len(b))
1638
1500
        if new == '':
1639
1501
            break # eof
1640
1502
        b += new
1641
1503
    return b
1642
1504
 
1643
1505
 
1644
 
def send_all(socket, bytes, report_activity=None):
 
1506
def send_all(socket, bytes):
1645
1507
    """Send all bytes on a socket.
1646
1508
 
1647
1509
    Regular socket.sendall() can give socket error 10053 on Windows.  This
1648
1510
    implementation sends no more than 64k at a time, which avoids this problem.
1649
 
 
1650
 
    :param report_activity: Call this as bytes are read, see
1651
 
        Transport._report_activity
1652
1511
    """
1653
1512
    chunk_size = 2**16
1654
1513
    for pos in xrange(0, len(bytes), chunk_size):
1655
 
        block = bytes[pos:pos+chunk_size]
1656
 
        if report_activity is not None:
1657
 
            report_activity(len(block), 'write')
1658
 
        until_no_eintr(socket.sendall, block)
 
1514
        socket.sendall(bytes[pos:pos+chunk_size])
1659
1515
 
1660
1516
 
1661
1517
def dereference_path(path):
1706
1562
    return open(filename, 'rU').read()
1707
1563
 
1708
1564
 
1709
 
def file_kind_from_stat_mode_thunk(mode):
1710
 
    global file_kind_from_stat_mode
1711
 
    if file_kind_from_stat_mode is file_kind_from_stat_mode_thunk:
1712
 
        try:
1713
 
            from bzrlib._readdir_pyx import UTF8DirReader
1714
 
            file_kind_from_stat_mode = UTF8DirReader().kind_from_mode
1715
 
        except ImportError:
1716
 
            from bzrlib._readdir_py import (
1717
 
                _kind_from_mode as file_kind_from_stat_mode
1718
 
                )
1719
 
    return file_kind_from_stat_mode(mode)
1720
 
file_kind_from_stat_mode = file_kind_from_stat_mode_thunk
1721
 
 
1722
 
 
1723
 
def file_kind(f, _lstat=os.lstat):
1724
 
    try:
1725
 
        return file_kind_from_stat_mode(_lstat(f).st_mode)
1726
 
    except OSError, e:
1727
 
        if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
1728
 
            raise errors.NoSuchFile(f)
1729
 
        raise
1730
 
 
1731
 
 
1732
 
def until_no_eintr(f, *a, **kw):
1733
 
    """Run f(*a, **kw), retrying if an EINTR error occurs."""
1734
 
    # Borrowed from Twisted's twisted.python.util.untilConcludes function.
1735
 
    while True:
1736
 
        try:
1737
 
            return f(*a, **kw)
1738
 
        except (IOError, OSError), e:
1739
 
            if e.errno == errno.EINTR:
1740
 
                continue
1741
 
            raise
1742
 
 
1743
 
def re_compile_checked(re_string, flags=0, where=""):
1744
 
    """Return a compiled re, or raise a sensible error.
1745
 
    
1746
 
    This should only be used when compiling user-supplied REs.
1747
 
 
1748
 
    :param re_string: Text form of regular expression.
1749
 
    :param flags: eg re.IGNORECASE
1750
 
    :param where: Message explaining to the user the context where 
1751
 
        it occurred, eg 'log search filter'.
1752
 
    """
1753
 
    # from https://bugs.launchpad.net/bzr/+bug/251352
1754
 
    try:
1755
 
        re_obj = re.compile(re_string, flags)
1756
 
        re_obj.search("")
1757
 
        return re_obj
1758
 
    except re.error, e:
1759
 
        if where:
1760
 
            where = ' in ' + where
1761
 
        # despite the name 'error' is a type
1762
 
        raise errors.BzrCommandError('Invalid regular expression%s: %r: %s'
1763
 
            % (where, re_string, e))
1764
 
 
1765
 
 
1766
 
if sys.platform == "win32":
1767
 
    import msvcrt
1768
 
    def getchar():
1769
 
        return msvcrt.getch()
1770
 
else:
1771
 
    import tty
1772
 
    import termios
1773
 
    def getchar():
1774
 
        fd = sys.stdin.fileno()
1775
 
        settings = termios.tcgetattr(fd)
1776
 
        try:
1777
 
            tty.setraw(fd)
1778
 
            ch = sys.stdin.read(1)
1779
 
        finally:
1780
 
            termios.tcsetattr(fd, termios.TCSADRAIN, settings)
1781
 
        return ch
 
1565
try:
 
1566
    from bzrlib._readdir_pyx import read_dir as _read_dir
 
1567
except ImportError:
 
1568
    from bzrlib._readdir_py import read_dir as _read_dir