~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Vincent Ladeuil
  • Date: 2007-07-15 11:24:18 UTC
  • mfrom: (2617 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2646.
  • Revision ID: v.ladeuil+lp@free.fr-20070715112418-9nn4n6esxv60ny4b
merge bzr.dev@1617

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Bazaar-NG -- distributed version control
2
 
#
3
 
# Copyright (C) 2005 by Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
4
2
#
5
3
# This program is free software; you can redistribute it and/or modify
6
4
# it under the terms of the GNU General Public License as published by
17
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
16
 
19
17
from cStringIO import StringIO
 
18
import os
 
19
import re
 
20
import stat
 
21
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
 
22
                  S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
 
23
import sys
 
24
import time
 
25
 
 
26
from bzrlib.lazy_import import lazy_import
 
27
lazy_import(globals(), """
 
28
import codecs
 
29
from datetime import datetime
20
30
import errno
21
31
from ntpath import (abspath as _nt_abspath,
22
32
                    join as _nt_join,
24
34
                    realpath as _nt_realpath,
25
35
                    splitdrive as _nt_splitdrive,
26
36
                    )
27
 
import os
28
 
from os import listdir
29
37
import posixpath
30
 
import re
31
38
import sha
32
39
import shutil
33
 
from shutil import copyfile
34
 
import stat
35
 
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
36
 
                  S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
37
 
import string
38
 
import sys
39
 
import time
40
 
import types
 
40
from shutil import (
 
41
    rmtree,
 
42
    )
41
43
import tempfile
 
44
from tempfile import (
 
45
    mkdtemp,
 
46
    )
42
47
import unicodedata
43
48
 
 
49
from bzrlib import (
 
50
    cache_utf8,
 
51
    errors,
 
52
    win32utils,
 
53
    )
 
54
""")
 
55
 
44
56
import bzrlib
45
 
from bzrlib.errors import (BzrError,
46
 
                           BzrBadParameterNotUnicode,
47
 
                           NoSuchFile,
48
 
                           PathNotChild,
49
 
                           IllegalPath,
50
 
                           )
51
 
from bzrlib.symbol_versioning import (deprecated_function, 
52
 
        zero_nine)
 
57
from bzrlib import symbol_versioning
 
58
from bzrlib.symbol_versioning import (
 
59
    deprecated_function,
 
60
    zero_nine,
 
61
    )
53
62
from bzrlib.trace import mutter
54
63
 
55
64
 
 
65
# On win32, O_BINARY is used to indicate the file should
 
66
# be opened in binary mode, rather than text mode.
 
67
# On other platforms, O_BINARY doesn't exist, because
 
68
# they always open in binary mode, so it is okay to
 
69
# OR with 0 on those platforms
 
70
O_BINARY = getattr(os, 'O_BINARY', 0)
 
71
 
 
72
# On posix, use lstat instead of stat so that we can
 
73
# operate on broken symlinks. On Windows revert to stat.
 
74
lstat = getattr(os, 'lstat', os.stat)
 
75
 
56
76
def make_readonly(filename):
57
77
    """Make a filename read-only."""
58
 
    mod = os.stat(filename).st_mode
59
 
    mod = mod & 0777555
60
 
    os.chmod(filename, mod)
 
78
    mod = lstat(filename).st_mode
 
79
    if not stat.S_ISLNK(mod):
 
80
        mod = mod & 0777555
 
81
        os.chmod(filename, mod)
61
82
 
62
83
 
63
84
def make_writable(filename):
64
 
    mod = os.stat(filename).st_mode
65
 
    mod = mod | 0200
66
 
    os.chmod(filename, mod)
 
85
    mod = lstat(filename).st_mode
 
86
    if not stat.S_ISLNK(mod):
 
87
        mod = mod | 0200
 
88
        os.chmod(filename, mod)
67
89
 
68
90
 
69
91
_QUOTE_RE = None
76
98
    Windows."""
77
99
    # TODO: I'm not really sure this is the best format either.x
78
100
    global _QUOTE_RE
79
 
    if _QUOTE_RE == None:
 
101
    if _QUOTE_RE is None:
80
102
        _QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/\\_~-])')
81
103
        
82
104
    if _QUOTE_RE.search(f):
114
136
        return _mapper(_lstat(f).st_mode)
115
137
    except OSError, e:
116
138
        if getattr(e, 'errno', None) == errno.ENOENT:
117
 
            raise bzrlib.errors.NoSuchFile(f)
 
139
            raise errors.NoSuchFile(f)
118
140
        raise
119
141
 
120
142
 
 
143
def get_umask():
 
144
    """Return the current umask"""
 
145
    # Assume that people aren't messing with the umask while running
 
146
    # XXX: This is not thread safe, but there is no way to get the
 
147
    #      umask without setting it
 
148
    umask = os.umask(0)
 
149
    os.umask(umask)
 
150
    return umask
 
151
 
 
152
 
 
153
_kind_marker_map = {
 
154
    "file": "",
 
155
    _directory_kind: "/",
 
156
    "symlink": "@",
 
157
    'tree-reference': '+',
 
158
}
 
159
 
 
160
 
121
161
def kind_marker(kind):
122
 
    if kind == 'file':
123
 
        return ''
124
 
    elif kind == _directory_kind:
125
 
        return '/'
126
 
    elif kind == 'symlink':
127
 
        return '@'
128
 
    else:
129
 
        raise BzrError('invalid file kind %r' % kind)
 
162
    try:
 
163
        return _kind_marker_map[kind]
 
164
    except KeyError:
 
165
        raise errors.BzrError('invalid file kind %r' % kind)
 
166
 
130
167
 
131
168
lexists = getattr(os.path, 'lexists', None)
132
169
if lexists is None:
133
170
    def lexists(f):
134
171
        try:
135
 
            if hasattr(os, 'lstat'):
136
 
                os.lstat(f)
137
 
            else:
138
 
                os.stat(f)
 
172
            stat = getattr(os, 'lstat', os.stat)
 
173
            stat(f)
139
174
            return True
140
 
        except OSError,e:
 
175
        except OSError, e:
141
176
            if e.errno == errno.ENOENT:
142
177
                return False;
143
178
            else:
144
 
                raise BzrError("lstat/stat of (%r): %r" % (f, e))
 
179
                raise errors.BzrError("lstat/stat of (%r): %r" % (f, e))
145
180
 
146
181
 
147
182
def fancy_rename(old, new, rename_func, unlink_func):
168
203
    file_existed = False
169
204
    try:
170
205
        rename_func(new, tmp_name)
171
 
    except (NoSuchFile,), e:
 
206
    except (errors.NoSuchFile,), e:
172
207
        pass
173
208
    except IOError, e:
174
209
        # RBC 20060103 abstraction leakage: the paramiko SFTP clients rename
175
 
        # function raises an IOError with errno == None when a rename fails.
 
210
        # function raises an IOError with errno is None when a rename fails.
176
211
        # This then gets caught here.
177
212
        if e.errno not in (None, errno.ENOENT, errno.ENOTDIR):
178
213
            raise
179
214
    except Exception, e:
180
 
        if (not hasattr(e, 'errno') 
 
215
        if (getattr(e, 'errno', None) is None
181
216
            or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
182
217
            raise
183
218
    else:
203
238
# choke on a Unicode string containing a relative path if
204
239
# os.getcwd() returns a non-sys.getdefaultencoding()-encoded
205
240
# string.
206
 
_fs_enc = sys.getfilesystemencoding()
 
241
_fs_enc = sys.getfilesystemencoding() or 'utf-8'
207
242
def _posix_abspath(path):
208
243
    # jam 20060426 rather than encoding to fsencoding
209
244
    # copy posixpath.abspath, but use os.getcwdu instead
234
269
    return _win32_fixdrive(_nt_abspath(unicode(path)).replace('\\', '/'))
235
270
 
236
271
 
 
272
def _win98_abspath(path):
 
273
    """Return the absolute version of a path.
 
274
    Windows 98 safe implementation (python reimplementation
 
275
    of Win32 API function GetFullPathNameW)
 
276
    """
 
277
    # Corner cases:
 
278
    #   C:\path     => C:/path
 
279
    #   C:/path     => C:/path
 
280
    #   \\HOST\path => //HOST/path
 
281
    #   //HOST/path => //HOST/path
 
282
    #   path        => C:/cwd/path
 
283
    #   /path       => C:/path
 
284
    path = unicode(path)
 
285
    # check for absolute path
 
286
    drive = _nt_splitdrive(path)[0]
 
287
    if drive == '' and path[:2] not in('//','\\\\'):
 
288
        cwd = os.getcwdu()
 
289
        # we cannot simply os.path.join cwd and path
 
290
        # because os.path.join('C:','/path') produce '/path'
 
291
        # and this is incorrect
 
292
        if path[:1] in ('/','\\'):
 
293
            cwd = _nt_splitdrive(cwd)[0]
 
294
            path = path[1:]
 
295
        path = cwd + '\\' + path
 
296
    return _win32_fixdrive(_nt_normpath(path).replace('\\', '/'))
 
297
 
 
298
if win32utils.winver == 'Windows 98':
 
299
    _win32_abspath = _win98_abspath
 
300
 
 
301
 
237
302
def _win32_realpath(path):
238
303
    # Real _nt_realpath doesn't have a problem with a unicode cwd
239
304
    return _win32_fixdrive(_nt_realpath(unicode(path)).replace('\\', '/'))
284
349
pathjoin = os.path.join
285
350
normpath = os.path.normpath
286
351
getcwd = os.getcwdu
287
 
mkdtemp = tempfile.mkdtemp
288
352
rename = os.rename
289
353
dirname = os.path.dirname
290
354
basename = os.path.basename
291
 
rmtree = shutil.rmtree
 
355
split = os.path.split
 
356
splitext = os.path.splitext
 
357
# These were already imported into local scope
 
358
# mkdtemp = tempfile.mkdtemp
 
359
# rmtree = shutil.rmtree
292
360
 
293
361
MIN_ABS_PATHLENGTH = 1
294
362
 
308
376
        """Error handler for shutil.rmtree function [for win32]
309
377
        Helps to remove files and dirs marked as read-only.
310
378
        """
311
 
        type_, value = excinfo[:2]
 
379
        exception = excinfo[1]
312
380
        if function in (os.remove, os.rmdir) \
313
 
            and type_ == OSError \
314
 
            and value.errno == errno.EACCES:
315
 
            bzrlib.osutils.make_writable(path)
 
381
            and isinstance(exception, OSError) \
 
382
            and exception.errno == errno.EACCES:
 
383
            make_writable(path)
316
384
            function(path)
317
385
        else:
318
386
            raise
348
416
            mutter('encoding stdout as sys.stdin encoding %r', output_encoding)
349
417
    else:
350
418
        mutter('encoding stdout as sys.stdout encoding %r', output_encoding)
 
419
    if output_encoding == 'cp0':
 
420
        # invalid encoding (cp0 means 'no codepage' on Windows)
 
421
        output_encoding = bzrlib.user_encoding
 
422
        mutter('cp0 is invalid encoding.'
 
423
               ' encoding stdout as bzrlib.user_encoding %r', output_encoding)
 
424
    # check encoding
 
425
    try:
 
426
        codecs.lookup(output_encoding)
 
427
    except LookupError:
 
428
        sys.stderr.write('bzr: warning:'
 
429
                         ' unknown terminal encoding %s.\n'
 
430
                         '  Using encoding %s instead.\n'
 
431
                         % (output_encoding, bzrlib.user_encoding)
 
432
                        )
 
433
        output_encoding = bzrlib.user_encoding
 
434
 
351
435
    return output_encoding
352
436
 
353
437
 
354
438
def normalizepath(f):
355
 
    if hasattr(os.path, 'realpath'):
 
439
    if getattr(os.path, 'realpath', None) is not None:
356
440
        F = realpath
357
441
    else:
358
442
        F = abspath
422
506
    
423
507
    The empty string as a dir name is taken as top-of-tree and matches 
424
508
    everything.
425
 
    
426
 
    >>> is_inside('src', pathjoin('src', 'foo.c'))
427
 
    True
428
 
    >>> is_inside('src', 'srccontrol')
429
 
    False
430
 
    >>> is_inside('src', pathjoin('src', 'a', 'a', 'a', 'foo.c'))
431
 
    True
432
 
    >>> is_inside('foo.c', 'foo.c')
433
 
    True
434
 
    >>> is_inside('foo.c', '')
435
 
    False
436
 
    >>> is_inside('', 'foo.c')
437
 
    True
438
509
    """
439
510
    # XXX: Most callers of this can actually do something smarter by 
440
511
    # looking at the inventory
455
526
    for dirname in dir_list:
456
527
        if is_inside(dirname, fname):
457
528
            return True
458
 
    else:
459
 
        return False
 
529
    return False
460
530
 
461
531
 
462
532
def is_inside_or_parent_of_any(dir_list, fname):
464
534
    for dirname in dir_list:
465
535
        if is_inside(dirname, fname) or is_inside(fname, dirname):
466
536
            return True
467
 
    else:
468
 
        return False
 
537
    return False
469
538
 
470
539
 
471
540
def pumpfile(fromfile, tofile):
487
556
 
488
557
 
489
558
def sha_file(f):
490
 
    if hasattr(f, 'tell'):
 
559
    if getattr(f, 'tell', None) is not None:
491
560
        assert f.tell() == 0
492
561
    s = sha.new()
493
562
    BUFSIZE = 128<<10
536
605
 
537
606
def local_time_offset(t=None):
538
607
    """Return offset of local zone from GMT, either at present or at time t."""
539
 
    # python2.3 localtime() can't take None
540
 
    if t == None:
 
608
    if t is None:
541
609
        t = time.time()
542
 
        
543
 
    if time.localtime(t).tm_isdst and time.daylight:
544
 
        return -time.altzone
545
 
    else:
546
 
        return -time.timezone
 
610
    offset = datetime.fromtimestamp(t) - datetime.utcfromtimestamp(t)
 
611
    return offset.days * 86400 + offset.seconds
547
612
 
548
613
    
549
 
def format_date(t, offset=0, timezone='original', date_fmt=None, 
 
614
def format_date(t, offset=0, timezone='original', date_fmt=None,
550
615
                show_offset=True):
551
 
    ## TODO: Perhaps a global option to use either universal or local time?
552
 
    ## Or perhaps just let people set $TZ?
553
 
    assert isinstance(t, float)
554
 
    
 
616
    """Return a formatted date string.
 
617
 
 
618
    :param t: Seconds since the epoch.
 
619
    :param offset: Timezone offset in seconds east of utc.
 
620
    :param timezone: How to display the time: 'utc', 'original' for the
 
621
         timezone specified by offset, or 'local' for the process's current
 
622
         timezone.
 
623
    :param show_offset: Whether to append the timezone.
 
624
    :param date_fmt: strftime format.
 
625
    """
555
626
    if timezone == 'utc':
556
627
        tt = time.gmtime(t)
557
628
        offset = 0
558
629
    elif timezone == 'original':
559
 
        if offset == None:
 
630
        if offset is None:
560
631
            offset = 0
561
632
        tt = time.gmtime(t + offset)
562
633
    elif timezone == 'local':
563
634
        tt = time.localtime(t)
564
635
        offset = local_time_offset(t)
565
636
    else:
566
 
        raise BzrError("unsupported timezone format %r" % timezone,
567
 
                       ['options are "utc", "original", "local"'])
 
637
        raise errors.BzrError("unsupported timezone format %r" % timezone,
 
638
                              ['options are "utc", "original", "local"'])
568
639
    if date_fmt is None:
569
640
        date_fmt = "%a %Y-%m-%d %H:%M:%S"
570
641
    if show_offset:
578
649
    return time.strftime('%Y%m%d%H%M%S', time.gmtime(when))
579
650
    
580
651
 
 
652
def format_delta(delta):
 
653
    """Get a nice looking string for a time delta.
 
654
 
 
655
    :param delta: The time difference in seconds, can be positive or negative.
 
656
        positive indicates time in the past, negative indicates time in the
 
657
        future. (usually time.time() - stored_time)
 
658
    :return: String formatted to show approximate resolution
 
659
    """
 
660
    delta = int(delta)
 
661
    if delta >= 0:
 
662
        direction = 'ago'
 
663
    else:
 
664
        direction = 'in the future'
 
665
        delta = -delta
 
666
 
 
667
    seconds = delta
 
668
    if seconds < 90: # print seconds up to 90 seconds
 
669
        if seconds == 1:
 
670
            return '%d second %s' % (seconds, direction,)
 
671
        else:
 
672
            return '%d seconds %s' % (seconds, direction)
 
673
 
 
674
    minutes = int(seconds / 60)
 
675
    seconds -= 60 * minutes
 
676
    if seconds == 1:
 
677
        plural_seconds = ''
 
678
    else:
 
679
        plural_seconds = 's'
 
680
    if minutes < 90: # print minutes, seconds up to 90 minutes
 
681
        if minutes == 1:
 
682
            return '%d minute, %d second%s %s' % (
 
683
                    minutes, seconds, plural_seconds, direction)
 
684
        else:
 
685
            return '%d minutes, %d second%s %s' % (
 
686
                    minutes, seconds, plural_seconds, direction)
 
687
 
 
688
    hours = int(minutes / 60)
 
689
    minutes -= 60 * hours
 
690
    if minutes == 1:
 
691
        plural_minutes = ''
 
692
    else:
 
693
        plural_minutes = 's'
 
694
 
 
695
    if hours == 1:
 
696
        return '%d hour, %d minute%s %s' % (hours, minutes,
 
697
                                            plural_minutes, direction)
 
698
    return '%d hours, %d minute%s %s' % (hours, minutes,
 
699
                                         plural_minutes, direction)
581
700
 
582
701
def filesize(f):
583
702
    """Return size of given open file."""
593
712
except (NotImplementedError, AttributeError):
594
713
    # If python doesn't have os.urandom, or it doesn't work,
595
714
    # then try to first pull random data from /dev/urandom
596
 
    if os.path.exists("/dev/urandom"):
 
715
    try:
597
716
        rand_bytes = file('/dev/urandom', 'rb').read
598
717
    # Otherwise, use this hack as a last resort
599
 
    else:
 
718
    except (IOError, OSError):
600
719
        # not well seeded, but better than nothing
601
720
        def rand_bytes(n):
602
721
            import random
624
743
## decomposition (might be too tricksy though.)
625
744
 
626
745
def splitpath(p):
627
 
    """Turn string into list of parts.
628
 
 
629
 
    >>> splitpath('a')
630
 
    ['a']
631
 
    >>> splitpath('a/b')
632
 
    ['a', 'b']
633
 
    >>> splitpath('a/./b')
634
 
    ['a', 'b']
635
 
    >>> splitpath('a/.b')
636
 
    ['a', '.b']
637
 
    >>> splitpath('a/../b')
638
 
    Traceback (most recent call last):
639
 
    ...
640
 
    BzrError: sorry, '..' not allowed in path
641
 
    """
642
 
    assert isinstance(p, types.StringTypes)
 
746
    """Turn string into list of parts."""
 
747
    assert isinstance(p, basestring)
643
748
 
644
749
    # split on either delimiter because people might use either on
645
750
    # Windows
648
753
    rps = []
649
754
    for f in ps:
650
755
        if f == '..':
651
 
            raise BzrError("sorry, %r not allowed in path" % f)
 
756
            raise errors.BzrError("sorry, %r not allowed in path" % f)
652
757
        elif (f == '.') or (f == ''):
653
758
            pass
654
759
        else:
656
761
    return rps
657
762
 
658
763
def joinpath(p):
659
 
    assert isinstance(p, list)
 
764
    assert isinstance(p, (list, tuple))
660
765
    for f in p:
661
 
        if (f == '..') or (f == None) or (f == ''):
662
 
            raise BzrError("sorry, %r not allowed in path" % f)
 
766
        if (f == '..') or (f is None) or (f == ''):
 
767
            raise errors.BzrError("sorry, %r not allowed in path" % f)
663
768
    return pathjoin(*p)
664
769
 
665
770
 
687
792
def link_or_copy(src, dest):
688
793
    """Hardlink a file, or copy it if it can't be hardlinked."""
689
794
    if not hardlinks_good():
690
 
        copyfile(src, dest)
 
795
        shutil.copyfile(src, dest)
691
796
        return
692
797
    try:
693
798
        os.link(src, dest)
694
799
    except (OSError, IOError), e:
695
800
        if e.errno != errno.EXDEV:
696
801
            raise
697
 
        copyfile(src, dest)
 
802
        shutil.copyfile(src, dest)
698
803
 
699
804
def delete_any(full_path):
700
805
    """Delete a file or directory."""
708
813
 
709
814
 
710
815
def has_symlinks():
711
 
    if hasattr(os, 'symlink'):
 
816
    if getattr(os, 'symlink', None) is not None:
712
817
        return True
713
818
    else:
714
819
        return False
716
821
 
717
822
def contains_whitespace(s):
718
823
    """True if there are any whitespace characters in s."""
719
 
    for ch in string.whitespace:
 
824
    # string.whitespace can include '\xa0' in certain locales, because it is
 
825
    # considered "non-breaking-space" as part of ISO-8859-1. But it
 
826
    # 1) Isn't a breaking whitespace
 
827
    # 2) Isn't one of ' \t\r\n' which are characters we sometimes use as
 
828
    #    separators
 
829
    # 3) '\xa0' isn't unicode safe since it is >128.
 
830
 
 
831
    # This should *not* be a unicode set of characters in case the source
 
832
    # string is not a Unicode string. We can auto-up-cast the characters since
 
833
    # they are ascii, but we don't want to auto-up-cast the string in case it
 
834
    # is utf-8
 
835
    for ch in ' \t\n\r\v\f':
720
836
        if ch in s:
721
837
            return True
722
838
    else:
758
874
        if tail:
759
875
            s.insert(0, tail)
760
876
    else:
761
 
        raise PathNotChild(rp, base)
 
877
        raise errors.PathNotChild(rp, base)
762
878
 
763
879
    if s:
764
880
        return pathjoin(*s)
779
895
    try:
780
896
        return unicode_or_utf8_string.decode('utf8')
781
897
    except UnicodeDecodeError:
782
 
        raise BzrBadParameterNotUnicode(unicode_or_utf8_string)
 
898
        raise errors.BzrBadParameterNotUnicode(unicode_or_utf8_string)
 
899
 
 
900
 
 
901
def safe_utf8(unicode_or_utf8_string):
 
902
    """Coerce unicode_or_utf8_string to a utf8 string.
 
903
 
 
904
    If it is a str, it is returned.
 
905
    If it is Unicode, it is encoded into a utf-8 string.
 
906
    """
 
907
    if isinstance(unicode_or_utf8_string, str):
 
908
        # TODO: jam 20070209 This is overkill, and probably has an impact on
 
909
        #       performance if we are dealing with lots of apis that want a
 
910
        #       utf-8 revision id
 
911
        try:
 
912
            # Make sure it is a valid utf-8 string
 
913
            unicode_or_utf8_string.decode('utf-8')
 
914
        except UnicodeDecodeError:
 
915
            raise errors.BzrBadParameterNotUnicode(unicode_or_utf8_string)
 
916
        return unicode_or_utf8_string
 
917
    return unicode_or_utf8_string.encode('utf-8')
 
918
 
 
919
 
 
920
_revision_id_warning = ('Unicode revision ids were deprecated in bzr 0.15.'
 
921
                        ' Revision id generators should be creating utf8'
 
922
                        ' revision ids.')
 
923
 
 
924
 
 
925
def safe_revision_id(unicode_or_utf8_string, warn=True):
 
926
    """Revision ids should now be utf8, but at one point they were unicode.
 
927
 
 
928
    :param unicode_or_utf8_string: A possibly Unicode revision_id. (can also be
 
929
        utf8 or None).
 
930
    :param warn: Functions that are sanitizing user data can set warn=False
 
931
    :return: None or a utf8 revision id.
 
932
    """
 
933
    if (unicode_or_utf8_string is None
 
934
        or unicode_or_utf8_string.__class__ == str):
 
935
        return unicode_or_utf8_string
 
936
    if warn:
 
937
        symbol_versioning.warn(_revision_id_warning, DeprecationWarning,
 
938
                               stacklevel=2)
 
939
    return cache_utf8.encode(unicode_or_utf8_string)
 
940
 
 
941
 
 
942
_file_id_warning = ('Unicode file ids were deprecated in bzr 0.15. File id'
 
943
                    ' generators should be creating utf8 file ids.')
 
944
 
 
945
 
 
946
def safe_file_id(unicode_or_utf8_string, warn=True):
 
947
    """File ids should now be utf8, but at one point they were unicode.
 
948
 
 
949
    This is the same as safe_utf8, except it uses the cached encode functions
 
950
    to save a little bit of performance.
 
951
 
 
952
    :param unicode_or_utf8_string: A possibly Unicode file_id. (can also be
 
953
        utf8 or None).
 
954
    :param warn: Functions that are sanitizing user data can set warn=False
 
955
    :return: None or a utf8 file id.
 
956
    """
 
957
    if (unicode_or_utf8_string is None
 
958
        or unicode_or_utf8_string.__class__ == str):
 
959
        return unicode_or_utf8_string
 
960
    if warn:
 
961
        symbol_versioning.warn(_file_id_warning, DeprecationWarning,
 
962
                               stacklevel=2)
 
963
    return cache_utf8.encode(unicode_or_utf8_string)
783
964
 
784
965
 
785
966
_platform_normalizes_filenames = False
829
1010
def terminal_width():
830
1011
    """Return estimated terminal width."""
831
1012
    if sys.platform == 'win32':
832
 
        import bzrlib.win32console
833
 
        return bzrlib.win32console.get_console_size()[0]
 
1013
        return win32utils.get_console_size()[0]
834
1014
    width = 0
835
1015
    try:
836
1016
        import struct, fcntl, termios
849
1029
 
850
1030
    return width
851
1031
 
 
1032
 
852
1033
def supports_executable():
853
1034
    return sys.platform != "win32"
854
1035
 
855
1036
 
 
1037
def supports_posix_readonly():
 
1038
    """Return True if 'readonly' has POSIX semantics, False otherwise.
 
1039
 
 
1040
    Notably, a win32 readonly file cannot be deleted, unlike POSIX where the
 
1041
    directory controls creation/deletion, etc.
 
1042
 
 
1043
    And under win32, readonly means that the directory itself cannot be
 
1044
    deleted.  The contents of a readonly directory can be changed, unlike POSIX
 
1045
    where files in readonly directories cannot be added, deleted or renamed.
 
1046
    """
 
1047
    return sys.platform != "win32"
 
1048
 
 
1049
 
 
1050
def set_or_unset_env(env_variable, value):
 
1051
    """Modify the environment, setting or removing the env_variable.
 
1052
 
 
1053
    :param env_variable: The environment variable in question
 
1054
    :param value: The value to set the environment to. If None, then
 
1055
        the variable will be removed.
 
1056
    :return: The original value of the environment variable.
 
1057
    """
 
1058
    orig_val = os.environ.get(env_variable)
 
1059
    if value is None:
 
1060
        if orig_val is not None:
 
1061
            del os.environ[env_variable]
 
1062
    else:
 
1063
        if isinstance(value, unicode):
 
1064
            value = value.encode(bzrlib.user_encoding)
 
1065
        os.environ[env_variable] = value
 
1066
    return orig_val
 
1067
 
 
1068
 
856
1069
_validWin32PathRE = re.compile(r'^([A-Za-z]:[/\\])?[^:<>*"?\|]*$')
857
1070
 
858
1071
 
864
1077
    if sys.platform != "win32":
865
1078
        return
866
1079
    if _validWin32PathRE.match(path) is None:
867
 
        raise IllegalPath(path)
 
1080
        raise errors.IllegalPath(path)
868
1081
 
869
1082
 
870
1083
def walkdirs(top, prefix=""):
875
1088
    to exclude some directories, they are then not descended into.
876
1089
    
877
1090
    The data yielded is of the form:
878
 
    [(relpath, basename, kind, lstat, path_from_top), ...]
 
1091
    ((directory-relpath, directory-path-from-top),
 
1092
    [(directory-relpath, basename, kind, lstat, path-from-top), ...]),
 
1093
     - directory-relpath is the relative path of the directory being returned
 
1094
       with respect to top. prefix is prepended to this.
 
1095
     - directory-path-from-root is the path including top for this directory. 
 
1096
       It is suitable for use with os functions.
 
1097
     - relpath is the relative path within the subtree being walked.
 
1098
     - basename is the basename of the path
 
1099
     - kind is the kind of the file now. If unknown then the file is not
 
1100
       present within the tree - but it may be recorded as versioned. See
 
1101
       versioned_kind.
 
1102
     - lstat is the stat data *if* the file was statted.
 
1103
     - planned, not implemented: 
 
1104
       path_from_tree_root is the path from the root of the tree.
879
1105
 
880
1106
    :param prefix: Prefix the relpaths that are yielded with 'prefix'. This 
881
1107
        allows one to walk a subtree but get paths that are relative to a tree
882
1108
        rooted higher up.
883
1109
    :return: an iterator over the dirs.
884
1110
    """
885
 
    lstat = os.lstat
886
 
    pending = []
 
1111
    #TODO there is a bit of a smell where the results of the directory-
 
1112
    # summary in this, and the path from the root, may not agree 
 
1113
    # depending on top and prefix - i.e. ./foo and foo as a pair leads to
 
1114
    # potentially confusing output. We should make this more robust - but
 
1115
    # not at a speed cost. RBC 20060731
 
1116
    _lstat = os.lstat
887
1117
    _directory = _directory_kind
888
 
    _listdir = listdir
889
 
    pending = [(prefix, "", _directory, None, top)]
 
1118
    _listdir = os.listdir
 
1119
    _kind_from_mode = _formats.get
 
1120
    pending = [(safe_unicode(prefix), "", _directory, None, safe_unicode(top))]
890
1121
    while pending:
891
 
        dirblock = []
892
 
        currentdir = pending.pop()
893
1122
        # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
894
 
        top = currentdir[4]
895
 
        if currentdir[0]:
896
 
            relroot = currentdir[0] + '/'
897
 
        else:
898
 
            relroot = ""
899
 
        for name in sorted(_listdir(top)):
900
 
            abspath = top + '/' + name
901
 
            statvalue = lstat(abspath)
902
 
            dirblock.append ((relroot + name, name, file_kind_from_stat_mode(statvalue.st_mode), statvalue, abspath))
903
 
        yield dirblock
904
 
        # push the user specified dirs from dirblock
905
 
        for dir in reversed(dirblock):
906
 
            if dir[2] == _directory:
907
 
                pending.append(dir)
 
1123
        relroot, _, _, _, top = pending.pop()
 
1124
        if relroot:
 
1125
            relprefix = relroot + u'/'
 
1126
        else:
 
1127
            relprefix = ''
 
1128
        top_slash = top + u'/'
 
1129
 
 
1130
        dirblock = []
 
1131
        append = dirblock.append
 
1132
        for name in sorted(_listdir(top)):
 
1133
            abspath = top_slash + name
 
1134
            statvalue = _lstat(abspath)
 
1135
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1136
            append((relprefix + name, name, kind, statvalue, abspath))
 
1137
        yield (relroot, top), dirblock
 
1138
 
 
1139
        # push the user specified dirs from dirblock
 
1140
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
 
1141
 
 
1142
 
 
1143
def _walkdirs_utf8(top, prefix=""):
 
1144
    """Yield data about all the directories in a tree.
 
1145
 
 
1146
    This yields the same information as walkdirs() only each entry is yielded
 
1147
    in utf-8. On platforms which have a filesystem encoding of utf8 the paths
 
1148
    are returned as exact byte-strings.
 
1149
 
 
1150
    :return: yields a tuple of (dir_info, [file_info])
 
1151
        dir_info is (utf8_relpath, path-from-top)
 
1152
        file_info is (utf8_relpath, utf8_name, kind, lstat, path-from-top)
 
1153
        if top is an absolute path, path-from-top is also an absolute path.
 
1154
        path-from-top might be unicode or utf8, but it is the correct path to
 
1155
        pass to os functions to affect the file in question. (such as os.lstat)
 
1156
    """
 
1157
    fs_encoding = _fs_enc.upper()
 
1158
    if (sys.platform == 'win32' or
 
1159
        fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968')): # ascii
 
1160
        return _walkdirs_unicode_to_utf8(top, prefix=prefix)
 
1161
    else:
 
1162
        return _walkdirs_fs_utf8(top, prefix=prefix)
 
1163
 
 
1164
 
 
1165
def _walkdirs_fs_utf8(top, prefix=""):
 
1166
    """See _walkdirs_utf8.
 
1167
 
 
1168
    This sub-function is called when we know the filesystem is already in utf8
 
1169
    encoding. So we don't need to transcode filenames.
 
1170
    """
 
1171
    _lstat = os.lstat
 
1172
    _directory = _directory_kind
 
1173
    _listdir = os.listdir
 
1174
    _kind_from_mode = _formats.get
 
1175
 
 
1176
    # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
 
1177
    # But we don't actually uses 1-3 in pending, so set them to None
 
1178
    pending = [(safe_utf8(prefix), None, None, None, safe_utf8(top))]
 
1179
    while pending:
 
1180
        relroot, _, _, _, top = pending.pop()
 
1181
        if relroot:
 
1182
            relprefix = relroot + '/'
 
1183
        else:
 
1184
            relprefix = ''
 
1185
        top_slash = top + '/'
 
1186
 
 
1187
        dirblock = []
 
1188
        append = dirblock.append
 
1189
        for name in sorted(_listdir(top)):
 
1190
            abspath = top_slash + name
 
1191
            statvalue = _lstat(abspath)
 
1192
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1193
            append((relprefix + name, name, kind, statvalue, abspath))
 
1194
        yield (relroot, top), dirblock
 
1195
 
 
1196
        # push the user specified dirs from dirblock
 
1197
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
 
1198
 
 
1199
 
 
1200
def _walkdirs_unicode_to_utf8(top, prefix=""):
 
1201
    """See _walkdirs_utf8
 
1202
 
 
1203
    Because Win32 has a Unicode api, all of the 'path-from-top' entries will be
 
1204
    Unicode paths.
 
1205
    This is currently the fallback code path when the filesystem encoding is
 
1206
    not UTF-8. It may be better to implement an alternative so that we can
 
1207
    safely handle paths that are not properly decodable in the current
 
1208
    encoding.
 
1209
    """
 
1210
    _utf8_encode = codecs.getencoder('utf8')
 
1211
    _lstat = os.lstat
 
1212
    _directory = _directory_kind
 
1213
    _listdir = os.listdir
 
1214
    _kind_from_mode = _formats.get
 
1215
 
 
1216
    pending = [(safe_utf8(prefix), None, None, None, safe_unicode(top))]
 
1217
    while pending:
 
1218
        relroot, _, _, _, top = pending.pop()
 
1219
        if relroot:
 
1220
            relprefix = relroot + '/'
 
1221
        else:
 
1222
            relprefix = ''
 
1223
        top_slash = top + u'/'
 
1224
 
 
1225
        dirblock = []
 
1226
        append = dirblock.append
 
1227
        for name in sorted(_listdir(top)):
 
1228
            name_utf8 = _utf8_encode(name)[0]
 
1229
            abspath = top_slash + name
 
1230
            statvalue = _lstat(abspath)
 
1231
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1232
            append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
 
1233
        yield (relroot, top), dirblock
 
1234
 
 
1235
        # push the user specified dirs from dirblock
 
1236
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
 
1237
 
 
1238
 
 
1239
def copy_tree(from_path, to_path, handlers={}):
 
1240
    """Copy all of the entries in from_path into to_path.
 
1241
 
 
1242
    :param from_path: The base directory to copy. 
 
1243
    :param to_path: The target directory. If it does not exist, it will
 
1244
        be created.
 
1245
    :param handlers: A dictionary of functions, which takes a source and
 
1246
        destinations for files, directories, etc.
 
1247
        It is keyed on the file kind, such as 'directory', 'symlink', or 'file'
 
1248
        'file', 'directory', and 'symlink' should always exist.
 
1249
        If they are missing, they will be replaced with 'os.mkdir()',
 
1250
        'os.readlink() + os.symlink()', and 'shutil.copy2()', respectively.
 
1251
    """
 
1252
    # Now, just copy the existing cached tree to the new location
 
1253
    # We use a cheap trick here.
 
1254
    # Absolute paths are prefixed with the first parameter
 
1255
    # relative paths are prefixed with the second.
 
1256
    # So we can get both the source and target returned
 
1257
    # without any extra work.
 
1258
 
 
1259
    def copy_dir(source, dest):
 
1260
        os.mkdir(dest)
 
1261
 
 
1262
    def copy_link(source, dest):
 
1263
        """Copy the contents of a symlink"""
 
1264
        link_to = os.readlink(source)
 
1265
        os.symlink(link_to, dest)
 
1266
 
 
1267
    real_handlers = {'file':shutil.copy2,
 
1268
                     'symlink':copy_link,
 
1269
                     'directory':copy_dir,
 
1270
                    }
 
1271
    real_handlers.update(handlers)
 
1272
 
 
1273
    if not os.path.exists(to_path):
 
1274
        real_handlers['directory'](from_path, to_path)
 
1275
 
 
1276
    for dir_info, entries in walkdirs(from_path, prefix=to_path):
 
1277
        for relpath, name, kind, st, abspath in entries:
 
1278
            real_handlers[kind](abspath, relpath)
908
1279
 
909
1280
 
910
1281
def path_prefix_key(path):
922
1293
    return cmp(key_a, key_b)
923
1294
 
924
1295
 
 
1296
_cached_user_encoding = None
 
1297
 
 
1298
 
 
1299
def get_user_encoding(use_cache=True):
 
1300
    """Find out what the preferred user encoding is.
 
1301
 
 
1302
    This is generally the encoding that is used for command line parameters
 
1303
    and file contents. This may be different from the terminal encoding
 
1304
    or the filesystem encoding.
 
1305
 
 
1306
    :param  use_cache:  Enable cache for detected encoding.
 
1307
                        (This parameter is turned on by default,
 
1308
                        and required only for selftesting)
 
1309
 
 
1310
    :return: A string defining the preferred user encoding
 
1311
    """
 
1312
    global _cached_user_encoding
 
1313
    if _cached_user_encoding is not None and use_cache:
 
1314
        return _cached_user_encoding
 
1315
 
 
1316
    if sys.platform == 'darwin':
 
1317
        # work around egregious python 2.4 bug
 
1318
        sys.platform = 'posix'
 
1319
        try:
 
1320
            import locale
 
1321
        finally:
 
1322
            sys.platform = 'darwin'
 
1323
    else:
 
1324
        import locale
 
1325
 
 
1326
    try:
 
1327
        user_encoding = locale.getpreferredencoding()
 
1328
    except locale.Error, e:
 
1329
        sys.stderr.write('bzr: warning: %s\n'
 
1330
                         '  Could not determine what text encoding to use.\n'
 
1331
                         '  This error usually means your Python interpreter\n'
 
1332
                         '  doesn\'t support the locale set by $LANG (%s)\n'
 
1333
                         "  Continuing with ascii encoding.\n"
 
1334
                         % (e, os.environ.get('LANG')))
 
1335
        user_encoding = 'ascii'
 
1336
 
 
1337
    # Windows returns 'cp0' to indicate there is no code page. So we'll just
 
1338
    # treat that as ASCII, and not support printing unicode characters to the
 
1339
    # console.
 
1340
    if user_encoding in (None, 'cp0'):
 
1341
        user_encoding = 'ascii'
 
1342
    else:
 
1343
        # check encoding
 
1344
        try:
 
1345
            codecs.lookup(user_encoding)
 
1346
        except LookupError:
 
1347
            sys.stderr.write('bzr: warning:'
 
1348
                             ' unknown encoding %s.'
 
1349
                             ' Continuing with ascii encoding.\n'
 
1350
                             % user_encoding
 
1351
                            )
 
1352
            user_encoding = 'ascii'
 
1353
 
 
1354
    if use_cache:
 
1355
        _cached_user_encoding = user_encoding
 
1356
 
 
1357
    return user_encoding
 
1358
 
 
1359
 
 
1360
def recv_all(socket, bytes):
 
1361
    """Receive an exact number of bytes.
 
1362
 
 
1363
    Regular Socket.recv() may return less than the requested number of bytes,
 
1364
    dependning on what's in the OS buffer.  MSG_WAITALL is not available
 
1365
    on all platforms, but this should work everywhere.  This will return
 
1366
    less than the requested amount if the remote end closes.
 
1367
 
 
1368
    This isn't optimized and is intended mostly for use in testing.
 
1369
    """
 
1370
    b = ''
 
1371
    while len(b) < bytes:
 
1372
        new = socket.recv(bytes - len(b))
 
1373
        if new == '':
 
1374
            break # eof
 
1375
        b += new
 
1376
    return b
 
1377
 
 
1378
def dereference_path(path):
 
1379
    """Determine the real path to a file.
 
1380
 
 
1381
    All parent elements are dereferenced.  But the file itself is not
 
1382
    dereferenced.
 
1383
    :param path: The original path.  May be absolute or relative.
 
1384
    :return: the real path *to* the file
 
1385
    """
 
1386
    parent, base = os.path.split(path)
 
1387
    # The pathjoin for '.' is a workaround for Python bug #1213894.
 
1388
    # (initial path components aren't dereferenced)
 
1389
    return pathjoin(realpath(pathjoin('.', parent)), base)